Codebase list rabbitmq-server / 7bdc82c
New upstream version 3.6.10 Thomas Goirand 6 years ago
862 changed file(s) with 55553 addition(s) and 51432 deletion(s). Raw diff Collapse all Expand all
00 PROJECT = rabbitmq_server_release
1 VERSION ?= 0.0.0
1 PROJECT_DESCRIPTION = RabbitMQ Server
2
3 # Propagate PROJECT_VERSION (from the command line or environment) to
4 # other components. If PROJECT_VERSION is unset, then an empty variable
5 # is propagated and the default version will fallback to the default
6 # value from rabbitmq-components.mk.
7 export RABBITMQ_VERSION = $(PROJECT_VERSION)
28
39 # Release artifacts are put in $(PACKAGES_DIR).
410 PACKAGES_DIR ?= $(abspath PACKAGES)
511
12 # List of plugins to include in a RabbitMQ release.
13 include plugins.mk
14
615 DEPS = rabbit_common rabbit $(PLUGINS)
716
8 # List of plugins to include in a RabbitMQ release.
9 PLUGINS := rabbitmq_amqp1_0 \
10 rabbitmq_auth_backend_ldap \
11 rabbitmq_auth_mechanism_ssl \
12 rabbitmq_consistent_hash_exchange \
13 rabbitmq_event_exchange \
14 rabbitmq_federation \
15 rabbitmq_federation_management \
16 rabbitmq_jms_topic_exchange \
17 rabbitmq_management \
18 rabbitmq_management_agent \
19 rabbitmq_management_visualiser \
20 rabbitmq_mqtt \
21 rabbitmq_recent_history_exchange \
22 rabbitmq_sharding \
23 rabbitmq_shovel \
24 rabbitmq_shovel_management \
25 rabbitmq_stomp \
26 rabbitmq_top \
27 rabbitmq_tracing \
28 rabbitmq_trust_store \
29 rabbitmq_web_dispatch \
30 rabbitmq_web_stomp \
31 rabbitmq_web_stomp_examples
32
33 DEP_PLUGINS = rabbit_common/mk/rabbitmq-run.mk \
34 rabbit_common/mk/rabbitmq-dist.mk \
17 DEP_PLUGINS = rabbit_common/mk/rabbitmq-dist.mk \
18 rabbit_common/mk/rabbitmq-run.mk \
3519 rabbit_common/mk/rabbitmq-tools.mk
3620
3721 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
5135
5236 SOURCE_DIST_BASE ?= rabbitmq-server
5337 SOURCE_DIST_SUFFIXES ?= tar.xz zip
54 SOURCE_DIST ?= $(PACKAGES_DIR)/$(SOURCE_DIST_BASE)-$(VERSION)
38 SOURCE_DIST ?= $(PACKAGES_DIR)/$(SOURCE_DIST_BASE)-$(PROJECT_VERSION)
5539
5640 # The first source distribution file is used by packages: if the archive
5741 # type changes, you must update all packages' Makefile.
7761 --exclude '.travis.yml' \
7862 --exclude '.*.plt' \
7963 --exclude '$(notdir $(ERLANG_MK_TMP))' \
80 --exclude 'ebin' \
81 --exclude 'packaging' \
82 --exclude 'erl_crash.dump' \
83 --exclude 'MnesiaCore.*' \
64 --exclude '_build/' \
8465 --exclude 'cover/' \
8566 --exclude 'deps/' \
8667 --exclude 'ebin/' \
68 --exclude 'erl_crash.dump' \
69 --exclude 'MnesiaCore.*' \
8770 --exclude '$(notdir $(DEPS_DIR))/' \
71 --exclude 'hexer*' \
8872 --exclude 'logs/' \
73 --exclude 'packaging' \
8974 --exclude 'plugins/' \
9075 --exclude '$(notdir $(DIST_DIR))/' \
9176 --exclude 'test' \
9277 --exclude 'xrefr' \
9378 --exclude '/$(notdir $(PACKAGES_DIR))/' \
9479 --exclude '/PACKAGES/' \
80 --exclude '/upgrade/' \
81 --exclude '/amqp_client/doc/' \
82 --exclude '/amqp_client/rebar.config' \
9583 --exclude '/cowboy/doc/' \
9684 --exclude '/cowboy/examples/' \
85 --exclude '/rabbit/escript/' \
9786 --exclude '/rabbitmq_amqp1_0/test/swiftmq/build/'\
9887 --exclude '/rabbitmq_amqp1_0/test/swiftmq/swiftmq*'\
88 --exclude '/rabbitmq_cli/escript/' \
9989 --exclude '/rabbitmq_mqtt/test/build/' \
10090 --exclude '/rabbitmq_mqtt/test/test_client/'\
91 --exclude '/ranch/doc/' \
92 --exclude '/ranch/examples/' \
93 --exclude '/sockjs/examples/' \
10194 --delete \
10295 --delete-excluded
10396
123116 $(SOURCE_DIST): $(ERLANG_MK_RECURSIVE_DEPS_LIST)
124117 $(verbose) mkdir -p $(dir $@)
125118 $(gen_verbose) $(RSYNC) $(RSYNC_FLAGS) ./ $@/
126 $(verbose) echo "$(PROJECT) $$(git rev-parse HEAD) $$(git describe --tags --exact-match 2>/dev/null || git symbolic-ref -q --short HEAD)" > $@/git-revisions.txt
119 $(verbose) echo "$(PROJECT_DESCRIPTION) $(PROJECT_VERSION)" > $@/git-revisions.txt
120 $(verbose) echo "$(PROJECT) $$(git rev-parse HEAD) $$(git describe --tags --exact-match 2>/dev/null || git symbolic-ref -q --short HEAD)" >> $@/git-revisions.txt
127121 $(verbose) cat packaging/common/LICENSE.head > $@/LICENSE
128122 $(verbose) mkdir -p $@/deps/licensing
129123 $(verbose) for dep in $$(cat $(ERLANG_MK_RECURSIVE_DEPS_LIST) | LC_COLLATE=C sort); do \
148142 $(verbose) cat packaging/common/LICENSE.tail >> $@/LICENSE
149143 $(verbose) find $@/deps/licensing -name 'LICENSE-*' -exec cp '{}' $@ \;
150144 $(verbose) for file in $$(find $@ -name '*.app.src'); do \
151 sed -E -i.bak -e 's/[{]vsn[[:blank:]]*,[[:blank:]]*(""|"0.0.0")[[:blank:]]*}/{vsn, "$(VERSION)"}/' $$file; \
145 sed -E -i.bak \
146 -e 's/[{]vsn[[:blank:]]*,[[:blank:]]*(""|"0.0.0")[[:blank:]]*}/{vsn, "$(PROJECT_VERSION)"}/' \
147 -e 's/[{]broker_version_requirements[[:blank:]]*,[[:blank:]]*\[\][[:blank:]]*}/{broker_version_requirements, ["$(PROJECT_VERSION)"]}/' \
148 $$file; \
152149 rm $$file.bak; \
153150 done
151 $(verbose) echo "PLUGINS := $(PLUGINS)" > $@/plugins.mk
152
153 $(SOURCE_DIST).manifest: $(SOURCE_DIST)
154 $(gen_verbose) cd $(dir $(SOURCE_DIST)) && \
155 find $(notdir $(SOURCE_DIST)) | LC_COLLATE=C sort > $@
154156
155157 # TODO: Fix file timestamps to have reproducible source archives.
156158 # $(verbose) find $@ -not -name 'git-revisions.txt' -print0 | xargs -0 touch -r $@/git-revisions.txt
157159
158 $(SOURCE_DIST).tar.gz: $(SOURCE_DIST)
159 $(gen_verbose) cd $(dir $(SOURCE_DIST)) && \
160 find $(notdir $(SOURCE_DIST)) -print0 | LC_COLLATE=C sort -z | \
161 xargs -0 $(TAR) $(TAR_V) --no-recursion -cf - | \
160 $(SOURCE_DIST).tar.gz: $(SOURCE_DIST).manifest
161 $(gen_verbose) cd $(dir $(SOURCE_DIST)) && \
162 $(TAR) $(TAR_V) -T $(SOURCE_DIST).manifest --no-recursion -cf - | \
162163 $(GZIP) --best > $@
163164
164 $(SOURCE_DIST).tar.bz2: $(SOURCE_DIST)
165 $(gen_verbose) cd $(dir $(SOURCE_DIST)) && \
166 find $(notdir $(SOURCE_DIST)) -print0 | LC_COLLATE=C sort -z | \
167 xargs -0 $(TAR) $(TAR_V) --no-recursion -cf - | \
165 $(SOURCE_DIST).tar.bz2: $(SOURCE_DIST).manifest
166 $(gen_verbose) cd $(dir $(SOURCE_DIST)) && \
167 $(TAR) $(TAR_V) -T $(SOURCE_DIST).manifest --no-recursion -cf - | \
168168 $(BZIP2) > $@
169169
170 $(SOURCE_DIST).tar.xz: $(SOURCE_DIST)
171 $(gen_verbose) cd $(dir $(SOURCE_DIST)) && \
172 find $(notdir $(SOURCE_DIST)) -print0 | LC_COLLATE=C sort -z | \
173 xargs -0 $(TAR) $(TAR_V) --no-recursion -cf - | \
170 $(SOURCE_DIST).tar.xz: $(SOURCE_DIST).manifest
171 $(gen_verbose) cd $(dir $(SOURCE_DIST)) && \
172 $(TAR) $(TAR_V) -T $(SOURCE_DIST).manifest --no-recursion -cf - | \
174173 $(XZ) > $@
175174
176 $(SOURCE_DIST).zip: $(SOURCE_DIST)
175 $(SOURCE_DIST).zip: $(SOURCE_DIST).manifest
177176 $(verbose) rm -f $@
178177 $(gen_verbose) cd $(dir $(SOURCE_DIST)) && \
179 find $(notdir $(SOURCE_DIST)) -print0 | LC_COLLATE=C sort -z | \
180 xargs -0 $(ZIP) $(ZIP_V) $@
181
182 clean:: clean-source-dist clean-upgrade
183
184 clean-upgrade:
185 cd upgrade && make clean
178 $(ZIP) $(ZIP_V) --names-stdin $@ < $(SOURCE_DIST).manifest
179
180 clean:: clean-source-dist
186181
187182 clean-source-dist:
188183 $(gen_verbose) rm -rf -- $(SOURCE_DIST_BASE)-*
189184
190 distclean:: distclean-packages distclean-upgrade
191
192 distclean-upgrade:
193 cd upgrade && make distclean
185 distclean:: distclean-packages
194186
195187 distclean-packages:
196188 $(gen_verbose) rm -rf -- $(PACKAGES_DIR)
209201 .PHONY: packages package-deb \
210202 package-rpm package-rpm-fedora package-rpm-suse \
211203 package-windows package-standalone-macosx \
204 package-standalone-linux-x86_64 \
205 package-standalone-freebsd-x86_64 \
212206 package-generic-unix
213207
214208 # This variable is exported so sub-make instances know where to find the
217211
218212 packages package-deb package-rpm package-rpm-fedora \
219213 package-rpm-suse package-windows package-standalone-macosx \
214 package-standalone-linux-x86_64 \
215 package-standalone-freebsd-x86_64 \
220216 package-generic-unix: $(PACKAGES_SOURCE_DIST_FILE)
221217 $(verbose) $(MAKE) -C packaging $@ \
222218 SOURCE_DIST_FILE=$(abspath $(PACKAGES_SOURCE_DIST_FILE))
237233 DESTDIR ?=
238234
239235 PREFIX ?= /usr/local
240 WINDOWS_PREFIX ?= rabbitmq-server-windows-$(VERSION)
236 WINDOWS_PREFIX ?= rabbitmq-server-windows-$(PROJECT_VERSION)
241237
242238 MANDIR ?= $(PREFIX)/share/man
243239 RMQ_ROOTDIR ?= $(PREFIX)/lib/erlang
244240 RMQ_BINDIR ?= $(RMQ_ROOTDIR)/bin
245241 RMQ_LIBDIR ?= $(RMQ_ROOTDIR)/lib
246 RMQ_ERLAPP_DIR ?= $(RMQ_LIBDIR)/rabbitmq_server-$(VERSION)
242 RMQ_ERLAPP_DIR ?= $(RMQ_LIBDIR)/rabbitmq_server-$(PROJECT_VERSION)
247243
248244 SCRIPTS = rabbitmq-defaults \
249245 rabbitmq-env \
370366 *) mv "$$file" "$$file.txt" ;; \
371367 esac; \
372368 done
373
374 test-upgrade:
375 $(MAKE) -C upgrade
0 # RabbitMQ server releases
1
2 This repository provides scripts and Makefiles we use to create RabbitMQ
3 server releases. It is organized in the following way:
4 * The top-level `Makefile` manages the source archive.
5 * There is a subdirectory inside `packaging` for each type of package we
6 support.
7
8 ## TL;DR
9
10 * To create a source archive and all supported packages:
11
12 ```
13 make packages
14 ```
15
16 * To create a source archive and all supported packages, with a given version:
17
18 ```
19 make packages PROJECT_VERSION=3.8.1-rc.1
20 ```
21
22 * To create all suported packages from an existing source archive:
23
24 ```
25 make -C packaging SOURCE_DIST_FILE=/path/to/rabbitmq-server-3.8.1-rc.1.tar.xz
26 ```
27
28 The standalone package is different because it embeds the build
29 platform's Erlang copy. Thus on Linux for instance, only the
30 `linux-x86_64` standalone package will be built. To build the OS X
31 standalone package, you need to run the following command on an OS X
32 build host:
33
34 ```
35 make package-standalone-macosx
36 # or
37 make -C packaging package-standalone-macosx SOURCE_DIST_FILE=/path/to/rabbitmq-server-3.8.1-rc.1.tar.xz
38 ```
39
40 ## Source archive
41
42 ### How to create it
43
44 The source archive is created with the following command:
45 ```
46 make source-dist
47 ```
48
49 It uses Erlang.mk's `PROJECT_VERSION` variable to set the version of the
50 source archive. If the variable is unset, Erlang.mk computes a value
51 based on the last tag and the current HEAD.
52
53 Here is an example with an explicit version:
54 ```
55 make source-dist PROJECT_VERSION=3.8.1-rc.1
56 ```
57
58 The version is automatically propagated to the broker and plugins so
59 they all advertise the same version.
60
61 The result is then available in the `PACKAGES` subdirectory. You can
62 override the output directory with the `PACKAGES_DIR` variable:
63 ```
64 make source-dist PROJDCT_VERSION=3.8.1-rc.1 \
65 PACKAGES_DIR=/tmp
66 ```
67
68 By default, two archives are produced:
69 * a `tar.xz` file;
70 * a `zip` file.
71
72 You can ask for more/different types by specifying the
73 `SOURCE_DIST_SUFFIXES` variable:
74 ```
75 make source-dist PROJECT_VERSION=3.8.1-rc.1 \
76 SOURCE_DIST_SUFFIXES='tar.xz tar.gz'
77 ```
78
79 Supported archive types are:
80 * `tar.bz2`;
81 * `tar.gz`;
82 * `tar.xz`;
83 * `zip`.
84
85 ### What is included
86
87 The source archive includes the broker and a set of plugins. The default
88 list of plugins is in the `plugins.mk` file.
89
90 You can override this list by setting the `PLUGINS` variable to the list
91 you want:
92 ```
93 make source-dist PROJECT_VERSION=3.8.1-rc.1 \
94 PLUGINS='rabbitmq_shovel rabbitmq_rabbitmq_shovel_management'
95 ```
96
97 Dependencies are automatically included.
98
99 ## Packages
100
101 Packages can be built with an existing source archive or create the
102 source archive automatically.
103
104 If you want to use an existing archive, use `packaging/Makefile`:
105 ```
106 make -C packaging package-$type \
107 SOURCE_DIST_FILE=/path/to/rabbitmq-server-$version.tar.xz \
108 ...
109 ```
110
111 This has the following rules:
112 * The archive must be a `tar.xz` file.
113 * It can automatically take the only archive available under `PACKAGES`.
114 However, if there is none or multiple archive, you must specify the
115 `SOURCE_DIST_FILE` variable.
116
117 If you want the source archive to be created automatically, use the
118 top-level `Makefile`:
119 ```
120 make package-$type PROJECT_VERSION=3.8.1-rc.1 ...
121 ```
122
123 Packages are written to `PACKAGES_DIR`, like the source archive.
124
125 Each package type is further described separately because most of them
126 have versioning specificities.
127
128 ### `generic-unix` package
129
130 To create it:
131 ```
132 make package-generic-unix
133 ```
134
135 There is no package revision, only the project version and no
136 restriction on it.
137
138 `packaging/generic-unix/Makefile` tries to determine the version based
139 on the source archive filename. If it fails, you can specify the version
140 with the `VERSION` variable:
141 ```
142 make -C packaging package-generic-unix \
143 SOURCE_DIST_FILE=rabbitmq-server.tar.xz \
144 VERSION=3.8.1-rc.1
145 ```
146
147 ### Debian package
148
149 To create it:
150 ```
151 make package-deb
152 ```
153
154 The package may have a different versioning than the project and may
155 include an additional package revision. In particular, the package
156 version can't have any `-` characters.
157
158 `packaging/debs/Debian/Makefile` tries to determine the version based
159 on the source archive filename. If it fails, you can specify the version
160 with the `VERSION` variable:
161 ```
162 make -C packaging package-deb \
163 SOURCE_DIST_FILE=rabbitmq-server.tar.xz \
164 VERSION=3.8.1-rc.1
165 ```
166
167 By default, the package version is converted from `VERSION` with
168 all `-` characters replaced by `~` (eg. `3.8.1~rc.1` in the example
169 above). If you want to override that conversion, you can specify the
170 `DEBIAN_VERSION` variable:
171 ```
172 make -C packaging package-deb \
173 SOURCE_DIST_FILE=rabbitmq-server.tar.xz \
174 VERSION=3.8.1-rc.1
175 DEBIAN_VERSION=3.8.1~rc.1
176 ```
177
178 ### RPM package
179
180 We support RedHat and OpenSUSE RPM packages and both are created by default:
181
182 To create them:
183 ```
184 make package-rpm
185 ```
186
187 You can create a single one with:
188 ```
189 make package-rpm-fedora
190 make package-rpm-suse
191 ```
192
193 RPM packages have the same restrictions as Debian packages and use the
194 same default version conversion. To override the converted version, use
195 the `RPM_VERSION` variable. See the "Debian package" section above for
196 more details.
197
198 `packaging/RPMS/Fedora/Makefile`, which handles both RedHar and OpenSUSE
199 flavors, accepts the `RPM_OS` variable to set the flavor. It can be:
200 * `fedora`;
201 * `suse`.
202
203 ### Windows package
204
205 We create two artefacts:
206 * a Zip archive, resembling the `generic-unix` package;
207 * an installer.
208
209 To create them:
210 ```
211 make package-windows
212 ```
213
214 To create them separately:
215 ```
216 make -C packaging/windows # the Zip archive
217 make -C packaging/windows-exe # the installer
218 ```
219
220 The Zip archive has no package revision, only the project version and no
221 restriction on it. It supports the same `VERSION` as the `generic-unix`
222 package.
223
224 The installer requires a *product version* which must be 4 integers
225 separated by `.` characters. Furthermore, unlike other packages, this
226 one requires the Zip archive as its input, not the source archive.
227
228 So you need to built the Zip archive first, then the installer. You can
229 specify the path to the Zip archive using the `ZIP` variable:
230 ```
231 make -C packaging/windows-exe ZIP=/path/to/rabbitmq-server-windows.zip
232 ```
233
234 By default, the *product version* is the project version where
235 everything following the third integer was replaced by `.0`. Thus it's
236 only fine if the version is a semver-based version (eg. 3.8.1-pre.3 or
237 3.8.2). If the version doesn't conform to that, you need to set the
238 `PRODUCT_VERSION` variable:
239 ```
240 make package-windows PROJECT_VERSION=3.8.1-rc.1 PRODUCT_VERSION=3.8.1.0
241 ```
242
243 ### Standalone package
244
245 This is the equivalent of the `generic-unix` package with Erlang
246 embbeded.
247
248 To create it:
249 ```
250 make -C packaging/standalone SOURCE_DIST_FILE=... VERSION=...
251 ```
252
253 There is no package revision, only the project version and no
254 restriction on it.
255
256 Unlike other packages, the top-level `Makefile` and `packaging/Makefile`
257 provide targets to build the standalone package for specific platforms:
258 ```
259 make package-standalone-macosx
260 make package-standalone-linux-x86_64
261 make package-standalone-freebsd-x86_64
262 ```
263
264 Cross-build isn't supported so using those targets on incompatible
265 platforms is a no-op.
266
267 If you want to build a standalone package for your platform, you can use
268 `packaging/standalone/Makefile` as described at the beginning of this
269 section.
270
271 ### Building all packages in one go
272
273 If you want to build all packages in one command, you can use the
274 following helpers:
275 ```
276 # Automatically creates the source archive.
277 make packages
278
279 # Use an existing archive.
280 make -C packaging package SOURCE_DIST_FILE=...
281 ```
282
283 However, be careful with the versioning! Because all package have
284 incompatible requirements, you can only use a version with 3 integers
285 (like a final semver-based version):
286 ```
287 make packages PROJECT_VERSION=3.8.1
288 make -C packaging packages SOURCE_DIST_FILE=rabbitmq-server-3.8.1.tar.xz
289 ```
290
291 If you do not follow that rule, the build will fail one way or another;
292 probably in the Windows package because of the *product version*
293 restrictions.
294
295 Another possibility is to specify the Windows *product version* and
296 rely on automatic conversion for Debian and RPM packages (or use the
297 `DEBIAN_VERSION` and `RPM_VERSION` variables), but this is untested:
298 ```
299 make packages PROJECT_VERSION=3.8.1-rc.1 PRODUCT_VERSION=3.8.1.0
300 ```
00 PROJECT = amqp_client
1 VERSION ?= $(call get_app_version,src/$(PROJECT).app.src)
2 ifeq ($(VERSION),)
3 VERSION = 0.0.0
4 endif
1 PROJECT_DESCRIPTION = RabbitMQ AMQP Client
2 PROJECT_MOD = amqp_client
3 PROJECT_REGISTERED = amqp_sup
4
5 define PROJECT_ENV
6 [
7 {prefer_ipv6, false},
8 {ssl_options, []}
9 ]
10 endef
11
12 define PROJECT_APP_EXTRA_KEYS
13 %% Hex.pm package informations.
14 {maintainers, [
15 "RabbitMQ Team <info@rabbitmq.com>",
16 "Jean-Sebastien Pedron <jean-sebastien@rabbitmq.com>"
17 ]},
18 {licenses, ["MPL 1.1"]},
19 {links, [
20 {"Website", "http://www.rabbitmq.com/"},
21 {"GitHub", "https://github.com/rabbitmq/rabbitmq-erlang-client"},
22 {"User guide", "http://www.rabbitmq.com/erlang-client-user-guide.html"}
23 ]},
24 {build_tools, ["make", "rebar3"]},
25 {files, [
26 $(RABBITMQ_HEXPM_DEFAULT_FILES)
27 ]}
28 endef
529
630 # Release artifacts are put in $(PACKAGES_DIR).
731 PACKAGES_DIR ?= $(abspath PACKAGES)
832
33 LOCAL_DEPS = xmerl
934 DEPS = rabbit_common
1035 TEST_DEPS = rabbitmq_ct_helpers rabbit
1136
37 DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-test.mk
1238 DEP_PLUGINS = rabbit_common/mk/rabbitmq-build.mk \
39 rabbit_common/mk/rabbitmq-hexpm.mk \
1340 rabbit_common/mk/rabbitmq-dist.mk \
1441 rabbit_common/mk/rabbitmq-run.mk \
42 rabbit_common/mk/rabbitmq-test.mk \
1543 rabbit_common/mk/rabbitmq-tools.mk
1644
1745 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
3664
3765 doc/overview.edoc: src/overview.edoc.in
3866 mkdir -p doc
39 sed -e 's:%%VERSION%%:$(VERSION):g' < $< > $@
67 sed -e 's:%%VERSION%%:$(PROJECT_VERSION):g' < $< > $@
4068
4169 .PHONY: source-dist clean-source-dist
4270
4371 SOURCE_DIST_BASE ?= $(PROJECT)
4472 SOURCE_DIST_SUFFIXES ?= tar.xz zip
45 SOURCE_DIST ?= $(PACKAGES_DIR)/$(SOURCE_DIST_BASE)-$(VERSION)-src
73 SOURCE_DIST ?= $(PACKAGES_DIR)/$(SOURCE_DIST_BASE)-$(PROJECT_VERSION)-src
4674
4775 # The first source distribution file is used by packages: if the archive
4876 # type changes, you must update all packages' Makefile.
6189 RSYNC_FLAGS += -a $(RSYNC_V) \
6290 --exclude '.sw?' --exclude '.*.sw?' \
6391 --exclude '*.beam' \
92 --exclude '*.d' \
6493 --exclude '*.pyc' \
6594 --exclude '.git*' \
6695 --exclude '.hg*' \
6796 --exclude '.travis.yml' \
97 --exclude '.*.plt' \
6898 --exclude '$(notdir $(ERLANG_MK_TMP))' \
69 --exclude 'ebin' \
99 --exclude 'cover/' \
100 --exclude 'deps/' \
101 --exclude 'ebin/' \
70102 --exclude 'erl_crash.dump' \
71 --exclude 'deps/' \
72103 --exclude '$(notdir $(DEPS_DIR))/' \
73104 --exclude 'doc/' \
105 --exclude 'hexer*' \
106 --exclude 'logs/' \
74107 --exclude 'plugins/' \
75108 --exclude '$(notdir $(DIST_DIR))/' \
109 --exclude 'test' \
110 --exclude 'xrefr' \
76111 --exclude '/$(notdir $(PACKAGES_DIR))/' \
112 --exclude '/PACKAGES/' \
77113 --delete \
78114 --delete-excluded
79115
98134 $(SOURCE_DIST): $(ERLANG_MK_RECURSIVE_DEPS_LIST)
99135 $(verbose) mkdir -p $(dir $@)
100136 $(gen_verbose) $(RSYNC) $(RSYNC_FLAGS) ./ $@/
101 $(verbose) sed -E -i.bak \
102 -e 's/[{]vsn[[:blank:]]*,[^}]+}/{vsn, "$(VERSION)"}/' \
103 $@/src/$(PROJECT).app.src && \
104 rm $@/src/$(PROJECT).app.src.bak
137 $(verbose) echo "$(PROJECT_DESCRIPTION) $(PROJECT_VERSION)" > $@/git-revisions.txt
138 $(verbose) echo "$(PROJECT) $$(git rev-parse HEAD) $$(git describe --tags --exact-match 2>/dev/null || git symbolic-ref -q --short HEAD)" >> $@/git-revisions.txt
105139 $(verbose) for dep in $$(cat $(ERLANG_MK_RECURSIVE_DEPS_LIST) | grep -v '/$(PROJECT)$$' | LC_COLLATE=C sort); do \
106140 $(RSYNC) $(RSYNC_FLAGS) \
107141 $$dep \
114148 sed -E -i.bak "s|^[[:blank:]]*include[[:blank:]]+\.\./.*erlang.mk$$|include ../../erlang.mk|" \
115149 $@/deps/$$(basename $$dep)/Makefile && \
116150 rm $@/deps/$$(basename $$dep)/Makefile.bak; \
151 (cd $$dep; echo "$$(basename "$$dep") $$(git rev-parse HEAD) $$(git describe --tags --exact-match 2>/dev/null || git symbolic-ref -q --short HEAD)") >> $@/git-revisions.txt; \
117152 done
118153 $(verbose) for file in $$(find $@ -name '*.app.src'); do \
119 sed -E -i.bak -e 's/[{]vsn[[:blank:]]*,[[:blank:]]*""[[:blank:]]*}/{vsn, "$(VERSION)"}/' $$file; \
154 sed -E -i.bak \
155 -e 's/[{]vsn[[:blank:]]*,[[:blank:]]*(""|"0.0.0")[[:blank:]]*}/{vsn, "$(PROJECT_VERSION)"}/' \
156 $$file; \
120157 rm $$file.bak; \
121 done
122 $(verbose) echo "$(PROJECT) $$(git rev-parse HEAD) $$(git describe --tags --exact-match 2>/dev/null || git symbolic-ref -q --short HEAD)" > $@/git-revisions.txt
123 $(verbose) for dep in $$(cat $(ERLANG_MK_RECURSIVE_DEPS_LIST)); do \
124 (cd $$dep; echo "$$(basename "$$dep") $$(git rev-parse HEAD) $$(git describe --tags --exact-match 2>/dev/null || git symbolic-ref -q --short HEAD)") >> $@/git-revisions.txt; \
125158 done
126159 $(verbose) rm $@/README.in
127160 $(verbose) cp README.in $@/README
128 $(verbose) cat "$(BUILD_DOC)" >> $@/README
161 $(verbose) if test "$(BUILD_DOC)"; then cat "$(BUILD_DOC)" >> $@/README; fi
129162
130163 # TODO: Fix file timestamps to have reproducible source archives.
131164 # $(verbose) find $@ -not -name 'git-revisions.txt' -print0 | xargs -0 touch -r $@/git-revisions.txt
0 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
11 #
22 # Permission to use, copy, modify, and/or distribute this software for any
33 # purpose with or without fee is hereby granted, provided that the above
1111 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1212 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1313
14 .PHONY: all app apps deps search rel docs install-docs check tests clean distclean help erlang-mk
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
1515
1616 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17
18 ERLANG_MK_VERSION = 2.0.0-pre.2-144-g647ffd1
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
1931
2032 # Core configuration.
2133
2436
2537 PROJECT_VERSION ?= rolling
2638 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
2740
2841 # Verbosity.
2942
8497 rel::
8598 $(verbose) :
8699
100 relup:: deps app
101
87102 check:: tests
88103
89104 clean:: clean-crashdump
101116 help::
102117 $(verbose) printf "%s\n" \
103118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
104 "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
105120 "" \
106121 "Usage: [V=1] $(MAKE) [target]..." \
107122 "" \
149164 core_native_path = $1
150165 endif
151166
152 ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
153 define core_http_get
154 wget --no-check-certificate -O $(1) $(2)|| rm $(1)
155 endef
156 else
157 define core_http_get.erl
158 ssl:start(),
159 inets:start(),
160 case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
161 {ok, {{_, 200, _}, _, Body}} ->
162 case file:write_file("$(1)", Body) of
163 ok -> ok;
164 {error, R1} -> halt(R1)
165 end;
166 {error, R2} ->
167 halt(R2)
168 end,
169 halt(0).
170 endef
171
172 define core_http_get
173 $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
174 endef
175 endif
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
176168
177169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
178170
192184 ERLANG_MK_BUILD_CONFIG ?= build.config
193185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
194186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
195188 erlang-mk:
196189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
197190 ifdef ERLANG_MK_COMMIT
198191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
199192 endif
200193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
201 $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
202195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
203196 rm -rf $(ERLANG_MK_BUILD_DIR)
204197
285278 pkg_apns_repo = https://github.com/inaka/apns4erl
286279 pkg_apns_commit = master
287280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
288289 PACKAGES += azdht
289290 pkg_azdht_name = azdht
290291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
405406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
406407 pkg_bootstrap_commit = master
407408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
408417 PACKAGES += boss_db
409418 pkg_boss_db_name = boss_db
410419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
413422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
414423 pkg_boss_db_commit = master
415424
416 PACKAGES += boss
417 pkg_boss_name = boss
418 pkg_boss_description = Erlang web MVC, now featuring Comet
419 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
420 pkg_boss_fetch = git
421 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
422 pkg_boss_commit = master
423
424425 PACKAGES += brod
425426 pkg_brod_name = brod
426427 pkg_brod_description = Kafka client in Erlang
565566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
566567 pkg_cloudi_service_api_requests_commit = master
567568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
568585 PACKAGES += cloudi_service_db_cassandra_cql
569586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
570587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
573590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
574591 pkg_cloudi_service_db_cassandra_cql_commit = master
575592
576 PACKAGES += cloudi_service_db_cassandra
577 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
579 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
580 pkg_cloudi_service_db_cassandra_fetch = git
581 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
582 pkg_cloudi_service_db_cassandra_commit = master
583
584593 PACKAGES += cloudi_service_db_couchdb
585594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
586595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
637646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
638647 pkg_cloudi_service_db_tokyotyrant_commit = master
639648
640 PACKAGES += cloudi_service_db
641 pkg_cloudi_service_db_name = cloudi_service_db
642 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
643 pkg_cloudi_service_db_homepage = http://cloudi.org/
644 pkg_cloudi_service_db_fetch = git
645 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
646 pkg_cloudi_service_db_commit = master
647
648649 PACKAGES += cloudi_service_filesystem
649650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
650651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
10371038 pkg_edown_repo = https://github.com/uwiger/edown
10381039 pkg_edown_commit = master
10391040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
10401049 PACKAGES += eep_app
10411050 pkg_eep_app_name = eep_app
10421051 pkg_eep_app_description = Embedded Event Processing
10451054 pkg_eep_app_repo = https://github.com/darach/eep-erl
10461055 pkg_eep_app_commit = master
10471056
1048 PACKAGES += eep
1049 pkg_eep_name = eep
1050 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1051 pkg_eep_homepage = https://github.com/virtan/eep
1052 pkg_eep_fetch = git
1053 pkg_eep_repo = https://github.com/virtan/eep
1054 pkg_eep_commit = master
1055
10561057 PACKAGES += efene
10571058 pkg_efene_name = efene
10581059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
12371238 pkg_eqm_repo = https://github.com/loucash/eqm
12381239 pkg_eqm_commit = master
12391240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
12401249 PACKAGES += eredis_pool
12411250 pkg_eredis_pool_name = eredis_pool
12421251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
12451254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
12461255 pkg_eredis_pool_commit = master
12471256
1248 PACKAGES += eredis
1249 pkg_eredis_name = eredis
1250 pkg_eredis_description = Erlang Redis client
1251 pkg_eredis_homepage = https://github.com/wooga/eredis
1252 pkg_eredis_fetch = git
1253 pkg_eredis_repo = https://github.com/wooga/eredis
1254 pkg_eredis_commit = master
1255
12561257 PACKAGES += erl_streams
12571258 pkg_erl_streams_name = erl_streams
12581259 pkg_erl_streams_description = Streams in Erlang
15411542 pkg_etap_repo = https://github.com/ngerakines/etap
15421543 pkg_etap_commit = master
15431544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
15441553 PACKAGES += etest_http
15451554 pkg_etest_http_name = etest_http
15461555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
15491558 pkg_etest_http_repo = https://github.com/wooga/etest_http
15501559 pkg_etest_http_commit = master
15511560
1552 PACKAGES += etest
1553 pkg_etest_name = etest
1554 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1555 pkg_etest_homepage = https://github.com/wooga/etest
1556 pkg_etest_fetch = git
1557 pkg_etest_repo = https://github.com/wooga/etest
1558 pkg_etest_commit = master
1559
15601561 PACKAGES += etoml
15611562 pkg_etoml_name = etoml
15621563 pkg_etoml_description = TOML language erlang parser
15651566 pkg_etoml_repo = https://github.com/kalta/etoml
15661567 pkg_etoml_commit = master
15671568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
15681577 PACKAGES += eunit_formatters
15691578 pkg_eunit_formatters_name = eunit_formatters
15701579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
15731582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
15741583 pkg_eunit_formatters_commit = master
15751584
1576 PACKAGES += eunit
1577 pkg_eunit_name = eunit
1578 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1579 pkg_eunit_homepage = https://github.com/richcarl/eunit
1580 pkg_eunit_fetch = git
1581 pkg_eunit_repo = https://github.com/richcarl/eunit
1582 pkg_eunit_commit = master
1583
15841585 PACKAGES += euthanasia
15851586 pkg_euthanasia_name = euthanasia
15861587 pkg_euthanasia_description = Merciful killer for your Erlang processes
15981599 pkg_evum_commit = master
15991600
16001601 PACKAGES += exec
1601 pkg_exec_name = exec
1602 pkg_exec_name = erlexec
16021603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
16031604 pkg_exec_homepage = http://saleyn.github.com/erlexec
16041605 pkg_exec_fetch = git
17171718 pkg_fn_repo = https://github.com/reiddraper/fn
17181719 pkg_fn_commit = master
17191720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
17201729 PACKAGES += folsom_cowboy
17211730 pkg_folsom_cowboy_name = folsom_cowboy
17221731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
17251734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
17261735 pkg_folsom_cowboy_commit = master
17271736
1728 PACKAGES += folsom
1729 pkg_folsom_name = folsom
1730 pkg_folsom_description = Expose Erlang Events and Metrics
1731 pkg_folsom_homepage = https://github.com/boundary/folsom
1732 pkg_folsom_fetch = git
1733 pkg_folsom_repo = https://github.com/boundary/folsom
1734 pkg_folsom_commit = master
1735
17361737 PACKAGES += folsomite
17371738 pkg_folsomite_name = folsomite
17381739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
20932094 pkg_jesse_repo = https://github.com/for-GET/jesse
20942095 pkg_jesse_commit = master
20952096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
20962105 PACKAGES += jiffy_v
20972106 pkg_jiffy_v_name = jiffy_v
20982107 pkg_jiffy_v_description = JSON validation utility
21012110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
21022111 pkg_jiffy_v_commit = master
21032112
2104 PACKAGES += jiffy
2105 pkg_jiffy_name = jiffy
2106 pkg_jiffy_description = JSON NIFs for Erlang.
2107 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2108 pkg_jiffy_fetch = git
2109 pkg_jiffy_repo = https://github.com/davisp/jiffy
2110 pkg_jiffy_commit = master
2111
21122113 PACKAGES += jobs
21132114 pkg_jobs_name = jobs
21142115 pkg_jobs_description = a Job scheduler for load regulation
21252126 pkg_joxa_repo = https://github.com/joxa/joxa
21262127 pkg_joxa_commit = master
21272128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
21282137 PACKAGES += json_rec
21292138 pkg_json_rec_name = json_rec
21302139 pkg_json_rec_description = JSON to erlang record
21332142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
21342143 pkg_json_rec_commit = master
21352144
2136 PACKAGES += json
2137 pkg_json_name = json
2138 pkg_json_description = a high level json library for erlang (17.0+)
2139 pkg_json_homepage = https://github.com/talentdeficit/json
2140 pkg_json_fetch = git
2141 pkg_json_repo = https://github.com/talentdeficit/json
2142 pkg_json_commit = master
2143
21442145 PACKAGES += jsone
21452146 pkg_jsone_name = jsone
21462147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
21812182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
21822183 pkg_jsx_commit = master
21832184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
21842193 PACKAGES += kafka_protocol
21852194 pkg_kafka_protocol_name = kafka_protocol
21862195 pkg_kafka_protocol_description = Kafka protocol Erlang library
21892198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
21902199 pkg_kafka_protocol_commit = master
21912200
2192 PACKAGES += kafka
2193 pkg_kafka_name = kafka
2194 pkg_kafka_description = Kafka consumer and producer in Erlang
2195 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2196 pkg_kafka_fetch = git
2197 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2198 pkg_kafka_commit = master
2199
22002201 PACKAGES += kai
22012202 pkg_kai_name = kai
22022203 pkg_kai_description = DHT storage by Takeshi Inoue
22932294 pkg_kvs_repo = https://github.com/synrc/kvs
22942295 pkg_kvs_commit = master
22952296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
22962305 PACKAGES += lager_amqp_backend
22972306 pkg_lager_amqp_backend_name = lager_amqp_backend
22982307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
23092318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
23102319 pkg_lager_syslog_commit = master
23112320
2312 PACKAGES += lager
2313 pkg_lager_name = lager
2314 pkg_lager_description = A logging framework for Erlang/OTP.
2315 pkg_lager_homepage = https://github.com/basho/lager
2316 pkg_lager_fetch = git
2317 pkg_lager_repo = https://github.com/basho/lager
2318 pkg_lager_commit = master
2319
23202321 PACKAGES += lambdapad
23212322 pkg_lambdapad_name = lambdapad
23222323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
25732574 pkg_mixer_repo = https://github.com/chef/mixer
25742575 pkg_mixer_commit = master
25752576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
25762585 PACKAGES += mochiweb_xpath
25772586 pkg_mochiweb_xpath_name = mochiweb_xpath
25782587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
25812590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
25822591 pkg_mochiweb_xpath_commit = master
25832592
2584 PACKAGES += mochiweb
2585 pkg_mochiweb_name = mochiweb
2586 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2587 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2588 pkg_mochiweb_fetch = git
2589 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2590 pkg_mochiweb_commit = master
2591
25922593 PACKAGES += mockgyver
25932594 pkg_mockgyver_name = mockgyver
25942595 pkg_mockgyver_description = A mocking library for Erlang
30613062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
30623063 pkg_quickrand_commit = master
30633064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
30643073 PACKAGES += rabbit_exchange_type_riak
30653074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
30663075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
30693078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
30703079 pkg_rabbit_exchange_type_riak_commit = master
30713080
3072 PACKAGES += rabbit
3073 pkg_rabbit_name = rabbit
3074 pkg_rabbit_description = RabbitMQ Server
3075 pkg_rabbit_homepage = https://www.rabbitmq.com/
3076 pkg_rabbit_fetch = git
3077 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3078 pkg_rabbit_commit = master
3079
30803081 PACKAGES += rack
30813082 pkg_rack_name = rack
30823083 pkg_rack_description = Rack handler for erlang
34933494 pkg_smother_repo = https://github.com/ramsay-t/Smother
34943495 pkg_smother_commit = master
34953496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
34963505 PACKAGES += social
34973506 pkg_social_name = social
34983507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
35413550 pkg_stable_repo = https://github.com/dvv/stable
35423551 pkg_stable_commit = master
35433552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
35443561 PACKAGES += statebox_riak
35453562 pkg_statebox_riak_name = statebox_riak
35463563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
35493566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
35503567 pkg_statebox_riak_commit = master
35513568
3552 PACKAGES += statebox
3553 pkg_statebox_name = statebox
3554 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3555 pkg_statebox_homepage = https://github.com/mochi/statebox
3556 pkg_statebox_fetch = git
3557 pkg_statebox_repo = https://github.com/mochi/statebox
3558 pkg_statebox_commit = master
3559
35603569 PACKAGES += statman
35613570 pkg_statman_name = statman
35623571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
37253734 pkg_tirerl_repo = https://github.com/inaka/tirerl
37263735 pkg_tirerl_commit = master
37273736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
37283745 PACKAGES += traffic_tools
37293746 pkg_traffic_tools_name = traffic_tools
37303747 pkg_traffic_tools_description = Simple traffic limiting library
40614078 pkg_zucchini_repo = https://github.com/devinus/zucchini
40624079 pkg_zucchini_commit = master
40634080
4064 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
40654082 # This file is part of erlang.mk and subject to the terms of the ISC License.
40664083
40674084 .PHONY: search
40884105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
40894106 endif
40904107
4091 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
40924109 # This file is part of erlang.mk and subject to the terms of the ISC License.
40934110
4094 .PHONY: distclean-deps
4111 .PHONY: distclean-deps clean-tmp-deps.log
40954112
40964113 # Configuration.
40974114
41104127
41114128 REBAR_DEPS_DIR = $(DEPS_DIR)
41124129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
41134150
41144151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
41154152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
41164153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
41174154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
41184155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
41194157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
41204158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
41214159
41384176
41394177 # Core targets.
41404178
4141 ifdef IS_APP
4142 apps::
4143 else
4144 apps:: $(ALL_APPS_DIRS)
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
41454180 ifeq ($(IS_APP)$(IS_DEP),)
41464181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
41474182 endif
41494184 # Create ebin directory for all apps to make sure Erlang recognizes them
41504185 # as proper OTP applications when using -include_lib. This is a temporary
41514186 # fix, a proper fix would be to compile apps/* in the right order.
4152 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4153 mkdir -p $$dep/ebin || exit $$?; \
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
41544190 done
4155 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
41564196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
41574197 :; \
41584198 else \
41594199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4160 $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
4200 $(MAKE) -C $$dep IS_APP=1; \
41614201 fi \
41624202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
41634207 endif
41644208
41654209 ifneq ($(SKIP_DEPS),)
41664210 deps::
41674211 else
4168 deps:: $(ALL_DEPS_DIRS) apps
4169 ifeq ($(IS_APP)$(IS_DEP),)
4170 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4171 endif
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
41724213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4173 $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
41744215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
41754216 :; \
41764217 else \
41774218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
41784219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4179 $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
4220 $(MAKE) -C $$dep IS_DEP=1; \
41804221 else \
4181 echo "Error: No Makefile to build dependency $$dep."; \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
41824223 exit 2; \
41834224 fi \
41844225 fi \
41924233 # in practice only Makefile is needed so far.
41934234 define dep_autopatch
41944235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
41954237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
41964238 $(call dep_autopatch_erlang_mk,$(1)); \
41974239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4198 if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
41994243 $(call dep_autopatch2,$(1)); \
4200 elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
42014245 $(call dep_autopatch2,$(1)); \
4202 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
42034247 $(call dep_autopatch2,$(1)); \
4204 else \
4205 $(call erlang,$(call dep_autopatch_app.erl,$(1))); \
42064248 fi \
42074249 else \
42084250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
42144256 endef
42154257
42164258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
42174262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
42184263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
42194264 fi; \
42204265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4221 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
42224267 $(call dep_autopatch_fetch_rebar); \
42234268 $(call dep_autopatch_rebar,$(1)); \
42244269 else \
42304275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
42314276 endef
42324277
4233 # Overwrite erlang.mk with the current file by default.
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
42344280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
42354281 define dep_autopatch_erlang_mk
4236 echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \
4237 > $(DEPS_DIR)/$1/erlang.mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
42384287 endef
42394288 else
42404289 define dep_autopatch_erlang_mk
42534302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
42544303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
42554304 cd $(ERLANG_MK_TMP)/rebar; \
4256 git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
42574306 $(MAKE); \
42584307 cd -; \
42594308 fi
42704319 define dep_autopatch_rebar.erl
42714320 application:load(rebar),
42724321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
42734323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
42744324 {ok, Conf0} -> Conf0;
42754325 _ -> []
44234473 [] -> ok;
44244474 _ ->
44254475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4426 PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
44274477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4428 PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L \\"~s\\" -lerl_interface -lei\n",
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
44294479 [code:lib_dir(erl_interface, lib)])),
44304480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
44314481 FilterEnv = fun(Env) ->
44644514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44654515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44664516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4467 [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
44684518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
44694519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
44704520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
44764526 end,
44774527 [PortSpec(S) || S <- PortSpecs]
44784528 end,
4479 Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"),
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
44804530 RunPlugin = fun(Plugin, Step) ->
44814531 case erlang:function_exported(Plugin, Step, 2) of
44824532 false -> ok;
45244574 halt()
45254575 endef
45264576
4527 define dep_autopatch_app.erl
4528 UpdateModules = fun(App) ->
4529 case filelib:is_regular(App) of
4530 false -> ok;
4531 true ->
4532 {ok, [{application, '$(1)', L0}]} = file:consult(App),
4533 Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true,
4534 fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
4535 L = lists:keystore(modules, 1, L0, {modules, Mods}),
4536 ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}]))
4537 end
4538 end,
4539 UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"),
4540 halt()
4541 endef
4542
45434577 define dep_autopatch_appsrc_script.erl
45444578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
45454579 AppSrcScript = AppSrc ++ ".script",
45874621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
45884622 endef
45894623
4590 define dep_fetch_hex.erl
4591 ssl:start(),
4592 inets:start(),
4593 {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
4594 {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
4595 [], [{body_format, binary}]),
4596 {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
4597 {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
4598 ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
4599 halt()
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
46004626 endef
46014627
46024628 # Hex only has a package version. No need to look in the Erlang.mk packages.
46034629 define dep_fetch_hex
4604 $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
46054634 endef
46064635
46074636 define dep_fetch_fail
46314660 $(eval DEP_NAME := $(call dep_name,$1))
46324661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
46334662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4634 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
46354664 exit 17; \
46364665 fi
46374666 $(verbose) mkdir -p $(DEPS_DIR)
46734702 clean:: clean-apps
46744703
46754704 clean-apps:
4676 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4677 $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
46784707 done
46794708
46804709 distclean:: distclean-apps
46814710
46824711 distclean-apps:
4683 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4684 $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
46854714 done
46864715 endif
46874716
47014730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
47024731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
47034732
4704 # External plugins.
4705
4706 DEP_PLUGINS ?=
4707
4708 define core_dep_plugin
4709 -include $(DEPS_DIR)/$(1)
4710
4711 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4712 endef
4713
4714 $(foreach p,$(DEP_PLUGINS),\
4715 $(eval $(if $(findstring /,$p),\
4716 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4717 $(call core_dep_plugin,$p/plugins.mk,$p))))
4718
4719 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4720 # This file is part of erlang.mk and subject to the terms of the ISC License.
4721
4722 # Configuration.
4723
4724 DTL_FULL_PATH ?=
4725 DTL_PATH ?= templates/
4726 DTL_SUFFIX ?= _dtl
4727 DTL_OPTS ?=
4728
4729 # Verbosity.
4730
4731 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
4732 dtl_verbose = $(dtl_verbose_$(V))
4733
4734 # Core targets.
4735
4736 DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl))
4737
4738 ifneq ($(DTL_FILES),)
4739
4740 ifdef DTL_FULL_PATH
4741 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
4742 else
4743 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
4744 endif
4745
4746 ifneq ($(words $(DTL_FILES)),0)
4747 # Rebuild templates when the Makefile changes.
4748 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
4749 @mkdir -p $(ERLANG_MK_TMP)
4750 @if test -f $@; then \
4751 touch $(DTL_FILES); \
4752 fi
4753 @touch $@
4754
4755 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
4756 endif
4757
4758 define erlydtl_compile.erl
4759 [begin
4760 Module0 = case "$(strip $(DTL_FULL_PATH))" of
4761 "" ->
4762 filename:basename(F, ".dtl");
4763 _ ->
4764 "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
4765 re:replace(F2, "/", "_", [{return, list}, global])
4766 end,
4767 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
4768 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
4769 ok -> ok;
4770 {ok, _} -> ok
4771 end
4772 end || F <- string:tokens("$(1)", " ")],
4773 halt().
4774 endef
4775
4776 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
4777 $(if $(strip $?),\
4778 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
4779
4780 endif
4781
4782 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
47834734 # This file is part of erlang.mk and subject to the terms of the ISC License.
47844735
47854736 # Verbosity.
47984749
47994750 define compile_proto.erl
48004751 [begin
4801 Dir = filename:dirname(filename:dirname(F)),
48024752 protobuffs_compile:generate_source(F,
4803 [{output_include_dir, Dir ++ "/include"},
4804 {output_src_dir, Dir ++ "/ebin"}])
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
48054755 end || F <- string:tokens("$(1)", " ")],
48064756 halt().
48074757 endef
48114761 $(if $(strip $?),$(call compile_proto,$?))
48124762 endif
48134763
4814 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
48154765 # This file is part of erlang.mk and subject to the terms of the ISC License.
48164766
48174767 .PHONY: clean-app
48254775 ERLC_EXCLUDE ?=
48264776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
48274777
4778 ERLC_ASN1_OPTS ?=
4779
48284780 ERLC_MIB_OPTS ?=
48294781 COMPILE_MIB_FIRST ?=
48304782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
48744826
48754827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
48764828 define app_file
4877 {application, $(PROJECT), [
4829 {application, '$(PROJECT)', [
48784830 {description, "$(PROJECT_DESCRIPTION)"},
48794831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48804832 {id$(comma)$(space)"$(1)"}$(comma))
48814833 {modules, [$(call comma_list,$(2))]},
48824834 {registered, []},
4883 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48844837 ]}.
48854838 endef
48864839 else
48874840 define app_file
4888 {application, $(PROJECT), [
4841 {application, '$(PROJECT)', [
48894842 {description, "$(PROJECT_DESCRIPTION)"},
48904843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48914844 {id$(comma)$(space)"$(1)"}$(comma))
48924845 {modules, [$(call comma_list,$(2))]},
48934846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
48944847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4895 {mod, {$(PROJECT_MOD), []}}
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48964850 ]}.
48974851 endef
48984852 endif
49024856
49034857 # Source files.
49044858
4905 ERL_FILES = $(sort $(call core_find,src/,*.erl))
4906 CORE_FILES = $(sort $(call core_find,src/,*.core))
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
49074863
49084864 # ASN.1 files.
49094865
49134869
49144870 define compile_asn1
49154871 $(verbose) mkdir -p include/
4916 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
49174873 $(verbose) mv asn1/*.erl src/
49184874 $(verbose) mv asn1/*.hrl include/
49194875 $(verbose) mv asn1/*.asn1db include/
49364892
49374893 # Leex and Yecc files.
49384894
4939 XRL_FILES = $(sort $(call core_find,src/,*.xrl))
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
49404896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
49414897 ERL_FILES += $(XRL_ERL_FILES)
49424898
4943 YRL_FILES = $(sort $(call core_find,src/,*.yrl))
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
49444900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
49454901 ERL_FILES += $(YRL_ERL_FILES)
49464902
49474903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4948 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
49494905
49504906 # Erlang and Core Erlang files.
49514907
49954951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49964952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49974953 (F, Mod, import, {Imp, _}) ->
4998 case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
49994960 false -> ok;
50004961 true -> Add(Mod, Imp)
50014962 end;
50194980 end || F <- ErlFiles],
50204981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
50214982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
50224991 ok = file:write_file("$(1)", [
50234992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
5024 "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n"
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
50254994 ]),
50264995 halt()
50274996 endef
50345003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
50355004 # Rebuild everything when the Makefile changes.
50365005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5037 @mkdir -p $(ERLANG_MK_TMP)
5038 @if test -f $@; then \
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
50395008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
50405009 touch -c $(PROJECT).d; \
50415010 fi
5042 @touch $@
5011 $(verbose) touch $@
50435012
50445013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
50455014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
50465015 endif
50475016
5048 -include $(PROJECT).d
5017 include $(wildcard $(PROJECT).d)
50495018
50505019 ebin/$(PROJECT).app:: ebin/
50515020
50645033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
50655034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
50665035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5067 $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
50685037 > ebin/$(PROJECT).app
50695038 else
50705039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
50885057
50895058 endif
50905059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
50915061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
50925062 # This file is part of erlang.mk and subject to the terms of the ISC License.
50935063
51055075 doc-deps:
51065076 else
51075077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5108 $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5109 endif
5110
5111 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51125082 # This file is part of erlang.mk and subject to the terms of the ISC License.
51135083
51145084 .PHONY: rel-deps
51255095 rel-deps:
51265096 else
51275097 rel-deps: $(ALL_REL_DEPS_DIRS)
5128 $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5129 endif
5130
5131 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51325102 # This file is part of erlang.mk and subject to the terms of the ISC License.
51335103
51345104 .PHONY: test-deps test-dir test-build clean-test-dir
51505120 test-deps:
51515121 else
51525122 test-deps: $(ALL_TEST_DEPS_DIRS)
5153 $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
51545124 endif
51555125
51565126 ifneq ($(wildcard $(TEST_DIR)),)
51835153 endif
51845154 endif
51855155
5186 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51875157 # This file is part of erlang.mk and subject to the terms of the ISC License.
51885158
51895159 .PHONY: rebar.config
52195189 rebar.config:
52205190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
52215191
5222 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
52235193 # This file is part of erlang.mk and subject to the terms of the ISC License.
52245194
5225 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
5226
5227 MAN_INSTALL_PATH ?= /usr/local/share/man
5228 MAN_SECTIONS ?= 3 7
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
52295200
52305201 docs:: asciidoc
52315202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
52325207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
52335210
52345211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
52355212 asciidoc-guide:
52365213 else
5237 asciidoc-guide: distclean-asciidoc doc-deps
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
52385215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
52395216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5240 endif
5241
5242 ifeq ($(wildcard doc/src/manual/*.asciidoc),)
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
52435227 asciidoc-manual:
52445228 else
5245 asciidoc-manual: distclean-asciidoc doc-deps
5246 for f in doc/src/manual/*.asciidoc ; do \
5247 a2x -v -f manpage $$f ; \
5248 done
5249 for s in $(MAN_SECTIONS); do \
5250 mkdir -p doc/man$$s/ ; \
5251 mv doc/src/manual/*.$$s doc/man$$s/ ; \
5252 gzip doc/man$$s/*.$$s ; \
5253 done
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
52545262
52555263 install-docs:: install-asciidoc
52565264
52575265 install-asciidoc: asciidoc-manual
5258 for s in $(MAN_SECTIONS); do \
5259 mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
5260 install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
5261 done
5262 endif
5263
5264 distclean:: distclean-asciidoc
5265
5266 distclean-asciidoc:
5267 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
5268
5269 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
52705276 # This file is part of erlang.mk and subject to the terms of the ISC License.
52715277
52725278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
53235329 define bs_Makefile
53245330 PROJECT = $p
53255331 PROJECT_DESCRIPTION = New project
5326 PROJECT_VERSION = 0.0.1
5332 PROJECT_VERSION = 0.1.0
53275333
53285334 # Whitespace to be used when creating files from templates.
53295335 SP = $(SP)
53335339 define bs_Makefile
53345340 PROJECT = $p
53355341 PROJECT_DESCRIPTION = New project
5336 PROJECT_VERSION = 0.0.1
5342 PROJECT_VERSION = 0.1.0
53375343
53385344 endef
53395345 endif
53415347 define bs_apps_Makefile
53425348 PROJECT = $p
53435349 PROJECT_DESCRIPTION = New project
5344 PROJECT_VERSION = 0.0.1
5350 PROJECT_VERSION = 0.1.0
53455351
53465352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
53475353 endef
53615367 endef
53625368
53635369 define bs_relx_config
5364 {release, {$p_release, "1"}, [$p]}.
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
53655371 {extended_start_script, true}.
53665372 {sys_config, "rel/sys.config"}.
53675373 {vm_args, "rel/vm.args"}.
57125718 ifndef t
57135719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57145720 endif
5715 ifndef tpl_$(t)
5716 $(error Unknown template)
5717 endif
57185721 ifndef n
57195722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57205723 endif
57275730 list-templates:
57285731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
57295732
5730 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
57315734 # This file is part of erlang.mk and subject to the terms of the ISC License.
57325735
57335736 .PHONY: clean-c_src distclean-c_src-env
59625965 $(call render_template,bs_erl_nif,src/$n.erl)
59635966 endif
59645967
5965 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
59665969 # This file is part of erlang.mk and subject to the terms of the ISC License.
59675970
5968 .PHONY: ci ci-setup distclean-kerl
5969
5970 KERL ?= $(CURDIR)/kerl
5971 export KERL
5972
5973 KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
5974
5975 OTP_GIT ?= https://github.com/erlang/otp
5976
5977 CI_INSTALL_DIR ?= $(HOME)/erlang
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
59785973 CI_OTP ?=
5979
5980 ifeq ($(strip $(CI_OTP)),)
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
59815986 ci::
59825987 else
5983 ci:: $(addprefix ci-,$(CI_OTP))
5984
5985 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
59866007
59876008 ci-setup::
6009
6010 ci-extra::
59886011
59896012 ci_verbose_0 = @echo " CI " $(1);
59906013 ci_verbose = $(ci_verbose_$(V))
59916014
59926015 define ci_target
5993 ci-$(1): $(CI_INSTALL_DIR)/$(1)
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
59946018 $(ci_verbose) \
5995 PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
5996 CI_OTP_RELEASE="$(1)" \
5997 CT_OPTS="-label $(1)" \
5998 $(MAKE) clean ci-setup tests
5999 endef
6000
6001 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
60026030
60036031 define ci_otp_target
60046032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
60056033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6006 $(KERL) build git $(OTP_GIT) $(1) $(1)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
60076035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
60086036 endif
60096037 endef
60106038
60116039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
60126040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
60136052 $(KERL):
6014 $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
60156056 $(verbose) chmod +x $(KERL)
60166057
60176058 help::
60286069 $(gen_verbose) rm -rf $(KERL)
60296070 endif
60306071
6031 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
60326073 # This file is part of erlang.mk and subject to the terms of the ISC License.
60336074
60346075 .PHONY: ct apps-ct distclean-ct
60366077 # Configuration.
60376078
60386079 CT_OPTS ?=
6080
60396081 ifneq ($(wildcard $(TEST_DIR)),)
6040 CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6041 else
6042 CT_SUITES ?=
6043 endif
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
60446088
60456089 # Core targets.
60466090
60636107 -noinput \
60646108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
60656109 -dir $(TEST_DIR) \
6066 -logdir $(CURDIR)/logs
6110 -logdir $(CT_LOGS_DIR)
60676111
60686112 ifeq ($(CT_SUITES),)
60696113 ct: $(if $(IS_APP),,apps-ct)
60706114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
60716117 ct: test-build $(if $(IS_APP),,apps-ct)
6072 $(verbose) mkdir -p $(CURDIR)/logs/
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
60736119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
60746121 endif
60756122
60766123 ifneq ($(ALL_APPS_DIRS),)
60976144
60986145 define ct_suite_target
60996146 ct-$(1): test-build
6100 $(verbose) mkdir -p $(CURDIR)/logs/
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
61016148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
61026149 endef
61036150
61046151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
61056152
61066153 distclean-ct:
6107 $(gen_verbose) rm -rf $(CURDIR)/logs/
6108
6109 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61106157 # This file is part of erlang.mk and subject to the terms of the ISC License.
61116158
61126159 .PHONY: plt distclean-plt dialyze
61506197 endef
61516198
61526199 $(DIALYZER_PLT): deps app
6153 $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
61546204
61556205 plt: $(DIALYZER_PLT)
61566206
61646214 endif
61656215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
61666216
6167 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61686218 # This file is part of erlang.mk and subject to the terms of the ISC License.
61696219
61706220 .PHONY: distclean-edoc edoc
61726222 # Configuration.
61736223
61746224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
61756235
61766236 # Core targets.
61776237
6178 ifneq ($(wildcard doc/overview.edoc),)
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
61796239 docs:: edoc
61806240 endif
61816241
61846244 # Plugin-specific targets.
61856245
61866246 edoc: distclean-edoc doc-deps
6187 $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
61886248
61896249 distclean-edoc:
61906250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
61916251
6192 # Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61936253 # This file is part of erlang.mk and subject to the terms of the ISC License.
61946254
6195 .PHONY: distclean-escript escript
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
61966320
61976321 # Configuration.
61986322
61996323 ESCRIPT_NAME ?= $(PROJECT)
62006324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
62016325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
62026327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6203
6204 ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
6205 ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
6206 ESCRIPT_EMU_ARGS ?= -pa . \
6207 -sasl errlog_type error \
6208 -escript main $(ESCRIPT_NAME)
6209 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6210 ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
62116332
62126333 # Core targets.
62136334
62206341
62216342 # Plugin-specific targets.
62226343
6223 # Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
6224 # Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
6225 # Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
6226 # Software may only be used for the great good and the true happiness of all
6227 # sentient beings.
6228
6229 define ESCRIPT_RAW
6230 'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
6231 'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
6232 ' [F || F <- A, not filelib:is_dir(F) ] end,'\
6233 'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
6234 'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
6235 'Ez = fun(Escript) ->'\
6236 ' Static = Files([$(ESCRIPT_STATIC)]),'\
6237 ' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
6238 ' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
6239 ' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
6240 ' {archive, Archive, [memory]},'\
6241 ' {shebang, "$(ESCRIPT_SHEBANG)"},'\
6242 ' {comment, "$(ESCRIPT_COMMENT)"},'\
6243 ' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
6244 ' ]),'\
6245 ' file:change_mode(Escript, 8#755)'\
6246 'end,'\
6247 'Ez("$(ESCRIPT_FILE)"),'\
6248 'halt().'
6249 endef
6250
6251 ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
6252
6253 escript:: distclean-escript deps app
6254 $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
62556360
62566361 distclean-escript:
6257 $(gen_verbose) rm -f $(ESCRIPT_NAME)
6258
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
62596365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6260 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
62616366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
62626367
62636368 .PHONY: eunit apps-eunit
63216426
63226427 ifneq ($(ALL_APPS_DIRS),)
63236428 apps-eunit:
6324 $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done
6325 endif
6326 endif
6327
6328 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
63296436 # This file is part of erlang.mk and subject to the terms of the ISC License.
63306437
6331 .PHONY: relx-rel distclean-relx-rel distclean-relx run
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
63326439
63336440 # Configuration.
63346441
6335 RELX ?= $(CURDIR)/relx
6442 RELX ?= $(ERLANG_MK_TMP)/relx
63366443 RELX_CONFIG ?= $(CURDIR)/relx.config
63376444
63386445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
63396446 RELX_OPTS ?=
63406447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
63416454
63426455 ifeq ($(firstword $(RELX_OPTS)),-o)
63436456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
63506463 ifeq ($(IS_DEP),)
63516464 ifneq ($(wildcard $(RELX_CONFIG)),)
63526465 rel:: relx-rel
6353 endif
6354 endif
6355
6356 distclean:: distclean-relx-rel distclean-relx
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
63576472
63586473 # Plugin-specific targets.
63596474
63626477 $(verbose) chmod +x $(RELX)
63636478
63646479 relx-rel: $(RELX) rel-deps app
6365 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
63666484
63676485 distclean-relx-rel:
63686486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6369
6370 distclean-relx:
6371 $(gen_verbose) rm -rf $(RELX)
63726487
63736488 # Run target.
63746489
63776492 else
63786493
63796494 define get_relx_release.erl
6380 {ok, Config} = file:consult("$(RELX_CONFIG)"),
6381 {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
6382 io:format("~s", [Name]),
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
63836504 halt(0).
63846505 endef
63856506
6386 RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
63876514
63886515 run: all
6389 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
63906517
63916518 help::
63926519 $(verbose) printf "%s\n" "" \
63956522
63966523 endif
63976524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
63986526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6399 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
64006527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
64016528
64026529 .PHONY: shell
64216548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
64226549
64236550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6424 $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
64256552
64266553 shell: build-shell-deps
64276554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
64286555
6429 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
64306571 # This file is part of erlang.mk and subject to the terms of the ISC License.
64316572
64326573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
64376578 tests:: triq
64386579
64396580 define triq_check.erl
6440 code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
64416582 try
64426583 case $(1) of
64436584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
64696610 endif
64706611 endif
64716612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
64726614 # Copyright (c) 2015, Erlang Solutions Ltd.
64736615 # This file is part of erlang.mk and subject to the terms of the ISC License.
64746616
64856627 XREFR ?= $(CURDIR)/xrefr
64866628 export XREFR
64876629
6488 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
64896631
64906632 # Core targets.
64916633
64926634 help::
6493 $(verbose) printf "%s\n" "" \
6494 "Xref targets:" \
6495 " xref Run Xrefr using $XREF_CONFIG as config file if defined"
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
64966638
64976639 distclean:: distclean-xref
64986640
65086650 distclean-xref:
65096651 $(gen_verbose) rm -rf $(XREFR)
65106652
6511 # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
65126655 # This file is part of erlang.mk and subject to the terms of the ISC License.
65136656
65146657 COVER_REPORT_DIR = cover
65176660
65186661 ifdef COVER
65196662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
65206664 # All modules in 'ebin'
65216665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
65226666
65316675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
65326676 endif
65336677 endif
6678 endif
65346679
65356680 # Core targets
65366681
65526697 "Cover targets:" \
65536698 " cover-report Generate a HTML coverage report from previously collected" \
65546699 " cover data." \
6555 " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
65566701 "" \
65576702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
65586703 "target tests additionally generates a HTML coverage report from the combined" \
65656710
65666711 .PHONY: coverdata-clean
65676712 coverdata-clean:
6568 $(gen_verbose) rm -f *.coverdata ct.cover.spec
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
65696714
65706715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
65716721 all.coverdata: $(COVERDATA)
6572 $(gen_verbose) $(ERL) -eval ' \
6573 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
6574 cover:export("$@"), halt(0).'
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
65756723
65766724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
65776725 # empty if you want the coverdata files but not the HTML report.
65896737 # Modules which include eunit.hrl always contain one line without coverage
65906738 # because eunit defines test/0 which is never called. We compensate for this.
65916739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6592 grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
65936741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
65946742
65956743 define cover_report.erl
66246772 endef
66256773
66266774 cover-report:
6627 $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
66286776 $(gen_verbose) $(call erlang,$(cover_report.erl))
66296777
66306778 endif
66316779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
66326839
66336840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
66346841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
66976904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
66986905 endif
66996906 ifndef IS_APP
6700 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
67016908 $(MAKE) -C $$dep $@ \
67026909 IS_APP=1 \
6703 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6704 || exit $$?; \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67056911 done
67066912 endif
6707 $(verbose) for dep in $^ ; do \
6913 $(verbose) set -e; for dep in $^ ; do \
67086914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
67096915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6710 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
67116917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
67126918 $(MAKE) -C $$dep fetch-deps \
67136919 IS_DEP=1 \
6714 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6715 || exit $$?; \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67166921 fi \
67176922 fi \
67186923 done
22 # before the inclusion of erlang.mk leading to the wrong target becoming
33 # the default.
44 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
526 endif
627
728 # --------------------------------------------------------------------
2041 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
2142 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
2243 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
2345 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
2446 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
2547 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
2648 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
2749 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
2851 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
2952 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
3054 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
3155 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
3256 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
3559 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
3660 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
3761 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
3863 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
3964 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
4065 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
7095
7196 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
7297
73 # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
74 # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
75 # needs to add "ranch" as a BUILD_DEPS. The list of projects needing
76 # this workaround are:
77 # o rabbitmq-web-stomp
78 dep_ranch = git https://github.com/ninenines/ranch 1.2.1
98 # Third-party dependencies version pinning.
99 #
100 # We do that in this file, which is copied in all projects, to ensure
101 # all projects use the same versions. It avoids conflicts and makes it
102 # possible to work with rabbitmq-public-umbrella.
103
104 dep_cowboy_commit = 1.0.4
105 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
106 dep_ranch_commit = 1.3.1
107 dep_webmachine_commit = 1.10.8p2
79108
80109 RABBITMQ_COMPONENTS = amqp_client \
81110 rabbit \
82111 rabbit_common \
83112 rabbitmq_amqp1_0 \
84113 rabbitmq_auth_backend_amqp \
114 rabbitmq_auth_backend_cache \
85115 rabbitmq_auth_backend_http \
86116 rabbitmq_auth_backend_ldap \
87117 rabbitmq_auth_mechanism_ssl \
88118 rabbitmq_boot_steps_visualiser \
89119 rabbitmq_clusterer \
120 rabbitmq_cli \
90121 rabbitmq_codegen \
91122 rabbitmq_consistent_hash_exchange \
123 rabbitmq_ct_client_helpers \
92124 rabbitmq_ct_helpers \
93125 rabbitmq_delayed_message_exchange \
94126 rabbitmq_dotnet_client \
97129 rabbitmq_federation_management \
98130 rabbitmq_java_client \
99131 rabbitmq_jms_client \
132 rabbitmq_jms_cts \
100133 rabbitmq_jms_topic_exchange \
101134 rabbitmq_lvc \
102135 rabbitmq_management \
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% @private
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% @type close_reason(Type) = {shutdown, amqp_reason(Type)}.
102102 %% true | false, only relevant in the direct
103103 %% client case.
104104 %% when true, consumers will manually notify
105 %% queue pids using rabbit_amqqueue:notify_sent/2
105 %% queue pids using rabbit_amqqueue_common:notify_sent/2
106106 %% to prevent the queue from overwhelming slow
107107 %% consumers that use automatic acknowledgement
108108 %% mode.
424424 {noreply, State#state{delivery_flow_control = true}};
425425 %% @private
426426 handle_cast({send_notify, {QPid, ChPid}}, State) ->
427 rabbit_amqqueue:notify_sent(QPid, ChPid),
427 rabbit_amqqueue_common:notify_sent(QPid, ChPid),
428428 {noreply, State};
429429 %% @private
430430 handle_cast({cast, Method, AmqpMsg, Sender, noflow}, State) ->
488488 State = #state{delivery_flow_control = MFC}) ->
489489 case MFC of
490490 false -> handle_method_from_server(Method, Content, State),
491 rabbit_amqqueue:notify_sent(QPid, ChPid);
491 rabbit_amqqueue_common:notify_sent(QPid, ChPid);
492492 true -> handle_method_from_server(Method, Content,
493493 {self(), QPid, ChPid}, State)
494494 end,
511511 "connection closing~n", [self()]),
512512 {stop, timed_out_flushing_channel, State};
513513 %% @private
514 handle_info({'DOWN', _, process, ReturnHandler, shutdown},
515 State = #state{return_handler = {ReturnHandler, _Ref}}) ->
516 {noreply, State#state{return_handler = none}};
514517 handle_info({'DOWN', _, process, ReturnHandler, Reason},
515518 State = #state{return_handler = {ReturnHandler, _Ref}}) ->
516519 ?LOG_WARN("Channel (~p): Unregistering return handler ~p because it died. "
517520 "Reason: ~p~n", [self(), ReturnHandler, Reason]),
518521 {noreply, State#state{return_handler = none}};
519522 %% @private
523 handle_info({'DOWN', _, process, ConfirmHandler, shutdown},
524 State = #state{confirm_handler = {ConfirmHandler, _Ref}}) ->
525 {noreply, State#state{confirm_handler = none}};
520526 handle_info({'DOWN', _, process, ConfirmHandler, Reason},
521527 State = #state{confirm_handler = {ConfirmHandler, _Ref}}) ->
522528 ?LOG_WARN("Channel (~p): Unregistering confirm handler ~p because it died. "
523529 "Reason: ~p~n", [self(), ConfirmHandler, Reason]),
524530 {noreply, State#state{confirm_handler = none}};
525531 %% @private
532 handle_info({'DOWN', _, process, FlowHandler, shutdown},
533 State = #state{flow_handler = {FlowHandler, _Ref}}) ->
534 {noreply, State#state{flow_handler = none}};
526535 handle_info({'DOWN', _, process, FlowHandler, Reason},
527536 State = #state{flow_handler = {FlowHandler, _Ref}}) ->
528537 ?LOG_WARN("Channel (~p): Unregistering flow handler ~p because it died. "
529538 "Reason: ~p~n", [self(), FlowHandler, Reason]),
530539 {noreply, State#state{flow_handler = none}};
531540 handle_info({'DOWN', _, process, QPid, _Reason}, State) ->
532 rabbit_amqqueue:notify_sent_queue_down(QPid),
541 rabbit_amqqueue_common:notify_sent_queue_down(QPid),
533542 {noreply, State};
534543 handle_info({confirm_timeout, From}, State = #state{waiting_set = WSet}) ->
535544 case gb_trees:lookup(From, WSet) of
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% @private
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% @private
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% @private
+0
-9
deps/amqp_client/src/amqp_client.app.src less more
0 {application, amqp_client,
1 [{description, "RabbitMQ AMQP Client"},
2 {vsn, "3.6.6"},
3 {modules, []},
4 {registered, [amqp_sup]},
5 {env, [{prefer_ipv6, false},
6 {ssl_options, []}]},
7 {mod, {amqp_client, []}},
8 {applications, [kernel, stdlib, xmerl, rabbit_common]}]}.
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% @private
2626 %%---------------------------------------------------------------------------
2727
2828 start() ->
29 %% rabbit_common needs compiler and syntax_tools, see
30 %%
31 %% * https://github.com/rabbitmq/rabbitmq-erlang-client/issues/72
32 %% * https://github.com/rabbitmq/rabbitmq-common/pull/149
33 application:start(syntax_tools),
34 application:start(compiler),
35 application:start(xmerl),
2936 application:start(rabbit_common),
3037 application:start(amqp_client).
3138
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% @type close_reason(Type) = {shutdown, amqp_reason(Type)}.
156156 %% running in the same process space. If the port is set to 'undefined',
157157 %% the default ports will be selected depending on whether this is a
158158 %% normal or an SSL connection.
159 %% If ConnectionName is binary - it will be added to client_properties as
159 %% If ConnectionName is binary - it will be added to client_properties as
160160 %% user specified connection name.
161161 start(AmqpParams, ConnName) when ConnName == undefined; is_binary(ConnName) ->
162162 ensure_started(),
174174 amqp_gen_connection:connect(Connection).
175175
176176 set_connection_name(undefined, Params) -> Params;
177 set_connection_name(ConnName,
177 set_connection_name(ConnName,
178178 #amqp_params_network{client_properties = Props} = Params) ->
179179 Params#amqp_params_network{
180180 client_properties = [
181181 {<<"connection_name">>, longstr, ConnName} | Props
182182 ]};
183 set_connection_name(ConnName,
183 set_connection_name(ConnName,
184184 #amqp_params_direct{client_properties = Props} = Params) ->
185185 Params#amqp_params_direct{
186186 client_properties = [
194194 %% application controller is in the process of shutting down the very
195195 %% application which is making this call.
196196 ensure_started() ->
197 [ensure_started(App) || App <- [xmerl, rabbit_common, amqp_client]].
197 [ensure_started(App) || App <- [syntax_tools, compiler, xmerl,
198 rabbit_common, amqp_client]].
198199
199200 ensure_started(App) ->
200201 case is_pid(application_controller:get_master(App)) andalso amqp_sup:is_ready() of
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% @private
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% @private
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% @private
7777 {stop, {shutdown, node_down}, State};
7878 handle_message({'DOWN', _MRef, process, _ConnSup, Reason}, State) ->
7979 {stop, {remote_node_down, Reason}, State};
80 handle_message({'EXIT', Pid, Reason}, State) ->
81 {stop, rabbit_misc:format("stopping because dependent process ~p died: ~p", [Pid, Reason]), State};
8082 handle_message(Msg, State) ->
8183 {stop, {unexpected_msg, Msg}, State}.
8284
211213 case rabbit_net:peercert(Sock) of
212214 {ok, Cert} ->
213215 [{peer_cert_issuer, list_to_binary(
214 rabbit_ssl:peer_cert_issuer(Cert))},
216 rabbit_cert_info:issuer(Cert))},
215217 {peer_cert_subject, list_to_binary(
216 rabbit_ssl:peer_cert_subject(Cert))},
218 rabbit_cert_info:subject(Cert))},
217219 {peer_cert_validity, list_to_binary(
218 rabbit_ssl:peer_cert_validity(Cert))}];
220 rabbit_cert_info:validity(Cert))}];
219221 _ ->
220222 []
221223 end.
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% @private
220220 %% @private
221221 handle_info({'DOWN', _, process, BlockHandler, Reason},
222222 State = #state{block_handler = {BlockHandler, _Ref}}) ->
223 ?LOG_WARN("Connection (~p): Unregistering block handler ~p because it died. "
223 ?LOG_WARN("Connection (~p): Unregistering connection.{blocked,unblocked} handler ~p because it died. "
224224 "Reason: ~p~n", [self(), BlockHandler, Reason]),
225225 {noreply, State#state{block_handler = none}};
226 handle_info({'EXIT', BlockHandler, Reason},
227 State = #state{block_handler = {BlockHandler, Ref}}) ->
228 ?LOG_WARN("Connection (~p): Unregistering connection.{blocked,unblocked} handler ~p because it died. "
229 "Reason: ~p~n", [self(), BlockHandler, Reason]),
230 erlang:demonitor(Ref, [flush]),
231 {noreply, State#state{block_handler = none}};
232 %% propagate the exit to the module that will stop with a sensible reason logged
233 handle_info({'EXIT', _Pid, _Reason} = Info, State) ->
234 callback(handle_message, [Info], State);
226235 handle_info(Info, State) ->
227236 callback(handle_message, [Info], State).
228237
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% @private
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% @private
7272 {stop, heartbeat_timeout, State};
7373 handle_message(closing_timeout, State = #state{closing_reason = Reason}) ->
7474 {stop, Reason, State};
75 handle_message({'EXIT', Pid, Reason}, State) ->
76 {stop, rabbit_misc:format("stopping because dependent process ~p died: ~p", [Pid, Reason]), State};
7577 %% see http://erlang.org/pipermail/erlang-bugs/2012-June/002933.html
7678 handle_message({Ref, {error, Reason}},
7779 State = #state{waiting_socket_close = Waiting,
143145 [Family | ?RABBIT_TCP_OPTS] ++ ExtraOpts,
144146 Timeout) of
145147 {ok, Sock} ->
146 SslOpts = rabbit_networking:fix_ssl_options(
148 SslOpts = rabbit_ssl_options:fix(
147149 orddict:to_list(
148150 orddict:merge(fun (_, _A, B) -> B end,
149151 orddict:from_list(GlobalSslOpts),
302304 {<<"version">>, longstr, list_to_binary(Vsn)},
303305 {<<"platform">>, longstr, <<"Erlang">>},
304306 {<<"copyright">>, longstr,
305 <<"Copyright (c) 2007-2016 Pivotal Software, Inc.">>},
307 <<"Copyright (c) 2007-2017 Pivotal Software, Inc.">>},
306308 {<<"information">>, longstr,
307309 <<"Licensed under the MPL. "
308310 "See http://www.rabbitmq.com/">>},
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% @doc This module allows the simple execution of an asynchronous RPC over
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% @doc This is a utility module that is used to expose an arbitrary function
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% @private
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(amqp_uri).
2525
2626 %% Reformat a URI to remove authentication secrets from it (before we
2727 %% log it or display it anywhere).
28 -spec remove_credentials(URI :: string() | binary()) -> string().
2829 remove_credentials(URI) ->
29 Props = uri_parser:parse(URI,
30 UriString = rabbit_data_coercion:to_list(URI),
31 Props = uri_parser:parse(UriString,
3032 [{host, undefined}, {path, undefined},
3133 {port, undefined}, {'query', []}]),
3234 PortPart = case proplists:get_value(port, Props) of
6163 %% than once). The extra parameters that may be specified for an SSL
6264 %% connection are cacertfile, certfile, keyfile, verify,
6365 %% fail_if_no_peer_cert, password, and depth.
66 -type parse_result() :: {ok, #amqp_params_network{}} |
67 {ok, #amqp_params_direct{}} |
68 {error, {any(), string()}}.
69
70 -spec parse(Uri :: string() | binary()) -> parse_result().
6471 parse(Uri) -> parse(Uri, <<"/">>).
6572
73 -spec parse(Uri :: string() | binary(), DefaultVHost :: binary()) -> parse_result().
6674 parse(Uri, DefaultVHost) ->
6775 try return(parse1(Uri, DefaultVHost))
6876 catch throw:Err -> {error, {Err, Uri}};
6977 error:Err -> {error, {Err, Uri}}
7078 end.
7179
72 parse1(Uri, DefaultVHost) when is_list(Uri) ->
73 case uri_parser:parse(Uri, [{host, undefined}, {path, undefined},
80 parse1(Uri, DefaultVHost) when is_list(Uri); is_binary(Uri) ->
81 UriString = rabbit_data_coercion:to_list(Uri),
82 case uri_parser:parse(UriString, [{host, undefined}, {path, undefined},
7483 {port, undefined}, {'query', []}]) of
7584 {error, Err} ->
7685 throw({unable_to_parse_uri, Err});
3838 %% are: 'scheme', 'userinfo', 'host', 'port', 'path', 'query',
3939 %% 'fragment'.
4040
41 -spec parse(AbsURI :: string() | binary(), Defaults :: list()) -> string().
4142 parse(AbsURI, Defaults) ->
42 case parse_scheme(AbsURI) of
43 AbsUriString = rabbit_data_coercion:to_list(AbsURI),
44 case parse_scheme(AbsUriString) of
4345 {error, Reason} ->
4446 {error, Reason};
4547 {Scheme, Rest} ->
00 CHANGELOG
11 =========
2
3 1.0.4
4 -----
5
6 * Fix a crash when using iolist() as payload for some Websocket frames
27
38 1.0.3
49 -----
88
99 pre:
1010 - sudo pip install autobahntestsuite
11 - sudo apt-get update
1112 - sudo apt-get install autoconf2.59
1213 - cd $HOME/bin && ln -s /usr/bin/autoconf2.59 autoconf
1314 - cd $HOME/bin && ln -s /usr/bin/autoheader2.59 autoheader
00 {application,cowboy,
11 [{description,"Small, fast, modular HTTP server."},
2 {vsn,"1.0.3"},
2 {vsn,"1.0.4"},
33 {id,"git"},
44 {modules,[]},
55 {registered,[cowboy_clock,cowboy_sup]},
569569 %% Ping control frame. Send a pong back and forward the ping to the handler.
570570 websocket_dispatch(State=#state{socket=Socket, transport=Transport},
571571 Req, HandlerState, RemainingData, 9, Payload) ->
572 Len = payload_length_to_binary(byte_size(Payload)),
572 Len = payload_length_to_binary(iolist_size(Payload)),
573573 Transport:send(Socket, << 1:1, 0:3, 10:4, 0:1, Len/bits, Payload/binary >>),
574574 handler_call(State, Req, HandlerState, RemainingData,
575575 websocket_handle, {ping, Payload}, fun websocket_data/4);
00 Cowlib is available thanks to the work of:
11
22 Loïc Hoguin
3 Krzysztof Jurewicz
34 Mikkel Jensen
00 CHANGELOG
11 =========
2
3 1.0.2
4 -----
5
6 * Fix handling of default values in cookie options
27
38 1.0.1
49 -----
11
22 PROJECT = cowlib
33 PLT_APPS = crypto
4 CI_OTP = OTP_R15B OTP_R15B01 OTP_R15B02 OTP_R15B03-1 OTP_R16B OTP_R16B01 OTP_R16B02 OTP_R16B03-1 OTP-17.0.2 OTP-17.1.2 OTP-17.2.2 OTP-17.3.4 OTP-17.4.1 OTP-17.5.6.3 OTP-18.0.3
45
56 include erlang.mk
7
8 TEST_ERLC_OPTS += +'{parse_transform, eunit_autoexport}' -DEXTRA=1
69
710 .PHONY: gen perfs
811
+0
-17
deps/cowlib/all.sh less more
0 #!/bin/sh
1
2 KERL_INSTALL_PATH=~/erlang
3 KERL_RELEASES="r15b r15b01 r15b02 r15b03 r16b r16b01 r16b02 r16b03-1 17.0 17.1.2"
4
5 make build-ct-suites
6
7 for rel in $KERL_RELEASES
8 do
9 echo
10 echo " TESTING $rel"
11 echo
12 . $KERL_INSTALL_PATH/$rel/activate
13 CT_OPTS="-label $rel" make tests
14 done
15
16 xdg-open logs/all_runs.html
+0
-20
deps/cowlib/build.config less more
0 # Core modules.
1 #
2 # Do *not* comment or remove them
3 # unless you know what you are doing!
4 core/core
5 core/deps
6 core/erlc
7
8 # Plugins.
9 #
10 # Comment to disable, uncomment to enable.
11 plugins/bootstrap
12 #plugins/c_src
13 plugins/ct
14 plugins/dialyzer
15 #plugins/edoc
16 plugins/elvis
17 #plugins/erlydtl
18 #plugins/relx
19 plugins/shell
0 dependencies:
1 cache_directories:
2 - "~/.kerl"
3 - "~/erlang"
4
5 pre:
6 - sudo apt-get update
7 - sudo apt-get install autoconf2.59
8 - cd $HOME/bin && ln -s /usr/bin/autoconf2.59 autoconf
9 - cd $HOME/bin && ln -s /usr/bin/autoheader2.59 autoheader
10 - make ci-prepare:
11 timeout: 3600
12
13 test:
14 override:
15 - source $HOME/erlang/OTP-18.0.3/activate && make dialyze
16 - make -k ci:
17 timeout: 3600
196196 end,
197197 SecureBin = case lists:keyfind(secure, 1, Opts) of
198198 false -> <<>>;
199 {_, false} -> <<>>;
199200 {_, true} -> <<"; Secure">>
200201 end,
201202 HttpOnlyBin = case lists:keyfind(http_only, 1, Opts) of
202203 false -> <<>>;
204 {_, false} -> <<>>;
203205 {_, true} -> <<"; HttpOnly">>
204206 end,
205207 [Name, <<"=">>, Value, <<"; Version=1">>,
216218 {<<"Customer">>, <<"WILE_E_COYOTE">>,
217219 [{path, <<"/acme">>}],
218220 <<"Customer=WILE_E_COYOTE; Version=1; Path=/acme">>},
221 {<<"Customer">>, <<"WILE_E_COYOTE">>,
222 [{secure, true}],
223 <<"Customer=WILE_E_COYOTE; Version=1; Secure">>},
224 {<<"Customer">>, <<"WILE_E_COYOTE">>,
225 [{secure, false}, {http_only, false}],
226 <<"Customer=WILE_E_COYOTE; Version=1">>},
219227 {<<"Customer">>, <<"WILE_E_COYOTE">>,
220228 [{path, <<"/acme">>}, {badoption, <<"negatory">>}],
221229 <<"Customer=WILE_E_COYOTE; Version=1; Path=/acme">>}
00 {application,cowlib,
11 [{description,"Support library for manipulating Web protocols."},
2 {vsn,"1.0.1"},
2 {vsn,"1.0.2"},
33 {id,"git"},
44 {modules,[]},
55 {registered,[]},
+0
-17
deps/mochiweb/.editorconfig less more
0 # EditorConfig file: http://EditorConfig.org
1
2 # top-most EditorConfig file
3 root = true
4
5 # Unix-style newlines with a newline ending every file
6 [*]
7 end_of_line = lf
8 insert_final_newline = true
9 charset = utf-8
10 trim_trailing_whitespace = true
11 insert_final_newline = true
12
13 # 4 space indentation
14 [*.{erl,src,hrl}]
15 indent_style = space
16 indent_size = 4
+0
-206
deps/mochiweb/CHANGES.md less more
0 Version 2.13.1 released 2016-03-13
1
2 * Fix mochiweb_html regression parsing invalid charref sequences
3 https://github.com/mochi/mochiweb/issues/167
4
5 Version 2.13.0 released 2016-02-08
6
7 * Support parsing of UTF-16 surrogate pairs encoded as character
8 references in mochiweb_html
9 https://github.com/mochi/mochiweb/issues/164
10 * Avoid swallowing messages that are not related to the socket
11 during request parsing
12 https://github.com/mochi/mochiweb/pull/161
13 * Ensure correct ordering of Set-Cookie headers: first in, first out
14 https://github.com/mochi/mochiweb/issues/162
15 * Improve response times by caching a formatted date once per second
16 for the response headers with a mochiweb_clock service
17 https://github.com/mochi/mochiweb/pull/158
18
19 Version 2.12.2 released 2015-02-21
20
21 * Close connections quietly when setopts fails with a closed socket.
22 https://github.com/mochi/mochiweb/pull/152
23
24 Version 2.12.1 released 2015-02-01
25
26 * Fix active_socket accounting
27 https://github.com/mochi/mochiweb/issues/149
28 * Added full MIT license preludes to each source file to make it
29 easier for mochiweb's code to be used piecemeal
30 https://github.com/mochi/mochiweb/pull/148
31
32 Version 2.12.0 released 2015-01-16
33
34 * Send "Connection: close" header when the server is going to close
35 a Keep-Alive connection, usually due to unread data from the
36 client
37 https://github.com/mochi/mochiweb/issues/146
38
39 Version 2.11.2 released 2015-01-16
40
41 * Fix regression introduced in #147
42 https://github.com/mochi/mochiweb/pull/147
43
44 Version 2.11.1 released 2015-01-16
45
46 * Accept range end position which exceededs the resource size
47 https://github.com/mochi/mochiweb/pull/147
48
49 Version 2.11.0 released 2015-01-12
50
51 * Perform SSL handshake after releasing acceptor back into the pool,
52 and slow accept rate when file descriptors are not available,
53 to mitigate a potential DoS attack. Adds new mochiweb_socket
54 functions transport_accept/1 and finish_accept/1 which should be
55 used in preference to the now deprecated accept/1 function.
56 https://github.com/mochi/mochiweb/issues/138
57
58 Version 2.10.1 released 2015-01-11
59
60 * Fixes issue with SSL and mochiweb_websocket. Note that
61 mochiweb_websocket is still experimental and the API
62 is subject to change in future versions.
63 https://github.com/mochi/mochiweb/pull/144
64
65 Version 2.10.0 released 2014-12-17
66
67 * Added new `recbuf` option to mochiweb_http to allow the receive
68 buffer to be configured.
69 https://github.com/mochi/mochiweb/pull/134
70
71 Version 2.9.2 released 2014-10-16
72
73 * Add timeouts to SSL connect to prevent DoS by opening a connection
74 and not doing anything.
75 https://github.com/mochi/mochiweb/pull/140
76 * Prevent using ECDH cipher in R16B because it is broken
77 https://github.com/mochi/mochiweb/pull/140
78 * For default SSL connections, remove usage of sslv3 and not-so-secure
79 ciphers.
80 https://github.com/mochi/mochiweb/pull/140
81
82 Version 2.9.1 released 2014-09-29
83
84 * Fix Makefile rule for building docs
85 https://github.com/mochi/mochiweb/issues/135
86 * Minimize gen_tcp:send calls to optimize performance.
87 https://github.com/mochi/mochiweb/pull/137
88
89 Version 2.9.0 released 2014-06-24
90
91 * Increased timeout in test suite for FreeBSD
92 https://github.com/mochi/mochiweb/pull/121
93 * Updated rebar to v2.5.0 and fixed associated build issues
94 https://github.com/mochi/mochiweb/issues/131
95
96 Version 2.8.0 released 2014-01-01
97
98 * Websocket support
99 https://github.com/mochi/mochiweb/pull/120
100 * Force files named "crossdomain.xml" to have MIME type
101 text/x-cross-domain-policy.
102 https://github.com/mochi/mochiweb/pull/118
103
104 Version 2.7.0 released 2013-08-01
105
106 * Fix 0-length range responses
107 https://github.com/mochi/mochiweb/pull/87
108 * Add support for all possible `erlang:decode_packet/3` responses,
109 previously these would just crash.
110 https://github.com/mochi/mochiweb/pull/114
111 * Makefile fixed to make `make test` work before `make all`
112 https://github.com/mochi/mochiweb/pull/116
113 * Usage of the crypto module made R16B01+ compatible
114 https://github.com/mochi/mochiweb/pull/115
115 * Build fixed for R16B01
116 https://github.com/mochi/mochiweb/pull/112
117 * `mochiweb_socket_server:stop/1` is now a synchronous
118 call instead of an asynchronous cast
119 * `mochiweb_html:parse_tokens/1` (and `parse/1`) will now create a
120 html element to wrap documents that have a HTML5 doctype
121 (`<!doctype html>`) but no html element
122 https://github.com/mochi/mochiweb/issues/110
123
124 Version 2.6.0 released 2013-04-15
125
126 * Enable R15B gen_tcp workaround only on R15B
127 https://github.com/mochi/mochiweb/pull/107
128
129 Version 2.5.0 released 2013-03-04
130
131 * Replace now() with os:timestamp() in acceptor (optimization)
132 https://github.com/mochi/mochiweb/pull/102
133 * New mochiweb_session module for managing session cookies.
134 NOTE: this module is only supported on R15B02 and later!
135 https://github.com/mochi/mochiweb/pull/94
136 * New mochiweb_base64url module for base64url encoding
137 (URL and Filename safe alphabet, see RFC 4648).
138 * Fix rebar.config in mochiwebapp_skel to use {branch, "master"}
139 https://github.com/mochi/mochiweb/issues/105
140
141 Version 2.4.2 released 2013-02-05
142
143 * Fixed issue in mochiweb_response introduced in v2.4.0
144 https://github.com/mochi/mochiweb/pull/100
145
146 Version 2.4.1 released 2013-01-30
147
148 * Fixed issue in mochiweb_request introduced in v2.4.0
149 https://github.com/mochi/mochiweb/issues/97
150 * Fixed issue in mochifmt_records introduced in v2.4.0
151 https://github.com/mochi/mochiweb/issues/96
152
153 Version 2.4.0 released 2013-01-23
154
155 * Switch from parameterized modules to explicit tuple module calls for
156 R16 compatibility (#95)
157 * Fix for mochiweb_acceptor crash with extra-long HTTP headers under
158 R15B02 (#91)
159 * Fix case in handling range headers (#85)
160 * Handle combined Content-Length header (#88)
161 * Windows security fix for `safe_relative_path`, any path with a
162 backslash on any platform is now considered unsafe (#92)
163
164 Version 2.3.2 released 2012-07-27
165
166 * Case insensitive match for "Connection: close" (#81)
167
168 Version 2.3.1 released 2012-03-31
169
170 * Fix edoc warnings (#63)
171 * Fix mochiweb_html handling of invalid charref sequences (unescaped &) (#69).
172 * Add a manual garbage collection between requests to avoid worst case behavior
173 on keep-alive sockets.
174 * Fix dst cookie bug (#73)
175 * Removed unnecessary template_dir option, see
176 https://github.com/basho/rebar/issues/203
177
178 Version 2.3.0 released 2011-10-14
179
180 * Handle ssl_closed message in mochiweb_http (#59)
181 * Added support for new MIME types (otf, eot, m4v, svg, svgz, ttc, ttf,
182 vcf, webm, webp, woff) (#61)
183 * Updated mochiweb_charref to support all HTML5 entities. Note that
184 if you are using this module directly, the spec has changed to return
185 `[integer()]` for some entities. (#64)
186
187 Version 2.2.1 released 2011-08-31
188
189 * Removed `mochiweb_skel` module from the pre-rebar era
190
191 Version 2.2.0 released 2011-08-29
192
193 * Added new `mochiweb_http:start_link/1` and
194 `mochiweb_socket_server:start_link/1` APIs to explicitly start linked
195 servers. Also added `{link, false}` option to the `start/1` variants
196 to explicitly start unlinked. This is in expectation that we will
197 eventually change the default behavior of `start/1` to be unlinked as you
198 would expect it to. See https://github.com/mochi/mochiweb/issues/58 for
199 discussion.
200
201 Version 2.1.0 released 2011-08-29
202
203 * Added new `mochijson2:decode/2` with `{format, struct | proplist | eep18}`
204 options for easy decoding to various proplist formats. Also added encoding
205 support for eep18 style objects.
+0
-9
deps/mochiweb/LICENSE less more
0 This is the MIT license.
1
2 Copyright (c) 2007 Mochi Media, Inc.
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5
6 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7
8 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+0
-22
deps/mochiweb/Makefile less more
0 IGNORE_DEPS += edown eper eunit_formatters meck node_package rebar_lock_deps_plugin rebar_vsn_plugin reltool_util
1 C_SRC_DIR = /path/do/not/exist
2 C_SRC_TYPE = rebar
3 DRV_CFLAGS = -fPIC
4 export DRV_CFLAGS
5 ERLANG_ARCH = 64
6 export ERLANG_ARCH
7 ERLC_OPTS = +debug_info
8 export ERLC_OPTS
9
10 COMPILE_FIRST +=
11
12
13 rebar_dep: preprocess pre-deps deps pre-app app
14
15 preprocess::
16
17 pre-deps::
18
19 pre-app::
20
21 include ../../erlang.mk
+0
-24
deps/mochiweb/Makefile.orig.mk less more
0 PREFIX:=../
1 DEST:=$(PREFIX)$(PROJECT)
2
3 REBAR=./rebar
4
5 .PHONY: all edoc test clean build_plt dialyzer app
6
7 all:
8 @$(REBAR) prepare-deps
9
10 edoc: all
11 @$(REBAR) doc
12
13 test:
14 @rm -rf .eunit
15 @mkdir -p .eunit
16 @$(REBAR) eunit
17
18 clean:
19 @$(REBAR) clean
20
21 app:
22 @[ -z "$(PROJECT)" ] && echo "ERROR: required variable PROJECT missing" 1>&2 && exit 1 || true
23 @$(REBAR) -r create template=mochiwebapp dest=$(DEST) appid=$(PROJECT)
+0
-17
deps/mochiweb/README less more
0 MochiWeb is an Erlang library for building lightweight HTTP servers.
1
2 The latest version of MochiWeb is available at http://github.com/mochi/mochiweb
3
4 The mailing list for MochiWeb is at http://groups.google.com/group/mochiweb/
5
6 R12B compatibility:
7 The master of MochiWeb is tested with R14A and later. A branch compatible
8 with R12B is maintained separately at http://github.com/lemenkov/mochiweb
9 The R12B branch of that repository is mirrored in the official repository
10 occasionally for convenience.
11
12 To create a new mochiweb using project:
13 make app PROJECT=project_name
14
15 To create a new mochiweb using project in a specific directory:
16 make app PROJECT=project_name PREFIX=$HOME/projects/
+0
-206
deps/mochiweb/examples/hmac_api/README less more
0 Introduction
1 ------------
2
3 This example shows how to make an Amazon-style HMAC authentication system for an API with mochiweb.
4
5 Purpose
6 -------
7
8 The purpose of this example is to:
9 * make it easy to implement an API in mochiweb
10 - using a proven approach so that 'amateurs' don't have to reinvent crypto
11 * make it easy to generate client libraries for that API so that client-side implementers can:
12 - reuse closely related code examples
13 - build compatibility unit tests instead of fiddling around debugging their library against live implementations of the system
14
15 Scope
16 -----
17
18 The scope of this document is:
19 * a description of the client-server exchange
20 * a reference implementation of
21 - the server-side implementation of the exchange
22 - the client-side implementation of the exchange
23 * developing a custom implementation of an API
24 * deploying that implementation to new client-side users to build their client libraries
25
26 Contents
27 --------
28
29 Subsequent sections of this document are:
30 * the client-server exchange
31 * the reference implementation in this example
32 * building a custom implementation
33 * deploying a custom implementation
34
35 The Client-Server Exchange
36 --------------------------
37
38 OVERVIEW
39
40 This section describes the client-server exchange for an Amazon-style API authentication schema. It has the following characteristics:
41 * based on a public key/private key
42 * used to authenticate non-SSL api requests
43 * not a full once-use schema and is vulnerable to replay attacks within a short time window
44
45 TYPE OF API
46
47 The api described in this document is:
48 * suitable for machine-machine communication
49
50 The api described in this document is NOT:
51 * an implementation of 2-legged OAUTH
52 - see https://github.com/tim/erlang-oauth
53 * an implementation of 3-legged OAUTH
54
55 It is not suitable for use in applications where an end user has to log into a service and piggy-back on top of a keypair security system.
56
57 THE CLIENT LIBRARY HERE IS **NOT** AN AMAZON CLIENT LIBRARY. AMAZON DOES FUNKY STUFF WITH HOSTNAMES AND PUSHES THEM ONTO THE URL IN CANONICALIZATION! THE CLIENT LIBRARY IS AMAZON-A-LIKE ENOUGH TO USE THE AMAZON DOCOS TO BUILD A TEST SUITE.
58
59 STEP 1
60
61 The client is issued with a pair of keys, one public, one private, for example:
62 * public: "bcaa49f2a4f7d4f92ac36c8bf66d5bb6"
63 * private: "92bc93d6b8aaec1cde772f903e06daf5"
64
65 In the Amazon docs these are referred to as:
66 * AWSAccessKeyId (public)
67 * AWSSecretAccessKey (private)
68
69 These can be generated by the function:
70 hmac_api_lib:get_api_keypair/0
71
72 This function returns cryptographically strong random numbers using the openSSL crypto library under the covers.
73
74 The public key is used as a declaration of identity, "I am bcaa49..."
75
76 The private key is never passed over the wire and is used to construct the same hash on both the client- and the server-side.
77
78 STEP 2
79
80 The client prepares their request:
81 * url
82 * time of request
83 * action (GET, POST, etc)
84 * type of request (application/json, etc)
85 * contents of request
86 * etc, etc
87
88 These components are then turned into a string called the canonical form.
89
90 The HTTP protocol is permissive; it treats different requests as if they were the same. For instance it doesn't care about the order in which headers are sent, and allows the same header to contain multiple values as a list or be specified multiple times as a key-value pair.
91
92 Intermediate machines between the client and server MAY pack and repack the HTTP request as long as they don't alter its meaning in a narrow sense. This means that the format of the HTTP request is not guaranteed to be maintained.
93
94 The canonical form simply ensures that all the valid ways of making the same request are represented by the same string - irrespective of how this is done.
95
96 The canonical form handles POST bodies and query parameters and silently discards anchors in URL's.
97
98 A hash of this string is made with the private key.
99
100 STEP 3
101
102 The client makes the request to the server:
103 * the signature is included in the request in the standard HTTPAuthorization header. (As the Amazon documentation points out this is infelicitous as it is being used for Authentication not Authorization, but hey!).
104
105 The Authorization header constructed has the form:
106 <schema name><space><public key><colon><signature>
107
108 An Amazon one looks like:
109 Authorization: AWS 0PN5J17HBGZHT7JJ3X82:frJIUN8DYpKDtOLCwo//yllqDzg=
110 --- -------------------- ----------------------------
111 sch public key signature
112
113 The HTTP request is made.
114
115 STEP 4
116
117 The request is processed:
118 * the server receives the request
119 * the server constructs the canonical form from the attributes of the request:
120 - url
121 - date header
122 - action (GET, POST, etc)
123 - content type of request (application/json, etc)
124 - some custom headers
125 - etc, etc
126 * the server takes the client's public key from the HTTPAuthorization header and looks up the client's private key
127 * the server signs the canonical form with the private key
128 * the server compares:
129 - the signature in the request to the signature it has just generated
130 - the time encoded in the request with the server time
131 * the request is accepted or denied
132
133 The time comparison is 'fuzzy'. Different server's clocks will be out of sync to a degree, the request may have acquired a time from an intermediate machine along the way, etc, etc. Normally a 'clock skew' time is allowed - in Amazon's case this is 15 minutes.
134
135 NOTA BENE: THIS CLOCK SKEW TIME ALLOWS FOR REPLAY ATTACKS WHERE A BAD GUY SIMPLY CAPTURES AND REPLAYS TRAFFIC.
136
137 EXTENSION
138
139 It is possible to extend this schema to prevent replay attacks. The server issues a nonce token (a random string) which is included in the signature. When the server authorizes the request it stores the token and prevents any request with that token (ie a replay) being authorized again.
140
141 The client receives its next nonce token in the response to a successful request.
142
143 The Reference Implementation In This Example
144 --------------------------------------------
145
146 The reference implementation used in this example is that described in the Amazon documentation here:
147 http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?RESTAuthentication.html
148
149 The try out the reference implementation:
150 * create a new mochiweb project as per the mochiweb README
151 - make app PROJECT=project_name
152 * copy hmac_api_lib.erl and hmac_api_client.erl into project_name/src
153 * copy hmac_api.hrl into project_name/include
154 * edit project_name_web.erl and add a call to hmac_api_lib:authorize_request/1
155
156 authorize/request/1 should be called in the loop of project_name_web.erl as per:
157
158 loop(Req, DocRoot) ->
159 Auth = hmac_api_lib:authorize_request(Req),
160 io:format("Auth is ~p~n", [Auth]),
161 "/" ++ Path = Req:get(path),
162 ...
163
164 When this is done you are ready to test the api:
165 * run 'make' in project_name/ to build the Erlang
166 * start the web server with 'start-dev.sh' in project_name/ (this will also open an Erlang shell to the Erlang VM)
167
168 To test the api run this command in the Erlang shell:
169 * hmac_api_client:fire().
170
171 The reference implementation uses 5 constants defined in hmac_api.hrl.
172 * schema
173 * headerprefix
174 * dateheader
175 * publickey
176 * privatekey
177
178 Building A Custom Implementation
179 --------------------------------
180
181 The simplest custom implementation is to simply take the existing code and change the values of the following constants:
182 * schema
183 * headerprefix
184 * dateheader
185
186 If the API is to be used 'as is', please use the values which are commented out in hmac_api.hrl. This will make easier for software developers to work out which version of which client-side libraries they can use.
187
188 Client libraries written in other languages than Erlang can reemployment the test suite in hmac_api_lib.erl.
189
190 More sophisticated changes will involve changes to the canonicalization functions.
191
192 Use of a generic schema should make reuse of client libraries easier across different platforms.
193
194 If you develop an ‘as-is’ client-side library in another language please consider submitting its code to this example.
195
196 Deploying A Custom Implementation
197 ---------------------------------
198
199 When deploying a custom implementation, the server-side code should be released with unit tests so the client-side developer can easily build a robust client.
200
201 In addition to that you will need to specify:
202 * description of how the API works:
203 - ie the acceptable methods and urls
204 - custom headers and their usage (if appropriate)
205
+0
-43
deps/mochiweb/examples/hmac_api/hmac_api.hrl less more
0 -author("Hypernumbers Ltd <gordon@hypernumbers.com>").
1
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %%% %%%
4 %%% Reference values for testing against Amazon documents %%%
5 %%% %%%
6 %%% These need to be changed in production! %%%
7 %%% %%%
8 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9 -define(schema, "AWS").
10 %% defines the prefix for headers to be included in the signature
11 -define(headerprefix, "x-amz-").
12 %% defines the date header
13 -define(dateheader, "x-amz-date").
14
15 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16 %%% %%%
17 %%% Default values for defining a generic API %%%
18 %%% %%%
19 %%% Only change these if you alter the canonicalisation %%%
20 %%% %%%
21 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
22 %%-define(schema, "MOCHIAPI").
23 %%-define(headerprefix, "x-mochiapi-").
24 %%-define(dateheader, "x-mochiapi-date").
25
26 %% a couple of keys for testing
27 %% these are taken from the document
28 %% % http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?RESTAuthentication.html
29 %% they are not valid keys!
30 -define(publickey, "0PN5J17HBGZHT7JJ3X82").
31 -define(privatekey, "uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o").
32
33
34 -record(hmac_signature,
35 {
36 method,
37 contentmd5,
38 contenttype,
39 date,
40 headers,
41 resource
42 }).
+0
-34
deps/mochiweb/examples/hmac_api/hmac_api_client.erl less more
0 -module(hmac_api_client).
1
2 -export([
3 fire/0
4 ]).
5
6 -include("hmac_api.hrl").
7 -author("Hypernumbers Ltd <gordon@hypernumbers.com>").
8
9 fire() ->
10 URL = "http://127.0.0.1:8080/some/page/yeah/",
11 %% Dates SHOULD conform to Section 3.3 of RFC2616
12 %% the examples from the RFC are:
13 %% Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
14 %% Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
15 %% Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
16
17 %% Dates can be conveniently generated using dh_date.erl
18 %% https://github.com/daleharvey/dh_date
19 %% which is largely compatible with
20 %% http://uk.php.net/date
21
22 %% You MIGHT find it convenient to insist on times in UTC only
23 %% as it reduces the errors caused by summer time and other
24 %% conversion issues
25 Method = post,
26 Headers = [{"content-type", "application/json"},
27 {"date", "Sun, 10 Jul 2011 05:07:19"}],
28 ContentType = "application/json",
29 Body = "blah",
30 HTTPAuthHeader = hmac_api_lib:sign(?privatekey, Method, URL,
31 Headers, ContentType),
32 httpc:request(Method, {URL, [HTTPAuthHeader | Headers],
33 ContentType, Body}, [], []).
+0
-435
deps/mochiweb/examples/hmac_api/hmac_api_lib.erl less more
0 -module(hmac_api_lib).
1
2 -include("hmac_api.hrl").
3 -include_lib("eunit/include/eunit.hrl").
4
5 -author("Hypernumbers Ltd <gordon@hypernumbers.com>").
6
7 %%% this library supports the hmac_sha api on both the client-side
8 %%% AND the server-side
9 %%%
10 %%% sign/5 is used client-side to sign a request
11 %%% - it returns an HTTPAuthorization header
12 %%%
13 %%% authorize_request/1 takes a mochiweb Request as an arguement
14 %%% and checks that the request matches the signature
15 %%%
16 %%% get_api_keypair/0 creates a pair of public/private keys
17 %%%
18 %%% THIS LIB DOESN'T IMPLEMENT THE AMAZON API IT ONLY IMPLEMENTS
19 %%% ENOUGH OF IT TO GENERATE A TEST SUITE.
20 %%%
21 %%% THE AMAZON API MUNGES HOSTNAME AND PATHS IN A CUSTOM WAY
22 %%% THIS IMPLEMENTATION DOESN'T
23 -export([
24 authorize_request/1,
25 sign/5,
26 get_api_keypair/0
27 ]).
28
29 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
30 %%% %%%
31 %%% API %%%
32 %%% %%%
33 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
34
35 authorize_request(Req) ->
36 Method = Req:get(method),
37 Path = Req:get(path),
38 Headers = normalise(mochiweb_headers:to_list(Req:get(headers))),
39 ContentMD5 = get_header(Headers, "content-md5"),
40 ContentType = get_header(Headers, "content-type"),
41 Date = get_header(Headers, "date"),
42 IncAuth = get_header(Headers, "authorization"),
43 {_Schema, _PublicKey, _Sig} = breakout(IncAuth),
44 %% normally you would use the public key to look up the private key
45 PrivateKey = ?privatekey,
46 Signature = #hmac_signature{method = Method,
47 contentmd5 = ContentMD5,
48 contenttype = ContentType,
49 date = Date,
50 headers = Headers,
51 resource = Path},
52 Signed = sign_data(PrivateKey, Signature),
53 {_, AuthHeader} = make_HTTPAuth_header(Signed),
54 case AuthHeader of
55 IncAuth -> "match";
56 _ -> "no_match"
57 end.
58
59 sign(PrivateKey, Method, URL, Headers, ContentType) ->
60 Headers2 = normalise(Headers),
61 ContentMD5 = get_header(Headers2, "content-md5"),
62 Date = get_header(Headers2, "date"),
63 Signature = #hmac_signature{method = Method,
64 contentmd5 = ContentMD5,
65 contenttype = ContentType,
66 date = Date,
67 headers = Headers,
68 resource = URL},
69 SignedSig = sign_data(PrivateKey, Signature),
70 make_HTTPAuth_header(SignedSig).
71
72
73 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
74 %%% %%%
75 %%% Internal Functions %%%
76 %%% %%%
77 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
78
79 breakout(Header) ->
80 [Schema, Tail] = string:tokens(Header, " "),
81 [PublicKey, Signature] = string:tokens(Tail, ":"),
82 {Schema, PublicKey, Signature}.
83
84 get_api_keypair() ->
85 Public = mochihex:to_hex(binary_to_list(crypto:strong_rand_bytes(16))),
86 Private = mochihex:to_hex(binary_to_list(crypto:strong_rand_bytes(16))),
87 {Public, Private}.
88
89 make_HTTPAuth_header(Signature) ->
90 {"Authorization", ?schema ++ " "
91 ++ ?publickey ++ ":" ++ Signature}.
92
93 make_signature_string(#hmac_signature{} = S) ->
94 Date = get_date(S#hmac_signature.headers, S#hmac_signature.date),
95 string:to_upper(atom_to_list(S#hmac_signature.method)) ++ "\n"
96 ++ S#hmac_signature.contentmd5 ++ "\n"
97 ++ S#hmac_signature.contenttype ++ "\n"
98 ++ Date ++ "\n"
99 ++ canonicalise_headers(S#hmac_signature.headers)
100 ++ canonicalise_resource(S#hmac_signature.resource).
101
102 sign_data(PrivateKey, #hmac_signature{} = Signature) ->
103 Str = make_signature_string(Signature),
104 sign2(PrivateKey, Str).
105
106 %% this fn is the entry point for a unit test which is why it is broken out...
107 %% if yer encryption and utf8 and base45 doo-dahs don't work then
108 %% yer Donald is well and truly Ducked so ye may as weel test it...
109 sign2(PrivateKey, Str) ->
110 Sign = xmerl_ucs:to_utf8(Str),
111 binary_to_list(base64:encode(crypto:sha_mac(PrivateKey, Sign))).
112
113 canonicalise_headers([]) -> "\n";
114 canonicalise_headers(List) when is_list(List) ->
115 List2 = [{string:to_lower(K), V} || {K, V} <- lists:sort(List)],
116 c_headers2(consolidate(List2, []), []).
117
118 c_headers2([], Acc) -> string:join(Acc, "\n") ++ "\n";
119 c_headers2([{?headerprefix ++ Rest, Key} | T], Acc) ->
120 Hd = string:strip(?headerprefix ++ Rest) ++ ":" ++ string:strip(Key),
121 c_headers2(T, [Hd | Acc]);
122 c_headers2([_H | T], Acc) -> c_headers2(T, Acc).
123
124 consolidate([H | []], Acc) -> [H | Acc];
125 consolidate([{H, K1}, {H, K2} | Rest], Acc) ->
126 consolidate([{H, join(K1, K2)} | Rest], Acc);
127 consolidate([{H1, K1}, {H2, K2} | Rest], Acc) ->
128 consolidate([{rectify(H2), rectify(K2)} | Rest], [{H1, K1} | Acc]).
129
130 join(A, B) -> string:strip(A) ++ ";" ++ string:strip(B).
131
132 %% removes line spacing as per RFC 2616 Section 4.2
133 rectify(String) ->
134 Re = "[\x20* | \t*]+",
135 re:replace(String, Re, " ", [{return, list}, global]).
136
137 canonicalise_resource("http://" ++ Rest) -> c_res2(Rest);
138 canonicalise_resource("https://" ++ Rest) -> c_res2(Rest);
139 canonicalise_resource(X) -> c_res3(X).
140
141 c_res2(Rest) ->
142 N = string:str(Rest, "/"),
143 {_, Tail} = lists:split(N, Rest),
144 c_res3("/" ++ Tail).
145
146 c_res3(Tail) ->
147 URL = case string:str(Tail, "#") of
148 0 -> Tail;
149 N -> {U, _Anchor} = lists:split(N, Tail),
150 U
151 end,
152 U3 = case string:str(URL, "?") of
153 0 -> URL;
154 N2 -> {U2, Q} = lists:split(N2, URL),
155 U2 ++ canonicalise_query(Q)
156 end,
157 string:to_lower(U3).
158
159 canonicalise_query(List) ->
160 List1 = string:to_lower(List),
161 List2 = string:tokens(List1, "&"),
162 string:join(lists:sort(List2), "&").
163
164 %% if there's a header date take it and ditch the date
165 get_date([], Date) -> Date;
166 get_date([{K, _V} | T], Date) -> case string:to_lower(K) of
167 ?dateheader -> [];
168 _ -> get_date(T, Date)
169 end.
170
171 normalise(List) -> norm2(List, []).
172
173 norm2([], Acc) -> Acc;
174 norm2([{K, V} | T], Acc) when is_atom(K) ->
175 norm2(T, [{string:to_lower(atom_to_list(K)), V} | Acc]);
176 norm2([H | T], Acc) -> norm2(T, [H | Acc]).
177
178 get_header(Headers, Type) ->
179 case lists:keyfind(Type, 1, Headers) of
180 false -> [];
181 {_K, V} -> V
182 end.
183
184
185 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
186 %%% %%%
187 %%% Unit Tests %%%
188 %%% %%%
189 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
190
191 % taken from Amazon docs
192 %% http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?RESTAuthentication.html
193 hash_test1(_) ->
194 Sig = "DELETE\n\n\n\nx-amz-date:Tue, 27 Mar 2007 21:20:26 +0000\n/johnsmith/photos/puppy.jpg",
195 Key = ?privatekey,
196 Hash = sign2(Key, Sig),
197 Expected = "k3nL7gH3+PadhTEVn5Ip83xlYzk=",
198 ?assertEqual(Expected, Hash).
199
200 %% taken from Amazon docs
201 %% http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?RESTAuthentication.html
202 hash_test2(_) ->
203 Sig = "GET\n\n\nTue, 27 Mar 2007 19:44:46 +0000\n/johnsmith/?acl",
204 Key = "uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o",
205 Hash = sign2(Key, Sig),
206 Expected = "thdUi9VAkzhkniLj96JIrOPGi0g=",
207 ?assertEqual(Expected, Hash).
208
209 %% taken from Amazon docs
210 %% http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?RESTAuthentication.html
211 hash_test3(_) ->
212 Sig = "GET\n\n\nWed, 28 Mar 2007 01:49:49 +0000\n/dictionary/"
213 ++ "fran%C3%A7ais/pr%c3%a9f%c3%a8re",
214 Key = "uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o",
215 Hash = sign2(Key, Sig),
216 Expected = "dxhSBHoI6eVSPcXJqEghlUzZMnY=",
217 ?assertEqual(Expected, Hash).
218
219 signature_test1(_) ->
220 URL = "http://example.com:90/tongs/ya/bas",
221 Method = post,
222 ContentMD5 = "",
223 ContentType = "",
224 Date = "Sun, 10 Jul 2011 05:07:19 UTC",
225 Headers = [],
226 Signature = #hmac_signature{method = Method,
227 contentmd5 = ContentMD5,
228 contenttype = ContentType,
229 date = Date,
230 headers = Headers,
231 resource = URL},
232 Sig = make_signature_string(Signature),
233 Expected = "POST\n\n\nSun, 10 Jul 2011 05:07:19 UTC\n\n/tongs/ya/bas",
234 ?assertEqual(Expected, Sig).
235
236 signature_test2(_) ->
237 URL = "http://example.com:90/tongs/ya/bas",
238 Method = get,
239 ContentMD5 = "",
240 ContentType = "",
241 Date = "Sun, 10 Jul 2011 05:07:19 UTC",
242 Headers = [{"x-amz-acl", "public-read"}],
243 Signature = #hmac_signature{method = Method,
244 contentmd5 = ContentMD5,
245 contenttype = ContentType,
246 date = Date,
247 headers = Headers,
248 resource = URL},
249 Sig = make_signature_string(Signature),
250 Expected = "GET\n\n\nSun, 10 Jul 2011 05:07:19 UTC\nx-amz-acl:public-read\n/tongs/ya/bas",
251 ?assertEqual(Expected, Sig).
252
253 signature_test3(_) ->
254 URL = "http://example.com:90/tongs/ya/bas",
255 Method = get,
256 ContentMD5 = "",
257 ContentType = "",
258 Date = "Sun, 10 Jul 2011 05:07:19 UTC",
259 Headers = [{"x-amz-acl", "public-read"},
260 {"yantze", "blast-off"},
261 {"x-amz-doobie", "bongwater"},
262 {"x-amz-acl", "public-write"}],
263 Signature = #hmac_signature{method = Method,
264 contentmd5 = ContentMD5,
265 contenttype = ContentType,
266 date = Date,
267 headers = Headers,
268 resource = URL},
269 Sig = make_signature_string(Signature),
270 Expected = "GET\n\n\nSun, 10 Jul 2011 05:07:19 UTC\nx-amz-acl:public-read;public-write\nx-amz-doobie:bongwater\n/tongs/ya/bas",
271 ?assertEqual(Expected, Sig).
272
273 signature_test4(_) ->
274 URL = "http://example.com:90/tongs/ya/bas",
275 Method = get,
276 ContentMD5 = "",
277 ContentType = "",
278 Date = "Sun, 10 Jul 2011 05:07:19 UTC",
279 Headers = [{"x-amz-acl", "public-read"},
280 {"yantze", "blast-off"},
281 {"x-amz-doobie oobie \t boobie ", "bongwater"},
282 {"x-amz-acl", "public-write"}],
283 Signature = #hmac_signature{method = Method,
284 contentmd5 = ContentMD5,
285 contenttype = ContentType,
286 date = Date,
287 headers = Headers,
288 resource = URL},
289 Sig = make_signature_string(Signature),
290 Expected = "GET\n\n\nSun, 10 Jul 2011 05:07:19 UTC\nx-amz-acl:public-read;public-write\nx-amz-doobie oobie boobie:bongwater\n/tongs/ya/bas",
291 ?assertEqual(Expected, Sig).
292
293 signature_test5(_) ->
294 URL = "http://example.com:90/tongs/ya/bas",
295 Method = get,
296 ContentMD5 = "",
297 ContentType = "",
298 Date = "Sun, 10 Jul 2011 05:07:19 UTC",
299 Headers = [{"x-amz-acl", "public-Read"},
300 {"yantze", "Blast-Off"},
301 {"x-amz-doobie Oobie \t boobie ", "bongwater"},
302 {"x-amz-acl", "public-write"}],
303 Signature = #hmac_signature{method = Method,
304 contentmd5 = ContentMD5,
305 contenttype = ContentType,
306 date = Date,
307 headers = Headers,
308 resource = URL},
309 Sig = make_signature_string(Signature),
310 Expected = "GET\n\n\nSun, 10 Jul 2011 05:07:19 UTC\nx-amz-acl:public-Read;public-write\nx-amz-doobie oobie boobie:bongwater\n/tongs/ya/bas",
311 ?assertEqual(Expected, Sig).
312
313 signature_test6(_) ->
314 URL = "http://example.com:90/tongs/ya/bas/?andy&zbish=bash&bosh=burp",
315 Method = get,
316 ContentMD5 = "",
317 ContentType = "",
318 Date = "Sun, 10 Jul 2011 05:07:19 UTC",
319 Headers = [],
320 Signature = #hmac_signature{method = Method,
321 contentmd5 = ContentMD5,
322 contenttype = ContentType,
323 date = Date,
324 headers = Headers,
325 resource = URL},
326 Sig = make_signature_string(Signature),
327 Expected = "GET\n\n\nSun, 10 Jul 2011 05:07:19 UTC\n\n"
328 ++ "/tongs/ya/bas/?andy&bosh=burp&zbish=bash",
329 ?assertEqual(Expected, Sig).
330
331 signature_test7(_) ->
332 URL = "http://exAMPLE.Com:90/tONgs/ya/bas/?ANdy&ZBish=Bash&bOsh=burp",
333 Method = get,
334 ContentMD5 = "",
335 ContentType = "",
336 Date = "Sun, 10 Jul 2011 05:07:19 UTC",
337 Headers = [],
338 Signature = #hmac_signature{method = Method,
339 contentmd5 = ContentMD5,
340 contenttype = ContentType,
341 date = Date,
342 headers = Headers,
343 resource = URL},
344 Sig = make_signature_string(Signature),
345 Expected = "GET\n\n\nSun, 10 Jul 2011 05:07:19 UTC\n\n"
346 ++"/tongs/ya/bas/?andy&bosh=burp&zbish=bash",
347 ?assertEqual(Expected, Sig).
348
349 signature_test8(_) ->
350 URL = "http://exAMPLE.Com:90/tONgs/ya/bas/?ANdy&ZBish=Bash&bOsh=burp",
351 Method = get,
352 ContentMD5 = "",
353 ContentType = "",
354 Date = "",
355 Headers = [{"x-aMz-daTe", "Tue, 27 Mar 2007 21:20:26 +0000"}],
356 Signature = #hmac_signature{method = Method,
357 contentmd5 = ContentMD5,
358 contenttype = ContentType,
359 date = Date,
360 headers = Headers,
361 resource = URL},
362 Sig = make_signature_string(Signature),
363 Expected = "GET\n\n\n\n"
364 ++"x-amz-date:Tue, 27 Mar 2007 21:20:26 +0000\n"
365 ++"/tongs/ya/bas/?andy&bosh=burp&zbish=bash",
366 ?assertEqual(Expected, Sig).
367
368 signature_test9(_) ->
369 URL = "http://exAMPLE.Com:90/tONgs/ya/bas/?ANdy&ZBish=Bash&bOsh=burp",
370 Method = get,
371 ContentMD5 = "",
372 ContentType = "",
373 Date = "Sun, 10 Jul 2011 05:07:19 UTC",
374 Headers = [{"x-amz-date", "Tue, 27 Mar 2007 21:20:26 +0000"}],
375 Signature = #hmac_signature{method = Method,
376 contentmd5 = ContentMD5,
377 contenttype = ContentType,
378 date = Date,
379 headers = Headers,
380 resource = URL},
381 Sig = make_signature_string(Signature),
382 Expected = "GET\n\n\n\n"
383 ++"x-amz-date:Tue, 27 Mar 2007 21:20:26 +0000\n"
384 ++"/tongs/ya/bas/?andy&bosh=burp&zbish=bash",
385 ?assertEqual(Expected, Sig).
386
387 amazon_test1(_) ->
388 URL = "http://exAMPLE.Com:90/johnsmith/photos/puppy.jpg",
389 Method = delete,
390 ContentMD5 = "",
391 ContentType = "",
392 Date = "",
393 Headers = [{"x-amz-date", "Tue, 27 Mar 2007 21:20:26 +0000"}],
394 Signature = #hmac_signature{method = Method,
395 contentmd5 = ContentMD5,
396 contenttype = ContentType,
397 date = Date,
398 headers = Headers,
399 resource = URL},
400 Sig = sign_data(?privatekey, Signature),
401 Expected = "k3nL7gH3+PadhTEVn5Ip83xlYzk=",
402 ?assertEqual(Expected, Sig).
403
404 unit_test_() ->
405 Setup = fun() -> ok end,
406 Cleanup = fun(_) -> ok end,
407
408 Series1 = [
409 fun hash_test1/1,
410 fun hash_test2/1,
411 fun hash_test3/1
412 ],
413
414 Series2 = [
415 fun signature_test1/1,
416 fun signature_test2/1,
417 fun signature_test3/1,
418 fun signature_test4/1,
419 fun signature_test5/1,
420 fun signature_test6/1,
421 fun signature_test7/1,
422 fun signature_test8/1,
423 fun signature_test9/1
424 ],
425
426 Series3 = [
427 fun amazon_test1/1
428 ],
429
430 {setup, Setup, Cleanup, [
431 {with, [], Series1},
432 {with, [], Series2},
433 {with, [], Series3}
434 ]}.
+0
-146
deps/mochiweb/examples/https/https_store.erl less more
0
1 %% Trivial web storage app. It's available over both HTTP (port 8442)
2 %% and HTTPS (port 8443). You use a PUT to store items, a GET to
3 %% retrieve them and DELETE to delete them. The HTTP POST method is
4 %% invalid for this application. Example (using HTTPS transport):
5 %%
6 %% $ curl -k --verbose https://localhost:8443/flintstones
7 %% ...
8 %% 404 Not Found
9 %% ...
10 %% $ echo -e "Fred\nWilma\nBarney" |
11 %% curl -k --verbose https://localhost:8443/flintstones \
12 %% -X PUT -H "Content-Type: text/plain" --data-binary @-
13 %% ...
14 %% 201 Created
15 %% ...
16 %% $ curl -k --verbose https://localhost:8443/flintstones
17 %% ...
18 %% Fred
19 %% Wilma
20 %% Barney
21 %% ...
22 %% $ curl -k --verbose https://localhost:8443/flintstones -X DELETE
23 %% ...
24 %% 200 OK
25 %% ...
26 %% $ curl -k --verbose https://localhost:8443/flintstones
27 %% ...
28 %% 404 Not Found
29 %% ...
30 %%
31 %% All submitted data is stored in memory (in an ets table). Could be
32 %% useful for ad-hoc testing.
33
34 -module(https_store).
35
36 -export([start/0,
37 stop/0,
38 dispatch/1,
39 loop/1
40 ]).
41
42 -define(HTTP_OPTS, [
43 {loop, {?MODULE, dispatch}},
44 {port, 8442},
45 {name, http_8442}
46 ]).
47
48 -define(HTTPS_OPTS, [
49 {loop, {?MODULE, dispatch}},
50 {port, 8443},
51 {name, https_8443},
52 {ssl, true},
53 {ssl_opts, [
54 {certfile, "server_cert.pem"},
55 {keyfile, "server_key.pem"}]}
56 ]).
57
58 -record(sd, {http, https}).
59 -record(resource, {type, data}).
60
61 start() ->
62 {ok, Http} = mochiweb_http:start(?HTTP_OPTS),
63 {ok, Https} = mochiweb_http:start(?HTTPS_OPTS),
64 SD = #sd{http=Http, https=Https},
65 Pid = spawn_link(fun() ->
66 ets:new(?MODULE, [named_table]),
67 loop(SD)
68 end),
69 register(http_store, Pid),
70 ok.
71
72 stop() ->
73 http_store ! stop,
74 ok.
75
76 dispatch(Req) ->
77 case Req:get(method) of
78 'GET' ->
79 get_resource(Req);
80 'PUT' ->
81 put_resource(Req);
82 'DELETE' ->
83 delete_resource(Req);
84 _ ->
85 Headers = [{"Allow", "GET,PUT,DELETE"}],
86 Req:respond({405, Headers, "405 Method Not Allowed\r\n"})
87 end.
88
89 get_resource(Req) ->
90 Path = Req:get(path),
91 case ets:lookup(?MODULE, Path) of
92 [{Path, #resource{type=Type, data=Data}}] ->
93 Req:ok({Type, Data});
94 [] ->
95 Req:respond({404, [], "404 Not Found\r\n"})
96 end.
97
98 put_resource(Req) ->
99 ContentType = case Req:get_header_value("Content-Type") of
100 undefined ->
101 "application/octet-stream";
102 S ->
103 S
104 end,
105 Resource = #resource{type=ContentType, data=Req:recv_body()},
106 http_store ! {self(), {put, Req:get(path), Resource}},
107 Pid = whereis(http_store),
108 receive
109 {Pid, created} ->
110 Req:respond({201, [], "201 Created\r\n"});
111 {Pid, updated} ->
112 Req:respond({200, [], "200 OK\r\n"})
113 end.
114
115 delete_resource(Req) ->
116 http_store ! {self(), {delete, Req:get(path)}},
117 Pid = whereis(http_store),
118 receive
119 {Pid, ok} ->
120 Req:respond({200, [], "200 OK\r\n"})
121 end.
122
123 loop(#sd{http=Http, https=Https} = SD) ->
124 receive
125 stop ->
126 ok = mochiweb_http:stop(Http),
127 ok = mochiweb_http:stop(Https),
128 exit(normal);
129 {From, {put, Key, Val}} ->
130 Exists = ets:member(?MODULE, Key),
131 ets:insert(?MODULE, {Key, Val}),
132 case Exists of
133 true ->
134 From ! {self(), updated};
135 false ->
136 From ! {self(), created}
137 end;
138 {From, {delete, Key}} ->
139 ets:delete(?MODULE, Key),
140 From ! {self(), ok};
141 _ ->
142 ignore
143 end,
144 ?MODULE:loop(SD).
145
+0
-19
deps/mochiweb/examples/https/server_cert.pem less more
0 -----BEGIN CERTIFICATE-----
1 MIIDIDCCAgigAwIBAgIJAJLkNZzERPIUMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV
2 BAMTCWxvY2FsaG9zdDAeFw0xMDAzMTgxOTM5MThaFw0yMDAzMTUxOTM5MThaMBQx
3 EjAQBgNVBAMTCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
4 ggEBAJeUCOZxbmtngF4S5lXckjSDLc+8C+XjMBYBPyy5eKdJY20AQ1s9/hhp3ulI
5 8pAvl+xVo4wQ+iBSvOzcy248Q+Xi6+zjceF7UNRgoYPgtJjKhdwcHV3mvFFrS/fp
6 9ggoAChaJQWDO1OCfUgTWXImhkw+vcDR11OVMAJ/h73dqzJPI9mfq44PTTHfYtgr
7 v4LAQAOlhXIAa2B+a6PlF6sqDqJaW5jLTcERjsBwnRhUGi7JevQzkejujX/vdA+N
8 jRBjKH/KLU5h3Q7wUchvIez0PXWVTCnZjpA9aR4m7YV05nKQfxtGd71czYDYk+j8
9 hd005jetT4ir7JkAWValBybJVksCAwEAAaN1MHMwHQYDVR0OBBYEFJl9s51SnjJt
10 V/wgKWqV5Q6jnv1ZMEQGA1UdIwQ9MDuAFJl9s51SnjJtV/wgKWqV5Q6jnv1ZoRik
11 FjAUMRIwEAYDVQQDEwlsb2NhbGhvc3SCCQCS5DWcxETyFDAMBgNVHRMEBTADAQH/
12 MA0GCSqGSIb3DQEBBQUAA4IBAQB2ldLeLCc+lxK5i0EZquLamMBJwDIjGpT0JMP9
13 b4XQOK2JABIu54BQIZhwcjk3FDJz/uOW5vm8k1kYni8FCjNZAaRZzCUfiUYTbTKL
14 Rq9LuIAODyP2dnTqyKaQOOJHvrx9MRZ3XVecXPS0Tib4aO57vCaAbIkmhtYpTWmw
15 e3t8CAIDVtgvjR6Se0a1JA4LktR7hBu22tDImvCSJn1nVAaHpani6iPBPPdMuMsP
16 TBoeQfj8VpqBUjCStqJGa8ytjDFX73YaxV2mgrtGwPNme1x3YNRR11yTu7tksyMO
17 GrmgxNriqYRchBhNEf72AKF0LR1ByKwfbDB9rIsV00HtCgOp
18 -----END CERTIFICATE-----
+0
-27
deps/mochiweb/examples/https/server_key.pem less more
0 -----BEGIN RSA PRIVATE KEY-----
1 MIIEpAIBAAKCAQEAl5QI5nFua2eAXhLmVdySNIMtz7wL5eMwFgE/LLl4p0ljbQBD
2 Wz3+GGne6UjykC+X7FWjjBD6IFK87NzLbjxD5eLr7ONx4XtQ1GChg+C0mMqF3Bwd
3 Xea8UWtL9+n2CCgAKFolBYM7U4J9SBNZciaGTD69wNHXU5UwAn+Hvd2rMk8j2Z+r
4 jg9NMd9i2Cu/gsBAA6WFcgBrYH5ro+UXqyoOolpbmMtNwRGOwHCdGFQaLsl69DOR
5 6O6Nf+90D42NEGMof8otTmHdDvBRyG8h7PQ9dZVMKdmOkD1pHibthXTmcpB/G0Z3
6 vVzNgNiT6PyF3TTmN61PiKvsmQBZVqUHJslWSwIDAQABAoIBACI8Ky5xHDFh9RpK
7 Rn/KC7OUlTpADKflgizWJ0Cgu2F9L9mkn5HyFHvLHa+u7CootbWJOiEejH/UcBtH
8 WyMQtX0snYCpdkUpJv5wvMoebGu+AjHOn8tfm9T/2O6rhwgckLyMb6QpGbMo28b1
9 p9QiY17BJPZx7qJQJcHKsAvwDwSThlb7MFmWf42LYWlzybpeYQvwpd+UY4I0WXLu
10 /dqJIS9Npq+5Y5vbo2kAEAssb2hSCvhCfHmwFdKmBzlvgOn4qxgZ1iHQgfKI6Z3Y
11 J0573ZgOVTuacn+lewtdg5AaHFcl/zIYEr9SNqRoPNGbPliuv6k6N2EYcufWL5lR
12 sCmmmHECgYEAxm+7OpepGr++K3+O1e1MUhD7vSPkKJrCzNtUxbOi2NWj3FFUSPRU
13 adWhuxvUnZgTcgM1+KuQ0fB2VmxXe9IDcrSFS7PKFGtd2kMs/5mBw4UgDZkOQh+q
14 kDiBEV3HYYJWRq0w3NQ/9Iy1jxxdENHtGmG9aqamHxNtuO608wGW2S8CgYEAw4yG
15 ZyAic0Q/U9V2OHI0MLxLCzuQz17C2wRT1+hBywNZuil5YeTuIt2I46jro6mJmWI2
16 fH4S/geSZzg2RNOIZ28+aK79ab2jWBmMnvFCvaru+odAuser4N9pfAlHZvY0pT+S
17 1zYX3f44ygiio+oosabLC5nWI0zB2gG8pwaJlaUCgYEAgr7poRB+ZlaCCY0RYtjo
18 mYYBKD02vp5BzdKSB3V1zeLuBWM84pjB6b3Nw0fyDig+X7fH3uHEGN+USRs3hSj6
19 BqD01s1OT6fyfbYXNw5A1r+nP+5h26Wbr0zblcKxdQj4qbbBZC8hOJNhqTqqA0Qe
20 MmzF7jiBaiZV/Cyj4x1f9BcCgYEAhjL6SeuTuOctTqs/5pz5lDikh6DpUGcH8qaV
21 o6aRAHHcMhYkZzpk8yh1uUdD7516APmVyvn6rrsjjhLVq4ZAJjwB6HWvE9JBN0TR
22 bILF+sREHUqU8Zn2Ku0nxyfXCKIOnxlx/J/y4TaGYqBqfXNFWiXNUrjQbIlQv/xR
23 K48g/MECgYBZdQlYbMSDmfPCC5cxkdjrkmAl0EgV051PWAi4wR+hLxIMRjHBvAk7
24 IweobkFvT4TICulgroLkYcSa5eOZGxB/DHqcQCbWj3reFV0VpzmTDoFKG54sqBRl
25 vVntGt0pfA40fF17VoS7riAdHF53ippTtsovHEsg5tq5NrBl5uKm2g==
26 -----END RSA PRIVATE KEY-----
+0
-81
deps/mochiweb/examples/keepalive/keepalive.erl less more
0 -module(keepalive).
1
2 %% your web app can push data to clients using a technique called comet long
3 %% polling. browsers make a request and your server waits to send a
4 %% response until data is available. see wikipedia for a better explanation:
5 %% http://en.wikipedia.org/wiki/Comet_(programming)#Ajax_with_long_polling
6 %%
7 %% since the majority of your http handlers will be idle at any given moment,
8 %% you might consider making them hibernate while they wait for more data from
9 %% another process. however, since the execution stack is discarded when a
10 %% process hibernates, the handler would usually terminate after your response
11 %% code runs. this means http keep alives wouldn't work; the handler process
12 %% would terminate after each response and close its socket rather than
13 %% returning to the big @mochiweb_http@ loop and processing another request.
14 %%
15 %% however, if mochiweb exposes a continuation that encapsulates the return to
16 %% the top of the big loop in @mochiweb_http@, we can call that after the
17 %% response. if you do that then control flow returns to the proper place,
18 %% and keep alives work like they would if you hadn't hibernated.
19
20 -export([ start/1, loop/1
21 ]).
22
23 %% internal export (so hibernate can reach it)
24 -export([ resume/3
25 ]).
26
27 -define(LOOP, {?MODULE, loop}).
28
29 start(Options = [{port, _Port}]) ->
30 mochiweb_http:start([{name, ?MODULE}, {loop, ?LOOP} | Options]).
31
32 loop(Req) ->
33 Path = Req:get(path),
34 case string:tokens(Path, "/") of
35 ["longpoll" | RestOfPath] ->
36 %% the "reentry" is a continuation -- what @mochiweb_http@
37 %% needs to do to start its loop back at the top
38 Reentry = mochiweb_http:reentry(?LOOP),
39
40 %% here we could send a message to some other process and hope
41 %% to get an interesting message back after a while. for
42 %% simplicity let's just send ourselves a message after a few
43 %% seconds
44 erlang:send_after(2000, self(), "honk honk"),
45
46 %% since we expect to wait for a long time before getting a
47 %% reply, let's hibernate. memory usage will be minimized, so
48 %% we won't be wasting memory just sitting in a @receive@
49 proc_lib:hibernate(?MODULE, resume, [Req, RestOfPath, Reentry]),
50
51 %% we'll never reach this point, and this function @loop/1@
52 %% won't ever return control to @mochiweb_http@. luckily
53 %% @resume/3@ will take care of that.
54 io:format("not gonna happen~n", []);
55
56 _ ->
57 ok(Req, io_lib:format("some other page: ~p", [Path]))
58 end,
59
60 io:format("restarting loop normally in ~p~n", [Path]),
61 ok.
62
63 %% this is the function that's called when a message arrives.
64 resume(Req, RestOfPath, Reentry) ->
65 receive
66 Msg ->
67 Text = io_lib:format("wake up message: ~p~nrest of path: ~p", [Msg, RestOfPath]),
68 ok(Req, Text)
69 end,
70
71 %% if we didn't call @Reentry@ here then the function would finish and the
72 %% process would exit. calling @Reentry@ takes care of returning control
73 %% to @mochiweb_http@
74 io:format("reentering loop via continuation in ~p~n", [Req:get(path)]),
75 Reentry(Req).
76
77 ok(Req, Response) ->
78 Req:ok({_ContentType = "text/plain",
79 _Headers = [],
80 Response}).
+0
-59
deps/mochiweb/examples/websocket/index.html less more
0 <!doctype html>
1 <html>
2 <head>
3 <title>Websockets With Mochiweb Demo</title>
4 </head>
5 <body>
6 <h1>Mochiweb websocket demo</h1>
7
8 <div id="connect">
9 <button id="btnConn">Connect</button>
10 &nbsp; State: <span id="connstate" style="font-weight:bold;"></span>
11 </div>
12 <br/><i>Protip: open your javascript error console, just in case..</i><br/>
13 <hr/>
14 <div id="connected">
15 <form id="sendForm">
16 <input id="phrase" type="text"/>
17 <input id="btnSend" class="button" type="submit" name="connect"
18 value="Send"/>
19 </form>
20 </div>
21 <hr/>
22 <div id="msgs"></div>
23
24 <script type="text/javascript">
25 var ws;
26 if (!window.WebSocket) {
27 alert("WebSocket not supported by this browser");
28 }
29 function $(id) {
30 return document.getElementById(id);
31 }
32 function go() {
33 ws = new WebSocket("ws://" + location.host + "/");
34 ws.onopen = function () {
35 $('connstate').innerHTML = 'CONNECTED';
36 }
37 ws.onclose = function () {
38 $('connstate').innerHTML = 'CLOSED';
39 }
40 ws.onmessage = function (e) {
41 var p = document.createElement('pre');
42 p.appendChild(document.createTextNode(e.data));
43 $('msgs').appendChild(p);
44 }
45 }
46 $('sendForm').onsubmit = function (event) {
47 var p = $('phrase');
48 ws.send(p.value);
49 p.value='';
50 return false;
51 }
52 $('btnConn').onclick = function(event) {
53 go(); return false;
54 };
55 </script>
56 </body>
57 </html>
58
+0
-148
deps/mochiweb/examples/websocket/websocket.erl less more
0 -module(websocket).
1
2 %% To run: erlc websocket.erl && erl -pa ../../ebin -s websocket
3
4 %% The MIT License (MIT)
5
6 %% Copyright (c) 2012 Zadane.pl sp. z o.o.
7
8 %% Permission is hereby granted, free of charge, to any person obtaining a copy
9 %% of this software and associated documentation files (the "Software"), to deal
10 %% in the Software without restriction, including without limitation the rights
11 %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 %% copies of the Software, and to permit persons to whom the Software is
13 %% furnished to do so, subject to the following conditions:
14
15 %% The above copyright notice and this permission notice shall be included in
16 %% all copies or substantial portions of the Software.
17
18 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 %% THE SOFTWARE.
25
26 -export([start/0, start_link/0, ws_loop/3, loop/2]).
27 -export([broadcast_server/1]).
28
29 %%
30 %% Mochiweb websocket example
31 %%
32 %% [1]: At first you have to start HTTP server which will listen for HTTP
33 %% requests and eventually upgrade connection to websocket
34 %% [2]: Attempt to upgrade connection to websocket.
35 %% Function mochiweb_websocket:upgrade_connection/2:
36 %% * first argument is mochiweb_request
37 %% * second is M:F which will handle further websocket messages.
38 %% Function return two funs:
39 %% * ReentryWs/1 - use it to enter to messages handling loop
40 %% (in this example ws_loop/3)
41 %% * ReplyChannel/1 - use to send messages to client. May be passed to
42 %% other processes
43 %% [3]: Example of sending message to client
44 %% [4]: State that will be passed to message handling loop
45 %% [5]: Pass control to messages handling loop. From this moment each message
46 %% received from client can be handled...
47 %% [6]: ...here as Payload. State is variable intended for holding your custom
48 %% state. ReplyChannel is the same function as in [3].
49 %% Notice! Payload is list of messages received from client. Websocket
50 %% framing mechanism concatenates messages which are sent one after another
51 %% in short time.
52 %% [7]: Print payload received from client and send it back
53 %% [8]: Message handling function must return new state value
54 start() ->
55 spawn(
56 fun () ->
57 application:start(sasl),
58 start_link(),
59 receive
60 stop -> ok
61 end
62 end).
63
64 start_link() ->
65 %% [1]
66 io:format("Listening at http://127.0.0.1:8080/~n"),
67 Broadcaster = spawn_link(?MODULE, broadcast_server, [dict:new()]),
68 mochiweb_http:start_link([
69 {name, client_access},
70 {loop, {?MODULE, loop, [Broadcaster]}},
71 {port, 8080}
72 ]).
73
74 ws_loop(Payload, Broadcaster, _ReplyChannel) ->
75 %% [6]
76
77 %% [7]
78 io:format("Received data: ~p~n", [Payload]),
79 Received = list_to_binary(Payload),
80 Broadcaster ! {broadcast, self(), Received},
81
82 %% [8]
83 Broadcaster.
84
85 loop(Req, Broadcaster) ->
86 H = mochiweb_request:get_header_value("Upgrade", Req),
87 loop(Req,
88 Broadcaster,
89 H =/= undefined andalso string:to_lower(H) =:= "websocket").
90
91 loop(Req, _Broadcaster, false) ->
92 mochiweb_request:serve_file("index.html", "./", Req);
93 loop(Req, Broadcaster, true) ->
94 {ReentryWs, ReplyChannel} = mochiweb_websocket:upgrade_connection(
95 Req, fun ?MODULE:ws_loop/3),
96 %% [3]
97 Broadcaster ! {register, self(), ReplyChannel},
98 %% [4]
99 %% [5]
100 ReentryWs(Broadcaster).
101
102
103 %% This server keeps track of connected pids
104 broadcast_server(Pids) ->
105 Pids1 = receive
106 {register, Pid, Channel} ->
107 broadcast_register(Pid, Channel, Pids);
108 {broadcast, Pid, Message} ->
109 broadcast_sendall(Pid, Message, Pids);
110 {'DOWN', MRef, process, Pid, _Reason} ->
111 broadcast_down(Pid, MRef, Pids);
112 Msg ->
113 io:format("Unknown message: ~p~n", [Msg]),
114 Pids
115 end,
116 erlang:hibernate(?MODULE, broadcast_server, [Pids1]).
117
118 broadcast_register(Pid, Channel, Pids) ->
119 MRef = erlang:monitor(process, Pid),
120 broadcast_sendall(
121 Pid, "connected", dict:store(Pid, {Channel, MRef}, Pids)).
122
123 broadcast_down(Pid, MRef, Pids) ->
124 Pids1 = case dict:find(Pid, Pids) of
125 {ok, {_, MRef}} ->
126 dict:erase(Pid, Pids);
127 _ ->
128 Pids
129 end,
130 broadcast_sendall(Pid, "disconnected", Pids1).
131
132 broadcast_sendall(Pid, Msg, Pids) ->
133 M = iolist_to_binary([pid_to_list(Pid), ": ", Msg]),
134 dict:fold(
135 fun (K, {Reply, MRef}, Acc) ->
136 try
137 begin
138 Reply(M),
139 dict:store(K, {Reply, MRef}, Acc)
140 end
141 catch
142 _:_ ->
143 Acc
144 end
145 end,
146 dict:new(),
147 Pids).
+0
-3
deps/mochiweb/include/internal.hrl less more
0
1 -define(RECBUF_SIZE, 8192).
2
deps/mochiweb/rebar less more
Binary diff not shown
+0
-17
deps/mochiweb/rebar.config less more
0 % -*- mode: erlang -*-
1 {erl_opts, [debug_info,
2 {platform_define, "R15", 'gen_tcp_r15b_workaround'},
3 {platform_define, "(R14|R15|R16B-)", 'crypto_compatibility'}]}.
4 {cover_enabled, true}.
5 {eunit_opts, [verbose, {report,{eunit_surefire,[{dir,"."}]}}]}.
6 {dialyzer_opts, [{warnings, [no_return,
7 no_unused,
8 no_improper_lists,
9 no_fun_app,
10 no_match,
11 no_opaque,
12 no_fail_call,
13 error_handling,
14 race_conditions,
15 behaviours,
16 unmatched_returns]}]}.
+0
-45
deps/mochiweb/scripts/entities.erl less more
0 #!/usr/bin/env escript
1 %% -*- mode: erlang -*-
2 -export([main/1]).
3
4 %% @doc Script used to generate mochiweb_charref.erl table.
5
6 main(_) ->
7 application:start(inets),
8 code:add_patha("ebin"),
9 {ok, {_, _, HTML}} = httpc:request("http://www.w3.org/TR/html5/named-character-references.html"),
10 print(lists:sort(search(mochiweb_html:parse(HTML)))).
11
12 print([F | T]) ->
13 io:put_chars([clause(F), ";\n"]),
14 print(T);
15 print([]) ->
16 io:put_chars(["entity(_) -> undefined.\n"]),
17 ok.
18
19 clause({Title, [Codepoint]}) ->
20 ["entity(\"", Title, "\") -> 16#", Codepoint];
21 clause({Title, [First | Rest]}) ->
22 ["entity(\"", Title, "\") -> [16#", First,
23 [[", 16#", Codepoint] || Codepoint <- Rest],
24 "]"].
25
26
27 search(Elem) ->
28 search(Elem, []).
29
30 search({<<"tr">>, [{<<"id">>, <<"entity-", _/binary>>} | _], Children}, Acc) ->
31 %% HTML5 charrefs can have more than one code point(!)
32 [{<<"td">>, _, [{<<"code">>, _, [TitleSemi]}]},
33 {<<"td">>, [], [RawCPs]} | _] = Children,
34 L = byte_size(TitleSemi) - 1,
35 <<Title:L/binary, $;>> = TitleSemi,
36 {match, Matches} = re:run(RawCPs, "(?:\\s*U\\+)([a-fA-F0-9]+)",
37 [{capture, all, binary}, global]),
38 [{Title, [CP || [_, CP] <- Matches]} | Acc];
39 search({Tag, Attrs, [H | T]}, Acc) ->
40 search({Tag, Attrs, T}, search(H, Acc));
41 search({_Tag, _Attrs, []}, Acc) ->
42 Acc;
43 search(<<_/binary>>, Acc) ->
44 Acc.
+0
-443
deps/mochiweb/src/mochifmt.erl less more
0 %% @author Bob Ippolito <bob@mochimedia.com>
1 %% @copyright 2008 Mochi Media, Inc.
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 %% @doc String Formatting for Erlang, inspired by Python 2.6
22 %% (<a href="http://www.python.org/dev/peps/pep-3101/">PEP 3101</a>).
23 %%
24 -module(mochifmt).
25 -author('bob@mochimedia.com').
26 -export([format/2, format_field/2, convert_field/2, get_value/2, get_field/2]).
27 -export([tokenize/1, format/3, get_field/3, format_field/3]).
28 -export([bformat/2, bformat/3]).
29 -export([f/2, f/3]).
30
31 -record(conversion, {length, precision, ctype, align, fill_char, sign}).
32
33 %% @spec tokenize(S::string()) -> tokens()
34 %% @doc Tokenize a format string into mochifmt's internal format.
35 tokenize(S) ->
36 {?MODULE, tokenize(S, "", [])}.
37
38 %% @spec convert_field(Arg, Conversion::conversion()) -> term()
39 %% @doc Process Arg according to the given explicit conversion specifier.
40 convert_field(Arg, "") ->
41 Arg;
42 convert_field(Arg, "r") ->
43 repr(Arg);
44 convert_field(Arg, "s") ->
45 str(Arg).
46
47 %% @spec get_value(Key::string(), Args::args()) -> term()
48 %% @doc Get the Key from Args. If Args is a tuple then convert Key to
49 %% an integer and get element(1 + Key, Args). If Args is a list and Key
50 %% can be parsed as an integer then use lists:nth(1 + Key, Args),
51 %% otherwise try and look for Key in Args as a proplist, converting
52 %% Key to an atom or binary if necessary.
53 get_value(Key, Args) when is_tuple(Args) ->
54 element(1 + list_to_integer(Key), Args);
55 get_value(Key, Args) when is_list(Args) ->
56 try lists:nth(1 + list_to_integer(Key), Args)
57 catch error:_ ->
58 {_K, V} = proplist_lookup(Key, Args),
59 V
60 end.
61
62 %% @spec get_field(Key::string(), Args) -> term()
63 %% @doc Consecutively call get_value/2 on parts of Key delimited by ".",
64 %% replacing Args with the result of the previous get_value. This
65 %% is used to implement formats such as {0.0}.
66 get_field(Key, Args) ->
67 get_field(Key, Args, ?MODULE).
68
69 %% @spec get_field(Key::string(), Args, Module) -> term()
70 %% @doc Consecutively call Module:get_value/2 on parts of Key delimited by ".",
71 %% replacing Args with the result of the previous get_value. This
72 %% is used to implement formats such as {0.0}.
73 get_field(Key, Args, Module) ->
74 {Name, Next} = lists:splitwith(fun (C) -> C =/= $. end, Key),
75 Res = try Module:get_value(Name, Args)
76 catch error:undef -> get_value(Name, Args) end,
77 case Next of
78 "" ->
79 Res;
80 "." ++ S1 ->
81 get_field(S1, Res, Module)
82 end.
83
84 %% @spec format(Format::string(), Args) -> iolist()
85 %% @doc Format Args with Format.
86 format(Format, Args) ->
87 format(Format, Args, ?MODULE).
88
89 %% @spec format(Format::string(), Args, Module) -> iolist()
90 %% @doc Format Args with Format using Module.
91 format({?MODULE, Parts}, Args, Module) ->
92 format2(Parts, Args, Module, []);
93 format(S, Args, Module) ->
94 format(tokenize(S), Args, Module).
95
96 %% @spec format_field(Arg, Format) -> iolist()
97 %% @doc Format Arg with Format.
98 format_field(Arg, Format) ->
99 format_field(Arg, Format, ?MODULE).
100
101 %% @spec format_field(Arg, Format, _Module) -> iolist()
102 %% @doc Format Arg with Format.
103 format_field(Arg, Format, _Module) ->
104 F = default_ctype(Arg, parse_std_conversion(Format)),
105 fix_padding(fix_sign(convert2(Arg, F), F), F).
106
107 %% @spec f(Format::string(), Args) -> string()
108 %% @doc Format Args with Format and return a string().
109 f(Format, Args) ->
110 f(Format, Args, ?MODULE).
111
112 %% @spec f(Format::string(), Args, Module) -> string()
113 %% @doc Format Args with Format using Module and return a string().
114 f(Format, Args, Module) ->
115 case lists:member(${, Format) of
116 true ->
117 binary_to_list(bformat(Format, Args, Module));
118 false ->
119 Format
120 end.
121
122 %% @spec bformat(Format::string(), Args) -> binary()
123 %% @doc Format Args with Format and return a binary().
124 bformat(Format, Args) ->
125 iolist_to_binary(format(Format, Args)).
126
127 %% @spec bformat(Format::string(), Args, Module) -> binary()
128 %% @doc Format Args with Format using Module and return a binary().
129 bformat(Format, Args, Module) ->
130 iolist_to_binary(format(Format, Args, Module)).
131
132 %% Internal API
133
134 add_raw("", Acc) ->
135 Acc;
136 add_raw(S, Acc) ->
137 [{raw, lists:reverse(S)} | Acc].
138
139 tokenize([], S, Acc) ->
140 lists:reverse(add_raw(S, Acc));
141 tokenize("{{" ++ Rest, S, Acc) ->
142 tokenize(Rest, "{" ++ S, Acc);
143 tokenize("{" ++ Rest, S, Acc) ->
144 {Format, Rest1} = tokenize_format(Rest),
145 tokenize(Rest1, "", [{format, make_format(Format)} | add_raw(S, Acc)]);
146 tokenize("}}" ++ Rest, S, Acc) ->
147 tokenize(Rest, "}" ++ S, Acc);
148 tokenize([C | Rest], S, Acc) ->
149 tokenize(Rest, [C | S], Acc).
150
151 tokenize_format(S) ->
152 tokenize_format(S, 1, []).
153
154 tokenize_format("}" ++ Rest, 1, Acc) ->
155 {lists:reverse(Acc), Rest};
156 tokenize_format("}" ++ Rest, N, Acc) ->
157 tokenize_format(Rest, N - 1, "}" ++ Acc);
158 tokenize_format("{" ++ Rest, N, Acc) ->
159 tokenize_format(Rest, 1 + N, "{" ++ Acc);
160 tokenize_format([C | Rest], N, Acc) ->
161 tokenize_format(Rest, N, [C | Acc]).
162
163 make_format(S) ->
164 {Name0, Spec} = case lists:splitwith(fun (C) -> C =/= $: end, S) of
165 {_, ""} ->
166 {S, ""};
167 {SN, ":" ++ SS} ->
168 {SN, SS}
169 end,
170 {Name, Transform} = case lists:splitwith(fun (C) -> C =/= $! end, Name0) of
171 {_, ""} ->
172 {Name0, ""};
173 {TN, "!" ++ TT} ->
174 {TN, TT}
175 end,
176 {Name, Transform, Spec}.
177
178 proplist_lookup(S, P) ->
179 A = try list_to_existing_atom(S)
180 catch error:_ -> make_ref() end,
181 B = try list_to_binary(S)
182 catch error:_ -> make_ref() end,
183 proplist_lookup2({S, A, B}, P).
184
185 proplist_lookup2({KS, KA, KB}, [{K, V} | _])
186 when KS =:= K orelse KA =:= K orelse KB =:= K ->
187 {K, V};
188 proplist_lookup2(Keys, [_ | Rest]) ->
189 proplist_lookup2(Keys, Rest).
190
191 format2([], _Args, _Module, Acc) ->
192 lists:reverse(Acc);
193 format2([{raw, S} | Rest], Args, Module, Acc) ->
194 format2(Rest, Args, Module, [S | Acc]);
195 format2([{format, {Key, Convert, Format0}} | Rest], Args, Module, Acc) ->
196 Format = f(Format0, Args, Module),
197 V = case Module of
198 ?MODULE ->
199 V0 = get_field(Key, Args),
200 V1 = convert_field(V0, Convert),
201 format_field(V1, Format);
202 _ ->
203 V0 = try Module:get_field(Key, Args)
204 catch error:undef -> get_field(Key, Args, Module) end,
205 V1 = try Module:convert_field(V0, Convert)
206 catch error:undef -> convert_field(V0, Convert) end,
207 try Module:format_field(V1, Format)
208 catch error:undef -> format_field(V1, Format, Module) end
209 end,
210 format2(Rest, Args, Module, [V | Acc]).
211
212 default_ctype(_Arg, C=#conversion{ctype=N}) when N =/= undefined ->
213 C;
214 default_ctype(Arg, C) when is_integer(Arg) ->
215 C#conversion{ctype=decimal};
216 default_ctype(Arg, C) when is_float(Arg) ->
217 C#conversion{ctype=general};
218 default_ctype(_Arg, C) ->
219 C#conversion{ctype=string}.
220
221 fix_padding(Arg, #conversion{length=undefined}) ->
222 Arg;
223 fix_padding(Arg, F=#conversion{length=Length, fill_char=Fill0, align=Align0,
224 ctype=Type}) ->
225 Padding = Length - iolist_size(Arg),
226 Fill = case Fill0 of
227 undefined ->
228 $\s;
229 _ ->
230 Fill0
231 end,
232 Align = case Align0 of
233 undefined ->
234 case Type of
235 string ->
236 left;
237 _ ->
238 right
239 end;
240 _ ->
241 Align0
242 end,
243 case Padding > 0 of
244 true ->
245 do_padding(Arg, Padding, Fill, Align, F);
246 false ->
247 Arg
248 end.
249
250 do_padding(Arg, Padding, Fill, right, _F) ->
251 [lists:duplicate(Padding, Fill), Arg];
252 do_padding(Arg, Padding, Fill, center, _F) ->
253 LPadding = lists:duplicate(Padding div 2, Fill),
254 RPadding = case Padding band 1 of
255 1 ->
256 [Fill | LPadding];
257 _ ->
258 LPadding
259 end,
260 [LPadding, Arg, RPadding];
261 do_padding([$- | Arg], Padding, Fill, sign_right, _F) ->
262 [[$- | lists:duplicate(Padding, Fill)], Arg];
263 do_padding(Arg, Padding, Fill, sign_right, #conversion{sign=$-}) ->
264 [lists:duplicate(Padding, Fill), Arg];
265 do_padding([S | Arg], Padding, Fill, sign_right, #conversion{sign=S}) ->
266 [[S | lists:duplicate(Padding, Fill)], Arg];
267 do_padding(Arg, Padding, Fill, sign_right, #conversion{sign=undefined}) ->
268 [lists:duplicate(Padding, Fill), Arg];
269 do_padding(Arg, Padding, Fill, left, _F) ->
270 [Arg | lists:duplicate(Padding, Fill)].
271
272 fix_sign(Arg, #conversion{sign=$+}) when Arg >= 0 ->
273 [$+, Arg];
274 fix_sign(Arg, #conversion{sign=$\s}) when Arg >= 0 ->
275 [$\s, Arg];
276 fix_sign(Arg, _F) ->
277 Arg.
278
279 ctype($\%) -> percent;
280 ctype($s) -> string;
281 ctype($b) -> bin;
282 ctype($o) -> oct;
283 ctype($X) -> upper_hex;
284 ctype($x) -> hex;
285 ctype($c) -> char;
286 ctype($d) -> decimal;
287 ctype($g) -> general;
288 ctype($f) -> fixed;
289 ctype($e) -> exp.
290
291 align($<) -> left;
292 align($>) -> right;
293 align($^) -> center;
294 align($=) -> sign_right.
295
296 convert2(Arg, F=#conversion{ctype=percent}) ->
297 [convert2(100.0 * Arg, F#conversion{ctype=fixed}), $\%];
298 convert2(Arg, #conversion{ctype=string}) ->
299 str(Arg);
300 convert2(Arg, #conversion{ctype=bin}) ->
301 erlang:integer_to_list(Arg, 2);
302 convert2(Arg, #conversion{ctype=oct}) ->
303 erlang:integer_to_list(Arg, 8);
304 convert2(Arg, #conversion{ctype=upper_hex}) ->
305 erlang:integer_to_list(Arg, 16);
306 convert2(Arg, #conversion{ctype=hex}) ->
307 string:to_lower(erlang:integer_to_list(Arg, 16));
308 convert2(Arg, #conversion{ctype=char}) when Arg < 16#80 ->
309 [Arg];
310 convert2(Arg, #conversion{ctype=char}) ->
311 xmerl_ucs:to_utf8(Arg);
312 convert2(Arg, #conversion{ctype=decimal}) ->
313 integer_to_list(Arg);
314 convert2(Arg, #conversion{ctype=general, precision=undefined}) ->
315 try mochinum:digits(Arg)
316 catch error:undef -> io_lib:format("~g", [Arg]) end;
317 convert2(Arg, #conversion{ctype=fixed, precision=undefined}) ->
318 io_lib:format("~f", [Arg]);
319 convert2(Arg, #conversion{ctype=exp, precision=undefined}) ->
320 io_lib:format("~e", [Arg]);
321 convert2(Arg, #conversion{ctype=general, precision=P}) ->
322 io_lib:format("~." ++ integer_to_list(P) ++ "g", [Arg]);
323 convert2(Arg, #conversion{ctype=fixed, precision=P}) ->
324 io_lib:format("~." ++ integer_to_list(P) ++ "f", [Arg]);
325 convert2(Arg, #conversion{ctype=exp, precision=P}) ->
326 io_lib:format("~." ++ integer_to_list(P) ++ "e", [Arg]).
327
328 str(A) when is_atom(A) ->
329 atom_to_list(A);
330 str(I) when is_integer(I) ->
331 integer_to_list(I);
332 str(F) when is_float(F) ->
333 try mochinum:digits(F)
334 catch error:undef -> io_lib:format("~g", [F]) end;
335 str(L) when is_list(L) ->
336 L;
337 str(B) when is_binary(B) ->
338 B;
339 str(P) ->
340 repr(P).
341
342 repr(P) when is_float(P) ->
343 try mochinum:digits(P)
344 catch error:undef -> float_to_list(P) end;
345 repr(P) ->
346 io_lib:format("~p", [P]).
347
348 parse_std_conversion(S) ->
349 parse_std_conversion(S, #conversion{}).
350
351 parse_std_conversion("", Acc) ->
352 Acc;
353 parse_std_conversion([Fill, Align | Spec], Acc)
354 when Align =:= $< orelse Align =:= $> orelse Align =:= $= orelse Align =:= $^ ->
355 parse_std_conversion(Spec, Acc#conversion{fill_char=Fill,
356 align=align(Align)});
357 parse_std_conversion([Align | Spec], Acc)
358 when Align =:= $< orelse Align =:= $> orelse Align =:= $= orelse Align =:= $^ ->
359 parse_std_conversion(Spec, Acc#conversion{align=align(Align)});
360 parse_std_conversion([Sign | Spec], Acc)
361 when Sign =:= $+ orelse Sign =:= $- orelse Sign =:= $\s ->
362 parse_std_conversion(Spec, Acc#conversion{sign=Sign});
363 parse_std_conversion("0" ++ Spec, Acc) ->
364 Align = case Acc#conversion.align of
365 undefined ->
366 sign_right;
367 A ->
368 A
369 end,
370 parse_std_conversion(Spec, Acc#conversion{fill_char=$0, align=Align});
371 parse_std_conversion(Spec=[D|_], Acc) when D >= $0 andalso D =< $9 ->
372 {W, Spec1} = lists:splitwith(fun (C) -> C >= $0 andalso C =< $9 end, Spec),
373 parse_std_conversion(Spec1, Acc#conversion{length=list_to_integer(W)});
374 parse_std_conversion([$. | Spec], Acc) ->
375 case lists:splitwith(fun (C) -> C >= $0 andalso C =< $9 end, Spec) of
376 {"", Spec1} ->
377 parse_std_conversion(Spec1, Acc);
378 {P, Spec1} ->
379 parse_std_conversion(Spec1,
380 Acc#conversion{precision=list_to_integer(P)})
381 end;
382 parse_std_conversion([Type], Acc) ->
383 parse_std_conversion("", Acc#conversion{ctype=ctype(Type)}).
384
385
386 %%
387 %% Tests
388 %%
389 -ifdef(TEST).
390 -include_lib("eunit/include/eunit.hrl").
391
392 tokenize_test() ->
393 {?MODULE, [{raw, "ABC"}]} = tokenize("ABC"),
394 {?MODULE, [{format, {"0", "", ""}}]} = tokenize("{0}"),
395 {?MODULE, [{raw, "ABC"}, {format, {"1", "", ""}}, {raw, "DEF"}]} =
396 tokenize("ABC{1}DEF"),
397 ok.
398
399 format_test() ->
400 <<" -4">> = bformat("{0:4}", [-4]),
401 <<" 4">> = bformat("{0:4}", [4]),
402 <<" 4">> = bformat("{0:{0}}", [4]),
403 <<"4 ">> = bformat("{0:4}", ["4"]),
404 <<"4 ">> = bformat("{0:{0}}", ["4"]),
405 <<"1.2yoDEF">> = bformat("{2}{0}{1}{3}", {yo, "DE", 1.2, <<"F">>}),
406 <<"cafebabe">> = bformat("{0:x}", {16#cafebabe}),
407 <<"CAFEBABE">> = bformat("{0:X}", {16#cafebabe}),
408 <<"CAFEBABE">> = bformat("{0:X}", {16#cafebabe}),
409 <<"755">> = bformat("{0:o}", {8#755}),
410 <<"a">> = bformat("{0:c}", {97}),
411 %% Horizontal ellipsis
412 <<226, 128, 166>> = bformat("{0:c}", {16#2026}),
413 <<"11">> = bformat("{0:b}", {3}),
414 <<"11">> = bformat("{0:b}", [3]),
415 <<"11">> = bformat("{three:b}", [{three, 3}]),
416 <<"11">> = bformat("{three:b}", [{"three", 3}]),
417 <<"11">> = bformat("{three:b}", [{<<"three">>, 3}]),
418 <<"\"foo\"">> = bformat("{0!r}", {"foo"}),
419 <<"2008-5-4">> = bformat("{0.0}-{0.1}-{0.2}", {{2008,5,4}}),
420 <<"2008-05-04">> = bformat("{0.0:04}-{0.1:02}-{0.2:02}", {{2008,5,4}}),
421 <<"foo6bar-6">> = bformat("foo{1}{0}-{1}", {bar, 6}),
422 <<"-'atom test'-">> = bformat("-{arg!r}-", [{arg, 'atom test'}]),
423 <<"2008-05-04">> = bformat("{0.0:0{1.0}}-{0.1:0{1.1}}-{0.2:0{1.2}}",
424 {{2008,5,4}, {4, 2, 2}}),
425 ok.
426
427 std_test() ->
428 M = mochifmt_std:new(),
429 <<"01">> = bformat("{0}{1}", [0, 1], M),
430 ok.
431
432 records_test() ->
433 M = mochifmt_records:new([{conversion, record_info(fields, conversion)}]),
434 R = #conversion{length=long, precision=hard, sign=peace},
435 long = M:get_value("length", R),
436 hard = M:get_value("precision", R),
437 peace = M:get_value("sign", R),
438 <<"long hard">> = bformat("{length} {precision}", R, M),
439 <<"long hard">> = bformat("{0.length} {0.precision}", [R], M),
440 ok.
441
442 -endif.
+0
-60
deps/mochiweb/src/mochifmt_records.erl less more
0 %% @author Bob Ippolito <bob@mochimedia.com>
1 %% @copyright 2008 Mochi Media, Inc.
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 %% @doc Formatter that understands records.
22 %%
23 %% Usage:
24 %%
25 %% 1> M = mochifmt_records:new([{rec, record_info(fields, rec)}]),
26 %% M:format("{0.bar}", [#rec{bar=foo}]).
27 %% foo
28
29 -module(mochifmt_records).
30 -author('bob@mochimedia.com').
31 -export([new/1, get_value/3]).
32
33 new([{_Rec, RecFields}]=Recs) when is_list(RecFields) ->
34 {?MODULE, Recs}.
35
36 get_value(Key, Rec, {?MODULE, Recs})
37 when is_tuple(Rec) and is_atom(element(1, Rec)) ->
38 try begin
39 Atom = list_to_existing_atom(Key),
40 {_, Fields} = proplists:lookup(element(1, Rec), Recs),
41 element(get_rec_index(Atom, Fields, 2), Rec)
42 end
43 catch error:_ -> mochifmt:get_value(Key, Rec)
44 end;
45 get_value(Key, Args, {?MODULE, _Recs}) ->
46 mochifmt:get_value(Key, Args).
47
48 get_rec_index(Atom, [Atom | _], Index) ->
49 Index;
50 get_rec_index(Atom, [_ | Rest], Index) ->
51 get_rec_index(Atom, Rest, 1 + Index).
52
53
54 %%
55 %% Tests
56 %%
57 -ifdef(TEST).
58 -include_lib("eunit/include/eunit.hrl").
59 -endif.
+0
-51
deps/mochiweb/src/mochifmt_std.erl less more
0 %% @author Bob Ippolito <bob@mochimedia.com>
1 %% @copyright 2008 Mochi Media, Inc.
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 %% @doc Template module for a mochifmt formatter.
22
23 -module(mochifmt_std).
24 -author('bob@mochimedia.com').
25 -export([new/0, format/3, get_value/3, format_field/3, get_field/3, convert_field/3]).
26
27 new() ->
28 {?MODULE}.
29
30 format(Format, Args, {?MODULE}=THIS) ->
31 mochifmt:format(Format, Args, THIS).
32
33 get_field(Key, Args, {?MODULE}=THIS) ->
34 mochifmt:get_field(Key, Args, THIS).
35
36 convert_field(Key, Args, {?MODULE}) ->
37 mochifmt:convert_field(Key, Args).
38
39 get_value(Key, Args, {?MODULE}) ->
40 mochifmt:get_value(Key, Args).
41
42 format_field(Arg, Format, {?MODULE}=THIS) ->
43 mochifmt:format_field(Arg, Format, THIS).
44
45 %%
46 %% Tests
47 %%
48 -ifdef(TEST).
49 -include_lib("eunit/include/eunit.hrl").
50 -endif.
+0
-127
deps/mochiweb/src/mochiglobal.erl less more
0 %% @author Bob Ippolito <bob@mochimedia.com>
1 %% @copyright 2010 Mochi Media, Inc.
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21
22 %% @doc Abuse module constant pools as a "read-only shared heap" (since erts 5.6)
23 %% <a href="http://www.erlang.org/pipermail/erlang-questions/2009-March/042503.html">[1]</a>.
24 -module(mochiglobal).
25 -author("Bob Ippolito <bob@mochimedia.com>").
26 -export([get/1, get/2, put/2, delete/1]).
27
28 -spec get(atom()) -> any() | undefined.
29 %% @equiv get(K, undefined)
30 get(K) ->
31 get(K, undefined).
32
33 -spec get(atom(), T) -> any() | T.
34 %% @doc Get the term for K or return Default.
35 get(K, Default) ->
36 get(K, Default, key_to_module(K)).
37
38 get(_K, Default, Mod) ->
39 try Mod:term()
40 catch error:undef ->
41 Default
42 end.
43
44 -spec put(atom(), any()) -> ok.
45 %% @doc Store term V at K, replaces an existing term if present.
46 put(K, V) ->
47 put(K, V, key_to_module(K)).
48
49 put(_K, V, Mod) ->
50 Bin = compile(Mod, V),
51 code:purge(Mod),
52 {module, Mod} = code:load_binary(Mod, atom_to_list(Mod) ++ ".erl", Bin),
53 ok.
54
55 -spec delete(atom()) -> boolean().
56 %% @doc Delete term stored at K, no-op if non-existent.
57 delete(K) ->
58 delete(K, key_to_module(K)).
59
60 delete(_K, Mod) ->
61 code:purge(Mod),
62 code:delete(Mod).
63
64 -spec key_to_module(atom()) -> atom().
65 key_to_module(K) ->
66 list_to_atom("mochiglobal:" ++ atom_to_list(K)).
67
68 -spec compile(atom(), any()) -> binary().
69 compile(Module, T) ->
70 {ok, Module, Bin} = compile:forms(forms(Module, T),
71 [verbose, report_errors]),
72 Bin.
73
74 -spec forms(atom(), any()) -> [erl_syntax:syntaxTree()].
75 forms(Module, T) ->
76 [erl_syntax:revert(X) || X <- term_to_abstract(Module, term, T)].
77
78 -spec term_to_abstract(atom(), atom(), any()) -> [erl_syntax:syntaxTree()].
79 term_to_abstract(Module, Getter, T) ->
80 [%% -module(Module).
81 erl_syntax:attribute(
82 erl_syntax:atom(module),
83 [erl_syntax:atom(Module)]),
84 %% -export([Getter/0]).
85 erl_syntax:attribute(
86 erl_syntax:atom(export),
87 [erl_syntax:list(
88 [erl_syntax:arity_qualifier(
89 erl_syntax:atom(Getter),
90 erl_syntax:integer(0))])]),
91 %% Getter() -> T.
92 erl_syntax:function(
93 erl_syntax:atom(Getter),
94 [erl_syntax:clause([], none, [erl_syntax:abstract(T)])])].
95
96 %%
97 %% Tests
98 %%
99 -ifdef(TEST).
100 -include_lib("eunit/include/eunit.hrl").
101 get_put_delete_test() ->
102 K = '$$test$$mochiglobal',
103 delete(K),
104 ?assertEqual(
105 bar,
106 get(K, bar)),
107 try
108 ?MODULE:put(K, baz),
109 ?assertEqual(
110 baz,
111 get(K, bar)),
112 ?MODULE:put(K, wibble),
113 ?assertEqual(
114 wibble,
115 ?MODULE:get(K))
116 after
117 delete(K)
118 end,
119 ?assertEqual(
120 bar,
121 get(K, bar)),
122 ?assertEqual(
123 undefined,
124 ?MODULE:get(K)),
125 ok.
126 -endif.
+0
-106
deps/mochiweb/src/mochihex.erl less more
0 %% @author Bob Ippolito <bob@mochimedia.com>
1 %% @copyright 2006 Mochi Media, Inc.
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 %% @doc Utilities for working with hexadecimal strings.
22
23 -module(mochihex).
24 -author('bob@mochimedia.com').
25
26 -export([to_hex/1, to_bin/1, to_int/1, dehex/1, hexdigit/1]).
27
28 %% @spec to_hex(integer | iolist()) -> string()
29 %% @doc Convert an iolist to a hexadecimal string.
30 to_hex(0) ->
31 "0";
32 to_hex(I) when is_integer(I), I > 0 ->
33 to_hex_int(I, []);
34 to_hex(B) ->
35 to_hex(iolist_to_binary(B), []).
36
37 %% @spec to_bin(string()) -> binary()
38 %% @doc Convert a hexadecimal string to a binary.
39 to_bin(L) ->
40 to_bin(L, []).
41
42 %% @spec to_int(string()) -> integer()
43 %% @doc Convert a hexadecimal string to an integer.
44 to_int(L) ->
45 erlang:list_to_integer(L, 16).
46
47 %% @spec dehex(char()) -> integer()
48 %% @doc Convert a hex digit to its integer value.
49 dehex(C) when C >= $0, C =< $9 ->
50 C - $0;
51 dehex(C) when C >= $a, C =< $f ->
52 C - $a + 10;
53 dehex(C) when C >= $A, C =< $F ->
54 C - $A + 10.
55
56 %% @spec hexdigit(integer()) -> char()
57 %% @doc Convert an integer less than 16 to a hex digit.
58 hexdigit(C) when C >= 0, C =< 9 ->
59 C + $0;
60 hexdigit(C) when C =< 15 ->
61 C + $a - 10.
62
63 %% Internal API
64
65 to_hex(<<>>, Acc) ->
66 lists:reverse(Acc);
67 to_hex(<<C1:4, C2:4, Rest/binary>>, Acc) ->
68 to_hex(Rest, [hexdigit(C2), hexdigit(C1) | Acc]).
69
70 to_hex_int(0, Acc) ->
71 Acc;
72 to_hex_int(I, Acc) ->
73 to_hex_int(I bsr 4, [hexdigit(I band 15) | Acc]).
74
75 to_bin([], Acc) ->
76 iolist_to_binary(lists:reverse(Acc));
77 to_bin([C1, C2 | Rest], Acc) ->
78 to_bin(Rest, [(dehex(C1) bsl 4) bor dehex(C2) | Acc]).
79
80
81
82 %%
83 %% Tests
84 %%
85 -ifdef(TEST).
86 -include_lib("eunit/include/eunit.hrl").
87
88 to_hex_test() ->
89 "ff000ff1" = to_hex([255, 0, 15, 241]),
90 "ff000ff1" = to_hex(16#ff000ff1),
91 "0" = to_hex(16#0),
92 ok.
93
94 to_bin_test() ->
95 <<255, 0, 15, 241>> = to_bin("ff000ff1"),
96 <<255, 0, 10, 161>> = to_bin("Ff000aA1"),
97 ok.
98
99 to_int_test() ->
100 16#ff000ff1 = to_int("ff000ff1"),
101 16#ff000aa1 = to_int("FF000Aa1"),
102 16#0 = to_int("0"),
103 ok.
104
105 -endif.
+0
-547
deps/mochiweb/src/mochijson.erl less more
0 %% @author Bob Ippolito <bob@mochimedia.com>
1 %% @copyright 2006 Mochi Media, Inc.
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 %% @doc Yet another JSON (RFC 4627) library for Erlang.
22 -module(mochijson).
23 -author('bob@mochimedia.com').
24 -export([encoder/1, encode/1]).
25 -export([decoder/1, decode/1]).
26 -export([binary_encoder/1, binary_encode/1]).
27 -export([binary_decoder/1, binary_decode/1]).
28
29 % This is a macro to placate syntax highlighters..
30 -define(Q, $\").
31 -define(ADV_COL(S, N), S#decoder{column=N+S#decoder.column}).
32 -define(INC_COL(S), S#decoder{column=1+S#decoder.column}).
33 -define(INC_LINE(S), S#decoder{column=1, line=1+S#decoder.line}).
34
35 %% @type json_string() = atom | string() | binary()
36 %% @type json_number() = integer() | float()
37 %% @type json_array() = {array, [json_term()]}
38 %% @type json_object() = {struct, [{json_string(), json_term()}]}
39 %% @type json_term() = json_string() | json_number() | json_array() |
40 %% json_object()
41 %% @type encoding() = utf8 | unicode
42 %% @type encoder_option() = {input_encoding, encoding()} |
43 %% {handler, function()}
44 %% @type decoder_option() = {input_encoding, encoding()} |
45 %% {object_hook, function()}
46 %% @type bjson_string() = binary()
47 %% @type bjson_number() = integer() | float()
48 %% @type bjson_array() = [bjson_term()]
49 %% @type bjson_object() = {struct, [{bjson_string(), bjson_term()}]}
50 %% @type bjson_term() = bjson_string() | bjson_number() | bjson_array() |
51 %% bjson_object()
52 %% @type binary_encoder_option() = {handler, function()}
53 %% @type binary_decoder_option() = {object_hook, function()}
54
55 -record(encoder, {input_encoding=unicode,
56 handler=null}).
57
58 -record(decoder, {input_encoding=utf8,
59 object_hook=null,
60 line=1,
61 column=1,
62 state=null}).
63
64 %% @spec encoder([encoder_option()]) -> function()
65 %% @doc Create an encoder/1 with the given options.
66 encoder(Options) ->
67 State = parse_encoder_options(Options, #encoder{}),
68 fun (O) -> json_encode(O, State) end.
69
70 %% @spec encode(json_term()) -> iolist()
71 %% @doc Encode the given as JSON to an iolist.
72 encode(Any) ->
73 json_encode(Any, #encoder{}).
74
75 %% @spec decoder([decoder_option()]) -> function()
76 %% @doc Create a decoder/1 with the given options.
77 decoder(Options) ->
78 State = parse_decoder_options(Options, #decoder{}),
79 fun (O) -> json_decode(O, State) end.
80
81 %% @spec decode(iolist()) -> json_term()
82 %% @doc Decode the given iolist to Erlang terms.
83 decode(S) ->
84 json_decode(S, #decoder{}).
85
86 %% @spec binary_decoder([binary_decoder_option()]) -> function()
87 %% @doc Create a binary_decoder/1 with the given options.
88 binary_decoder(Options) ->
89 mochijson2:decoder(Options).
90
91 %% @spec binary_encoder([binary_encoder_option()]) -> function()
92 %% @doc Create a binary_encoder/1 with the given options.
93 binary_encoder(Options) ->
94 mochijson2:encoder(Options).
95
96 %% @spec binary_encode(bjson_term()) -> iolist()
97 %% @doc Encode the given as JSON to an iolist, using lists for arrays and
98 %% binaries for strings.
99 binary_encode(Any) ->
100 mochijson2:encode(Any).
101
102 %% @spec binary_decode(iolist()) -> bjson_term()
103 %% @doc Decode the given iolist to Erlang terms, using lists for arrays and
104 %% binaries for strings.
105 binary_decode(S) ->
106 mochijson2:decode(S).
107
108 %% Internal API
109
110 parse_encoder_options([], State) ->
111 State;
112 parse_encoder_options([{input_encoding, Encoding} | Rest], State) ->
113 parse_encoder_options(Rest, State#encoder{input_encoding=Encoding});
114 parse_encoder_options([{handler, Handler} | Rest], State) ->
115 parse_encoder_options(Rest, State#encoder{handler=Handler}).
116
117 parse_decoder_options([], State) ->
118 State;
119 parse_decoder_options([{input_encoding, Encoding} | Rest], State) ->
120 parse_decoder_options(Rest, State#decoder{input_encoding=Encoding});
121 parse_decoder_options([{object_hook, Hook} | Rest], State) ->
122 parse_decoder_options(Rest, State#decoder{object_hook=Hook}).
123
124 json_encode(true, _State) ->
125 "true";
126 json_encode(false, _State) ->
127 "false";
128 json_encode(null, _State) ->
129 "null";
130 json_encode(I, _State) when is_integer(I) ->
131 integer_to_list(I);
132 json_encode(F, _State) when is_float(F) ->
133 mochinum:digits(F);
134 json_encode(L, State) when is_list(L); is_binary(L); is_atom(L) ->
135 json_encode_string(L, State);
136 json_encode({array, Props}, State) when is_list(Props) ->
137 json_encode_array(Props, State);
138 json_encode({struct, Props}, State) when is_list(Props) ->
139 json_encode_proplist(Props, State);
140 json_encode(Bad, #encoder{handler=null}) ->
141 exit({json_encode, {bad_term, Bad}});
142 json_encode(Bad, State=#encoder{handler=Handler}) ->
143 json_encode(Handler(Bad), State).
144
145 json_encode_array([], _State) ->
146 "[]";
147 json_encode_array(L, State) ->
148 F = fun (O, Acc) ->
149 [$,, json_encode(O, State) | Acc]
150 end,
151 [$, | Acc1] = lists:foldl(F, "[", L),
152 lists:reverse([$\] | Acc1]).
153
154 json_encode_proplist([], _State) ->
155 "{}";
156 json_encode_proplist(Props, State) ->
157 F = fun ({K, V}, Acc) ->
158 KS = case K of
159 K when is_atom(K) ->
160 json_encode_string_utf8(atom_to_list(K));
161 K when is_integer(K) ->
162 json_encode_string(integer_to_list(K), State);
163 K when is_list(K); is_binary(K) ->
164 json_encode_string(K, State)
165 end,
166 VS = json_encode(V, State),
167 [$,, VS, $:, KS | Acc]
168 end,
169 [$, | Acc1] = lists:foldl(F, "{", Props),
170 lists:reverse([$\} | Acc1]).
171
172 json_encode_string(A, _State) when is_atom(A) ->
173 json_encode_string_unicode(xmerl_ucs:from_utf8(atom_to_list(A)));
174 json_encode_string(B, _State) when is_binary(B) ->
175 json_encode_string_unicode(xmerl_ucs:from_utf8(B));
176 json_encode_string(S, #encoder{input_encoding=utf8}) ->
177 json_encode_string_utf8(S);
178 json_encode_string(S, #encoder{input_encoding=unicode}) ->
179 json_encode_string_unicode(S).
180
181 json_encode_string_utf8(S) ->
182 [?Q | json_encode_string_utf8_1(S)].
183
184 json_encode_string_utf8_1([C | Cs]) when C >= 0, C =< 16#7f ->
185 NewC = case C of
186 $\\ -> "\\\\";
187 ?Q -> "\\\"";
188 _ when C >= $\s, C < 16#7f -> C;
189 $\t -> "\\t";
190 $\n -> "\\n";
191 $\r -> "\\r";
192 $\f -> "\\f";
193 $\b -> "\\b";
194 _ when C >= 0, C =< 16#7f -> unihex(C);
195 _ -> exit({json_encode, {bad_char, C}})
196 end,
197 [NewC | json_encode_string_utf8_1(Cs)];
198 json_encode_string_utf8_1(All=[C | _]) when C >= 16#80, C =< 16#10FFFF ->
199 [?Q | Rest] = json_encode_string_unicode(xmerl_ucs:from_utf8(All)),
200 Rest;
201 json_encode_string_utf8_1([]) ->
202 "\"".
203
204 json_encode_string_unicode(S) ->
205 [?Q | json_encode_string_unicode_1(S)].
206
207 json_encode_string_unicode_1([C | Cs]) ->
208 NewC = case C of
209 $\\ -> "\\\\";
210 ?Q -> "\\\"";
211 _ when C >= $\s, C < 16#7f -> C;
212 $\t -> "\\t";
213 $\n -> "\\n";
214 $\r -> "\\r";
215 $\f -> "\\f";
216 $\b -> "\\b";
217 _ when C >= 0, C =< 16#10FFFF -> unihex(C);
218 _ -> exit({json_encode, {bad_char, C}})
219 end,
220 [NewC | json_encode_string_unicode_1(Cs)];
221 json_encode_string_unicode_1([]) ->
222 "\"".
223
224 dehex(C) when C >= $0, C =< $9 ->
225 C - $0;
226 dehex(C) when C >= $a, C =< $f ->
227 C - $a + 10;
228 dehex(C) when C >= $A, C =< $F ->
229 C - $A + 10.
230
231 hexdigit(C) when C >= 0, C =< 9 ->
232 C + $0;
233 hexdigit(C) when C =< 15 ->
234 C + $a - 10.
235
236 unihex(C) when C < 16#10000 ->
237 <<D3:4, D2:4, D1:4, D0:4>> = <<C:16>>,
238 Digits = [hexdigit(D) || D <- [D3, D2, D1, D0]],
239 [$\\, $u | Digits];
240 unihex(C) when C =< 16#10FFFF ->
241 N = C - 16#10000,
242 S1 = 16#d800 bor ((N bsr 10) band 16#3ff),
243 S2 = 16#dc00 bor (N band 16#3ff),
244 [unihex(S1), unihex(S2)].
245
246 json_decode(B, S) when is_binary(B) ->
247 json_decode(binary_to_list(B), S);
248 json_decode(L, S) ->
249 {Res, L1, S1} = decode1(L, S),
250 {eof, [], _} = tokenize(L1, S1#decoder{state=trim}),
251 Res.
252
253 decode1(L, S=#decoder{state=null}) ->
254 case tokenize(L, S#decoder{state=any}) of
255 {{const, C}, L1, S1} ->
256 {C, L1, S1};
257 {start_array, L1, S1} ->
258 decode_array(L1, S1#decoder{state=any}, []);
259 {start_object, L1, S1} ->
260 decode_object(L1, S1#decoder{state=key}, [])
261 end.
262
263 make_object(V, #decoder{object_hook=null}) ->
264 V;
265 make_object(V, #decoder{object_hook=Hook}) ->
266 Hook(V).
267
268 decode_object(L, S=#decoder{state=key}, Acc) ->
269 case tokenize(L, S) of
270 {end_object, Rest, S1} ->
271 V = make_object({struct, lists:reverse(Acc)}, S1),
272 {V, Rest, S1#decoder{state=null}};
273 {{const, K}, Rest, S1} when is_list(K) ->
274 {colon, L2, S2} = tokenize(Rest, S1),
275 {V, L3, S3} = decode1(L2, S2#decoder{state=null}),
276 decode_object(L3, S3#decoder{state=comma}, [{K, V} | Acc])
277 end;
278 decode_object(L, S=#decoder{state=comma}, Acc) ->
279 case tokenize(L, S) of
280 {end_object, Rest, S1} ->
281 V = make_object({struct, lists:reverse(Acc)}, S1),
282 {V, Rest, S1#decoder{state=null}};
283 {comma, Rest, S1} ->
284 decode_object(Rest, S1#decoder{state=key}, Acc)
285 end.
286
287 decode_array(L, S=#decoder{state=any}, Acc) ->
288 case tokenize(L, S) of
289 {end_array, Rest, S1} ->
290 {{array, lists:reverse(Acc)}, Rest, S1#decoder{state=null}};
291 {start_array, Rest, S1} ->
292 {Array, Rest1, S2} = decode_array(Rest, S1#decoder{state=any}, []),
293 decode_array(Rest1, S2#decoder{state=comma}, [Array | Acc]);
294 {start_object, Rest, S1} ->
295 {Array, Rest1, S2} = decode_object(Rest, S1#decoder{state=key}, []),
296 decode_array(Rest1, S2#decoder{state=comma}, [Array | Acc]);
297 {{const, Const}, Rest, S1} ->
298 decode_array(Rest, S1#decoder{state=comma}, [Const | Acc])
299 end;
300 decode_array(L, S=#decoder{state=comma}, Acc) ->
301 case tokenize(L, S) of
302 {end_array, Rest, S1} ->
303 {{array, lists:reverse(Acc)}, Rest, S1#decoder{state=null}};
304 {comma, Rest, S1} ->
305 decode_array(Rest, S1#decoder{state=any}, Acc)
306 end.
307
308 tokenize_string(IoList=[C | _], S=#decoder{input_encoding=utf8}, Acc)
309 when is_list(C); is_binary(C); C >= 16#7f ->
310 List = xmerl_ucs:from_utf8(iolist_to_binary(IoList)),
311 tokenize_string(List, S#decoder{input_encoding=unicode}, Acc);
312 tokenize_string("\"" ++ Rest, S, Acc) ->
313 {lists:reverse(Acc), Rest, ?INC_COL(S)};
314 tokenize_string("\\\"" ++ Rest, S, Acc) ->
315 tokenize_string(Rest, ?ADV_COL(S, 2), [$\" | Acc]);
316 tokenize_string("\\\\" ++ Rest, S, Acc) ->
317 tokenize_string(Rest, ?ADV_COL(S, 2), [$\\ | Acc]);
318 tokenize_string("\\/" ++ Rest, S, Acc) ->
319 tokenize_string(Rest, ?ADV_COL(S, 2), [$/ | Acc]);
320 tokenize_string("\\b" ++ Rest, S, Acc) ->
321 tokenize_string(Rest, ?ADV_COL(S, 2), [$\b | Acc]);
322 tokenize_string("\\f" ++ Rest, S, Acc) ->
323 tokenize_string(Rest, ?ADV_COL(S, 2), [$\f | Acc]);
324 tokenize_string("\\n" ++ Rest, S, Acc) ->
325 tokenize_string(Rest, ?ADV_COL(S, 2), [$\n | Acc]);
326 tokenize_string("\\r" ++ Rest, S, Acc) ->
327 tokenize_string(Rest, ?ADV_COL(S, 2), [$\r | Acc]);
328 tokenize_string("\\t" ++ Rest, S, Acc) ->
329 tokenize_string(Rest, ?ADV_COL(S, 2), [$\t | Acc]);
330 tokenize_string([$\\, $u, C3, C2, C1, C0 | Rest], S, Acc) ->
331 % coalesce UTF-16 surrogate pair?
332 C = dehex(C0) bor
333 (dehex(C1) bsl 4) bor
334 (dehex(C2) bsl 8) bor
335 (dehex(C3) bsl 12),
336 tokenize_string(Rest, ?ADV_COL(S, 6), [C | Acc]);
337 tokenize_string([C | Rest], S, Acc) when C >= $\s; C < 16#10FFFF ->
338 tokenize_string(Rest, ?ADV_COL(S, 1), [C | Acc]).
339
340 tokenize_number(IoList=[C | _], Mode, S=#decoder{input_encoding=utf8}, Acc)
341 when is_list(C); is_binary(C); C >= 16#7f ->
342 List = xmerl_ucs:from_utf8(iolist_to_binary(IoList)),
343 tokenize_number(List, Mode, S#decoder{input_encoding=unicode}, Acc);
344 tokenize_number([$- | Rest], sign, S, []) ->
345 tokenize_number(Rest, int, ?INC_COL(S), [$-]);
346 tokenize_number(Rest, sign, S, []) ->
347 tokenize_number(Rest, int, S, []);
348 tokenize_number([$0 | Rest], int, S, Acc) ->
349 tokenize_number(Rest, frac, ?INC_COL(S), [$0 | Acc]);
350 tokenize_number([C | Rest], int, S, Acc) when C >= $1, C =< $9 ->
351 tokenize_number(Rest, int1, ?INC_COL(S), [C | Acc]);
352 tokenize_number([C | Rest], int1, S, Acc) when C >= $0, C =< $9 ->
353 tokenize_number(Rest, int1, ?INC_COL(S), [C | Acc]);
354 tokenize_number(Rest, int1, S, Acc) ->
355 tokenize_number(Rest, frac, S, Acc);
356 tokenize_number([$., C | Rest], frac, S, Acc) when C >= $0, C =< $9 ->
357 tokenize_number(Rest, frac1, ?ADV_COL(S, 2), [C, $. | Acc]);
358 tokenize_number([E | Rest], frac, S, Acc) when E == $e; E == $E ->
359 tokenize_number(Rest, esign, ?INC_COL(S), [$e, $0, $. | Acc]);
360 tokenize_number(Rest, frac, S, Acc) ->
361 {{int, lists:reverse(Acc)}, Rest, S};
362 tokenize_number([C | Rest], frac1, S, Acc) when C >= $0, C =< $9 ->
363 tokenize_number(Rest, frac1, ?INC_COL(S), [C | Acc]);
364 tokenize_number([E | Rest], frac1, S, Acc) when E == $e; E == $E ->
365 tokenize_number(Rest, esign, ?INC_COL(S), [$e | Acc]);
366 tokenize_number(Rest, frac1, S, Acc) ->
367 {{float, lists:reverse(Acc)}, Rest, S};
368 tokenize_number([C | Rest], esign, S, Acc) when C == $-; C == $+ ->
369 tokenize_number(Rest, eint, ?INC_COL(S), [C | Acc]);
370 tokenize_number(Rest, esign, S, Acc) ->
371 tokenize_number(Rest, eint, S, Acc);
372 tokenize_number([C | Rest], eint, S, Acc) when C >= $0, C =< $9 ->
373 tokenize_number(Rest, eint1, ?INC_COL(S), [C | Acc]);
374 tokenize_number([C | Rest], eint1, S, Acc) when C >= $0, C =< $9 ->
375 tokenize_number(Rest, eint1, ?INC_COL(S), [C | Acc]);
376 tokenize_number(Rest, eint1, S, Acc) ->
377 {{float, lists:reverse(Acc)}, Rest, S}.
378
379 tokenize([], S=#decoder{state=trim}) ->
380 {eof, [], S};
381 tokenize([L | Rest], S) when is_list(L) ->
382 tokenize(L ++ Rest, S);
383 tokenize([B | Rest], S) when is_binary(B) ->
384 tokenize(xmerl_ucs:from_utf8(B) ++ Rest, S);
385 tokenize("\r\n" ++ Rest, S) ->
386 tokenize(Rest, ?INC_LINE(S));
387 tokenize("\n" ++ Rest, S) ->
388 tokenize(Rest, ?INC_LINE(S));
389 tokenize([C | Rest], S) when C == $\s; C == $\t ->
390 tokenize(Rest, ?INC_COL(S));
391 tokenize("{" ++ Rest, S) ->
392 {start_object, Rest, ?INC_COL(S)};
393 tokenize("}" ++ Rest, S) ->
394 {end_object, Rest, ?INC_COL(S)};
395 tokenize("[" ++ Rest, S) ->
396 {start_array, Rest, ?INC_COL(S)};
397 tokenize("]" ++ Rest, S) ->
398 {end_array, Rest, ?INC_COL(S)};
399 tokenize("," ++ Rest, S) ->
400 {comma, Rest, ?INC_COL(S)};
401 tokenize(":" ++ Rest, S) ->
402 {colon, Rest, ?INC_COL(S)};
403 tokenize("null" ++ Rest, S) ->
404 {{const, null}, Rest, ?ADV_COL(S, 4)};
405 tokenize("true" ++ Rest, S) ->
406 {{const, true}, Rest, ?ADV_COL(S, 4)};
407 tokenize("false" ++ Rest, S) ->
408 {{const, false}, Rest, ?ADV_COL(S, 5)};
409 tokenize("\"" ++ Rest, S) ->
410 {String, Rest1, S1} = tokenize_string(Rest, ?INC_COL(S), []),
411 {{const, String}, Rest1, S1};
412 tokenize(L=[C | _], S) when C >= $0, C =< $9; C == $- ->
413 case tokenize_number(L, sign, S, []) of
414 {{int, Int}, Rest, S1} ->
415 {{const, list_to_integer(Int)}, Rest, S1};
416 {{float, Float}, Rest, S1} ->
417 {{const, list_to_float(Float)}, Rest, S1}
418 end.
419
420
421 %%
422 %% Tests
423 %%
424 -ifdef(TEST).
425 -include_lib("eunit/include/eunit.hrl").
426
427 %% testing constructs borrowed from the Yaws JSON implementation.
428
429 %% Create an object from a list of Key/Value pairs.
430
431 obj_new() ->
432 {struct, []}.
433
434 is_obj({struct, Props}) ->
435 F = fun ({K, _}) when is_list(K) ->
436 true;
437 (_) ->
438 false
439 end,
440 lists:all(F, Props).
441
442 obj_from_list(Props) ->
443 Obj = {struct, Props},
444 case is_obj(Obj) of
445 true -> Obj;
446 false -> exit(json_bad_object)
447 end.
448
449 %% Test for equivalence of Erlang terms.
450 %% Due to arbitrary order of construction, equivalent objects might
451 %% compare unequal as erlang terms, so we need to carefully recurse
452 %% through aggregates (tuples and objects).
453
454 equiv({struct, Props1}, {struct, Props2}) ->
455 equiv_object(Props1, Props2);
456 equiv({array, L1}, {array, L2}) ->
457 equiv_list(L1, L2);
458 equiv(N1, N2) when is_number(N1), is_number(N2) -> N1 == N2;
459 equiv(S1, S2) when is_list(S1), is_list(S2) -> S1 == S2;
460 equiv(true, true) -> true;
461 equiv(false, false) -> true;
462 equiv(null, null) -> true.
463
464 %% Object representation and traversal order is unknown.
465 %% Use the sledgehammer and sort property lists.
466
467 equiv_object(Props1, Props2) ->
468 L1 = lists:keysort(1, Props1),
469 L2 = lists:keysort(1, Props2),
470 Pairs = lists:zip(L1, L2),
471 true = lists:all(fun({{K1, V1}, {K2, V2}}) ->
472 equiv(K1, K2) and equiv(V1, V2)
473 end, Pairs).
474
475 %% Recursively compare tuple elements for equivalence.
476
477 equiv_list([], []) ->
478 true;
479 equiv_list([V1 | L1], [V2 | L2]) ->
480 equiv(V1, V2) andalso equiv_list(L1, L2).
481
482 e2j_vec_test() ->
483 test_one(e2j_test_vec(utf8), 1).
484
485 issue33_test() ->
486 %% http://code.google.com/p/mochiweb/issues/detail?id=33
487 Js = {struct, [{"key", [194, 163]}]},
488 Encoder = encoder([{input_encoding, utf8}]),
489 "{\"key\":\"\\u00a3\"}" = lists:flatten(Encoder(Js)).
490
491 test_one([], _N) ->
492 %% io:format("~p tests passed~n", [N-1]),
493 ok;
494 test_one([{E, J} | Rest], N) ->
495 %% io:format("[~p] ~p ~p~n", [N, E, J]),
496 true = equiv(E, decode(J)),
497 true = equiv(E, decode(encode(E))),
498 test_one(Rest, 1+N).
499
500 e2j_test_vec(utf8) ->
501 [
502 {1, "1"},
503 {3.1416, "3.14160"}, % text representation may truncate, trail zeroes
504 {-1, "-1"},
505 {-3.1416, "-3.14160"},
506 {12.0e10, "1.20000e+11"},
507 {1.234E+10, "1.23400e+10"},
508 {-1.234E-10, "-1.23400e-10"},
509 {10.0, "1.0e+01"},
510 {123.456, "1.23456E+2"},
511 {10.0, "1e1"},
512 {"foo", "\"foo\""},
513 {"foo" ++ [5] ++ "bar", "\"foo\\u0005bar\""},
514 {"", "\"\""},
515 {"\"", "\"\\\"\""},
516 {"\n\n\n", "\"\\n\\n\\n\""},
517 {"\\", "\"\\\\\""},
518 {"\" \b\f\r\n\t\"", "\"\\\" \\b\\f\\r\\n\\t\\\"\""},
519 {obj_new(), "{}"},
520 {obj_from_list([{"foo", "bar"}]), "{\"foo\":\"bar\"}"},
521 {obj_from_list([{"foo", "bar"}, {"baz", 123}]),
522 "{\"foo\":\"bar\",\"baz\":123}"},
523 {{array, []}, "[]"},
524 {{array, [{array, []}]}, "[[]]"},
525 {{array, [1, "foo"]}, "[1,\"foo\"]"},
526
527 % json array in a json object
528 {obj_from_list([{"foo", {array, [123]}}]),
529 "{\"foo\":[123]}"},
530
531 % json object in a json object
532 {obj_from_list([{"foo", obj_from_list([{"bar", true}])}]),
533 "{\"foo\":{\"bar\":true}}"},
534
535 % fold evaluation order
536 {obj_from_list([{"foo", {array, []}},
537 {"bar", obj_from_list([{"baz", true}])},
538 {"alice", "bob"}]),
539 "{\"foo\":[],\"bar\":{\"baz\":true},\"alice\":\"bob\"}"},
540
541 % json object in a json array
542 {{array, [-123, "foo", obj_from_list([{"bar", {array, []}}]), null]},
543 "[-123,\"foo\",{\"bar\":[]},null]"}
544 ].
545
546 -endif.
+0
-942
deps/mochiweb/src/mochijson2.erl less more
0 %% @author Bob Ippolito <bob@mochimedia.com>
1 %% @copyright 2007 Mochi Media, Inc.
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 %% @doc Yet another JSON (RFC 4627) library for Erlang. mochijson2 works
22 %% with binaries as strings, arrays as lists (without an {array, _})
23 %% wrapper and it only knows how to decode UTF-8 (and ASCII).
24 %%
25 %% JSON terms are decoded as follows (javascript -> erlang):
26 %% <ul>
27 %% <li>{"key": "value"} ->
28 %% {struct, [{&lt;&lt;"key">>, &lt;&lt;"value">>}]}</li>
29 %% <li>["array", 123, 12.34, true, false, null] ->
30 %% [&lt;&lt;"array">>, 123, 12.34, true, false, null]
31 %% </li>
32 %% </ul>
33 %% <ul>
34 %% <li>Strings in JSON decode to UTF-8 binaries in Erlang</li>
35 %% <li>Objects decode to {struct, PropList}</li>
36 %% <li>Numbers decode to integer or float</li>
37 %% <li>true, false, null decode to their respective terms.</li>
38 %% </ul>
39 %% The encoder will accept the same format that the decoder will produce,
40 %% but will also allow additional cases for leniency:
41 %% <ul>
42 %% <li>atoms other than true, false, null will be considered UTF-8
43 %% strings (even as a proplist key)
44 %% </li>
45 %% <li>{json, IoList} will insert IoList directly into the output
46 %% with no validation
47 %% </li>
48 %% <li>{array, Array} will be encoded as Array
49 %% (legacy mochijson style)
50 %% </li>
51 %% <li>A non-empty raw proplist will be encoded as an object as long
52 %% as the first pair does not have an atom key of json, struct,
53 %% or array
54 %% </li>
55 %% </ul>
56
57 -module(mochijson2).
58 -author('bob@mochimedia.com').
59 -export([encoder/1, encode/1]).
60 -export([decoder/1, decode/1, decode/2]).
61
62 %% This is a macro to placate syntax highlighters..
63 -define(Q, $\").
64 -define(ADV_COL(S, N), S#decoder{offset=N+S#decoder.offset,
65 column=N+S#decoder.column}).
66 -define(INC_COL(S), S#decoder{offset=1+S#decoder.offset,
67 column=1+S#decoder.column}).
68 -define(INC_LINE(S), S#decoder{offset=1+S#decoder.offset,
69 column=1,
70 line=1+S#decoder.line}).
71 -define(INC_CHAR(S, C),
72 case C of
73 $\n ->
74 S#decoder{column=1,
75 line=1+S#decoder.line,
76 offset=1+S#decoder.offset};
77 _ ->
78 S#decoder{column=1+S#decoder.column,
79 offset=1+S#decoder.offset}
80 end).
81 -define(IS_WHITESPACE(C),
82 (C =:= $\s orelse C =:= $\t orelse C =:= $\r orelse C =:= $\n)).
83
84 %% @type json_string() = atom | binary()
85 %% @type json_number() = integer() | float()
86 %% @type json_array() = [json_term()]
87 %% @type json_object() = {struct, [{json_string(), json_term()}]}
88 %% @type json_eep18_object() = {[{json_string(), json_term()}]}
89 %% @type json_iolist() = {json, iolist()}
90 %% @type json_term() = json_string() | json_number() | json_array() |
91 %% json_object() | json_eep18_object() | json_iolist()
92
93 -record(encoder, {handler=null,
94 utf8=false}).
95
96 -record(decoder, {object_hook=null,
97 offset=0,
98 line=1,
99 column=1,
100 state=null}).
101
102 %% @spec encoder([encoder_option()]) -> function()
103 %% @doc Create an encoder/1 with the given options.
104 %% @type encoder_option() = handler_option() | utf8_option()
105 %% @type utf8_option() = boolean(). Emit unicode as utf8 (default - false)
106 encoder(Options) ->
107 State = parse_encoder_options(Options, #encoder{}),
108 fun (O) -> json_encode(O, State) end.
109
110 %% @spec encode(json_term()) -> iolist()
111 %% @doc Encode the given as JSON to an iolist.
112 encode(Any) ->
113 json_encode(Any, #encoder{}).
114
115 %% @spec decoder([decoder_option()]) -> function()
116 %% @doc Create a decoder/1 with the given options.
117 decoder(Options) ->
118 State = parse_decoder_options(Options, #decoder{}),
119 fun (O) -> json_decode(O, State) end.
120
121 %% @spec decode(iolist(), [{format, proplist | eep18 | struct}]) -> json_term()
122 %% @doc Decode the given iolist to Erlang terms using the given object format
123 %% for decoding, where proplist returns JSON objects as [{binary(), json_term()}]
124 %% proplists, eep18 returns JSON objects as {[binary(), json_term()]}, and struct
125 %% returns them as-is.
126 decode(S, Options) ->
127 json_decode(S, parse_decoder_options(Options, #decoder{})).
128
129 %% @spec decode(iolist()) -> json_term()
130 %% @doc Decode the given iolist to Erlang terms.
131 decode(S) ->
132 json_decode(S, #decoder{}).
133
134 %% Internal API
135
136 parse_encoder_options([], State) ->
137 State;
138 parse_encoder_options([{handler, Handler} | Rest], State) ->
139 parse_encoder_options(Rest, State#encoder{handler=Handler});
140 parse_encoder_options([{utf8, Switch} | Rest], State) ->
141 parse_encoder_options(Rest, State#encoder{utf8=Switch}).
142
143 parse_decoder_options([], State) ->
144 State;
145 parse_decoder_options([{object_hook, Hook} | Rest], State) ->
146 parse_decoder_options(Rest, State#decoder{object_hook=Hook});
147 parse_decoder_options([{format, Format} | Rest], State)
148 when Format =:= struct orelse Format =:= eep18 orelse Format =:= proplist ->
149 parse_decoder_options(Rest, State#decoder{object_hook=Format}).
150
151 json_encode(true, _State) ->
152 <<"true">>;
153 json_encode(false, _State) ->
154 <<"false">>;
155 json_encode(null, _State) ->
156 <<"null">>;
157 json_encode(I, _State) when is_integer(I) ->
158 integer_to_list(I);
159 json_encode(F, _State) when is_float(F) ->
160 mochinum:digits(F);
161 json_encode(S, State) when is_binary(S); is_atom(S) ->
162 json_encode_string(S, State);
163 json_encode([{K, _}|_] = Props, State) when (K =/= struct andalso
164 K =/= array andalso
165 K =/= json) ->
166 json_encode_proplist(Props, State);
167 json_encode({struct, Props}, State) when is_list(Props) ->
168 json_encode_proplist(Props, State);
169 json_encode({Props}, State) when is_list(Props) ->
170 json_encode_proplist(Props, State);
171 json_encode({}, State) ->
172 json_encode_proplist([], State);
173 json_encode(Array, State) when is_list(Array) ->
174 json_encode_array(Array, State);
175 json_encode({array, Array}, State) when is_list(Array) ->
176 json_encode_array(Array, State);
177 json_encode({json, IoList}, _State) ->
178 IoList;
179 json_encode(Bad, #encoder{handler=null}) ->
180 exit({json_encode, {bad_term, Bad}});
181 json_encode(Bad, State=#encoder{handler=Handler}) ->
182 json_encode(Handler(Bad), State).
183
184 json_encode_array([], _State) ->
185 <<"[]">>;
186 json_encode_array(L, State) ->
187 F = fun (O, Acc) ->
188 [$,, json_encode(O, State) | Acc]
189 end,
190 [$, | Acc1] = lists:foldl(F, "[", L),
191 lists:reverse([$\] | Acc1]).
192
193 json_encode_proplist([], _State) ->
194 <<"{}">>;
195 json_encode_proplist(Props, State) ->
196 F = fun ({K, V}, Acc) ->
197 KS = json_encode_string(K, State),
198 VS = json_encode(V, State),
199 [$,, VS, $:, KS | Acc]
200 end,
201 [$, | Acc1] = lists:foldl(F, "{", Props),
202 lists:reverse([$\} | Acc1]).
203
204 json_encode_string(A, State) when is_atom(A) ->
205 json_encode_string(atom_to_binary(A, latin1), State);
206 json_encode_string(B, State) when is_binary(B) ->
207 case json_bin_is_safe(B) of
208 true ->
209 [?Q, B, ?Q];
210 false ->
211 json_encode_string_unicode(unicode:characters_to_list(B), State, [?Q])
212 end;
213 json_encode_string(I, _State) when is_integer(I) ->
214 [?Q, integer_to_list(I), ?Q];
215 json_encode_string(L, State) when is_list(L) ->
216 case json_string_is_safe(L) of
217 true ->
218 [?Q, L, ?Q];
219 false ->
220 json_encode_string_unicode(L, State, [?Q])
221 end.
222
223 json_string_is_safe([]) ->
224 true;
225 json_string_is_safe([C | Rest]) ->
226 case C of
227 ?Q ->
228 false;
229 $\\ ->
230 false;
231 $\b ->
232 false;
233 $\f ->
234 false;
235 $\n ->
236 false;
237 $\r ->
238 false;
239 $\t ->
240 false;
241 C when C >= 0, C < $\s; C >= 16#7f, C =< 16#10FFFF ->
242 false;
243 C when C < 16#7f ->
244 json_string_is_safe(Rest);
245 _ ->
246 exit({json_encode, {bad_char, C}})
247 end.
248
249 json_bin_is_safe(<<>>) ->
250 true;
251 json_bin_is_safe(<<C, Rest/binary>>) ->
252 case C of
253 ?Q ->
254 false;
255 $\\ ->
256 false;
257 $\b ->
258 false;
259 $\f ->
260 false;
261 $\n ->
262 false;
263 $\r ->
264 false;
265 $\t ->
266 false;
267 C when C >= 0, C < $\s; C >= 16#7f ->
268 false;
269 C when C < 16#7f ->
270 json_bin_is_safe(Rest)
271 end.
272
273 json_encode_string_unicode([], _State, Acc) ->
274 lists:reverse([$\" | Acc]);
275 json_encode_string_unicode([C | Cs], State, Acc) ->
276 Acc1 = case C of
277 ?Q ->
278 [?Q, $\\ | Acc];
279 %% Escaping solidus is only useful when trying to protect
280 %% against "</script>" injection attacks which are only
281 %% possible when JSON is inserted into a HTML document
282 %% in-line. mochijson2 does not protect you from this, so
283 %% if you do insert directly into HTML then you need to
284 %% uncomment the following case or escape the output of encode.
285 %%
286 %% $/ ->
287 %% [$/, $\\ | Acc];
288 %%
289 $\\ ->
290 [$\\, $\\ | Acc];
291 $\b ->
292 [$b, $\\ | Acc];
293 $\f ->
294 [$f, $\\ | Acc];
295 $\n ->
296 [$n, $\\ | Acc];
297 $\r ->
298 [$r, $\\ | Acc];
299 $\t ->
300 [$t, $\\ | Acc];
301 C when C >= 0, C < $\s ->
302 [unihex(C) | Acc];
303 C when C >= 16#7f, C =< 16#10FFFF, State#encoder.utf8 ->
304 [unicode:characters_to_binary([C]) | Acc];
305 C when C >= 16#7f, C =< 16#10FFFF, not State#encoder.utf8 ->
306 [unihex(C) | Acc];
307 C when C < 16#7f ->
308 [C | Acc];
309 _ ->
310 %% json_string_is_safe guarantees that this branch is dead
311 exit({json_encode, {bad_char, C}})
312 end,
313 json_encode_string_unicode(Cs, State, Acc1).
314
315 hexdigit(C) when C >= 0, C =< 9 ->
316 C + $0;
317 hexdigit(C) when C =< 15 ->
318 C + $a - 10.
319
320 unihex(C) when C < 16#10000 ->
321 <<D3:4, D2:4, D1:4, D0:4>> = <<C:16>>,
322 Digits = [hexdigit(D) || D <- [D3, D2, D1, D0]],
323 [$\\, $u | Digits];
324 unihex(C) when C =< 16#10FFFF ->
325 N = C - 16#10000,
326 S1 = 16#d800 bor ((N bsr 10) band 16#3ff),
327 S2 = 16#dc00 bor (N band 16#3ff),
328 [unihex(S1), unihex(S2)].
329
330 json_decode(L, S) when is_list(L) ->
331 json_decode(iolist_to_binary(L), S);
332 json_decode(B, S) ->
333 {Res, S1} = decode1(B, S),
334 {eof, _} = tokenize(B, S1#decoder{state=trim}),
335 Res.
336
337 decode1(B, S=#decoder{state=null}) ->
338 case tokenize(B, S#decoder{state=any}) of
339 {{const, C}, S1} ->
340 {C, S1};
341 {start_array, S1} ->
342 decode_array(B, S1);
343 {start_object, S1} ->
344 decode_object(B, S1)
345 end.
346
347 make_object(V, #decoder{object_hook=N}) when N =:= null orelse N =:= struct ->
348 V;
349 make_object({struct, P}, #decoder{object_hook=eep18}) ->
350 {P};
351 make_object({struct, P}, #decoder{object_hook=proplist}) ->
352 P;
353 make_object(V, #decoder{object_hook=Hook}) ->
354 Hook(V).
355
356 decode_object(B, S) ->
357 decode_object(B, S#decoder{state=key}, []).
358
359 decode_object(B, S=#decoder{state=key}, Acc) ->
360 case tokenize(B, S) of
361 {end_object, S1} ->
362 V = make_object({struct, lists:reverse(Acc)}, S1),
363 {V, S1#decoder{state=null}};
364 {{const, K}, S1} ->
365 {colon, S2} = tokenize(B, S1),
366 {V, S3} = decode1(B, S2#decoder{state=null}),
367 decode_object(B, S3#decoder{state=comma}, [{K, V} | Acc])
368 end;
369 decode_object(B, S=#decoder{state=comma}, Acc) ->
370 case tokenize(B, S) of
371 {end_object, S1} ->
372 V = make_object({struct, lists:reverse(Acc)}, S1),
373 {V, S1#decoder{state=null}};
374 {comma, S1} ->
375 decode_object(B, S1#decoder{state=key}, Acc)
376 end.
377
378 decode_array(B, S) ->
379 decode_array(B, S#decoder{state=any}, []).
380
381 decode_array(B, S=#decoder{state=any}, Acc) ->
382 case tokenize(B, S) of
383 {end_array, S1} ->
384 {lists:reverse(Acc), S1#decoder{state=null}};
385 {start_array, S1} ->
386 {Array, S2} = decode_array(B, S1),
387 decode_array(B, S2#decoder{state=comma}, [Array | Acc]);
388 {start_object, S1} ->
389 {Array, S2} = decode_object(B, S1),
390 decode_array(B, S2#decoder{state=comma}, [Array | Acc]);
391 {{const, Const}, S1} ->
392 decode_array(B, S1#decoder{state=comma}, [Const | Acc])
393 end;
394 decode_array(B, S=#decoder{state=comma}, Acc) ->
395 case tokenize(B, S) of
396 {end_array, S1} ->
397 {lists:reverse(Acc), S1#decoder{state=null}};
398 {comma, S1} ->
399 decode_array(B, S1#decoder{state=any}, Acc)
400 end.
401
402 tokenize_string(B, S=#decoder{offset=O}) ->
403 case tokenize_string_fast(B, O) of
404 {escape, O1} ->
405 Length = O1 - O,
406 S1 = ?ADV_COL(S, Length),
407 <<_:O/binary, Head:Length/binary, _/binary>> = B,
408 tokenize_string(B, S1, lists:reverse(binary_to_list(Head)));
409 O1 ->
410 Length = O1 - O,
411 <<_:O/binary, String:Length/binary, ?Q, _/binary>> = B,
412 {{const, String}, ?ADV_COL(S, Length + 1)}
413 end.
414
415 tokenize_string_fast(B, O) ->
416 case B of
417 <<_:O/binary, ?Q, _/binary>> ->
418 O;
419 <<_:O/binary, $\\, _/binary>> ->
420 {escape, O};
421 <<_:O/binary, C1, _/binary>> when C1 < 128 ->
422 tokenize_string_fast(B, 1 + O);
423 <<_:O/binary, C1, C2, _/binary>> when C1 >= 194, C1 =< 223,
424 C2 >= 128, C2 =< 191 ->
425 tokenize_string_fast(B, 2 + O);
426 <<_:O/binary, C1, C2, C3, _/binary>> when C1 >= 224, C1 =< 239,
427 C2 >= 128, C2 =< 191,
428 C3 >= 128, C3 =< 191 ->
429 tokenize_string_fast(B, 3 + O);
430 <<_:O/binary, C1, C2, C3, C4, _/binary>> when C1 >= 240, C1 =< 244,
431 C2 >= 128, C2 =< 191,
432 C3 >= 128, C3 =< 191,
433 C4 >= 128, C4 =< 191 ->
434 tokenize_string_fast(B, 4 + O);
435 _ ->
436 throw(invalid_utf8)
437 end.
438
439 tokenize_string(B, S=#decoder{offset=O}, Acc) ->
440 case B of
441 <<_:O/binary, ?Q, _/binary>> ->
442 {{const, iolist_to_binary(lists:reverse(Acc))}, ?INC_COL(S)};
443 <<_:O/binary, "\\\"", _/binary>> ->
444 tokenize_string(B, ?ADV_COL(S, 2), [$\" | Acc]);
445 <<_:O/binary, "\\\\", _/binary>> ->
446 tokenize_string(B, ?ADV_COL(S, 2), [$\\ | Acc]);
447 <<_:O/binary, "\\/", _/binary>> ->
448 tokenize_string(B, ?ADV_COL(S, 2), [$/ | Acc]);
449 <<_:O/binary, "\\b", _/binary>> ->
450 tokenize_string(B, ?ADV_COL(S, 2), [$\b | Acc]);
451 <<_:O/binary, "\\f", _/binary>> ->
452 tokenize_string(B, ?ADV_COL(S, 2), [$\f | Acc]);
453 <<_:O/binary, "\\n", _/binary>> ->
454 tokenize_string(B, ?ADV_COL(S, 2), [$\n | Acc]);
455 <<_:O/binary, "\\r", _/binary>> ->
456 tokenize_string(B, ?ADV_COL(S, 2), [$\r | Acc]);
457 <<_:O/binary, "\\t", _/binary>> ->
458 tokenize_string(B, ?ADV_COL(S, 2), [$\t | Acc]);
459 <<_:O/binary, "\\u", C3, C2, C1, C0, Rest/binary>> ->
460 C = erlang:list_to_integer([C3, C2, C1, C0], 16),
461 if C > 16#D7FF, C < 16#DC00 ->
462 %% coalesce UTF-16 surrogate pair
463 <<"\\u", D3, D2, D1, D0, _/binary>> = Rest,
464 D = erlang:list_to_integer([D3,D2,D1,D0], 16),
465 Acc1 = [unicode:characters_to_binary(
466 <<C:16/big-unsigned-integer,
467 D:16/big-unsigned-integer>>,
468 utf16)
469 | Acc],
470 tokenize_string(B, ?ADV_COL(S, 12), Acc1);
471 true ->
472 Acc1 = [unicode:characters_to_binary([C]) | Acc],
473 tokenize_string(B, ?ADV_COL(S, 6), Acc1)
474 end;
475 <<_:O/binary, C1, _/binary>> when C1 < 128 ->
476 tokenize_string(B, ?INC_CHAR(S, C1), [C1 | Acc]);
477 <<_:O/binary, C1, C2, _/binary>> when C1 >= 194, C1 =< 223,
478 C2 >= 128, C2 =< 191 ->
479 tokenize_string(B, ?ADV_COL(S, 2), [C2, C1 | Acc]);
480 <<_:O/binary, C1, C2, C3, _/binary>> when C1 >= 224, C1 =< 239,
481 C2 >= 128, C2 =< 191,
482 C3 >= 128, C3 =< 191 ->
483 tokenize_string(B, ?ADV_COL(S, 3), [C3, C2, C1 | Acc]);
484 <<_:O/binary, C1, C2, C3, C4, _/binary>> when C1 >= 240, C1 =< 244,
485 C2 >= 128, C2 =< 191,
486 C3 >= 128, C3 =< 191,
487 C4 >= 128, C4 =< 191 ->
488 tokenize_string(B, ?ADV_COL(S, 4), [C4, C3, C2, C1 | Acc]);
489 _ ->
490 throw(invalid_utf8)
491 end.
492
493 tokenize_number(B, S) ->
494 case tokenize_number(B, sign, S, []) of
495 {{int, Int}, S1} ->
496 {{const, list_to_integer(Int)}, S1};
497 {{float, Float}, S1} ->
498 {{const, list_to_float(Float)}, S1}
499 end.
500
501 tokenize_number(B, sign, S=#decoder{offset=O}, []) ->
502 case B of
503 <<_:O/binary, $-, _/binary>> ->
504 tokenize_number(B, int, ?INC_COL(S), [$-]);
505 _ ->
506 tokenize_number(B, int, S, [])
507 end;
508 tokenize_number(B, int, S=#decoder{offset=O}, Acc) ->
509 case B of
510 <<_:O/binary, $0, _/binary>> ->
511 tokenize_number(B, frac, ?INC_COL(S), [$0 | Acc]);
512 <<_:O/binary, C, _/binary>> when C >= $1 andalso C =< $9 ->
513 tokenize_number(B, int1, ?INC_COL(S), [C | Acc])
514 end;
515 tokenize_number(B, int1, S=#decoder{offset=O}, Acc) ->
516 case B of
517 <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 ->
518 tokenize_number(B, int1, ?INC_COL(S), [C | Acc]);
519 _ ->
520 tokenize_number(B, frac, S, Acc)
521 end;
522 tokenize_number(B, frac, S=#decoder{offset=O}, Acc) ->
523 case B of
524 <<_:O/binary, $., C, _/binary>> when C >= $0, C =< $9 ->
525 tokenize_number(B, frac1, ?ADV_COL(S, 2), [C, $. | Acc]);
526 <<_:O/binary, E, _/binary>> when E =:= $e orelse E =:= $E ->
527 tokenize_number(B, esign, ?INC_COL(S), [$e, $0, $. | Acc]);
528 _ ->
529 {{int, lists:reverse(Acc)}, S}
530 end;
531 tokenize_number(B, frac1, S=#decoder{offset=O}, Acc) ->
532 case B of
533 <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 ->
534 tokenize_number(B, frac1, ?INC_COL(S), [C | Acc]);
535 <<_:O/binary, E, _/binary>> when E =:= $e orelse E =:= $E ->
536 tokenize_number(B, esign, ?INC_COL(S), [$e | Acc]);
537 _ ->
538 {{float, lists:reverse(Acc)}, S}
539 end;
540 tokenize_number(B, esign, S=#decoder{offset=O}, Acc) ->
541 case B of
542 <<_:O/binary, C, _/binary>> when C =:= $- orelse C=:= $+ ->
543 tokenize_number(B, eint, ?INC_COL(S), [C | Acc]);
544 _ ->
545 tokenize_number(B, eint, S, Acc)
546 end;
547 tokenize_number(B, eint, S=#decoder{offset=O}, Acc) ->
548 case B of
549 <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 ->
550 tokenize_number(B, eint1, ?INC_COL(S), [C | Acc])
551 end;
552 tokenize_number(B, eint1, S=#decoder{offset=O}, Acc) ->
553 case B of
554 <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 ->
555 tokenize_number(B, eint1, ?INC_COL(S), [C | Acc]);
556 _ ->
557 {{float, lists:reverse(Acc)}, S}
558 end.
559
560 tokenize(B, S=#decoder{offset=O}) ->
561 case B of
562 <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) ->
563 tokenize(B, ?INC_CHAR(S, C));
564 <<_:O/binary, "{", _/binary>> ->
565 {start_object, ?INC_COL(S)};
566 <<_:O/binary, "}", _/binary>> ->
567 {end_object, ?INC_COL(S)};
568 <<_:O/binary, "[", _/binary>> ->
569 {start_array, ?INC_COL(S)};
570 <<_:O/binary, "]", _/binary>> ->
571 {end_array, ?INC_COL(S)};
572 <<_:O/binary, ",", _/binary>> ->
573 {comma, ?INC_COL(S)};
574 <<_:O/binary, ":", _/binary>> ->
575 {colon, ?INC_COL(S)};
576 <<_:O/binary, "null", _/binary>> ->
577 {{const, null}, ?ADV_COL(S, 4)};
578 <<_:O/binary, "true", _/binary>> ->
579 {{const, true}, ?ADV_COL(S, 4)};
580 <<_:O/binary, "false", _/binary>> ->
581 {{const, false}, ?ADV_COL(S, 5)};
582 <<_:O/binary, "\"", _/binary>> ->
583 tokenize_string(B, ?INC_COL(S));
584 <<_:O/binary, C, _/binary>> when (C >= $0 andalso C =< $9)
585 orelse C =:= $- ->
586 tokenize_number(B, S);
587 <<_:O/binary>> ->
588 trim = S#decoder.state,
589 {eof, S}
590 end.
591 %%
592 %% Tests
593 %%
594 -ifdef(TEST).
595 -include_lib("eunit/include/eunit.hrl").
596
597
598 %% testing constructs borrowed from the Yaws JSON implementation.
599
600 %% Create an object from a list of Key/Value pairs.
601
602 obj_new() ->
603 {struct, []}.
604
605 is_obj({struct, Props}) ->
606 F = fun ({K, _}) when is_binary(K) -> true end,
607 lists:all(F, Props).
608
609 obj_from_list(Props) ->
610 Obj = {struct, Props},
611 ?assert(is_obj(Obj)),
612 Obj.
613
614 %% Test for equivalence of Erlang terms.
615 %% Due to arbitrary order of construction, equivalent objects might
616 %% compare unequal as erlang terms, so we need to carefully recurse
617 %% through aggregates (tuples and objects).
618
619 equiv({struct, Props1}, {struct, Props2}) ->
620 equiv_object(Props1, Props2);
621 equiv(L1, L2) when is_list(L1), is_list(L2) ->
622 equiv_list(L1, L2);
623 equiv(N1, N2) when is_number(N1), is_number(N2) -> N1 == N2;
624 equiv(B1, B2) when is_binary(B1), is_binary(B2) -> B1 == B2;
625 equiv(A, A) when A =:= true orelse A =:= false orelse A =:= null -> true.
626
627 %% Object representation and traversal order is unknown.
628 %% Use the sledgehammer and sort property lists.
629
630 equiv_object(Props1, Props2) ->
631 L1 = lists:keysort(1, Props1),
632 L2 = lists:keysort(1, Props2),
633 Pairs = lists:zip(L1, L2),
634 true = lists:all(fun({{K1, V1}, {K2, V2}}) ->
635 equiv(K1, K2) and equiv(V1, V2)
636 end, Pairs).
637
638 %% Recursively compare tuple elements for equivalence.
639
640 equiv_list([], []) ->
641 true;
642 equiv_list([V1 | L1], [V2 | L2]) ->
643 equiv(V1, V2) andalso equiv_list(L1, L2).
644
645 decode_test() ->
646 [1199344435545.0, 1] = decode(<<"[1199344435545.0,1]">>),
647 <<16#F0,16#9D,16#9C,16#95>> = decode([34,"\\ud835","\\udf15",34]).
648
649 e2j_vec_test() ->
650 test_one(e2j_test_vec(utf8), 1).
651
652 test_one([], _N) ->
653 %% io:format("~p tests passed~n", [N-1]),
654 ok;
655 test_one([{E, J} | Rest], N) ->
656 %% io:format("[~p] ~p ~p~n", [N, E, J]),
657 true = equiv(E, decode(J)),
658 true = equiv(E, decode(encode(E))),
659 test_one(Rest, 1+N).
660
661 e2j_test_vec(utf8) ->
662 [
663 {1, "1"},
664 {3.1416, "3.14160"}, %% text representation may truncate, trail zeroes
665 {-1, "-1"},
666 {-3.1416, "-3.14160"},
667 {12.0e10, "1.20000e+11"},
668 {1.234E+10, "1.23400e+10"},
669 {-1.234E-10, "-1.23400e-10"},
670 {10.0, "1.0e+01"},
671 {123.456, "1.23456E+2"},
672 {10.0, "1e1"},
673 {<<"foo">>, "\"foo\""},
674 {<<"foo", 5, "bar">>, "\"foo\\u0005bar\""},
675 {<<"">>, "\"\""},
676 {<<"\n\n\n">>, "\"\\n\\n\\n\""},
677 {<<"\" \b\f\r\n\t\"">>, "\"\\\" \\b\\f\\r\\n\\t\\\"\""},
678 {obj_new(), "{}"},
679 {obj_from_list([{<<"foo">>, <<"bar">>}]), "{\"foo\":\"bar\"}"},
680 {obj_from_list([{<<"foo">>, <<"bar">>}, {<<"baz">>, 123}]),
681 "{\"foo\":\"bar\",\"baz\":123}"},
682 {[], "[]"},
683 {[[]], "[[]]"},
684 {[1, <<"foo">>], "[1,\"foo\"]"},
685
686 %% json array in a json object
687 {obj_from_list([{<<"foo">>, [123]}]),
688 "{\"foo\":[123]}"},
689
690 %% json object in a json object
691 {obj_from_list([{<<"foo">>, obj_from_list([{<<"bar">>, true}])}]),
692 "{\"foo\":{\"bar\":true}}"},
693
694 %% fold evaluation order
695 {obj_from_list([{<<"foo">>, []},
696 {<<"bar">>, obj_from_list([{<<"baz">>, true}])},
697 {<<"alice">>, <<"bob">>}]),
698 "{\"foo\":[],\"bar\":{\"baz\":true},\"alice\":\"bob\"}"},
699
700 %% json object in a json array
701 {[-123, <<"foo">>, obj_from_list([{<<"bar">>, []}]), null],
702 "[-123,\"foo\",{\"bar\":[]},null]"}
703 ].
704
705 %% test utf8 encoding
706 encoder_utf8_test() ->
707 %% safe conversion case (default)
708 <<"\"\\u0001\\u0442\\u0435\\u0441\\u0442\"">> =
709 iolist_to_binary(encode(<<1,"\321\202\320\265\321\201\321\202">>)),
710
711 %% raw utf8 output (optional)
712 Enc = mochijson2:encoder([{utf8, true}]),
713 <<34,"\\u0001",209,130,208,181,209,129,209,130,34>> =
714 iolist_to_binary(Enc(<<1,"\321\202\320\265\321\201\321\202">>)).
715
716 input_validation_test() ->
717 Good = [
718 {16#00A3, <<?Q, 16#C2, 16#A3, ?Q>>}, %% pound
719 {16#20AC, <<?Q, 16#E2, 16#82, 16#AC, ?Q>>}, %% euro
720 {16#10196, <<?Q, 16#F0, 16#90, 16#86, 16#96, ?Q>>} %% denarius
721 ],
722 lists:foreach(fun({CodePoint, UTF8}) ->
723 Expect = unicode:characters_to_binary([CodePoint]),
724 Expect = decode(UTF8)
725 end, Good),
726
727 Bad = [
728 %% 2nd, 3rd, or 4th byte of a multi-byte sequence w/o leading byte
729 <<?Q, 16#80, ?Q>>,
730 %% missing continuations, last byte in each should be 80-BF
731 <<?Q, 16#C2, 16#7F, ?Q>>,
732 <<?Q, 16#E0, 16#80,16#7F, ?Q>>,
733 <<?Q, 16#F0, 16#80, 16#80, 16#7F, ?Q>>,
734 %% we don't support code points > 10FFFF per RFC 3629
735 <<?Q, 16#F5, 16#80, 16#80, 16#80, ?Q>>,
736 %% escape characters trigger a different code path
737 <<?Q, $\\, $\n, 16#80, ?Q>>
738 ],
739 lists:foreach(
740 fun(X) ->
741 ok = try decode(X) catch invalid_utf8 -> ok end,
742 %% could be {ucs,{bad_utf8_character_code}} or
743 %% {json_encode,{bad_char,_}}
744 {'EXIT', _} = (catch encode(X))
745 end, Bad).
746
747 inline_json_test() ->
748 ?assertEqual(<<"\"iodata iodata\"">>,
749 iolist_to_binary(
750 encode({json, [<<"\"iodata">>, " iodata\""]}))),
751 ?assertEqual({struct, [{<<"key">>, <<"iodata iodata">>}]},
752 decode(
753 encode({struct,
754 [{key, {json, [<<"\"iodata">>, " iodata\""]}}]}))),
755 ok.
756
757 big_unicode_test() ->
758 UTF8Seq = unicode:characters_to_binary([16#0001d120]),
759 ?assertEqual(
760 <<"\"\\ud834\\udd20\"">>,
761 iolist_to_binary(encode(UTF8Seq))),
762 ?assertEqual(
763 UTF8Seq,
764 decode(iolist_to_binary(encode(UTF8Seq)))),
765 ok.
766
767 custom_decoder_test() ->
768 ?assertEqual(
769 {struct, [{<<"key">>, <<"value">>}]},
770 (decoder([]))("{\"key\": \"value\"}")),
771 F = fun ({struct, [{<<"key">>, <<"value">>}]}) -> win end,
772 ?assertEqual(
773 win,
774 (decoder([{object_hook, F}]))("{\"key\": \"value\"}")),
775 ok.
776
777 atom_test() ->
778 %% JSON native atoms
779 [begin
780 ?assertEqual(A, decode(atom_to_list(A))),
781 ?assertEqual(iolist_to_binary(atom_to_list(A)),
782 iolist_to_binary(encode(A)))
783 end || A <- [true, false, null]],
784 %% Atom to string
785 ?assertEqual(
786 <<"\"foo\"">>,
787 iolist_to_binary(encode(foo))),
788 ?assertEqual(
789 <<"\"\\ud834\\udd20\"">>,
790 iolist_to_binary(
791 encode(
792 binary_to_atom(
793 unicode:characters_to_binary([16#0001d120]), latin1)))),
794 ok.
795
796 key_encode_test() ->
797 %% Some forms are accepted as keys that would not be strings in other
798 %% cases
799 ?assertEqual(
800 <<"{\"foo\":1}">>,
801 iolist_to_binary(encode({struct, [{foo, 1}]}))),
802 ?assertEqual(
803 <<"{\"foo\":1}">>,
804 iolist_to_binary(encode({struct, [{<<"foo">>, 1}]}))),
805 ?assertEqual(
806 <<"{\"foo\":1}">>,
807 iolist_to_binary(encode({struct, [{"foo", 1}]}))),
808 ?assertEqual(
809 <<"{\"foo\":1}">>,
810 iolist_to_binary(encode([{foo, 1}]))),
811 ?assertEqual(
812 <<"{\"foo\":1}">>,
813 iolist_to_binary(encode([{<<"foo">>, 1}]))),
814 ?assertEqual(
815 <<"{\"foo\":1}">>,
816 iolist_to_binary(encode([{"foo", 1}]))),
817 ?assertEqual(
818 <<"{\"\\ud834\\udd20\":1}">>,
819 iolist_to_binary(
820 encode({struct, [{[16#0001d120], 1}]}))),
821 ?assertEqual(
822 <<"{\"1\":1}">>,
823 iolist_to_binary(encode({struct, [{1, 1}]}))),
824 ok.
825
826 unsafe_chars_test() ->
827 Chars = "\"\\\b\f\n\r\t",
828 [begin
829 ?assertEqual(false, json_string_is_safe([C])),
830 ?assertEqual(false, json_bin_is_safe(<<C>>)),
831 ?assertEqual(<<C>>, decode(encode(<<C>>)))
832 end || C <- Chars],
833 ?assertEqual(
834 false,
835 json_string_is_safe([16#0001d120])),
836 ?assertEqual(
837 false,
838 json_bin_is_safe(unicode:characters_to_binary([16#0001d120]))),
839 ?assertEqual(
840 [16#0001d120],
841 unicode:characters_to_list(
842 decode(
843 encode(
844 binary_to_atom(
845 unicode:characters_to_binary([16#0001d120]),
846 latin1))))),
847 ?assertEqual(
848 false,
849 json_string_is_safe([16#10ffff])),
850 ?assertEqual(
851 false,
852 json_bin_is_safe(unicode:characters_to_binary([16#10ffff]))),
853 %% solidus can be escaped but isn't unsafe by default
854 ?assertEqual(
855 <<"/">>,
856 decode(<<"\"\\/\"">>)),
857 ok.
858
859 int_test() ->
860 ?assertEqual(0, decode("0")),
861 ?assertEqual(1, decode("1")),
862 ?assertEqual(11, decode("11")),
863 ok.
864
865 large_int_test() ->
866 ?assertEqual(<<"-2147483649214748364921474836492147483649">>,
867 iolist_to_binary(encode(-2147483649214748364921474836492147483649))),
868 ?assertEqual(<<"2147483649214748364921474836492147483649">>,
869 iolist_to_binary(encode(2147483649214748364921474836492147483649))),
870 ok.
871
872 float_test() ->
873 ?assertEqual(<<"-2147483649.0">>, iolist_to_binary(encode(-2147483649.0))),
874 ?assertEqual(<<"2147483648.0">>, iolist_to_binary(encode(2147483648.0))),
875 ok.
876
877 handler_test() ->
878 ?assertEqual(
879 {'EXIT',{json_encode,{bad_term,{x,y}}}},
880 catch encode({x,y})),
881 F = fun ({x,y}) -> [] end,
882 ?assertEqual(
883 <<"[]">>,
884 iolist_to_binary((encoder([{handler, F}]))({x, y}))),
885 ok.
886
887 encode_empty_test_() ->
888 [{A, ?_assertEqual(<<"{}">>, iolist_to_binary(encode(B)))}
889 || {A, B} <- [{"eep18 {}", {}},
890 {"eep18 {[]}", {[]}},
891 {"{struct, []}", {struct, []}}]].
892
893 encode_test_() ->
894 P = [{<<"k">>, <<"v">>}],
895 JSON = iolist_to_binary(encode({struct, P})),
896 [{atom_to_list(F),
897 ?_assertEqual(JSON, iolist_to_binary(encode(decode(JSON, [{format, F}]))))}
898 || F <- [struct, eep18, proplist]].
899
900 format_test_() ->
901 P = [{<<"k">>, <<"v">>}],
902 JSON = iolist_to_binary(encode({struct, P})),
903 [{atom_to_list(F),
904 ?_assertEqual(A, decode(JSON, [{format, F}]))}
905 || {F, A} <- [{struct, {struct, P}},
906 {eep18, {P}},
907 {proplist, P}]].
908
909 array_test() ->
910 A = [<<"hello">>],
911 ?assertEqual(A, decode(encode({array, A}))).
912
913 bad_char_test() ->
914 ?assertEqual(
915 {'EXIT', {json_encode, {bad_char, 16#110000}}},
916 catch json_string_is_safe([16#110000])).
917
918 utf8_roundtrip_test_() ->
919 %% These are the boundary cases for UTF8 encoding
920 Codepoints = [%% 7 bits -> 1 byte
921 16#00, 16#7f,
922 %% 11 bits -> 2 bytes
923 16#080, 16#07ff,
924 %% 16 bits -> 3 bytes
925 16#0800, 16#ffff,
926 16#d7ff, 16#e000,
927 %% 21 bits -> 4 bytes
928 16#010000, 16#10ffff],
929 UTF8 = unicode:characters_to_binary(Codepoints),
930 Encode = encoder([{utf8, true}]),
931 [{"roundtrip escaped",
932 ?_assertEqual(UTF8, decode(encode(UTF8)))},
933 {"roundtrip utf8",
934 ?_assertEqual(UTF8, decode(Encode(UTF8)))}].
935
936 utf8_non_character_test_() ->
937 S = unicode:characters_to_binary([16#ffff, 16#fffe]),
938 [{"roundtrip escaped", ?_assertEqual(S, decode(encode(S)))},
939 {"roundtrip utf8", ?_assertEqual(S, decode((encoder([{utf8, true}]))(S)))}].
940
941 -endif.
+0
-122
deps/mochiweb/src/mochilists.erl less more
0 %% @copyright Copyright (c) 2010 Mochi Media, Inc.
1 %% @author David Reid <dreid@mochimedia.com>
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 %% @doc Utility functions for dealing with proplists.
22
23 -module(mochilists).
24 -author("David Reid <dreid@mochimedia.com>").
25 -export([get_value/2, get_value/3, is_defined/2, set_default/2, set_defaults/2]).
26
27 %% @spec set_default({Key::term(), Value::term()}, Proplist::list()) -> list()
28 %%
29 %% @doc Return new Proplist with {Key, Value} set if not is_defined(Key, Proplist).
30 set_default({Key, Value}, Proplist) ->
31 case is_defined(Key, Proplist) of
32 true ->
33 Proplist;
34 false ->
35 [{Key, Value} | Proplist]
36 end.
37
38 %% @spec set_defaults([{Key::term(), Value::term()}], Proplist::list()) -> list()
39 %%
40 %% @doc Return new Proplist with {Key, Value} set if not is_defined(Key, Proplist).
41 set_defaults(DefaultProps, Proplist) ->
42 lists:foldl(fun set_default/2, Proplist, DefaultProps).
43
44
45 %% @spec is_defined(Key::term(), Proplist::list()) -> bool()
46 %%
47 %% @doc Returns true if Propist contains at least one entry associated
48 %% with Key, otherwise false is returned.
49 is_defined(Key, Proplist) ->
50 lists:keyfind(Key, 1, Proplist) =/= false.
51
52
53 %% @spec get_value(Key::term(), Proplist::list()) -> term() | undefined
54 %%
55 %% @doc Return the value of <code>Key</code> or undefined
56 get_value(Key, Proplist) ->
57 get_value(Key, Proplist, undefined).
58
59 %% @spec get_value(Key::term(), Proplist::list(), Default::term()) -> term()
60 %%
61 %% @doc Return the value of <code>Key</code> or <code>Default</code>
62 get_value(_Key, [], Default) ->
63 Default;
64 get_value(Key, Proplist, Default) ->
65 case lists:keyfind(Key, 1, Proplist) of
66 false ->
67 Default;
68 {Key, Value} ->
69 Value
70 end.
71
72 %%
73 %% Tests
74 %%
75 -ifdef(TEST).
76 -include_lib("eunit/include/eunit.hrl").
77
78 set_defaults_test() ->
79 ?assertEqual(
80 [{k, v}],
81 set_defaults([{k, v}], [])),
82 ?assertEqual(
83 [{k, v}],
84 set_defaults([{k, vee}], [{k, v}])),
85 ?assertEqual(
86 lists:sort([{kay, vee}, {k, v}]),
87 lists:sort(set_defaults([{k, vee}, {kay, vee}], [{k, v}]))),
88 ok.
89
90 set_default_test() ->
91 ?assertEqual(
92 [{k, v}],
93 set_default({k, v}, [])),
94 ?assertEqual(
95 [{k, v}],
96 set_default({k, vee}, [{k, v}])),
97 ok.
98
99 get_value_test() ->
100 ?assertEqual(
101 undefined,
102 get_value(foo, [])),
103 ?assertEqual(
104 undefined,
105 get_value(foo, [{bar, baz}])),
106 ?assertEqual(
107 bar,
108 get_value(foo, [{foo, bar}])),
109 ?assertEqual(
110 default,
111 get_value(foo, [], default)),
112 ?assertEqual(
113 default,
114 get_value(foo, [{bar, baz}], default)),
115 ?assertEqual(
116 bar,
117 get_value(foo, [{foo, bar}], default)),
118 ok.
119
120 -endif.
121
+0
-158
deps/mochiweb/src/mochilogfile2.erl less more
0 %% @author Bob Ippolito <bob@mochimedia.com>
1 %% @copyright 2010 Mochi Media, Inc.
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 %% @doc Write newline delimited log files, ensuring that if a truncated
22 %% entry is found on log open then it is fixed before writing. Uses
23 %% delayed writes and raw files for performance.
24 -module(mochilogfile2).
25 -author('bob@mochimedia.com').
26
27 -export([open/1, write/2, close/1, name/1]).
28
29 %% @spec open(Name) -> Handle
30 %% @doc Open the log file Name, creating or appending as necessary. All data
31 %% at the end of the file will be truncated until a newline is found, to
32 %% ensure that all records are complete.
33 open(Name) ->
34 {ok, FD} = file:open(Name, [raw, read, write, delayed_write, binary]),
35 fix_log(FD),
36 {?MODULE, Name, FD}.
37
38 %% @spec name(Handle) -> string()
39 %% @doc Return the path of the log file.
40 name({?MODULE, Name, _FD}) ->
41 Name.
42
43 %% @spec write(Handle, IoData) -> ok
44 %% @doc Write IoData to the log file referenced by Handle.
45 write({?MODULE, _Name, FD}, IoData) ->
46 ok = file:write(FD, [IoData, $\n]),
47 ok.
48
49 %% @spec close(Handle) -> ok
50 %% @doc Close the log file referenced by Handle.
51 close({?MODULE, _Name, FD}) ->
52 ok = file:sync(FD),
53 ok = file:close(FD),
54 ok.
55
56 fix_log(FD) ->
57 {ok, Location} = file:position(FD, eof),
58 Seek = find_last_newline(FD, Location),
59 {ok, Seek} = file:position(FD, Seek),
60 ok = file:truncate(FD),
61 ok.
62
63 %% Seek backwards to the last valid log entry
64 find_last_newline(_FD, N) when N =< 1 ->
65 0;
66 find_last_newline(FD, Location) ->
67 case file:pread(FD, Location - 1, 1) of
68 {ok, <<$\n>>} ->
69 Location;
70 {ok, _} ->
71 find_last_newline(FD, Location - 1)
72 end.
73
74 %%
75 %% Tests
76 %%
77 -ifdef(TEST).
78 -include_lib("eunit/include/eunit.hrl").
79 name_test() ->
80 D = mochitemp:mkdtemp(),
81 FileName = filename:join(D, "open_close_test.log"),
82 H = open(FileName),
83 ?assertEqual(
84 FileName,
85 name(H)),
86 close(H),
87 file:delete(FileName),
88 file:del_dir(D),
89 ok.
90
91 open_close_test() ->
92 D = mochitemp:mkdtemp(),
93 FileName = filename:join(D, "open_close_test.log"),
94 OpenClose = fun () ->
95 H = open(FileName),
96 ?assertEqual(
97 true,
98 filelib:is_file(FileName)),
99 ok = close(H),
100 ?assertEqual(
101 {ok, <<>>},
102 file:read_file(FileName)),
103 ok
104 end,
105 OpenClose(),
106 OpenClose(),
107 file:delete(FileName),
108 file:del_dir(D),
109 ok.
110
111 write_test() ->
112 D = mochitemp:mkdtemp(),
113 FileName = filename:join(D, "write_test.log"),
114 F = fun () ->
115 H = open(FileName),
116 write(H, "test line"),
117 close(H),
118 ok
119 end,
120 F(),
121 ?assertEqual(
122 {ok, <<"test line\n">>},
123 file:read_file(FileName)),
124 F(),
125 ?assertEqual(
126 {ok, <<"test line\ntest line\n">>},
127 file:read_file(FileName)),
128 file:delete(FileName),
129 file:del_dir(D),
130 ok.
131
132 fix_log_test() ->
133 D = mochitemp:mkdtemp(),
134 FileName = filename:join(D, "write_test.log"),
135 file:write_file(FileName, <<"first line good\nsecond line bad">>),
136 F = fun () ->
137 H = open(FileName),
138 write(H, "test line"),
139 close(H),
140 ok
141 end,
142 F(),
143 ?assertEqual(
144 {ok, <<"first line good\ntest line\n">>},
145 file:read_file(FileName)),
146 file:write_file(FileName, <<"first line bad">>),
147 F(),
148 ?assertEqual(
149 {ok, <<"test line\n">>},
150 file:read_file(FileName)),
151 F(),
152 ?assertEqual(
153 {ok, <<"test line\ntest line\n">>},
154 file:read_file(FileName)),
155 ok.
156
157 -endif.
+0
-372
deps/mochiweb/src/mochinum.erl less more
0 %% @copyright 2007 Mochi Media, Inc.
1 %% @author Bob Ippolito <bob@mochimedia.com>
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 %% @doc Useful numeric algorithms for floats that cover some deficiencies
22 %% in the math module. More interesting is digits/1, which implements
23 %% the algorithm from:
24 %% http://www.cs.indiana.edu/~burger/fp/index.html
25 %% See also "Printing Floating-Point Numbers Quickly and Accurately"
26 %% in Proceedings of the SIGPLAN '96 Conference on Programming Language
27 %% Design and Implementation.
28
29 -module(mochinum).
30 -author("Bob Ippolito <bob@mochimedia.com>").
31 -export([digits/1, frexp/1, int_pow/2, int_ceil/1]).
32
33 %% IEEE 754 Float exponent bias
34 -define(FLOAT_BIAS, 1022).
35 -define(MIN_EXP, -1074).
36 -define(BIG_POW, 4503599627370496).
37
38 %% External API
39
40 %% @spec digits(number()) -> string()
41 %% @doc Returns a string that accurately represents the given integer or float
42 %% using a conservative amount of digits. Great for generating
43 %% human-readable output, or compact ASCII serializations for floats.
44 digits(N) when is_integer(N) ->
45 integer_to_list(N);
46 digits(0.0) ->
47 "0.0";
48 digits(Float) ->
49 {Frac1, Exp1} = frexp_int(Float),
50 [Place0 | Digits0] = digits1(Float, Exp1, Frac1),
51 {Place, Digits} = transform_digits(Place0, Digits0),
52 R = insert_decimal(Place, Digits),
53 case Float < 0 of
54 true ->
55 [$- | R];
56 _ ->
57 R
58 end.
59
60 %% @spec frexp(F::float()) -> {Frac::float(), Exp::float()}
61 %% @doc Return the fractional and exponent part of an IEEE 754 double,
62 %% equivalent to the libc function of the same name.
63 %% F = Frac * pow(2, Exp).
64 frexp(F) ->
65 frexp1(unpack(F)).
66
67 %% @spec int_pow(X::integer(), N::integer()) -> Y::integer()
68 %% @doc Moderately efficient way to exponentiate integers.
69 %% int_pow(10, 2) = 100.
70 int_pow(_X, 0) ->
71 1;
72 int_pow(X, N) when N > 0 ->
73 int_pow(X, N, 1).
74
75 %% @spec int_ceil(F::float()) -> integer()
76 %% @doc Return the ceiling of F as an integer. The ceiling is defined as
77 %% F when F == trunc(F);
78 %% trunc(F) when F &lt; 0;
79 %% trunc(F) + 1 when F &gt; 0.
80 int_ceil(X) ->
81 T = trunc(X),
82 case (X - T) of
83 Pos when Pos > 0 -> T + 1;
84 _ -> T
85 end.
86
87
88 %% Internal API
89
90 int_pow(X, N, R) when N < 2 ->
91 R * X;
92 int_pow(X, N, R) ->
93 int_pow(X * X, N bsr 1, case N band 1 of 1 -> R * X; 0 -> R end).
94
95 insert_decimal(0, S) ->
96 "0." ++ S;
97 insert_decimal(Place, S) when Place > 0 ->
98 L = length(S),
99 case Place - L of
100 0 ->
101 S ++ ".0";
102 N when N < 0 ->
103 {S0, S1} = lists:split(L + N, S),
104 S0 ++ "." ++ S1;
105 N when N < 6 ->
106 %% More places than digits
107 S ++ lists:duplicate(N, $0) ++ ".0";
108 _ ->
109 insert_decimal_exp(Place, S)
110 end;
111 insert_decimal(Place, S) when Place > -6 ->
112 "0." ++ lists:duplicate(abs(Place), $0) ++ S;
113 insert_decimal(Place, S) ->
114 insert_decimal_exp(Place, S).
115
116 insert_decimal_exp(Place, S) ->
117 [C | S0] = S,
118 S1 = case S0 of
119 [] ->
120 "0";
121 _ ->
122 S0
123 end,
124 Exp = case Place < 0 of
125 true ->
126 "e-";
127 false ->
128 "e+"
129 end,
130 [C] ++ "." ++ S1 ++ Exp ++ integer_to_list(abs(Place - 1)).
131
132
133 digits1(Float, Exp, Frac) ->
134 Round = ((Frac band 1) =:= 0),
135 case Exp >= 0 of
136 true ->
137 BExp = 1 bsl Exp,
138 case (Frac =/= ?BIG_POW) of
139 true ->
140 scale((Frac * BExp * 2), 2, BExp, BExp,
141 Round, Round, Float);
142 false ->
143 scale((Frac * BExp * 4), 4, (BExp * 2), BExp,
144 Round, Round, Float)
145 end;
146 false ->
147 case (Exp =:= ?MIN_EXP) orelse (Frac =/= ?BIG_POW) of
148 true ->
149 scale((Frac * 2), 1 bsl (1 - Exp), 1, 1,
150 Round, Round, Float);
151 false ->
152 scale((Frac * 4), 1 bsl (2 - Exp), 2, 1,
153 Round, Round, Float)
154 end
155 end.
156
157 scale(R, S, MPlus, MMinus, LowOk, HighOk, Float) ->
158 Est = int_ceil(math:log10(abs(Float)) - 1.0e-10),
159 %% Note that the scheme implementation uses a 326 element look-up table
160 %% for int_pow(10, N) where we do not.
161 case Est >= 0 of
162 true ->
163 fixup(R, S * int_pow(10, Est), MPlus, MMinus, Est,
164 LowOk, HighOk);
165 false ->
166 Scale = int_pow(10, -Est),
167 fixup(R * Scale, S, MPlus * Scale, MMinus * Scale, Est,
168 LowOk, HighOk)
169 end.
170
171 fixup(R, S, MPlus, MMinus, K, LowOk, HighOk) ->
172 TooLow = case HighOk of
173 true ->
174 (R + MPlus) >= S;
175 false ->
176 (R + MPlus) > S
177 end,
178 case TooLow of
179 true ->
180 [(K + 1) | generate(R, S, MPlus, MMinus, LowOk, HighOk)];
181 false ->
182 [K | generate(R * 10, S, MPlus * 10, MMinus * 10, LowOk, HighOk)]
183 end.
184
185 generate(R0, S, MPlus, MMinus, LowOk, HighOk) ->
186 D = R0 div S,
187 R = R0 rem S,
188 TC1 = case LowOk of
189 true ->
190 R =< MMinus;
191 false ->
192 R < MMinus
193 end,
194 TC2 = case HighOk of
195 true ->
196 (R + MPlus) >= S;
197 false ->
198 (R + MPlus) > S
199 end,
200 case TC1 of
201 false ->
202 case TC2 of
203 false ->
204 [D | generate(R * 10, S, MPlus * 10, MMinus * 10,
205 LowOk, HighOk)];
206 true ->
207 [D + 1]
208 end;
209 true ->
210 case TC2 of
211 false ->
212 [D];
213 true ->
214 case R * 2 < S of
215 true ->
216 [D];
217 false ->
218 [D + 1]
219 end
220 end
221 end.
222
223 unpack(Float) ->
224 <<Sign:1, Exp:11, Frac:52>> = <<Float:64/float>>,
225 {Sign, Exp, Frac}.
226
227 frexp1({_Sign, 0, 0}) ->
228 {0.0, 0};
229 frexp1({Sign, 0, Frac}) ->
230 Exp = log2floor(Frac),
231 <<Frac1:64/float>> = <<Sign:1, ?FLOAT_BIAS:11, (Frac-1):52>>,
232 {Frac1, -(?FLOAT_BIAS) - 52 + Exp};
233 frexp1({Sign, Exp, Frac}) ->
234 <<Frac1:64/float>> = <<Sign:1, ?FLOAT_BIAS:11, Frac:52>>,
235 {Frac1, Exp - ?FLOAT_BIAS}.
236
237 log2floor(Int) ->
238 log2floor(Int, 0).
239
240 log2floor(0, N) ->
241 N;
242 log2floor(Int, N) ->
243 log2floor(Int bsr 1, 1 + N).
244
245
246 transform_digits(Place, [0 | Rest]) ->
247 transform_digits(Place, Rest);
248 transform_digits(Place, Digits) ->
249 {Place, [$0 + D || D <- Digits]}.
250
251
252 frexp_int(F) ->
253 case unpack(F) of
254 {_Sign, 0, Frac} ->
255 {Frac, ?MIN_EXP};
256 {_Sign, Exp, Frac} ->
257 {Frac + (1 bsl 52), Exp - 53 - ?FLOAT_BIAS}
258 end.
259
260 %%
261 %% Tests
262 %%
263 -ifdef(TEST).
264 -include_lib("eunit/include/eunit.hrl").
265
266 int_ceil_test() ->
267 ?assertEqual(1, int_ceil(0.0001)),
268 ?assertEqual(0, int_ceil(0.0)),
269 ?assertEqual(1, int_ceil(0.99)),
270 ?assertEqual(1, int_ceil(1.0)),
271 ?assertEqual(-1, int_ceil(-1.5)),
272 ?assertEqual(-2, int_ceil(-2.0)),
273 ok.
274
275 int_pow_test() ->
276 ?assertEqual(1, int_pow(1, 1)),
277 ?assertEqual(1, int_pow(1, 0)),
278 ?assertEqual(1, int_pow(10, 0)),
279 ?assertEqual(10, int_pow(10, 1)),
280 ?assertEqual(100, int_pow(10, 2)),
281 ?assertEqual(1000, int_pow(10, 3)),
282 ok.
283
284 digits_test() ->
285 ?assertEqual("0",
286 digits(0)),
287 ?assertEqual("0.0",
288 digits(0.0)),
289 ?assertEqual("1.0",
290 digits(1.0)),
291 ?assertEqual("-1.0",
292 digits(-1.0)),
293 ?assertEqual("0.1",
294 digits(0.1)),
295 ?assertEqual("0.01",
296 digits(0.01)),
297 ?assertEqual("0.001",
298 digits(0.001)),
299 ?assertEqual("1.0e+6",
300 digits(1000000.0)),
301 ?assertEqual("0.5",
302 digits(0.5)),
303 ?assertEqual("4503599627370496.0",
304 digits(4503599627370496.0)),
305 %% small denormalized number
306 %% 4.94065645841246544177e-324 =:= 5.0e-324
307 <<SmallDenorm/float>> = <<0,0,0,0,0,0,0,1>>,
308 ?assertEqual("5.0e-324",
309 digits(SmallDenorm)),
310 ?assertEqual(SmallDenorm,
311 list_to_float(digits(SmallDenorm))),
312 %% large denormalized number
313 %% 2.22507385850720088902e-308
314 <<BigDenorm/float>> = <<0,15,255,255,255,255,255,255>>,
315 ?assertEqual("2.225073858507201e-308",
316 digits(BigDenorm)),
317 ?assertEqual(BigDenorm,
318 list_to_float(digits(BigDenorm))),
319 %% small normalized number
320 %% 2.22507385850720138309e-308
321 <<SmallNorm/float>> = <<0,16,0,0,0,0,0,0>>,
322 ?assertEqual("2.2250738585072014e-308",
323 digits(SmallNorm)),
324 ?assertEqual(SmallNorm,
325 list_to_float(digits(SmallNorm))),
326 %% large normalized number
327 %% 1.79769313486231570815e+308
328 <<LargeNorm/float>> = <<127,239,255,255,255,255,255,255>>,
329 ?assertEqual("1.7976931348623157e+308",
330 digits(LargeNorm)),
331 ?assertEqual(LargeNorm,
332 list_to_float(digits(LargeNorm))),
333 %% issue #10 - mochinum:frexp(math:pow(2, -1074)).
334 ?assertEqual("5.0e-324",
335 digits(math:pow(2, -1074))),
336 ok.
337
338 frexp_test() ->
339 %% zero
340 ?assertEqual({0.0, 0}, frexp(0.0)),
341 %% one
342 ?assertEqual({0.5, 1}, frexp(1.0)),
343 %% negative one
344 ?assertEqual({-0.5, 1}, frexp(-1.0)),
345 %% small denormalized number
346 %% 4.94065645841246544177e-324
347 <<SmallDenorm/float>> = <<0,0,0,0,0,0,0,1>>,
348 ?assertEqual({0.5, -1073}, frexp(SmallDenorm)),
349 %% large denormalized number
350 %% 2.22507385850720088902e-308
351 <<BigDenorm/float>> = <<0,15,255,255,255,255,255,255>>,
352 ?assertEqual(
353 {0.99999999999999978, -1022},
354 frexp(BigDenorm)),
355 %% small normalized number
356 %% 2.22507385850720138309e-308
357 <<SmallNorm/float>> = <<0,16,0,0,0,0,0,0>>,
358 ?assertEqual({0.5, -1021}, frexp(SmallNorm)),
359 %% large normalized number
360 %% 1.79769313486231570815e+308
361 <<LargeNorm/float>> = <<127,239,255,255,255,255,255,255>>,
362 ?assertEqual(
363 {0.99999999999999989, 1024},
364 frexp(LargeNorm)),
365 %% issue #10 - mochinum:frexp(math:pow(2, -1074)).
366 ?assertEqual(
367 {0.5, -1073},
368 frexp(math:pow(2, -1074))),
369 ok.
370
371 -endif.
+0
-329
deps/mochiweb/src/mochitemp.erl less more
0 %% @author Bob Ippolito <bob@mochimedia.com>
1 %% @copyright 2010 Mochi Media, Inc.
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 %% @doc Create temporary files and directories. Requires crypto to be started.
22
23 -module(mochitemp).
24 -export([gettempdir/0]).
25 -export([mkdtemp/0, mkdtemp/3]).
26 -export([rmtempdir/1]).
27 %% -export([mkstemp/4]).
28 -define(SAFE_CHARS, {$a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m,
29 $n, $o, $p, $q, $r, $s, $t, $u, $v, $w, $x, $y, $z,
30 $A, $B, $C, $D, $E, $F, $G, $H, $I, $J, $K, $L, $M,
31 $N, $O, $P, $Q, $R, $S, $T, $U, $V, $W, $X, $Y, $Z,
32 $0, $1, $2, $3, $4, $5, $6, $7, $8, $9, $_}).
33 -define(TMP_MAX, 10000).
34
35 -include_lib("kernel/include/file.hrl").
36
37 %% TODO: An ugly wrapper over the mktemp tool with open_port and sadness?
38 %% We can't implement this race-free in Erlang without the ability
39 %% to issue O_CREAT|O_EXCL. I suppose we could hack something with
40 %% mkdtemp, del_dir, open.
41 %% mkstemp(Suffix, Prefix, Dir, Options) ->
42 %% ok.
43
44 rmtempdir(Dir) ->
45 case file:del_dir(Dir) of
46 {error, eexist} ->
47 ok = rmtempdirfiles(Dir),
48 ok = file:del_dir(Dir);
49 ok ->
50 ok
51 end.
52
53 rmtempdirfiles(Dir) ->
54 {ok, Files} = file:list_dir(Dir),
55 ok = rmtempdirfiles(Dir, Files).
56
57 rmtempdirfiles(_Dir, []) ->
58 ok;
59 rmtempdirfiles(Dir, [Basename | Rest]) ->
60 Path = filename:join([Dir, Basename]),
61 case filelib:is_dir(Path) of
62 true ->
63 ok = rmtempdir(Path);
64 false ->
65 ok = file:delete(Path)
66 end,
67 rmtempdirfiles(Dir, Rest).
68
69 mkdtemp() ->
70 mkdtemp("", "tmp", gettempdir()).
71
72 mkdtemp(Suffix, Prefix, Dir) ->
73 mkdtemp_n(rngpath_fun(Suffix, Prefix, Dir), ?TMP_MAX).
74
75
76
77 mkdtemp_n(RngPath, 1) ->
78 make_dir(RngPath());
79 mkdtemp_n(RngPath, N) ->
80 try make_dir(RngPath())
81 catch throw:{error, eexist} ->
82 mkdtemp_n(RngPath, N - 1)
83 end.
84
85 make_dir(Path) ->
86 case file:make_dir(Path) of
87 ok ->
88 ok;
89 E={error, eexist} ->
90 throw(E)
91 end,
92 %% Small window for a race condition here because dir is created 777
93 ok = file:write_file_info(Path, #file_info{mode=8#0700}),
94 Path.
95
96 rngpath_fun(Prefix, Suffix, Dir) ->
97 fun () ->
98 filename:join([Dir, Prefix ++ rngchars(6) ++ Suffix])
99 end.
100
101 rngchars(0) ->
102 "";
103 rngchars(N) ->
104 [rngchar() | rngchars(N - 1)].
105
106 rngchar() ->
107 rngchar(crypto:rand_uniform(0, tuple_size(?SAFE_CHARS))).
108
109 rngchar(C) ->
110 element(1 + C, ?SAFE_CHARS).
111
112 %% @spec gettempdir() -> string()
113 %% @doc Get a usable temporary directory using the first of these that is a directory:
114 %% $TMPDIR, $TMP, $TEMP, "/tmp", "/var/tmp", "/usr/tmp", ".".
115 gettempdir() ->
116 gettempdir(gettempdir_checks(), fun normalize_dir/1).
117
118 gettempdir_checks() ->
119 [{fun os:getenv/1, ["TMPDIR", "TMP", "TEMP"]},
120 {fun gettempdir_identity/1, ["/tmp", "/var/tmp", "/usr/tmp"]},
121 {fun gettempdir_cwd/1, [cwd]}].
122
123 gettempdir_identity(L) ->
124 L.
125
126 gettempdir_cwd(cwd) ->
127 {ok, L} = file:get_cwd(),
128 L.
129
130 gettempdir([{_F, []} | RestF], Normalize) ->
131 gettempdir(RestF, Normalize);
132 gettempdir([{F, [L | RestL]} | RestF], Normalize) ->
133 case Normalize(F(L)) of
134 false ->
135 gettempdir([{F, RestL} | RestF], Normalize);
136 Dir ->
137 Dir
138 end.
139
140 normalize_dir(False) when False =:= false orelse False =:= "" ->
141 %% Erlang doesn't have an unsetenv, wtf.
142 false;
143 normalize_dir(L) ->
144 Dir = filename:absname(L),
145 case filelib:is_dir(Dir) of
146 false ->
147 false;
148 true ->
149 Dir
150 end.
151
152 %%
153 %% Tests
154 %%
155 -ifdef(TEST).
156 -include_lib("eunit/include/eunit.hrl").
157
158 pushenv(L) ->
159 [{K, os:getenv(K)} || K <- L].
160 popenv(L) ->
161 F = fun ({K, false}) ->
162 %% Erlang doesn't have an unsetenv, wtf.
163 os:putenv(K, "");
164 ({K, V}) ->
165 os:putenv(K, V)
166 end,
167 lists:foreach(F, L).
168
169 gettempdir_fallback_test() ->
170 ?assertEqual(
171 "/",
172 gettempdir([{fun gettempdir_identity/1, ["/--not-here--/"]},
173 {fun gettempdir_identity/1, ["/"]}],
174 fun normalize_dir/1)),
175 ?assertEqual(
176 "/",
177 %% simulate a true os:getenv unset env
178 gettempdir([{fun gettempdir_identity/1, [false]},
179 {fun gettempdir_identity/1, ["/"]}],
180 fun normalize_dir/1)),
181 ok.
182
183 gettempdir_identity_test() ->
184 ?assertEqual(
185 "/",
186 gettempdir([{fun gettempdir_identity/1, ["/"]}], fun normalize_dir/1)),
187 ok.
188
189 gettempdir_cwd_test() ->
190 {ok, Cwd} = file:get_cwd(),
191 ?assertEqual(
192 normalize_dir(Cwd),
193 gettempdir([{fun gettempdir_cwd/1, [cwd]}], fun normalize_dir/1)),
194 ok.
195
196 rngchars_test() ->
197 crypto:start(),
198 ?assertEqual(
199 "",
200 rngchars(0)),
201 ?assertEqual(
202 10,
203 length(rngchars(10))),
204 ok.
205
206 rngchar_test() ->
207 ?assertEqual(
208 $a,
209 rngchar(0)),
210 ?assertEqual(
211 $A,
212 rngchar(26)),
213 ?assertEqual(
214 $_,
215 rngchar(62)),
216 ok.
217
218 mkdtemp_n_failonce_test() ->
219 crypto:start(),
220 D = mkdtemp(),
221 Path = filename:join([D, "testdir"]),
222 %% Toggle the existence of a dir so that it fails
223 %% the first time and succeeds the second.
224 F = fun () ->
225 case filelib:is_dir(Path) of
226 true ->
227 file:del_dir(Path);
228 false ->
229 file:make_dir(Path)
230 end,
231 Path
232 end,
233 try
234 %% Fails the first time
235 ?assertThrow(
236 {error, eexist},
237 mkdtemp_n(F, 1)),
238 %% Reset state
239 file:del_dir(Path),
240 %% Succeeds the second time
241 ?assertEqual(
242 Path,
243 mkdtemp_n(F, 2))
244 after rmtempdir(D)
245 end,
246 ok.
247
248 mkdtemp_n_fail_test() ->
249 {ok, Cwd} = file:get_cwd(),
250 ?assertThrow(
251 {error, eexist},
252 mkdtemp_n(fun () -> Cwd end, 1)),
253 ?assertThrow(
254 {error, eexist},
255 mkdtemp_n(fun () -> Cwd end, 2)),
256 ok.
257
258 make_dir_fail_test() ->
259 {ok, Cwd} = file:get_cwd(),
260 ?assertThrow(
261 {error, eexist},
262 make_dir(Cwd)),
263 ok.
264
265 mkdtemp_test() ->
266 crypto:start(),
267 D = mkdtemp(),
268 ?assertEqual(
269 true,
270 filelib:is_dir(D)),
271 ?assertEqual(
272 ok,
273 file:del_dir(D)),
274 ok.
275
276 rmtempdir_test() ->
277 crypto:start(),
278 D1 = mkdtemp(),
279 ?assertEqual(
280 true,
281 filelib:is_dir(D1)),
282 ?assertEqual(
283 ok,
284 rmtempdir(D1)),
285 D2 = mkdtemp(),
286 ?assertEqual(
287 true,
288 filelib:is_dir(D2)),
289 ok = file:write_file(filename:join([D2, "foo"]), <<"bytes">>),
290 D3 = mkdtemp("suffix", "prefix", D2),
291 ?assertEqual(
292 true,
293 filelib:is_dir(D3)),
294 ok = file:write_file(filename:join([D3, "foo"]), <<"bytes">>),
295 ?assertEqual(
296 ok,
297 rmtempdir(D2)),
298 ?assertEqual(
299 {error, enoent},
300 file:consult(D3)),
301 ?assertEqual(
302 {error, enoent},
303 file:consult(D2)),
304 ok.
305
306 gettempdir_env_test() ->
307 Env = pushenv(["TMPDIR", "TEMP", "TMP"]),
308 FalseEnv = [{"TMPDIR", false}, {"TEMP", false}, {"TMP", false}],
309 try
310 popenv(FalseEnv),
311 popenv([{"TMPDIR", "/"}]),
312 ?assertEqual(
313 "/",
314 os:getenv("TMPDIR")),
315 ?assertEqual(
316 "/",
317 gettempdir()),
318 {ok, Cwd} = file:get_cwd(),
319 popenv(FalseEnv),
320 popenv([{"TMP", Cwd}]),
321 ?assertEqual(
322 normalize_dir(Cwd),
323 gettempdir())
324 after popenv(Env)
325 end,
326 ok.
327
328 -endif.
+0
-335
deps/mochiweb/src/mochiutf8.erl less more
0 %% @copyright 2010 Mochi Media, Inc.
1 %% @author Bob Ippolito <bob@mochimedia.com>
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 %% @doc Algorithm to convert any binary to a valid UTF-8 sequence by ignoring
22 %% invalid bytes.
23
24 -module(mochiutf8).
25 -export([valid_utf8_bytes/1, codepoint_to_bytes/1, codepoints_to_bytes/1]).
26 -export([bytes_to_codepoints/1, bytes_foldl/3, codepoint_foldl/3]).
27 -export([read_codepoint/1, len/1]).
28
29 %% External API
30
31 -type unichar_low() :: 0..16#d7ff.
32 -type unichar_high() :: 16#e000..16#10ffff.
33 -type unichar() :: unichar_low() | unichar_high().
34
35 -spec codepoint_to_bytes(unichar()) -> binary().
36 %% @doc Convert a unicode codepoint to UTF-8 bytes.
37 codepoint_to_bytes(C) when (C >= 16#00 andalso C =< 16#7f) ->
38 %% U+0000 - U+007F - 7 bits
39 <<C>>;
40 codepoint_to_bytes(C) when (C >= 16#080 andalso C =< 16#07FF) ->
41 %% U+0080 - U+07FF - 11 bits
42 <<0:5, B1:5, B0:6>> = <<C:16>>,
43 <<2#110:3, B1:5,
44 2#10:2, B0:6>>;
45 codepoint_to_bytes(C) when (C >= 16#0800 andalso C =< 16#FFFF) andalso
46 (C < 16#D800 orelse C > 16#DFFF) ->
47 %% U+0800 - U+FFFF - 16 bits (excluding UTC-16 surrogate code points)
48 <<B2:4, B1:6, B0:6>> = <<C:16>>,
49 <<2#1110:4, B2:4,
50 2#10:2, B1:6,
51 2#10:2, B0:6>>;
52 codepoint_to_bytes(C) when (C >= 16#010000 andalso C =< 16#10FFFF) ->
53 %% U+10000 - U+10FFFF - 21 bits
54 <<0:3, B3:3, B2:6, B1:6, B0:6>> = <<C:24>>,
55 <<2#11110:5, B3:3,
56 2#10:2, B2:6,
57 2#10:2, B1:6,
58 2#10:2, B0:6>>.
59
60 -spec codepoints_to_bytes([unichar()]) -> binary().
61 %% @doc Convert a list of codepoints to a UTF-8 binary.
62 codepoints_to_bytes(L) ->
63 <<<<(codepoint_to_bytes(C))/binary>> || C <- L>>.
64
65 -spec read_codepoint(binary()) -> {unichar(), binary(), binary()}.
66 read_codepoint(Bin = <<2#0:1, C:7, Rest/binary>>) ->
67 %% U+0000 - U+007F - 7 bits
68 <<B:1/binary, _/binary>> = Bin,
69 {C, B, Rest};
70 read_codepoint(Bin = <<2#110:3, B1:5,
71 2#10:2, B0:6,
72 Rest/binary>>) ->
73 %% U+0080 - U+07FF - 11 bits
74 case <<B1:5, B0:6>> of
75 <<C:11>> when C >= 16#80 ->
76 <<B:2/binary, _/binary>> = Bin,
77 {C, B, Rest}
78 end;
79 read_codepoint(Bin = <<2#1110:4, B2:4,
80 2#10:2, B1:6,
81 2#10:2, B0:6,
82 Rest/binary>>) ->
83 %% U+0800 - U+FFFF - 16 bits (excluding UTC-16 surrogate code points)
84 case <<B2:4, B1:6, B0:6>> of
85 <<C:16>> when (C >= 16#0800 andalso C =< 16#FFFF) andalso
86 (C < 16#D800 orelse C > 16#DFFF) ->
87 <<B:3/binary, _/binary>> = Bin,
88 {C, B, Rest}
89 end;
90 read_codepoint(Bin = <<2#11110:5, B3:3,
91 2#10:2, B2:6,
92 2#10:2, B1:6,
93 2#10:2, B0:6,
94 Rest/binary>>) ->
95 %% U+10000 - U+10FFFF - 21 bits
96 case <<B3:3, B2:6, B1:6, B0:6>> of
97 <<C:21>> when (C >= 16#010000 andalso C =< 16#10FFFF) ->
98 <<B:4/binary, _/binary>> = Bin,
99 {C, B, Rest}
100 end.
101
102 -spec codepoint_foldl(fun((unichar(), _) -> _), _, binary()) -> _.
103 codepoint_foldl(F, Acc, <<>>) when is_function(F, 2) ->
104 Acc;
105 codepoint_foldl(F, Acc, Bin) ->
106 {C, _, Rest} = read_codepoint(Bin),
107 codepoint_foldl(F, F(C, Acc), Rest).
108
109 -spec bytes_foldl(fun((binary(), _) -> _), _, binary()) -> _.
110 bytes_foldl(F, Acc, <<>>) when is_function(F, 2) ->
111 Acc;
112 bytes_foldl(F, Acc, Bin) ->
113 {_, B, Rest} = read_codepoint(Bin),
114 bytes_foldl(F, F(B, Acc), Rest).
115
116 -spec bytes_to_codepoints(binary()) -> [unichar()].
117 bytes_to_codepoints(B) ->
118 lists:reverse(codepoint_foldl(fun (C, Acc) -> [C | Acc] end, [], B)).
119
120 -spec len(binary()) -> non_neg_integer().
121 len(<<>>) ->
122 0;
123 len(B) ->
124 {_, _, Rest} = read_codepoint(B),
125 1 + len(Rest).
126
127 -spec valid_utf8_bytes(B::binary()) -> binary().
128 %% @doc Return only the bytes in B that represent valid UTF-8. Uses
129 %% the following recursive algorithm: skip one byte if B does not
130 %% follow UTF-8 syntax (a 1-4 byte encoding of some number),
131 %% skip sequence of 2-4 bytes if it represents an overlong encoding
132 %% or bad code point (surrogate U+D800 - U+DFFF or > U+10FFFF).
133 valid_utf8_bytes(B) when is_binary(B) ->
134 binary_skip_bytes(B, invalid_utf8_indexes(B)).
135
136 %% Internal API
137
138 -spec binary_skip_bytes(binary(), [non_neg_integer()]) -> binary().
139 %% @doc Return B, but skipping the 0-based indexes in L.
140 binary_skip_bytes(B, []) ->
141 B;
142 binary_skip_bytes(B, L) ->
143 binary_skip_bytes(B, L, 0, []).
144
145 %% @private
146 -spec binary_skip_bytes(binary(), [non_neg_integer()], non_neg_integer(), iolist()) -> binary().
147 binary_skip_bytes(B, [], _N, Acc) ->
148 iolist_to_binary(lists:reverse([B | Acc]));
149 binary_skip_bytes(<<_, RestB/binary>>, [N | RestL], N, Acc) ->
150 binary_skip_bytes(RestB, RestL, 1 + N, Acc);
151 binary_skip_bytes(<<C, RestB/binary>>, L, N, Acc) ->
152 binary_skip_bytes(RestB, L, 1 + N, [C | Acc]).
153
154 -spec invalid_utf8_indexes(binary()) -> [non_neg_integer()].
155 %% @doc Return the 0-based indexes in B that are not valid UTF-8.
156 invalid_utf8_indexes(B) ->
157 invalid_utf8_indexes(B, 0, []).
158
159 %% @private.
160 -spec invalid_utf8_indexes(binary(), non_neg_integer(), [non_neg_integer()]) -> [non_neg_integer()].
161 invalid_utf8_indexes(<<C, Rest/binary>>, N, Acc) when C < 16#80 ->
162 %% U+0000 - U+007F - 7 bits
163 invalid_utf8_indexes(Rest, 1 + N, Acc);
164 invalid_utf8_indexes(<<C1, C2, Rest/binary>>, N, Acc)
165 when C1 band 16#E0 =:= 16#C0,
166 C2 band 16#C0 =:= 16#80 ->
167 %% U+0080 - U+07FF - 11 bits
168 case ((C1 band 16#1F) bsl 6) bor (C2 band 16#3F) of
169 C when C < 16#80 ->
170 %% Overlong encoding.
171 invalid_utf8_indexes(Rest, 2 + N, [1 + N, N | Acc]);
172 _ ->
173 %% Upper bound U+07FF does not need to be checked
174 invalid_utf8_indexes(Rest, 2 + N, Acc)
175 end;
176 invalid_utf8_indexes(<<C1, C2, C3, Rest/binary>>, N, Acc)
177 when C1 band 16#F0 =:= 16#E0,
178 C2 band 16#C0 =:= 16#80,
179 C3 band 16#C0 =:= 16#80 ->
180 %% U+0800 - U+FFFF - 16 bits
181 case ((((C1 band 16#0F) bsl 6) bor (C2 band 16#3F)) bsl 6) bor
182 (C3 band 16#3F) of
183 C when (C < 16#800) orelse (C >= 16#D800 andalso C =< 16#DFFF) ->
184 %% Overlong encoding or surrogate.
185 invalid_utf8_indexes(Rest, 3 + N, [2 + N, 1 + N, N | Acc]);
186 _ ->
187 %% Upper bound U+FFFF does not need to be checked
188 invalid_utf8_indexes(Rest, 3 + N, Acc)
189 end;
190 invalid_utf8_indexes(<<C1, C2, C3, C4, Rest/binary>>, N, Acc)
191 when C1 band 16#F8 =:= 16#F0,
192 C2 band 16#C0 =:= 16#80,
193 C3 band 16#C0 =:= 16#80,
194 C4 band 16#C0 =:= 16#80 ->
195 %% U+10000 - U+10FFFF - 21 bits
196 case ((((((C1 band 16#0F) bsl 6) bor (C2 band 16#3F)) bsl 6) bor
197 (C3 band 16#3F)) bsl 6) bor (C4 band 16#3F) of
198 C when (C < 16#10000) orelse (C > 16#10FFFF) ->
199 %% Overlong encoding or invalid code point.
200 invalid_utf8_indexes(Rest, 4 + N, [3 + N, 2 + N, 1 + N, N | Acc]);
201 _ ->
202 invalid_utf8_indexes(Rest, 4 + N, Acc)
203 end;
204 invalid_utf8_indexes(<<_, Rest/binary>>, N, Acc) ->
205 %% Invalid char
206 invalid_utf8_indexes(Rest, 1 + N, [N | Acc]);
207 invalid_utf8_indexes(<<>>, _N, Acc) ->
208 lists:reverse(Acc).
209
210 %%
211 %% Tests
212 %%
213 -ifdef(TEST).
214 -include_lib("eunit/include/eunit.hrl").
215
216 binary_skip_bytes_test() ->
217 ?assertEqual(<<"foo">>,
218 binary_skip_bytes(<<"foo">>, [])),
219 ?assertEqual(<<"foobar">>,
220 binary_skip_bytes(<<"foo bar">>, [3])),
221 ?assertEqual(<<"foo">>,
222 binary_skip_bytes(<<"foo bar">>, [3, 4, 5, 6])),
223 ?assertEqual(<<"oo bar">>,
224 binary_skip_bytes(<<"foo bar">>, [0])),
225 ok.
226
227 invalid_utf8_indexes_test() ->
228 ?assertEqual(
229 [],
230 invalid_utf8_indexes(<<"unicode snowman for you: ", 226, 152, 131>>)),
231 ?assertEqual(
232 [0],
233 invalid_utf8_indexes(<<128>>)),
234 ?assertEqual(
235 [57,59,60,64,66,67],
236 invalid_utf8_indexes(<<"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; (",
237 167, 65, 170, 186, 73, 83, 80, 166, 87, 186, 217, 41, 41>>)),
238 ok.
239
240 codepoint_to_bytes_test() ->
241 %% U+0000 - U+007F - 7 bits
242 %% U+0080 - U+07FF - 11 bits
243 %% U+0800 - U+FFFF - 16 bits (excluding UTC-16 surrogate code points)
244 %% U+10000 - U+10FFFF - 21 bits
245 ?assertEqual(
246 <<"a">>,
247 codepoint_to_bytes($a)),
248 ?assertEqual(
249 <<16#c2, 16#80>>,
250 codepoint_to_bytes(16#80)),
251 ?assertEqual(
252 <<16#df, 16#bf>>,
253 codepoint_to_bytes(16#07ff)),
254 ?assertEqual(
255 <<16#ef, 16#bf, 16#bf>>,
256 codepoint_to_bytes(16#ffff)),
257 ?assertEqual(
258 <<16#f4, 16#8f, 16#bf, 16#bf>>,
259 codepoint_to_bytes(16#10ffff)),
260 ok.
261
262 bytes_foldl_test() ->
263 ?assertEqual(
264 <<"abc">>,
265 bytes_foldl(fun (B, Acc) -> <<Acc/binary, B/binary>> end, <<>>, <<"abc">>)),
266 ?assertEqual(
267 <<"abc", 226, 152, 131, 228, 184, 173, 194, 133, 244,143,191,191>>,
268 bytes_foldl(fun (B, Acc) -> <<Acc/binary, B/binary>> end, <<>>,
269 <<"abc", 226, 152, 131, 228, 184, 173, 194, 133, 244,143,191,191>>)),
270 ok.
271
272 bytes_to_codepoints_test() ->
273 ?assertEqual(
274 "abc" ++ [16#2603, 16#4e2d, 16#85, 16#10ffff],
275 bytes_to_codepoints(<<"abc", 226, 152, 131, 228, 184, 173, 194, 133, 244,143,191,191>>)),
276 ok.
277
278 codepoint_foldl_test() ->
279 ?assertEqual(
280 "cba",
281 codepoint_foldl(fun (C, Acc) -> [C | Acc] end, [], <<"abc">>)),
282 ?assertEqual(
283 [16#10ffff, 16#85, 16#4e2d, 16#2603 | "cba"],
284 codepoint_foldl(fun (C, Acc) -> [C | Acc] end, [],
285 <<"abc", 226, 152, 131, 228, 184, 173, 194, 133, 244,143,191,191>>)),
286 ok.
287
288 len_test() ->
289 ?assertEqual(
290 29,
291 len(<<"unicode snowman for you: ", 226, 152, 131, 228, 184, 173, 194, 133, 244, 143, 191, 191>>)),
292 ok.
293
294 codepoints_to_bytes_test() ->
295 ?assertEqual(
296 iolist_to_binary(lists:map(fun codepoint_to_bytes/1, lists:seq(1, 1000))),
297 codepoints_to_bytes(lists:seq(1, 1000))),
298 ok.
299
300 valid_utf8_bytes_test() ->
301 ?assertEqual(
302 <<"invalid U+11ffff: ">>,
303 valid_utf8_bytes(<<"invalid U+11ffff: ", 244, 159, 191, 191>>)),
304 ?assertEqual(
305 <<"U+10ffff: ", 244, 143, 191, 191>>,
306 valid_utf8_bytes(<<"U+10ffff: ", 244, 143, 191, 191>>)),
307 ?assertEqual(
308 <<"overlong 2-byte encoding (a): ">>,
309 valid_utf8_bytes(<<"overlong 2-byte encoding (a): ", 2#11000001, 2#10100001>>)),
310 ?assertEqual(
311 <<"overlong 2-byte encoding (!): ">>,
312 valid_utf8_bytes(<<"overlong 2-byte encoding (!): ", 2#11000000, 2#10100001>>)),
313 ?assertEqual(
314 <<"mu: ", 194, 181>>,
315 valid_utf8_bytes(<<"mu: ", 194, 181>>)),
316 ?assertEqual(
317 <<"bad coding bytes: ">>,
318 valid_utf8_bytes(<<"bad coding bytes: ", 2#10011111, 2#10111111, 2#11111111>>)),
319 ?assertEqual(
320 <<"low surrogate (unpaired): ">>,
321 valid_utf8_bytes(<<"low surrogate (unpaired): ", 237, 176, 128>>)),
322 ?assertEqual(
323 <<"high surrogate (unpaired): ">>,
324 valid_utf8_bytes(<<"high surrogate (unpaired): ", 237, 191, 191>>)),
325 ?assertEqual(
326 <<"unicode snowman for you: ", 226, 152, 131>>,
327 valid_utf8_bytes(<<"unicode snowman for you: ", 226, 152, 131>>)),
328 ?assertEqual(
329 <<"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; (AISPW))">>,
330 valid_utf8_bytes(<<"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; (",
331 167, 65, 170, 186, 73, 83, 80, 166, 87, 186, 217, 41, 41>>)),
332 ok.
333
334 -endif.
+0
-8
deps/mochiweb/src/mochiweb.app.src less more
0 {application,mochiweb,
1 [{description,"MochiMedia Web Server"},
2 {vsn,"2.13.1"},
3 {modules,[]},
4 {registered,[]},
5 {env,[]},
6 {applications,[kernel,stdlib,crypto,inets,ssl,xmerl,compiler,
7 syntax_tools]}]}.
+0
-101
deps/mochiweb/src/mochiweb.erl less more
0 %% @author Bob Ippolito <bob@mochimedia.com>
1 %% @copyright 2007 Mochi Media, Inc.
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 %% @doc Start and stop the MochiWeb server.
22
23 -module(mochiweb).
24 -author('bob@mochimedia.com').
25
26 -export([new_request/1, new_response/1]).
27 -export([all_loaded/0, all_loaded/1, reload/0]).
28 -export([ensure_started/1]).
29
30 reload() ->
31 [c:l(Module) || Module <- all_loaded()].
32
33 all_loaded() ->
34 all_loaded(filename:dirname(code:which(?MODULE))).
35
36 all_loaded(Base) when is_atom(Base) ->
37 [];
38 all_loaded(Base) ->
39 FullBase = Base ++ "/",
40 F = fun ({_Module, Loaded}, Acc) when is_atom(Loaded) ->
41 Acc;
42 ({Module, Loaded}, Acc) ->
43 case lists:prefix(FullBase, Loaded) of
44 true ->
45 [Module | Acc];
46 false ->
47 Acc
48 end
49 end,
50 lists:foldl(F, [], code:all_loaded()).
51
52 %% See the erlang:decode_packet/3 docs for the full type
53 -spec uri(HttpUri :: term()) -> string().
54 uri({abs_path, Uri}) ->
55 Uri;
56 %% TODO:
57 %% This makes it hard to implement certain kinds of proxies with mochiweb,
58 %% perhaps a field could be added to the mochiweb_request record to preserve
59 %% this information in raw_path.
60 uri({absoluteURI, _Protocol, _Host, _Port, Uri}) ->
61 Uri;
62 %% From http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
63 uri('*') ->
64 "*";
65 %% Erlang decode_packet will return this for requests like `CONNECT host:port`
66 uri({scheme, Hostname, Port}) ->
67 Hostname ++ ":" ++ Port;
68 uri(HttpString) when is_list(HttpString) ->
69 HttpString.
70
71 %% @spec new_request( {Socket, Request, Headers}
72 %% | {Socket, Opts, Request, Headers} ) -> MochiWebRequest
73 %% @doc Return a mochiweb_request data structure.
74 new_request({Socket, {Method, HttpUri, Version}, Headers}) ->
75 new_request({Socket, [], {Method, HttpUri, Version}, Headers});
76
77 new_request({Socket, Opts, {Method, HttpUri, Version}, Headers}) ->
78 mochiweb_request:new(Socket,
79 Opts,
80 Method,
81 uri(HttpUri),
82 Version,
83 mochiweb_headers:make(Headers)).
84
85 %% @spec new_response({Request, integer(), Headers}) -> MochiWebResponse
86 %% @doc Return a mochiweb_response data structure.
87 new_response({Request, Code, Headers}) ->
88 mochiweb_response:new(Request,
89 Code,
90 mochiweb_headers:make(Headers)).
91
92 %% @spec ensure_started(App::atom()) -> ok
93 %% @doc Start the given App if it has not been started already.
94 ensure_started(App) ->
95 case application:start(App) of
96 ok ->
97 ok;
98 {error, {already_started, App}} ->
99 ok
100 end.
+0
-83
deps/mochiweb/src/mochiweb_acceptor.erl less more
0 %% @author Bob Ippolito <bob@mochimedia.com>
1 %% @copyright 2010 Mochi Media, Inc.
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 %% @doc MochiWeb acceptor.
22
23 -module(mochiweb_acceptor).
24 -author('bob@mochimedia.com').
25
26 -include("internal.hrl").
27
28 -export([start_link/3, start_link/4, init/4]).
29
30 -define(EMFILE_SLEEP_MSEC, 100).
31
32 start_link(Server, Listen, Loop) ->
33 start_link(Server, Listen, Loop, []).
34
35 start_link(Server, Listen, Loop, Opts) ->
36 proc_lib:spawn_link(?MODULE, init, [Server, Listen, Loop, Opts]).
37
38 do_accept(Server, Listen) ->
39 T1 = os:timestamp(),
40 case mochiweb_socket:transport_accept(Listen) of
41 {ok, Socket} ->
42 gen_server:cast(Server, {accepted, self(), timer:now_diff(os:timestamp(), T1)}),
43 mochiweb_socket:finish_accept(Socket);
44 Other ->
45 Other
46 end.
47
48 init(Server, Listen, Loop, Opts) ->
49 case catch do_accept(Server, Listen) of
50 {ok, Socket} ->
51 call_loop(Loop, Socket, Opts);
52 {error, Err} when Err =:= closed orelse
53 Err =:= esslaccept orelse
54 Err =:= timeout ->
55 exit(normal);
56 Other ->
57 %% Mitigate out of file descriptor scenario by sleeping for a
58 %% short time to slow error rate
59 case Other of
60 {error, emfile} ->
61 receive
62 after ?EMFILE_SLEEP_MSEC ->
63 ok
64 end;
65 _ ->
66 ok
67 end,
68 error_logger:error_report(
69 [{application, mochiweb},
70 "Accept failed error",
71 lists:flatten(io_lib:format("~p", [Other]))]),
72 exit({error, accept_failed})
73 end.
74
75 call_loop({M, F}, Socket, Opts) ->
76 M:F(Socket, Opts);
77 call_loop({M, F, [A1]}, Socket, Opts) ->
78 M:F(Socket, Opts, A1);
79 call_loop({M, F, A}, Socket, Opts) ->
80 erlang:apply(M, F, [Socket, Opts | A]);
81 call_loop(Loop, Socket, Opts) ->
82 Loop(Socket, Opts).
+0
-105
deps/mochiweb/src/mochiweb_base64url.erl less more
0 %% @author Bob Ippolito <bob@mochimedia.com>
1 %% @copyright 2013 Mochi Media, Inc.
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 -module(mochiweb_base64url).
22 -export([encode/1, decode/1]).
23
24 %% @doc URL and filename safe base64 variant with no padding,
25 %% also known as "base64url" per RFC 4648.
26 %%
27 %% This differs from base64 in the following ways:
28 %% '-' is used in place of '+' (62),
29 %% '_' is used in place of '/' (63),
30 %% padding is implicit rather than explicit ('=').
31
32 -spec encode(iolist() | binary()) -> binary().
33 encode(B) when is_binary(B) ->
34 encode_binary(B);
35 encode(L) when is_list(L) ->
36 encode_binary(iolist_to_binary(L)).
37
38 -spec decode(iolist() | binary()) -> binary().
39 decode(B) when is_binary(B) ->
40 decode_binary(B);
41 decode(L) when is_list(L) ->
42 decode_binary(iolist_to_binary(L)).
43
44 %% Implementation, derived from stdlib base64.erl
45
46 %% One-based decode map.
47 -define(DECODE_MAP,
48 {bad,bad,bad,bad,bad,bad,bad,bad,ws,ws,bad,bad,ws,bad,bad, %1-15
49 bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad, %16-31
50 ws,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,62,bad,bad, %32-47
51 52,53,54,55,56,57,58,59,60,61,bad,bad,bad,bad,bad,bad, %48-63
52 bad,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14, %64-79
53 15,16,17,18,19,20,21,22,23,24,25,bad,bad,bad,bad,63, %80-95
54 bad,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, %96-111
55 41,42,43,44,45,46,47,48,49,50,51,bad,bad,bad,bad,bad, %112-127
56 bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,
57 bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,
58 bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,
59 bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,
60 bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,
61 bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,
62 bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,
63 bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad}).
64
65 encode_binary(Bin) ->
66 Split = 3*(byte_size(Bin) div 3),
67 <<Main0:Split/binary,Rest/binary>> = Bin,
68 Main = << <<(b64e(C)):8>> || <<C:6>> <= Main0 >>,
69 case Rest of
70 <<A:6,B:6,C:4>> ->
71 <<Main/binary,(b64e(A)):8,(b64e(B)):8,(b64e(C bsl 2)):8>>;
72 <<A:6,B:2>> ->
73 <<Main/binary,(b64e(A)):8,(b64e(B bsl 4)):8>>;
74 <<>> ->
75 Main
76 end.
77
78 decode_binary(Bin) ->
79 Main = << <<(b64d(C)):6>> || <<C>> <= Bin,
80 (C =/= $\t andalso C =/= $\s andalso
81 C =/= $\r andalso C =/= $\n) >>,
82 case bit_size(Main) rem 8 of
83 0 ->
84 Main;
85 N ->
86 Split = byte_size(Main) - 1,
87 <<Result:Split/bytes, _:N>> = Main,
88 Result
89 end.
90
91 %% accessors
92
93 b64e(X) ->
94 element(X+1,
95 {$A, $B, $C, $D, $E, $F, $G, $H, $I, $J, $K, $L, $M, $N,
96 $O, $P, $Q, $R, $S, $T, $U, $V, $W, $X, $Y, $Z,
97 $a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m, $n,
98 $o, $p, $q, $r, $s, $t, $u, $v, $w, $x, $y, $z,
99 $0, $1, $2, $3, $4, $5, $6, $7, $8, $9, $-, $_}).
100
101 b64d(X) ->
102 b64d_ok(element(X, ?DECODE_MAP)).
103
104 b64d_ok(I) when is_integer(I) -> I.
+0
-2201
deps/mochiweb/src/mochiweb_charref.erl less more
0 %% @author Bob Ippolito <bob@mochimedia.com>
1 %% @copyright 2007 Mochi Media, Inc.
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 %% @doc Converts HTML 5 charrefs and entities to codepoints (or lists of code points).
22 -module(mochiweb_charref).
23 -export([charref/1]).
24
25 %% External API.
26
27 %% @doc Convert a decimal charref, hex charref, or html entity to a unicode
28 %% codepoint, or return undefined on failure.
29 %% The input should not include an ampersand or semicolon.
30 %% charref("#38") = 38, charref("#x26") = 38, charref("amp") = 38.
31 -spec charref(binary() | string()) -> integer() | [integer()] | undefined.
32 charref(B) when is_binary(B) ->
33 charref(binary_to_list(B));
34 charref([$#, C | L]) when C =:= $x orelse C =:= $X ->
35 try erlang:list_to_integer(L, 16)
36 catch
37 error:badarg -> undefined
38 end;
39 charref([$# | L]) ->
40 try list_to_integer(L)
41 catch
42 error:badarg -> undefined
43 end;
44 charref(L) ->
45 entity(L).
46
47 %% Internal API.
48
49 %% [2011-10-14] Generated from:
50 %% http://www.w3.org/TR/html5/named-character-references.html
51
52 entity("AElig") -> 16#000C6;
53 entity("AMP") -> 16#00026;
54 entity("Aacute") -> 16#000C1;
55 entity("Abreve") -> 16#00102;
56 entity("Acirc") -> 16#000C2;
57 entity("Acy") -> 16#00410;
58 entity("Afr") -> 16#1D504;
59 entity("Agrave") -> 16#000C0;
60 entity("Alpha") -> 16#00391;
61 entity("Amacr") -> 16#00100;
62 entity("And") -> 16#02A53;
63 entity("Aogon") -> 16#00104;
64 entity("Aopf") -> 16#1D538;
65 entity("ApplyFunction") -> 16#02061;
66 entity("Aring") -> 16#000C5;
67 entity("Ascr") -> 16#1D49C;
68 entity("Assign") -> 16#02254;
69 entity("Atilde") -> 16#000C3;
70 entity("Auml") -> 16#000C4;
71 entity("Backslash") -> 16#02216;
72 entity("Barv") -> 16#02AE7;
73 entity("Barwed") -> 16#02306;
74 entity("Bcy") -> 16#00411;
75 entity("Because") -> 16#02235;
76 entity("Bernoullis") -> 16#0212C;
77 entity("Beta") -> 16#00392;
78 entity("Bfr") -> 16#1D505;
79 entity("Bopf") -> 16#1D539;
80 entity("Breve") -> 16#002D8;
81 entity("Bscr") -> 16#0212C;
82 entity("Bumpeq") -> 16#0224E;
83 entity("CHcy") -> 16#00427;
84 entity("COPY") -> 16#000A9;
85 entity("Cacute") -> 16#00106;
86 entity("Cap") -> 16#022D2;
87 entity("CapitalDifferentialD") -> 16#02145;
88 entity("Cayleys") -> 16#0212D;
89 entity("Ccaron") -> 16#0010C;
90 entity("Ccedil") -> 16#000C7;
91 entity("Ccirc") -> 16#00108;
92 entity("Cconint") -> 16#02230;
93 entity("Cdot") -> 16#0010A;
94 entity("Cedilla") -> 16#000B8;
95 entity("CenterDot") -> 16#000B7;
96 entity("Cfr") -> 16#0212D;
97 entity("Chi") -> 16#003A7;
98 entity("CircleDot") -> 16#02299;
99 entity("CircleMinus") -> 16#02296;
100 entity("CirclePlus") -> 16#02295;
101 entity("CircleTimes") -> 16#02297;
102 entity("ClockwiseContourIntegral") -> 16#02232;
103 entity("CloseCurlyDoubleQuote") -> 16#0201D;
104 entity("CloseCurlyQuote") -> 16#02019;
105 entity("Colon") -> 16#02237;
106 entity("Colone") -> 16#02A74;
107 entity("Congruent") -> 16#02261;
108 entity("Conint") -> 16#0222F;
109 entity("ContourIntegral") -> 16#0222E;
110 entity("Copf") -> 16#02102;
111 entity("Coproduct") -> 16#02210;
112 entity("CounterClockwiseContourIntegral") -> 16#02233;
113 entity("Cross") -> 16#02A2F;
114 entity("Cscr") -> 16#1D49E;
115 entity("Cup") -> 16#022D3;
116 entity("CupCap") -> 16#0224D;
117 entity("DD") -> 16#02145;
118 entity("DDotrahd") -> 16#02911;
119 entity("DJcy") -> 16#00402;
120 entity("DScy") -> 16#00405;
121 entity("DZcy") -> 16#0040F;
122 entity("Dagger") -> 16#02021;
123 entity("Darr") -> 16#021A1;
124 entity("Dashv") -> 16#02AE4;
125 entity("Dcaron") -> 16#0010E;
126 entity("Dcy") -> 16#00414;
127 entity("Del") -> 16#02207;
128 entity("Delta") -> 16#00394;
129 entity("Dfr") -> 16#1D507;
130 entity("DiacriticalAcute") -> 16#000B4;
131 entity("DiacriticalDot") -> 16#002D9;
132 entity("DiacriticalDoubleAcute") -> 16#002DD;
133 entity("DiacriticalGrave") -> 16#00060;
134 entity("DiacriticalTilde") -> 16#002DC;
135 entity("Diamond") -> 16#022C4;
136 entity("DifferentialD") -> 16#02146;
137 entity("Dopf") -> 16#1D53B;
138 entity("Dot") -> 16#000A8;
139 entity("DotDot") -> 16#020DC;
140 entity("DotEqual") -> 16#02250;
141 entity("DoubleContourIntegral") -> 16#0222F;
142 entity("DoubleDot") -> 16#000A8;
143 entity("DoubleDownArrow") -> 16#021D3;
144 entity("DoubleLeftArrow") -> 16#021D0;
145 entity("DoubleLeftRightArrow") -> 16#021D4;
146 entity("DoubleLeftTee") -> 16#02AE4;
147 entity("DoubleLongLeftArrow") -> 16#027F8;
148 entity("DoubleLongLeftRightArrow") -> 16#027FA;
149 entity("DoubleLongRightArrow") -> 16#027F9;
150 entity("DoubleRightArrow") -> 16#021D2;
151 entity("DoubleRightTee") -> 16#022A8;
152 entity("DoubleUpArrow") -> 16#021D1;
153 entity("DoubleUpDownArrow") -> 16#021D5;
154 entity("DoubleVerticalBar") -> 16#02225;
155 entity("DownArrow") -> 16#02193;
156 entity("DownArrowBar") -> 16#02913;
157 entity("DownArrowUpArrow") -> 16#021F5;
158 entity("DownBreve") -> 16#00311;
159 entity("DownLeftRightVector") -> 16#02950;
160 entity("DownLeftTeeVector") -> 16#0295E;
161 entity("DownLeftVector") -> 16#021BD;
162 entity("DownLeftVectorBar") -> 16#02956;
163 entity("DownRightTeeVector") -> 16#0295F;
164 entity("DownRightVector") -> 16#021C1;
165 entity("DownRightVectorBar") -> 16#02957;
166 entity("DownTee") -> 16#022A4;
167 entity("DownTeeArrow") -> 16#021A7;
168 entity("Downarrow") -> 16#021D3;
169 entity("Dscr") -> 16#1D49F;
170 entity("Dstrok") -> 16#00110;
171 entity("ENG") -> 16#0014A;
172 entity("ETH") -> 16#000D0;
173 entity("Eacute") -> 16#000C9;
174 entity("Ecaron") -> 16#0011A;
175 entity("Ecirc") -> 16#000CA;
176 entity("Ecy") -> 16#0042D;
177 entity("Edot") -> 16#00116;
178 entity("Efr") -> 16#1D508;
179 entity("Egrave") -> 16#000C8;
180 entity("Element") -> 16#02208;
181 entity("Emacr") -> 16#00112;
182 entity("EmptySmallSquare") -> 16#025FB;
183 entity("EmptyVerySmallSquare") -> 16#025AB;
184 entity("Eogon") -> 16#00118;
185 entity("Eopf") -> 16#1D53C;
186 entity("Epsilon") -> 16#00395;
187 entity("Equal") -> 16#02A75;
188 entity("EqualTilde") -> 16#02242;
189 entity("Equilibrium") -> 16#021CC;
190 entity("Escr") -> 16#02130;
191 entity("Esim") -> 16#02A73;
192 entity("Eta") -> 16#00397;
193 entity("Euml") -> 16#000CB;
194 entity("Exists") -> 16#02203;
195 entity("ExponentialE") -> 16#02147;
196 entity("Fcy") -> 16#00424;
197 entity("Ffr") -> 16#1D509;
198 entity("FilledSmallSquare") -> 16#025FC;
199 entity("FilledVerySmallSquare") -> 16#025AA;
200 entity("Fopf") -> 16#1D53D;
201 entity("ForAll") -> 16#02200;
202 entity("Fouriertrf") -> 16#02131;
203 entity("Fscr") -> 16#02131;
204 entity("GJcy") -> 16#00403;
205 entity("GT") -> 16#0003E;
206 entity("Gamma") -> 16#00393;
207 entity("Gammad") -> 16#003DC;
208 entity("Gbreve") -> 16#0011E;
209 entity("Gcedil") -> 16#00122;
210 entity("Gcirc") -> 16#0011C;
211 entity("Gcy") -> 16#00413;
212 entity("Gdot") -> 16#00120;
213 entity("Gfr") -> 16#1D50A;
214 entity("Gg") -> 16#022D9;
215 entity("Gopf") -> 16#1D53E;
216 entity("GreaterEqual") -> 16#02265;
217 entity("GreaterEqualLess") -> 16#022DB;
218 entity("GreaterFullEqual") -> 16#02267;
219 entity("GreaterGreater") -> 16#02AA2;
220 entity("GreaterLess") -> 16#02277;
221 entity("GreaterSlantEqual") -> 16#02A7E;
222 entity("GreaterTilde") -> 16#02273;
223 entity("Gscr") -> 16#1D4A2;
224 entity("Gt") -> 16#0226B;
225 entity("HARDcy") -> 16#0042A;
226 entity("Hacek") -> 16#002C7;
227 entity("Hat") -> 16#0005E;
228 entity("Hcirc") -> 16#00124;
229 entity("Hfr") -> 16#0210C;
230 entity("HilbertSpace") -> 16#0210B;
231 entity("Hopf") -> 16#0210D;
232 entity("HorizontalLine") -> 16#02500;
233 entity("Hscr") -> 16#0210B;
234 entity("Hstrok") -> 16#00126;
235 entity("HumpDownHump") -> 16#0224E;
236 entity("HumpEqual") -> 16#0224F;
237 entity("IEcy") -> 16#00415;
238 entity("IJlig") -> 16#00132;
239 entity("IOcy") -> 16#00401;
240 entity("Iacute") -> 16#000CD;
241 entity("Icirc") -> 16#000CE;
242 entity("Icy") -> 16#00418;
243 entity("Idot") -> 16#00130;
244 entity("Ifr") -> 16#02111;
245 entity("Igrave") -> 16#000CC;
246 entity("Im") -> 16#02111;
247 entity("Imacr") -> 16#0012A;
248 entity("ImaginaryI") -> 16#02148;
249 entity("Implies") -> 16#021D2;
250 entity("Int") -> 16#0222C;
251 entity("Integral") -> 16#0222B;
252 entity("Intersection") -> 16#022C2;
253 entity("InvisibleComma") -> 16#02063;
254 entity("InvisibleTimes") -> 16#02062;
255 entity("Iogon") -> 16#0012E;
256 entity("Iopf") -> 16#1D540;
257 entity("Iota") -> 16#00399;
258 entity("Iscr") -> 16#02110;
259 entity("Itilde") -> 16#00128;
260 entity("Iukcy") -> 16#00406;
261 entity("Iuml") -> 16#000CF;
262 entity("Jcirc") -> 16#00134;
263 entity("Jcy") -> 16#00419;
264 entity("Jfr") -> 16#1D50D;
265 entity("Jopf") -> 16#1D541;
266 entity("Jscr") -> 16#1D4A5;
267 entity("Jsercy") -> 16#00408;
268 entity("Jukcy") -> 16#00404;
269 entity("KHcy") -> 16#00425;
270 entity("KJcy") -> 16#0040C;
271 entity("Kappa") -> 16#0039A;
272 entity("Kcedil") -> 16#00136;
273 entity("Kcy") -> 16#0041A;
274 entity("Kfr") -> 16#1D50E;
275 entity("Kopf") -> 16#1D542;
276 entity("Kscr") -> 16#1D4A6;
277 entity("LJcy") -> 16#00409;
278 entity("LT") -> 16#0003C;
279 entity("Lacute") -> 16#00139;
280 entity("Lambda") -> 16#0039B;
281 entity("Lang") -> 16#027EA;
282 entity("Laplacetrf") -> 16#02112;
283 entity("Larr") -> 16#0219E;
284 entity("Lcaron") -> 16#0013D;
285 entity("Lcedil") -> 16#0013B;
286 entity("Lcy") -> 16#0041B;
287 entity("LeftAngleBracket") -> 16#027E8;
288 entity("LeftArrow") -> 16#02190;
289 entity("LeftArrowBar") -> 16#021E4;
290 entity("LeftArrowRightArrow") -> 16#021C6;
291 entity("LeftCeiling") -> 16#02308;
292 entity("LeftDoubleBracket") -> 16#027E6;
293 entity("LeftDownTeeVector") -> 16#02961;
294 entity("LeftDownVector") -> 16#021C3;
295 entity("LeftDownVectorBar") -> 16#02959;
296 entity("LeftFloor") -> 16#0230A;
297 entity("LeftRightArrow") -> 16#02194;
298 entity("LeftRightVector") -> 16#0294E;
299 entity("LeftTee") -> 16#022A3;
300 entity("LeftTeeArrow") -> 16#021A4;
301 entity("LeftTeeVector") -> 16#0295A;
302 entity("LeftTriangle") -> 16#022B2;
303 entity("LeftTriangleBar") -> 16#029CF;
304 entity("LeftTriangleEqual") -> 16#022B4;
305 entity("LeftUpDownVector") -> 16#02951;
306 entity("LeftUpTeeVector") -> 16#02960;
307 entity("LeftUpVector") -> 16#021BF;
308 entity("LeftUpVectorBar") -> 16#02958;
309 entity("LeftVector") -> 16#021BC;
310 entity("LeftVectorBar") -> 16#02952;
311 entity("Leftarrow") -> 16#021D0;
312 entity("Leftrightarrow") -> 16#021D4;
313 entity("LessEqualGreater") -> 16#022DA;
314 entity("LessFullEqual") -> 16#02266;
315 entity("LessGreater") -> 16#02276;
316 entity("LessLess") -> 16#02AA1;
317 entity("LessSlantEqual") -> 16#02A7D;
318 entity("LessTilde") -> 16#02272;
319 entity("Lfr") -> 16#1D50F;
320 entity("Ll") -> 16#022D8;
321 entity("Lleftarrow") -> 16#021DA;
322 entity("Lmidot") -> 16#0013F;
323 entity("LongLeftArrow") -> 16#027F5;
324 entity("LongLeftRightArrow") -> 16#027F7;
325 entity("LongRightArrow") -> 16#027F6;
326 entity("Longleftarrow") -> 16#027F8;
327 entity("Longleftrightarrow") -> 16#027FA;
328 entity("Longrightarrow") -> 16#027F9;
329 entity("Lopf") -> 16#1D543;
330 entity("LowerLeftArrow") -> 16#02199;
331 entity("LowerRightArrow") -> 16#02198;
332 entity("Lscr") -> 16#02112;
333 entity("Lsh") -> 16#021B0;
334 entity("Lstrok") -> 16#00141;
335 entity("Lt") -> 16#0226A;
336 entity("Map") -> 16#02905;
337 entity("Mcy") -> 16#0041C;
338 entity("MediumSpace") -> 16#0205F;
339 entity("Mellintrf") -> 16#02133;
340 entity("Mfr") -> 16#1D510;
341 entity("MinusPlus") -> 16#02213;
342 entity("Mopf") -> 16#1D544;
343 entity("Mscr") -> 16#02133;
344 entity("Mu") -> 16#0039C;
345 entity("NJcy") -> 16#0040A;
346 entity("Nacute") -> 16#00143;
347 entity("Ncaron") -> 16#00147;
348 entity("Ncedil") -> 16#00145;
349 entity("Ncy") -> 16#0041D;
350 entity("NegativeMediumSpace") -> 16#0200B;
351 entity("NegativeThickSpace") -> 16#0200B;
352 entity("NegativeThinSpace") -> 16#0200B;
353 entity("NegativeVeryThinSpace") -> 16#0200B;
354 entity("NestedGreaterGreater") -> 16#0226B;
355 entity("NestedLessLess") -> 16#0226A;
356 entity("NewLine") -> 16#0000A;
357 entity("Nfr") -> 16#1D511;
358 entity("NoBreak") -> 16#02060;
359 entity("NonBreakingSpace") -> 16#000A0;
360 entity("Nopf") -> 16#02115;
361 entity("Not") -> 16#02AEC;
362 entity("NotCongruent") -> 16#02262;
363 entity("NotCupCap") -> 16#0226D;
364 entity("NotDoubleVerticalBar") -> 16#02226;
365 entity("NotElement") -> 16#02209;
366 entity("NotEqual") -> 16#02260;
367 entity("NotEqualTilde") -> [16#02242, 16#00338];
368 entity("NotExists") -> 16#02204;
369 entity("NotGreater") -> 16#0226F;
370 entity("NotGreaterEqual") -> 16#02271;
371 entity("NotGreaterFullEqual") -> [16#02267, 16#00338];
372 entity("NotGreaterGreater") -> [16#0226B, 16#00338];
373 entity("NotGreaterLess") -> 16#02279;
374 entity("NotGreaterSlantEqual") -> [16#02A7E, 16#00338];
375 entity("NotGreaterTilde") -> 16#02275;
376 entity("NotHumpDownHump") -> [16#0224E, 16#00338];
377 entity("NotHumpEqual") -> [16#0224F, 16#00338];
378 entity("NotLeftTriangle") -> 16#022EA;
379 entity("NotLeftTriangleBar") -> [16#029CF, 16#00338];
380 entity("NotLeftTriangleEqual") -> 16#022EC;
381 entity("NotLess") -> 16#0226E;
382 entity("NotLessEqual") -> 16#02270;
383 entity("NotLessGreater") -> 16#02278;
384 entity("NotLessLess") -> [16#0226A, 16#00338];
385 entity("NotLessSlantEqual") -> [16#02A7D, 16#00338];
386 entity("NotLessTilde") -> 16#02274;
387 entity("NotNestedGreaterGreater") -> [16#02AA2, 16#00338];
388 entity("NotNestedLessLess") -> [16#02AA1, 16#00338];
389 entity("NotPrecedes") -> 16#02280;
390 entity("NotPrecedesEqual") -> [16#02AAF, 16#00338];
391 entity("NotPrecedesSlantEqual") -> 16#022E0;
392 entity("NotReverseElement") -> 16#0220C;
393 entity("NotRightTriangle") -> 16#022EB;
394 entity("NotRightTriangleBar") -> [16#029D0, 16#00338];
395 entity("NotRightTriangleEqual") -> 16#022ED;
396 entity("NotSquareSubset") -> [16#0228F, 16#00338];
397 entity("NotSquareSubsetEqual") -> 16#022E2;
398 entity("NotSquareSuperset") -> [16#02290, 16#00338];
399 entity("NotSquareSupersetEqual") -> 16#022E3;
400 entity("NotSubset") -> [16#02282, 16#020D2];
401 entity("NotSubsetEqual") -> 16#02288;
402 entity("NotSucceeds") -> 16#02281;
403 entity("NotSucceedsEqual") -> [16#02AB0, 16#00338];
404 entity("NotSucceedsSlantEqual") -> 16#022E1;
405 entity("NotSucceedsTilde") -> [16#0227F, 16#00338];
406 entity("NotSuperset") -> [16#02283, 16#020D2];
407 entity("NotSupersetEqual") -> 16#02289;
408 entity("NotTilde") -> 16#02241;
409 entity("NotTildeEqual") -> 16#02244;
410 entity("NotTildeFullEqual") -> 16#02247;
411 entity("NotTildeTilde") -> 16#02249;
412 entity("NotVerticalBar") -> 16#02224;
413 entity("Nscr") -> 16#1D4A9;
414 entity("Ntilde") -> 16#000D1;
415 entity("Nu") -> 16#0039D;
416 entity("OElig") -> 16#00152;
417 entity("Oacute") -> 16#000D3;
418 entity("Ocirc") -> 16#000D4;
419 entity("Ocy") -> 16#0041E;
420 entity("Odblac") -> 16#00150;
421 entity("Ofr") -> 16#1D512;
422 entity("Ograve") -> 16#000D2;
423 entity("Omacr") -> 16#0014C;
424 entity("Omega") -> 16#003A9;
425 entity("Omicron") -> 16#0039F;
426 entity("Oopf") -> 16#1D546;
427 entity("OpenCurlyDoubleQuote") -> 16#0201C;
428 entity("OpenCurlyQuote") -> 16#02018;
429 entity("Or") -> 16#02A54;
430 entity("Oscr") -> 16#1D4AA;
431 entity("Oslash") -> 16#000D8;
432 entity("Otilde") -> 16#000D5;
433 entity("Otimes") -> 16#02A37;
434 entity("Ouml") -> 16#000D6;
435 entity("OverBar") -> 16#0203E;
436 entity("OverBrace") -> 16#023DE;
437 entity("OverBracket") -> 16#023B4;
438 entity("OverParenthesis") -> 16#023DC;
439 entity("PartialD") -> 16#02202;
440 entity("Pcy") -> 16#0041F;
441 entity("Pfr") -> 16#1D513;
442 entity("Phi") -> 16#003A6;
443 entity("Pi") -> 16#003A0;
444 entity("PlusMinus") -> 16#000B1;
445 entity("Poincareplane") -> 16#0210C;
446 entity("Popf") -> 16#02119;
447 entity("Pr") -> 16#02ABB;
448 entity("Precedes") -> 16#0227A;
449 entity("PrecedesEqual") -> 16#02AAF;
450 entity("PrecedesSlantEqual") -> 16#0227C;
451 entity("PrecedesTilde") -> 16#0227E;
452 entity("Prime") -> 16#02033;
453 entity("Product") -> 16#0220F;
454 entity("Proportion") -> 16#02237;
455 entity("Proportional") -> 16#0221D;
456 entity("Pscr") -> 16#1D4AB;
457 entity("Psi") -> 16#003A8;
458 entity("QUOT") -> 16#00022;
459 entity("Qfr") -> 16#1D514;
460 entity("Qopf") -> 16#0211A;
461 entity("Qscr") -> 16#1D4AC;
462 entity("RBarr") -> 16#02910;
463 entity("REG") -> 16#000AE;
464 entity("Racute") -> 16#00154;
465 entity("Rang") -> 16#027EB;
466 entity("Rarr") -> 16#021A0;
467 entity("Rarrtl") -> 16#02916;
468 entity("Rcaron") -> 16#00158;
469 entity("Rcedil") -> 16#00156;
470 entity("Rcy") -> 16#00420;
471 entity("Re") -> 16#0211C;
472 entity("ReverseElement") -> 16#0220B;
473 entity("ReverseEquilibrium") -> 16#021CB;
474 entity("ReverseUpEquilibrium") -> 16#0296F;
475 entity("Rfr") -> 16#0211C;
476 entity("Rho") -> 16#003A1;
477 entity("RightAngleBracket") -> 16#027E9;
478 entity("RightArrow") -> 16#02192;
479 entity("RightArrowBar") -> 16#021E5;
480 entity("RightArrowLeftArrow") -> 16#021C4;
481 entity("RightCeiling") -> 16#02309;
482 entity("RightDoubleBracket") -> 16#027E7;
483 entity("RightDownTeeVector") -> 16#0295D;
484 entity("RightDownVector") -> 16#021C2;
485 entity("RightDownVectorBar") -> 16#02955;
486 entity("RightFloor") -> 16#0230B;
487 entity("RightTee") -> 16#022A2;
488 entity("RightTeeArrow") -> 16#021A6;
489 entity("RightTeeVector") -> 16#0295B;
490 entity("RightTriangle") -> 16#022B3;
491 entity("RightTriangleBar") -> 16#029D0;
492 entity("RightTriangleEqual") -> 16#022B5;
493 entity("RightUpDownVector") -> 16#0294F;
494 entity("RightUpTeeVector") -> 16#0295C;
495 entity("RightUpVector") -> 16#021BE;
496 entity("RightUpVectorBar") -> 16#02954;
497 entity("RightVector") -> 16#021C0;
498 entity("RightVectorBar") -> 16#02953;
499 entity("Rightarrow") -> 16#021D2;
500 entity("Ropf") -> 16#0211D;
501 entity("RoundImplies") -> 16#02970;
502 entity("Rrightarrow") -> 16#021DB;
503 entity("Rscr") -> 16#0211B;
504 entity("Rsh") -> 16#021B1;
505 entity("RuleDelayed") -> 16#029F4;
506 entity("SHCHcy") -> 16#00429;
507 entity("SHcy") -> 16#00428;
508 entity("SOFTcy") -> 16#0042C;
509 entity("Sacute") -> 16#0015A;
510 entity("Sc") -> 16#02ABC;
511 entity("Scaron") -> 16#00160;
512 entity("Scedil") -> 16#0015E;
513 entity("Scirc") -> 16#0015C;
514 entity("Scy") -> 16#00421;
515 entity("Sfr") -> 16#1D516;
516 entity("ShortDownArrow") -> 16#02193;
517 entity("ShortLeftArrow") -> 16#02190;
518 entity("ShortRightArrow") -> 16#02192;
519 entity("ShortUpArrow") -> 16#02191;
520 entity("Sigma") -> 16#003A3;
521 entity("SmallCircle") -> 16#02218;
522 entity("Sopf") -> 16#1D54A;
523 entity("Sqrt") -> 16#0221A;
524 entity("Square") -> 16#025A1;
525 entity("SquareIntersection") -> 16#02293;
526 entity("SquareSubset") -> 16#0228F;
527 entity("SquareSubsetEqual") -> 16#02291;
528 entity("SquareSuperset") -> 16#02290;
529 entity("SquareSupersetEqual") -> 16#02292;
530 entity("SquareUnion") -> 16#02294;
531 entity("Sscr") -> 16#1D4AE;
532 entity("Star") -> 16#022C6;
533 entity("Sub") -> 16#022D0;
534 entity("Subset") -> 16#022D0;
535 entity("SubsetEqual") -> 16#02286;
536 entity("Succeeds") -> 16#0227B;
537 entity("SucceedsEqual") -> 16#02AB0;
538 entity("SucceedsSlantEqual") -> 16#0227D;
539 entity("SucceedsTilde") -> 16#0227F;
540 entity("SuchThat") -> 16#0220B;
541 entity("Sum") -> 16#02211;
542 entity("Sup") -> 16#022D1;
543 entity("Superset") -> 16#02283;
544 entity("SupersetEqual") -> 16#02287;
545 entity("Supset") -> 16#022D1;
546 entity("THORN") -> 16#000DE;
547 entity("TRADE") -> 16#02122;
548 entity("TSHcy") -> 16#0040B;
549 entity("TScy") -> 16#00426;
550 entity("Tab") -> 16#00009;
551 entity("Tau") -> 16#003A4;
552 entity("Tcaron") -> 16#00164;
553 entity("Tcedil") -> 16#00162;
554 entity("Tcy") -> 16#00422;
555 entity("Tfr") -> 16#1D517;
556 entity("Therefore") -> 16#02234;
557 entity("Theta") -> 16#00398;
558 entity("ThickSpace") -> [16#0205F, 16#0200A];
559 entity("ThinSpace") -> 16#02009;
560 entity("Tilde") -> 16#0223C;
561 entity("TildeEqual") -> 16#02243;
562 entity("TildeFullEqual") -> 16#02245;
563 entity("TildeTilde") -> 16#02248;
564 entity("Topf") -> 16#1D54B;
565 entity("TripleDot") -> 16#020DB;
566 entity("Tscr") -> 16#1D4AF;
567 entity("Tstrok") -> 16#00166;
568 entity("Uacute") -> 16#000DA;
569 entity("Uarr") -> 16#0219F;
570 entity("Uarrocir") -> 16#02949;
571 entity("Ubrcy") -> 16#0040E;
572 entity("Ubreve") -> 16#0016C;
573 entity("Ucirc") -> 16#000DB;
574 entity("Ucy") -> 16#00423;
575 entity("Udblac") -> 16#00170;
576 entity("Ufr") -> 16#1D518;
577 entity("Ugrave") -> 16#000D9;
578 entity("Umacr") -> 16#0016A;
579 entity("UnderBar") -> 16#0005F;
580 entity("UnderBrace") -> 16#023DF;
581 entity("UnderBracket") -> 16#023B5;
582 entity("UnderParenthesis") -> 16#023DD;
583 entity("Union") -> 16#022C3;
584 entity("UnionPlus") -> 16#0228E;
585 entity("Uogon") -> 16#00172;
586 entity("Uopf") -> 16#1D54C;
587 entity("UpArrow") -> 16#02191;
588 entity("UpArrowBar") -> 16#02912;
589 entity("UpArrowDownArrow") -> 16#021C5;
590 entity("UpDownArrow") -> 16#02195;
591 entity("UpEquilibrium") -> 16#0296E;
592 entity("UpTee") -> 16#022A5;
593 entity("UpTeeArrow") -> 16#021A5;
594 entity("Uparrow") -> 16#021D1;
595 entity("Updownarrow") -> 16#021D5;
596 entity("UpperLeftArrow") -> 16#02196;
597 entity("UpperRightArrow") -> 16#02197;
598 entity("Upsi") -> 16#003D2;
599 entity("Upsilon") -> 16#003A5;
600 entity("Uring") -> 16#0016E;
601 entity("Uscr") -> 16#1D4B0;
602 entity("Utilde") -> 16#00168;
603 entity("Uuml") -> 16#000DC;
604 entity("VDash") -> 16#022AB;
605 entity("Vbar") -> 16#02AEB;
606 entity("Vcy") -> 16#00412;
607 entity("Vdash") -> 16#022A9;
608 entity("Vdashl") -> 16#02AE6;
609 entity("Vee") -> 16#022C1;
610 entity("Verbar") -> 16#02016;
611 entity("Vert") -> 16#02016;
612 entity("VerticalBar") -> 16#02223;
613 entity("VerticalLine") -> 16#0007C;
614 entity("VerticalSeparator") -> 16#02758;
615 entity("VerticalTilde") -> 16#02240;
616 entity("VeryThinSpace") -> 16#0200A;
617 entity("Vfr") -> 16#1D519;
618 entity("Vopf") -> 16#1D54D;
619 entity("Vscr") -> 16#1D4B1;
620 entity("Vvdash") -> 16#022AA;
621 entity("Wcirc") -> 16#00174;
622 entity("Wedge") -> 16#022C0;
623 entity("Wfr") -> 16#1D51A;
624 entity("Wopf") -> 16#1D54E;
625 entity("Wscr") -> 16#1D4B2;
626 entity("Xfr") -> 16#1D51B;
627 entity("Xi") -> 16#0039E;
628 entity("Xopf") -> 16#1D54F;
629 entity("Xscr") -> 16#1D4B3;
630 entity("YAcy") -> 16#0042F;
631 entity("YIcy") -> 16#00407;
632 entity("YUcy") -> 16#0042E;
633 entity("Yacute") -> 16#000DD;
634 entity("Ycirc") -> 16#00176;
635 entity("Ycy") -> 16#0042B;
636 entity("Yfr") -> 16#1D51C;
637 entity("Yopf") -> 16#1D550;
638 entity("Yscr") -> 16#1D4B4;
639 entity("Yuml") -> 16#00178;
640 entity("ZHcy") -> 16#00416;
641 entity("Zacute") -> 16#00179;
642 entity("Zcaron") -> 16#0017D;
643 entity("Zcy") -> 16#00417;
644 entity("Zdot") -> 16#0017B;
645 entity("ZeroWidthSpace") -> 16#0200B;
646 entity("Zeta") -> 16#00396;
647 entity("Zfr") -> 16#02128;
648 entity("Zopf") -> 16#02124;
649 entity("Zscr") -> 16#1D4B5;
650 entity("aacute") -> 16#000E1;
651 entity("abreve") -> 16#00103;
652 entity("ac") -> 16#0223E;
653 entity("acE") -> [16#0223E, 16#00333];
654 entity("acd") -> 16#0223F;
655 entity("acirc") -> 16#000E2;
656 entity("acute") -> 16#000B4;
657 entity("acy") -> 16#00430;
658 entity("aelig") -> 16#000E6;
659 entity("af") -> 16#02061;
660 entity("afr") -> 16#1D51E;
661 entity("agrave") -> 16#000E0;
662 entity("alefsym") -> 16#02135;
663 entity("aleph") -> 16#02135;
664 entity("alpha") -> 16#003B1;
665 entity("amacr") -> 16#00101;
666 entity("amalg") -> 16#02A3F;
667 entity("amp") -> 16#00026;
668 entity("and") -> 16#02227;
669 entity("andand") -> 16#02A55;
670 entity("andd") -> 16#02A5C;
671 entity("andslope") -> 16#02A58;
672 entity("andv") -> 16#02A5A;
673 entity("ang") -> 16#02220;
674 entity("ange") -> 16#029A4;
675 entity("angle") -> 16#02220;
676 entity("angmsd") -> 16#02221;
677 entity("angmsdaa") -> 16#029A8;
678 entity("angmsdab") -> 16#029A9;
679 entity("angmsdac") -> 16#029AA;
680 entity("angmsdad") -> 16#029AB;
681 entity("angmsdae") -> 16#029AC;
682 entity("angmsdaf") -> 16#029AD;
683 entity("angmsdag") -> 16#029AE;
684 entity("angmsdah") -> 16#029AF;
685 entity("angrt") -> 16#0221F;
686 entity("angrtvb") -> 16#022BE;
687 entity("angrtvbd") -> 16#0299D;
688 entity("angsph") -> 16#02222;
689 entity("angst") -> 16#000C5;
690 entity("angzarr") -> 16#0237C;
691 entity("aogon") -> 16#00105;
692 entity("aopf") -> 16#1D552;
693 entity("ap") -> 16#02248;
694 entity("apE") -> 16#02A70;
695 entity("apacir") -> 16#02A6F;
696 entity("ape") -> 16#0224A;
697 entity("apid") -> 16#0224B;
698 entity("apos") -> 16#00027;
699 entity("approx") -> 16#02248;
700 entity("approxeq") -> 16#0224A;
701 entity("aring") -> 16#000E5;
702 entity("ascr") -> 16#1D4B6;
703 entity("ast") -> 16#0002A;
704 entity("asymp") -> 16#02248;
705 entity("asympeq") -> 16#0224D;
706 entity("atilde") -> 16#000E3;
707 entity("auml") -> 16#000E4;
708 entity("awconint") -> 16#02233;
709 entity("awint") -> 16#02A11;
710 entity("bNot") -> 16#02AED;
711 entity("backcong") -> 16#0224C;
712 entity("backepsilon") -> 16#003F6;
713 entity("backprime") -> 16#02035;
714 entity("backsim") -> 16#0223D;
715 entity("backsimeq") -> 16#022CD;
716 entity("barvee") -> 16#022BD;
717 entity("barwed") -> 16#02305;
718 entity("barwedge") -> 16#02305;
719 entity("bbrk") -> 16#023B5;
720 entity("bbrktbrk") -> 16#023B6;
721 entity("bcong") -> 16#0224C;
722 entity("bcy") -> 16#00431;
723 entity("bdquo") -> 16#0201E;
724 entity("becaus") -> 16#02235;
725 entity("because") -> 16#02235;
726 entity("bemptyv") -> 16#029B0;
727 entity("bepsi") -> 16#003F6;
728 entity("bernou") -> 16#0212C;
729 entity("beta") -> 16#003B2;
730 entity("beth") -> 16#02136;
731 entity("between") -> 16#0226C;
732 entity("bfr") -> 16#1D51F;
733 entity("bigcap") -> 16#022C2;
734 entity("bigcirc") -> 16#025EF;
735 entity("bigcup") -> 16#022C3;
736 entity("bigodot") -> 16#02A00;
737 entity("bigoplus") -> 16#02A01;
738 entity("bigotimes") -> 16#02A02;
739 entity("bigsqcup") -> 16#02A06;
740 entity("bigstar") -> 16#02605;
741 entity("bigtriangledown") -> 16#025BD;
742 entity("bigtriangleup") -> 16#025B3;
743 entity("biguplus") -> 16#02A04;
744 entity("bigvee") -> 16#022C1;
745 entity("bigwedge") -> 16#022C0;
746 entity("bkarow") -> 16#0290D;
747 entity("blacklozenge") -> 16#029EB;
748 entity("blacksquare") -> 16#025AA;
749 entity("blacktriangle") -> 16#025B4;
750 entity("blacktriangledown") -> 16#025BE;
751 entity("blacktriangleleft") -> 16#025C2;
752 entity("blacktriangleright") -> 16#025B8;
753 entity("blank") -> 16#02423;
754 entity("blk12") -> 16#02592;
755 entity("blk14") -> 16#02591;
756 entity("blk34") -> 16#02593;
757 entity("block") -> 16#02588;
758 entity("bne") -> [16#0003D, 16#020E5];
759 entity("bnequiv") -> [16#02261, 16#020E5];
760 entity("bnot") -> 16#02310;
761 entity("bopf") -> 16#1D553;
762 entity("bot") -> 16#022A5;
763 entity("bottom") -> 16#022A5;
764 entity("bowtie") -> 16#022C8;
765 entity("boxDL") -> 16#02557;
766 entity("boxDR") -> 16#02554;
767 entity("boxDl") -> 16#02556;
768 entity("boxDr") -> 16#02553;
769 entity("boxH") -> 16#02550;
770 entity("boxHD") -> 16#02566;
771 entity("boxHU") -> 16#02569;
772 entity("boxHd") -> 16#02564;
773 entity("boxHu") -> 16#02567;
774 entity("boxUL") -> 16#0255D;
775 entity("boxUR") -> 16#0255A;
776 entity("boxUl") -> 16#0255C;
777 entity("boxUr") -> 16#02559;
778 entity("boxV") -> 16#02551;
779 entity("boxVH") -> 16#0256C;
780 entity("boxVL") -> 16#02563;
781 entity("boxVR") -> 16#02560;
782 entity("boxVh") -> 16#0256B;
783 entity("boxVl") -> 16#02562;
784 entity("boxVr") -> 16#0255F;
785 entity("boxbox") -> 16#029C9;
786 entity("boxdL") -> 16#02555;
787 entity("boxdR") -> 16#02552;
788 entity("boxdl") -> 16#02510;
789 entity("boxdr") -> 16#0250C;
790 entity("boxh") -> 16#02500;
791 entity("boxhD") -> 16#02565;
792 entity("boxhU") -> 16#02568;
793 entity("boxhd") -> 16#0252C;
794 entity("boxhu") -> 16#02534;
795 entity("boxminus") -> 16#0229F;
796 entity("boxplus") -> 16#0229E;
797 entity("boxtimes") -> 16#022A0;
798 entity("boxuL") -> 16#0255B;
799 entity("boxuR") -> 16#02558;
800 entity("boxul") -> 16#02518;
801 entity("boxur") -> 16#02514;
802 entity("boxv") -> 16#02502;
803 entity("boxvH") -> 16#0256A;
804 entity("boxvL") -> 16#02561;
805 entity("boxvR") -> 16#0255E;
806 entity("boxvh") -> 16#0253C;
807 entity("boxvl") -> 16#02524;
808 entity("boxvr") -> 16#0251C;
809 entity("bprime") -> 16#02035;
810 entity("breve") -> 16#002D8;
811 entity("brvbar") -> 16#000A6;
812 entity("bscr") -> 16#1D4B7;
813 entity("bsemi") -> 16#0204F;
814 entity("bsim") -> 16#0223D;
815 entity("bsime") -> 16#022CD;
816 entity("bsol") -> 16#0005C;
817 entity("bsolb") -> 16#029C5;
818 entity("bsolhsub") -> 16#027C8;
819 entity("bull") -> 16#02022;
820 entity("bullet") -> 16#02022;
821 entity("bump") -> 16#0224E;
822 entity("bumpE") -> 16#02AAE;
823 entity("bumpe") -> 16#0224F;
824 entity("bumpeq") -> 16#0224F;
825 entity("cacute") -> 16#00107;
826 entity("cap") -> 16#02229;
827 entity("capand") -> 16#02A44;
828 entity("capbrcup") -> 16#02A49;
829 entity("capcap") -> 16#02A4B;
830 entity("capcup") -> 16#02A47;
831 entity("capdot") -> 16#02A40;
832 entity("caps") -> [16#02229, 16#0FE00];
833 entity("caret") -> 16#02041;
834 entity("caron") -> 16#002C7;
835 entity("ccaps") -> 16#02A4D;
836 entity("ccaron") -> 16#0010D;
837 entity("ccedil") -> 16#000E7;
838 entity("ccirc") -> 16#00109;
839 entity("ccups") -> 16#02A4C;
840 entity("ccupssm") -> 16#02A50;
841 entity("cdot") -> 16#0010B;
842 entity("cedil") -> 16#000B8;
843 entity("cemptyv") -> 16#029B2;
844 entity("cent") -> 16#000A2;
845 entity("centerdot") -> 16#000B7;
846 entity("cfr") -> 16#1D520;
847 entity("chcy") -> 16#00447;
848 entity("check") -> 16#02713;
849 entity("checkmark") -> 16#02713;
850 entity("chi") -> 16#003C7;
851 entity("cir") -> 16#025CB;
852 entity("cirE") -> 16#029C3;
853 entity("circ") -> 16#002C6;
854 entity("circeq") -> 16#02257;
855 entity("circlearrowleft") -> 16#021BA;
856 entity("circlearrowright") -> 16#021BB;
857 entity("circledR") -> 16#000AE;
858 entity("circledS") -> 16#024C8;
859 entity("circledast") -> 16#0229B;
860 entity("circledcirc") -> 16#0229A;
861 entity("circleddash") -> 16#0229D;
862 entity("cire") -> 16#02257;
863 entity("cirfnint") -> 16#02A10;
864 entity("cirmid") -> 16#02AEF;
865 entity("cirscir") -> 16#029C2;
866 entity("clubs") -> 16#02663;
867 entity("clubsuit") -> 16#02663;
868 entity("colon") -> 16#0003A;
869 entity("colone") -> 16#02254;
870 entity("coloneq") -> 16#02254;
871 entity("comma") -> 16#0002C;
872 entity("commat") -> 16#00040;
873 entity("comp") -> 16#02201;
874 entity("compfn") -> 16#02218;
875 entity("complement") -> 16#02201;
876 entity("complexes") -> 16#02102;
877 entity("cong") -> 16#02245;
878 entity("congdot") -> 16#02A6D;
879 entity("conint") -> 16#0222E;
880 entity("copf") -> 16#1D554;
881 entity("coprod") -> 16#02210;
882 entity("copy") -> 16#000A9;
883 entity("copysr") -> 16#02117;
884 entity("crarr") -> 16#021B5;
885 entity("cross") -> 16#02717;
886 entity("cscr") -> 16#1D4B8;
887 entity("csub") -> 16#02ACF;
888 entity("csube") -> 16#02AD1;
889 entity("csup") -> 16#02AD0;
890 entity("csupe") -> 16#02AD2;
891 entity("ctdot") -> 16#022EF;
892 entity("cudarrl") -> 16#02938;
893 entity("cudarrr") -> 16#02935;
894 entity("cuepr") -> 16#022DE;
895 entity("cuesc") -> 16#022DF;
896 entity("cularr") -> 16#021B6;
897 entity("cularrp") -> 16#0293D;
898 entity("cup") -> 16#0222A;
899 entity("cupbrcap") -> 16#02A48;
900 entity("cupcap") -> 16#02A46;
901 entity("cupcup") -> 16#02A4A;
902 entity("cupdot") -> 16#0228D;
903 entity("cupor") -> 16#02A45;
904 entity("cups") -> [16#0222A, 16#0FE00];
905 entity("curarr") -> 16#021B7;
906 entity("curarrm") -> 16#0293C;
907 entity("curlyeqprec") -> 16#022DE;
908 entity("curlyeqsucc") -> 16#022DF;
909 entity("curlyvee") -> 16#022CE;
910 entity("curlywedge") -> 16#022CF;
911 entity("curren") -> 16#000A4;
912 entity("curvearrowleft") -> 16#021B6;
913 entity("curvearrowright") -> 16#021B7;
914 entity("cuvee") -> 16#022CE;
915 entity("cuwed") -> 16#022CF;
916 entity("cwconint") -> 16#02232;
917 entity("cwint") -> 16#02231;
918 entity("cylcty") -> 16#0232D;
919 entity("dArr") -> 16#021D3;
920 entity("dHar") -> 16#02965;
921 entity("dagger") -> 16#02020;
922 entity("daleth") -> 16#02138;
923 entity("darr") -> 16#02193;
924 entity("dash") -> 16#02010;
925 entity("dashv") -> 16#022A3;
926 entity("dbkarow") -> 16#0290F;
927 entity("dblac") -> 16#002DD;
928 entity("dcaron") -> 16#0010F;
929 entity("dcy") -> 16#00434;
930 entity("dd") -> 16#02146;
931 entity("ddagger") -> 16#02021;
932 entity("ddarr") -> 16#021CA;
933 entity("ddotseq") -> 16#02A77;
934 entity("deg") -> 16#000B0;
935 entity("delta") -> 16#003B4;
936 entity("demptyv") -> 16#029B1;
937 entity("dfisht") -> 16#0297F;
938 entity("dfr") -> 16#1D521;
939 entity("dharl") -> 16#021C3;
940 entity("dharr") -> 16#021C2;
941 entity("diam") -> 16#022C4;
942 entity("diamond") -> 16#022C4;
943 entity("diamondsuit") -> 16#02666;
944 entity("diams") -> 16#02666;
945 entity("die") -> 16#000A8;
946 entity("digamma") -> 16#003DD;
947 entity("disin") -> 16#022F2;
948 entity("div") -> 16#000F7;
949 entity("divide") -> 16#000F7;
950 entity("divideontimes") -> 16#022C7;
951 entity("divonx") -> 16#022C7;
952 entity("djcy") -> 16#00452;
953 entity("dlcorn") -> 16#0231E;
954 entity("dlcrop") -> 16#0230D;
955 entity("dollar") -> 16#00024;
956 entity("dopf") -> 16#1D555;
957 entity("dot") -> 16#002D9;
958 entity("doteq") -> 16#02250;
959 entity("doteqdot") -> 16#02251;
960 entity("dotminus") -> 16#02238;
961 entity("dotplus") -> 16#02214;
962 entity("dotsquare") -> 16#022A1;
963 entity("doublebarwedge") -> 16#02306;
964 entity("downarrow") -> 16#02193;
965 entity("downdownarrows") -> 16#021CA;
966 entity("downharpoonleft") -> 16#021C3;
967 entity("downharpoonright") -> 16#021C2;
968 entity("drbkarow") -> 16#02910;
969 entity("drcorn") -> 16#0231F;
970 entity("drcrop") -> 16#0230C;
971 entity("dscr") -> 16#1D4B9;
972 entity("dscy") -> 16#00455;
973 entity("dsol") -> 16#029F6;
974 entity("dstrok") -> 16#00111;
975 entity("dtdot") -> 16#022F1;
976 entity("dtri") -> 16#025BF;
977 entity("dtrif") -> 16#025BE;
978 entity("duarr") -> 16#021F5;
979 entity("duhar") -> 16#0296F;
980 entity("dwangle") -> 16#029A6;
981 entity("dzcy") -> 16#0045F;
982 entity("dzigrarr") -> 16#027FF;
983 entity("eDDot") -> 16#02A77;
984 entity("eDot") -> 16#02251;
985 entity("eacute") -> 16#000E9;
986 entity("easter") -> 16#02A6E;
987 entity("ecaron") -> 16#0011B;
988 entity("ecir") -> 16#02256;
989 entity("ecirc") -> 16#000EA;
990 entity("ecolon") -> 16#02255;
991 entity("ecy") -> 16#0044D;
992 entity("edot") -> 16#00117;
993 entity("ee") -> 16#02147;
994 entity("efDot") -> 16#02252;
995 entity("efr") -> 16#1D522;
996 entity("eg") -> 16#02A9A;
997 entity("egrave") -> 16#000E8;
998 entity("egs") -> 16#02A96;
999 entity("egsdot") -> 16#02A98;
1000 entity("el") -> 16#02A99;
1001 entity("elinters") -> 16#023E7;
1002 entity("ell") -> 16#02113;
1003 entity("els") -> 16#02A95;
1004 entity("elsdot") -> 16#02A97;
1005 entity("emacr") -> 16#00113;
1006 entity("empty") -> 16#02205;
1007 entity("emptyset") -> 16#02205;
1008 entity("emptyv") -> 16#02205;
1009 entity("emsp") -> 16#02003;
1010 entity("emsp13") -> 16#02004;
1011 entity("emsp14") -> 16#02005;
1012 entity("eng") -> 16#0014B;
1013 entity("ensp") -> 16#02002;
1014 entity("eogon") -> 16#00119;
1015 entity("eopf") -> 16#1D556;
1016 entity("epar") -> 16#022D5;
1017 entity("eparsl") -> 16#029E3;
1018 entity("eplus") -> 16#02A71;
1019 entity("epsi") -> 16#003B5;
1020 entity("epsilon") -> 16#003B5;
1021 entity("epsiv") -> 16#003F5;
1022 entity("eqcirc") -> 16#02256;
1023 entity("eqcolon") -> 16#02255;
1024 entity("eqsim") -> 16#02242;
1025 entity("eqslantgtr") -> 16#02A96;
1026 entity("eqslantless") -> 16#02A95;
1027 entity("equals") -> 16#0003D;
1028 entity("equest") -> 16#0225F;
1029 entity("equiv") -> 16#02261;
1030 entity("equivDD") -> 16#02A78;
1031 entity("eqvparsl") -> 16#029E5;
1032 entity("erDot") -> 16#02253;
1033 entity("erarr") -> 16#02971;
1034 entity("escr") -> 16#0212F;
1035 entity("esdot") -> 16#02250;
1036 entity("esim") -> 16#02242;
1037 entity("eta") -> 16#003B7;
1038 entity("eth") -> 16#000F0;
1039 entity("euml") -> 16#000EB;
1040 entity("euro") -> 16#020AC;
1041 entity("excl") -> 16#00021;
1042 entity("exist") -> 16#02203;
1043 entity("expectation") -> 16#02130;
1044 entity("exponentiale") -> 16#02147;
1045 entity("fallingdotseq") -> 16#02252;
1046 entity("fcy") -> 16#00444;
1047 entity("female") -> 16#02640;
1048 entity("ffilig") -> 16#0FB03;
1049 entity("fflig") -> 16#0FB00;
1050 entity("ffllig") -> 16#0FB04;
1051 entity("ffr") -> 16#1D523;
1052 entity("filig") -> 16#0FB01;
1053 entity("fjlig") -> [16#00066, 16#0006A];
1054 entity("flat") -> 16#0266D;
1055 entity("fllig") -> 16#0FB02;
1056 entity("fltns") -> 16#025B1;
1057 entity("fnof") -> 16#00192;
1058 entity("fopf") -> 16#1D557;
1059 entity("forall") -> 16#02200;
1060 entity("fork") -> 16#022D4;
1061 entity("forkv") -> 16#02AD9;
1062 entity("fpartint") -> 16#02A0D;
1063 entity("frac12") -> 16#000BD;
1064 entity("frac13") -> 16#02153;
1065 entity("frac14") -> 16#000BC;
1066 entity("frac15") -> 16#02155;
1067 entity("frac16") -> 16#02159;
1068 entity("frac18") -> 16#0215B;
1069 entity("frac23") -> 16#02154;
1070 entity("frac25") -> 16#02156;
1071 entity("frac34") -> 16#000BE;
1072 entity("frac35") -> 16#02157;
1073 entity("frac38") -> 16#0215C;
1074 entity("frac45") -> 16#02158;
1075 entity("frac56") -> 16#0215A;
1076 entity("frac58") -> 16#0215D;
1077 entity("frac78") -> 16#0215E;
1078 entity("frasl") -> 16#02044;
1079 entity("frown") -> 16#02322;
1080 entity("fscr") -> 16#1D4BB;
1081 entity("gE") -> 16#02267;
1082 entity("gEl") -> 16#02A8C;
1083 entity("gacute") -> 16#001F5;
1084 entity("gamma") -> 16#003B3;
1085 entity("gammad") -> 16#003DD;
1086 entity("gap") -> 16#02A86;
1087 entity("gbreve") -> 16#0011F;
1088 entity("gcirc") -> 16#0011D;
1089 entity("gcy") -> 16#00433;
1090 entity("gdot") -> 16#00121;
1091 entity("ge") -> 16#02265;
1092 entity("gel") -> 16#022DB;
1093 entity("geq") -> 16#02265;
1094 entity("geqq") -> 16#02267;
1095 entity("geqslant") -> 16#02A7E;
1096 entity("ges") -> 16#02A7E;
1097 entity("gescc") -> 16#02AA9;
1098 entity("gesdot") -> 16#02A80;
1099 entity("gesdoto") -> 16#02A82;
1100 entity("gesdotol") -> 16#02A84;
1101 entity("gesl") -> [16#022DB, 16#0FE00];
1102 entity("gesles") -> 16#02A94;
1103 entity("gfr") -> 16#1D524;
1104 entity("gg") -> 16#0226B;
1105 entity("ggg") -> 16#022D9;
1106 entity("gimel") -> 16#02137;
1107 entity("gjcy") -> 16#00453;
1108 entity("gl") -> 16#02277;
1109 entity("glE") -> 16#02A92;
1110 entity("gla") -> 16#02AA5;
1111 entity("glj") -> 16#02AA4;
1112 entity("gnE") -> 16#02269;
1113 entity("gnap") -> 16#02A8A;
1114 entity("gnapprox") -> 16#02A8A;
1115 entity("gne") -> 16#02A88;
1116 entity("gneq") -> 16#02A88;
1117 entity("gneqq") -> 16#02269;
1118 entity("gnsim") -> 16#022E7;
1119 entity("gopf") -> 16#1D558;
1120 entity("grave") -> 16#00060;
1121 entity("gscr") -> 16#0210A;
1122 entity("gsim") -> 16#02273;
1123 entity("gsime") -> 16#02A8E;
1124 entity("gsiml") -> 16#02A90;
1125 entity("gt") -> 16#0003E;
1126 entity("gtcc") -> 16#02AA7;
1127 entity("gtcir") -> 16#02A7A;
1128 entity("gtdot") -> 16#022D7;
1129 entity("gtlPar") -> 16#02995;
1130 entity("gtquest") -> 16#02A7C;
1131 entity("gtrapprox") -> 16#02A86;
1132 entity("gtrarr") -> 16#02978;
1133 entity("gtrdot") -> 16#022D7;
1134 entity("gtreqless") -> 16#022DB;
1135 entity("gtreqqless") -> 16#02A8C;
1136 entity("gtrless") -> 16#02277;
1137 entity("gtrsim") -> 16#02273;
1138 entity("gvertneqq") -> [16#02269, 16#0FE00];
1139 entity("gvnE") -> [16#02269, 16#0FE00];
1140 entity("hArr") -> 16#021D4;
1141 entity("hairsp") -> 16#0200A;
1142 entity("half") -> 16#000BD;
1143 entity("hamilt") -> 16#0210B;
1144 entity("hardcy") -> 16#0044A;
1145 entity("harr") -> 16#02194;
1146 entity("harrcir") -> 16#02948;
1147 entity("harrw") -> 16#021AD;
1148 entity("hbar") -> 16#0210F;
1149 entity("hcirc") -> 16#00125;
1150 entity("hearts") -> 16#02665;
1151 entity("heartsuit") -> 16#02665;
1152 entity("hellip") -> 16#02026;
1153 entity("hercon") -> 16#022B9;
1154 entity("hfr") -> 16#1D525;
1155 entity("hksearow") -> 16#02925;
1156 entity("hkswarow") -> 16#02926;
1157 entity("hoarr") -> 16#021FF;
1158 entity("homtht") -> 16#0223B;
1159 entity("hookleftarrow") -> 16#021A9;
1160 entity("hookrightarrow") -> 16#021AA;
1161 entity("hopf") -> 16#1D559;
1162 entity("horbar") -> 16#02015;
1163 entity("hscr") -> 16#1D4BD;
1164 entity("hslash") -> 16#0210F;
1165 entity("hstrok") -> 16#00127;
1166 entity("hybull") -> 16#02043;
1167 entity("hyphen") -> 16#02010;
1168 entity("iacute") -> 16#000ED;
1169 entity("ic") -> 16#02063;
1170 entity("icirc") -> 16#000EE;
1171 entity("icy") -> 16#00438;
1172 entity("iecy") -> 16#00435;
1173 entity("iexcl") -> 16#000A1;
1174 entity("iff") -> 16#021D4;
1175 entity("ifr") -> 16#1D526;
1176 entity("igrave") -> 16#000EC;
1177 entity("ii") -> 16#02148;
1178 entity("iiiint") -> 16#02A0C;
1179 entity("iiint") -> 16#0222D;
1180 entity("iinfin") -> 16#029DC;
1181 entity("iiota") -> 16#02129;
1182 entity("ijlig") -> 16#00133;
1183 entity("imacr") -> 16#0012B;
1184 entity("image") -> 16#02111;
1185 entity("imagline") -> 16#02110;
1186 entity("imagpart") -> 16#02111;
1187 entity("imath") -> 16#00131;
1188 entity("imof") -> 16#022B7;
1189 entity("imped") -> 16#001B5;
1190 entity("in") -> 16#02208;
1191 entity("incare") -> 16#02105;
1192 entity("infin") -> 16#0221E;
1193 entity("infintie") -> 16#029DD;
1194 entity("inodot") -> 16#00131;
1195 entity("int") -> 16#0222B;
1196 entity("intcal") -> 16#022BA;
1197 entity("integers") -> 16#02124;
1198 entity("intercal") -> 16#022BA;
1199 entity("intlarhk") -> 16#02A17;
1200 entity("intprod") -> 16#02A3C;
1201 entity("iocy") -> 16#00451;
1202 entity("iogon") -> 16#0012F;
1203 entity("iopf") -> 16#1D55A;
1204 entity("iota") -> 16#003B9;
1205 entity("iprod") -> 16#02A3C;
1206 entity("iquest") -> 16#000BF;
1207 entity("iscr") -> 16#1D4BE;
1208 entity("isin") -> 16#02208;
1209 entity("isinE") -> 16#022F9;
1210 entity("isindot") -> 16#022F5;
1211 entity("isins") -> 16#022F4;
1212 entity("isinsv") -> 16#022F3;
1213 entity("isinv") -> 16#02208;
1214 entity("it") -> 16#02062;
1215 entity("itilde") -> 16#00129;
1216 entity("iukcy") -> 16#00456;
1217 entity("iuml") -> 16#000EF;
1218 entity("jcirc") -> 16#00135;
1219 entity("jcy") -> 16#00439;
1220 entity("jfr") -> 16#1D527;
1221 entity("jmath") -> 16#00237;
1222 entity("jopf") -> 16#1D55B;
1223 entity("jscr") -> 16#1D4BF;
1224 entity("jsercy") -> 16#00458;
1225 entity("jukcy") -> 16#00454;
1226 entity("kappa") -> 16#003BA;
1227 entity("kappav") -> 16#003F0;
1228 entity("kcedil") -> 16#00137;
1229 entity("kcy") -> 16#0043A;
1230 entity("kfr") -> 16#1D528;
1231 entity("kgreen") -> 16#00138;
1232 entity("khcy") -> 16#00445;
1233 entity("kjcy") -> 16#0045C;
1234 entity("kopf") -> 16#1D55C;
1235 entity("kscr") -> 16#1D4C0;
1236 entity("lAarr") -> 16#021DA;
1237 entity("lArr") -> 16#021D0;
1238 entity("lAtail") -> 16#0291B;
1239 entity("lBarr") -> 16#0290E;
1240 entity("lE") -> 16#02266;
1241 entity("lEg") -> 16#02A8B;
1242 entity("lHar") -> 16#02962;
1243 entity("lacute") -> 16#0013A;
1244 entity("laemptyv") -> 16#029B4;
1245 entity("lagran") -> 16#02112;
1246 entity("lambda") -> 16#003BB;
1247 entity("lang") -> 16#027E8;
1248 entity("langd") -> 16#02991;
1249 entity("langle") -> 16#027E8;
1250 entity("lap") -> 16#02A85;
1251 entity("laquo") -> 16#000AB;
1252 entity("larr") -> 16#02190;
1253 entity("larrb") -> 16#021E4;
1254 entity("larrbfs") -> 16#0291F;
1255 entity("larrfs") -> 16#0291D;
1256 entity("larrhk") -> 16#021A9;
1257 entity("larrlp") -> 16#021AB;
1258 entity("larrpl") -> 16#02939;
1259 entity("larrsim") -> 16#02973;
1260 entity("larrtl") -> 16#021A2;
1261 entity("lat") -> 16#02AAB;
1262 entity("latail") -> 16#02919;
1263 entity("late") -> 16#02AAD;
1264 entity("lates") -> [16#02AAD, 16#0FE00];
1265 entity("lbarr") -> 16#0290C;
1266 entity("lbbrk") -> 16#02772;
1267 entity("lbrace") -> 16#0007B;
1268 entity("lbrack") -> 16#0005B;
1269 entity("lbrke") -> 16#0298B;
1270 entity("lbrksld") -> 16#0298F;
1271 entity("lbrkslu") -> 16#0298D;
1272 entity("lcaron") -> 16#0013E;
1273 entity("lcedil") -> 16#0013C;
1274 entity("lceil") -> 16#02308;
1275 entity("lcub") -> 16#0007B;
1276 entity("lcy") -> 16#0043B;
1277 entity("ldca") -> 16#02936;
1278 entity("ldquo") -> 16#0201C;
1279 entity("ldquor") -> 16#0201E;
1280 entity("ldrdhar") -> 16#02967;
1281 entity("ldrushar") -> 16#0294B;
1282 entity("ldsh") -> 16#021B2;
1283 entity("le") -> 16#02264;
1284 entity("leftarrow") -> 16#02190;
1285 entity("leftarrowtail") -> 16#021A2;
1286 entity("leftharpoondown") -> 16#021BD;
1287 entity("leftharpoonup") -> 16#021BC;
1288 entity("leftleftarrows") -> 16#021C7;
1289 entity("leftrightarrow") -> 16#02194;
1290 entity("leftrightarrows") -> 16#021C6;
1291 entity("leftrightharpoons") -> 16#021CB;
1292 entity("leftrightsquigarrow") -> 16#021AD;
1293 entity("leftthreetimes") -> 16#022CB;
1294 entity("leg") -> 16#022DA;
1295 entity("leq") -> 16#02264;
1296 entity("leqq") -> 16#02266;
1297 entity("leqslant") -> 16#02A7D;
1298 entity("les") -> 16#02A7D;
1299 entity("lescc") -> 16#02AA8;
1300 entity("lesdot") -> 16#02A7F;
1301 entity("lesdoto") -> 16#02A81;
1302 entity("lesdotor") -> 16#02A83;
1303 entity("lesg") -> [16#022DA, 16#0FE00];
1304 entity("lesges") -> 16#02A93;
1305 entity("lessapprox") -> 16#02A85;
1306 entity("lessdot") -> 16#022D6;
1307 entity("lesseqgtr") -> 16#022DA;
1308 entity("lesseqqgtr") -> 16#02A8B;
1309 entity("lessgtr") -> 16#02276;
1310 entity("lesssim") -> 16#02272;
1311 entity("lfisht") -> 16#0297C;
1312 entity("lfloor") -> 16#0230A;
1313 entity("lfr") -> 16#1D529;
1314 entity("lg") -> 16#02276;
1315 entity("lgE") -> 16#02A91;
1316 entity("lhard") -> 16#021BD;
1317 entity("lharu") -> 16#021BC;
1318 entity("lharul") -> 16#0296A;
1319 entity("lhblk") -> 16#02584;
1320 entity("ljcy") -> 16#00459;
1321 entity("ll") -> 16#0226A;
1322 entity("llarr") -> 16#021C7;
1323 entity("llcorner") -> 16#0231E;
1324 entity("llhard") -> 16#0296B;
1325 entity("lltri") -> 16#025FA;
1326 entity("lmidot") -> 16#00140;
1327 entity("lmoust") -> 16#023B0;
1328 entity("lmoustache") -> 16#023B0;
1329 entity("lnE") -> 16#02268;
1330 entity("lnap") -> 16#02A89;
1331 entity("lnapprox") -> 16#02A89;
1332 entity("lne") -> 16#02A87;
1333 entity("lneq") -> 16#02A87;
1334 entity("lneqq") -> 16#02268;
1335 entity("lnsim") -> 16#022E6;
1336 entity("loang") -> 16#027EC;
1337 entity("loarr") -> 16#021FD;
1338 entity("lobrk") -> 16#027E6;
1339 entity("longleftarrow") -> 16#027F5;
1340 entity("longleftrightarrow") -> 16#027F7;
1341 entity("longmapsto") -> 16#027FC;
1342 entity("longrightarrow") -> 16#027F6;
1343 entity("looparrowleft") -> 16#021AB;
1344 entity("looparrowright") -> 16#021AC;
1345 entity("lopar") -> 16#02985;
1346 entity("lopf") -> 16#1D55D;
1347 entity("loplus") -> 16#02A2D;
1348 entity("lotimes") -> 16#02A34;
1349 entity("lowast") -> 16#02217;
1350 entity("lowbar") -> 16#0005F;
1351 entity("loz") -> 16#025CA;
1352 entity("lozenge") -> 16#025CA;
1353 entity("lozf") -> 16#029EB;
1354 entity("lpar") -> 16#00028;
1355 entity("lparlt") -> 16#02993;
1356 entity("lrarr") -> 16#021C6;
1357 entity("lrcorner") -> 16#0231F;
1358 entity("lrhar") -> 16#021CB;
1359 entity("lrhard") -> 16#0296D;
1360 entity("lrm") -> 16#0200E;
1361 entity("lrtri") -> 16#022BF;
1362 entity("lsaquo") -> 16#02039;
1363 entity("lscr") -> 16#1D4C1;
1364 entity("lsh") -> 16#021B0;
1365 entity("lsim") -> 16#02272;
1366 entity("lsime") -> 16#02A8D;
1367 entity("lsimg") -> 16#02A8F;
1368 entity("lsqb") -> 16#0005B;
1369 entity("lsquo") -> 16#02018;
1370 entity("lsquor") -> 16#0201A;
1371 entity("lstrok") -> 16#00142;
1372 entity("lt") -> 16#0003C;
1373 entity("ltcc") -> 16#02AA6;
1374 entity("ltcir") -> 16#02A79;
1375 entity("ltdot") -> 16#022D6;
1376 entity("lthree") -> 16#022CB;
1377 entity("ltimes") -> 16#022C9;
1378 entity("ltlarr") -> 16#02976;
1379 entity("ltquest") -> 16#02A7B;
1380 entity("ltrPar") -> 16#02996;
1381 entity("ltri") -> 16#025C3;
1382 entity("ltrie") -> 16#022B4;
1383 entity("ltrif") -> 16#025C2;
1384 entity("lurdshar") -> 16#0294A;
1385 entity("luruhar") -> 16#02966;
1386 entity("lvertneqq") -> [16#02268, 16#0FE00];
1387 entity("lvnE") -> [16#02268, 16#0FE00];
1388 entity("mDDot") -> 16#0223A;
1389 entity("macr") -> 16#000AF;
1390 entity("male") -> 16#02642;
1391 entity("malt") -> 16#02720;
1392 entity("maltese") -> 16#02720;
1393 entity("map") -> 16#021A6;
1394 entity("mapsto") -> 16#021A6;
1395 entity("mapstodown") -> 16#021A7;
1396 entity("mapstoleft") -> 16#021A4;
1397 entity("mapstoup") -> 16#021A5;
1398 entity("marker") -> 16#025AE;
1399 entity("mcomma") -> 16#02A29;
1400 entity("mcy") -> 16#0043C;
1401 entity("mdash") -> 16#02014;
1402 entity("measuredangle") -> 16#02221;
1403 entity("mfr") -> 16#1D52A;
1404 entity("mho") -> 16#02127;
1405 entity("micro") -> 16#000B5;
1406 entity("mid") -> 16#02223;
1407 entity("midast") -> 16#0002A;
1408 entity("midcir") -> 16#02AF0;
1409 entity("middot") -> 16#000B7;
1410 entity("minus") -> 16#02212;
1411 entity("minusb") -> 16#0229F;
1412 entity("minusd") -> 16#02238;
1413 entity("minusdu") -> 16#02A2A;
1414 entity("mlcp") -> 16#02ADB;
1415 entity("mldr") -> 16#02026;
1416 entity("mnplus") -> 16#02213;
1417 entity("models") -> 16#022A7;
1418 entity("mopf") -> 16#1D55E;
1419 entity("mp") -> 16#02213;
1420 entity("mscr") -> 16#1D4C2;
1421 entity("mstpos") -> 16#0223E;
1422 entity("mu") -> 16#003BC;
1423 entity("multimap") -> 16#022B8;
1424 entity("mumap") -> 16#022B8;
1425 entity("nGg") -> [16#022D9, 16#00338];
1426 entity("nGt") -> [16#0226B, 16#020D2];
1427 entity("nGtv") -> [16#0226B, 16#00338];
1428 entity("nLeftarrow") -> 16#021CD;
1429 entity("nLeftrightarrow") -> 16#021CE;
1430 entity("nLl") -> [16#022D8, 16#00338];
1431 entity("nLt") -> [16#0226A, 16#020D2];
1432 entity("nLtv") -> [16#0226A, 16#00338];
1433 entity("nRightarrow") -> 16#021CF;
1434 entity("nVDash") -> 16#022AF;
1435 entity("nVdash") -> 16#022AE;
1436 entity("nabla") -> 16#02207;
1437 entity("nacute") -> 16#00144;
1438 entity("nang") -> [16#02220, 16#020D2];
1439 entity("nap") -> 16#02249;
1440 entity("napE") -> [16#02A70, 16#00338];
1441 entity("napid") -> [16#0224B, 16#00338];
1442 entity("napos") -> 16#00149;
1443 entity("napprox") -> 16#02249;
1444 entity("natur") -> 16#0266E;
1445 entity("natural") -> 16#0266E;
1446 entity("naturals") -> 16#02115;
1447 entity("nbsp") -> 16#000A0;
1448 entity("nbump") -> [16#0224E, 16#00338];
1449 entity("nbumpe") -> [16#0224F, 16#00338];
1450 entity("ncap") -> 16#02A43;
1451 entity("ncaron") -> 16#00148;
1452 entity("ncedil") -> 16#00146;
1453 entity("ncong") -> 16#02247;
1454 entity("ncongdot") -> [16#02A6D, 16#00338];
1455 entity("ncup") -> 16#02A42;
1456 entity("ncy") -> 16#0043D;
1457 entity("ndash") -> 16#02013;
1458 entity("ne") -> 16#02260;
1459 entity("neArr") -> 16#021D7;
1460 entity("nearhk") -> 16#02924;
1461 entity("nearr") -> 16#02197;
1462 entity("nearrow") -> 16#02197;
1463 entity("nedot") -> [16#02250, 16#00338];
1464 entity("nequiv") -> 16#02262;
1465 entity("nesear") -> 16#02928;
1466 entity("nesim") -> [16#02242, 16#00338];
1467 entity("nexist") -> 16#02204;
1468 entity("nexists") -> 16#02204;
1469 entity("nfr") -> 16#1D52B;
1470 entity("ngE") -> [16#02267, 16#00338];
1471 entity("nge") -> 16#02271;
1472 entity("ngeq") -> 16#02271;
1473 entity("ngeqq") -> [16#02267, 16#00338];
1474 entity("ngeqslant") -> [16#02A7E, 16#00338];
1475 entity("nges") -> [16#02A7E, 16#00338];
1476 entity("ngsim") -> 16#02275;
1477 entity("ngt") -> 16#0226F;
1478 entity("ngtr") -> 16#0226F;
1479 entity("nhArr") -> 16#021CE;
1480 entity("nharr") -> 16#021AE;
1481 entity("nhpar") -> 16#02AF2;
1482 entity("ni") -> 16#0220B;
1483 entity("nis") -> 16#022FC;
1484 entity("nisd") -> 16#022FA;
1485 entity("niv") -> 16#0220B;
1486 entity("njcy") -> 16#0045A;
1487 entity("nlArr") -> 16#021CD;
1488 entity("nlE") -> [16#02266, 16#00338];
1489 entity("nlarr") -> 16#0219A;
1490 entity("nldr") -> 16#02025;
1491 entity("nle") -> 16#02270;
1492 entity("nleftarrow") -> 16#0219A;
1493 entity("nleftrightarrow") -> 16#021AE;
1494 entity("nleq") -> 16#02270;
1495 entity("nleqq") -> [16#02266, 16#00338];
1496 entity("nleqslant") -> [16#02A7D, 16#00338];
1497 entity("nles") -> [16#02A7D, 16#00338];
1498 entity("nless") -> 16#0226E;
1499 entity("nlsim") -> 16#02274;
1500 entity("nlt") -> 16#0226E;
1501 entity("nltri") -> 16#022EA;
1502 entity("nltrie") -> 16#022EC;
1503 entity("nmid") -> 16#02224;
1504 entity("nopf") -> 16#1D55F;
1505 entity("not") -> 16#000AC;
1506 entity("notin") -> 16#02209;
1507 entity("notinE") -> [16#022F9, 16#00338];
1508 entity("notindot") -> [16#022F5, 16#00338];
1509 entity("notinva") -> 16#02209;
1510 entity("notinvb") -> 16#022F7;
1511 entity("notinvc") -> 16#022F6;
1512 entity("notni") -> 16#0220C;
1513 entity("notniva") -> 16#0220C;
1514 entity("notnivb") -> 16#022FE;
1515 entity("notnivc") -> 16#022FD;
1516 entity("npar") -> 16#02226;
1517 entity("nparallel") -> 16#02226;
1518 entity("nparsl") -> [16#02AFD, 16#020E5];
1519 entity("npart") -> [16#02202, 16#00338];
1520 entity("npolint") -> 16#02A14;
1521 entity("npr") -> 16#02280;
1522 entity("nprcue") -> 16#022E0;
1523 entity("npre") -> [16#02AAF, 16#00338];
1524 entity("nprec") -> 16#02280;
1525 entity("npreceq") -> [16#02AAF, 16#00338];
1526 entity("nrArr") -> 16#021CF;
1527 entity("nrarr") -> 16#0219B;
1528 entity("nrarrc") -> [16#02933, 16#00338];
1529 entity("nrarrw") -> [16#0219D, 16#00338];
1530 entity("nrightarrow") -> 16#0219B;
1531 entity("nrtri") -> 16#022EB;
1532 entity("nrtrie") -> 16#022ED;
1533 entity("nsc") -> 16#02281;
1534 entity("nsccue") -> 16#022E1;
1535 entity("nsce") -> [16#02AB0, 16#00338];
1536 entity("nscr") -> 16#1D4C3;
1537 entity("nshortmid") -> 16#02224;
1538 entity("nshortparallel") -> 16#02226;
1539 entity("nsim") -> 16#02241;
1540 entity("nsime") -> 16#02244;
1541 entity("nsimeq") -> 16#02244;
1542 entity("nsmid") -> 16#02224;
1543 entity("nspar") -> 16#02226;
1544 entity("nsqsube") -> 16#022E2;
1545 entity("nsqsupe") -> 16#022E3;
1546 entity("nsub") -> 16#02284;
1547 entity("nsubE") -> [16#02AC5, 16#00338];
1548 entity("nsube") -> 16#02288;
1549 entity("nsubset") -> [16#02282, 16#020D2];
1550 entity("nsubseteq") -> 16#02288;
1551 entity("nsubseteqq") -> [16#02AC5, 16#00338];
1552 entity("nsucc") -> 16#02281;
1553 entity("nsucceq") -> [16#02AB0, 16#00338];
1554 entity("nsup") -> 16#02285;
1555 entity("nsupE") -> [16#02AC6, 16#00338];
1556 entity("nsupe") -> 16#02289;
1557 entity("nsupset") -> [16#02283, 16#020D2];
1558 entity("nsupseteq") -> 16#02289;
1559 entity("nsupseteqq") -> [16#02AC6, 16#00338];
1560 entity("ntgl") -> 16#02279;
1561 entity("ntilde") -> 16#000F1;
1562 entity("ntlg") -> 16#02278;
1563 entity("ntriangleleft") -> 16#022EA;
1564 entity("ntrianglelefteq") -> 16#022EC;
1565 entity("ntriangleright") -> 16#022EB;
1566 entity("ntrianglerighteq") -> 16#022ED;
1567 entity("nu") -> 16#003BD;
1568 entity("num") -> 16#00023;
1569 entity("numero") -> 16#02116;
1570 entity("numsp") -> 16#02007;
1571 entity("nvDash") -> 16#022AD;
1572 entity("nvHarr") -> 16#02904;
1573 entity("nvap") -> [16#0224D, 16#020D2];
1574 entity("nvdash") -> 16#022AC;
1575 entity("nvge") -> [16#02265, 16#020D2];
1576 entity("nvgt") -> [16#0003E, 16#020D2];
1577 entity("nvinfin") -> 16#029DE;
1578 entity("nvlArr") -> 16#02902;
1579 entity("nvle") -> [16#02264, 16#020D2];
1580 entity("nvlt") -> [16#0003C, 16#020D2];
1581 entity("nvltrie") -> [16#022B4, 16#020D2];
1582 entity("nvrArr") -> 16#02903;
1583 entity("nvrtrie") -> [16#022B5, 16#020D2];
1584 entity("nvsim") -> [16#0223C, 16#020D2];
1585 entity("nwArr") -> 16#021D6;
1586 entity("nwarhk") -> 16#02923;
1587 entity("nwarr") -> 16#02196;
1588 entity("nwarrow") -> 16#02196;
1589 entity("nwnear") -> 16#02927;
1590 entity("oS") -> 16#024C8;
1591 entity("oacute") -> 16#000F3;
1592 entity("oast") -> 16#0229B;
1593 entity("ocir") -> 16#0229A;
1594 entity("ocirc") -> 16#000F4;
1595 entity("ocy") -> 16#0043E;
1596 entity("odash") -> 16#0229D;
1597 entity("odblac") -> 16#00151;
1598 entity("odiv") -> 16#02A38;
1599 entity("odot") -> 16#02299;
1600 entity("odsold") -> 16#029BC;
1601 entity("oelig") -> 16#00153;
1602 entity("ofcir") -> 16#029BF;
1603 entity("ofr") -> 16#1D52C;
1604 entity("ogon") -> 16#002DB;
1605 entity("ograve") -> 16#000F2;
1606 entity("ogt") -> 16#029C1;
1607 entity("ohbar") -> 16#029B5;
1608 entity("ohm") -> 16#003A9;
1609 entity("oint") -> 16#0222E;
1610 entity("olarr") -> 16#021BA;
1611 entity("olcir") -> 16#029BE;
1612 entity("olcross") -> 16#029BB;
1613 entity("oline") -> 16#0203E;
1614 entity("olt") -> 16#029C0;
1615 entity("omacr") -> 16#0014D;
1616 entity("omega") -> 16#003C9;
1617 entity("omicron") -> 16#003BF;
1618 entity("omid") -> 16#029B6;
1619 entity("ominus") -> 16#02296;
1620 entity("oopf") -> 16#1D560;
1621 entity("opar") -> 16#029B7;
1622 entity("operp") -> 16#029B9;
1623 entity("oplus") -> 16#02295;
1624 entity("or") -> 16#02228;
1625 entity("orarr") -> 16#021BB;
1626 entity("ord") -> 16#02A5D;
1627 entity("order") -> 16#02134;
1628 entity("orderof") -> 16#02134;
1629 entity("ordf") -> 16#000AA;
1630 entity("ordm") -> 16#000BA;
1631 entity("origof") -> 16#022B6;
1632 entity("oror") -> 16#02A56;
1633 entity("orslope") -> 16#02A57;
1634 entity("orv") -> 16#02A5B;
1635 entity("oscr") -> 16#02134;
1636 entity("oslash") -> 16#000F8;
1637 entity("osol") -> 16#02298;
1638 entity("otilde") -> 16#000F5;
1639 entity("otimes") -> 16#02297;
1640 entity("otimesas") -> 16#02A36;
1641 entity("ouml") -> 16#000F6;
1642 entity("ovbar") -> 16#0233D;
1643 entity("par") -> 16#02225;
1644 entity("para") -> 16#000B6;
1645 entity("parallel") -> 16#02225;
1646 entity("parsim") -> 16#02AF3;
1647 entity("parsl") -> 16#02AFD;
1648 entity("part") -> 16#02202;
1649 entity("pcy") -> 16#0043F;
1650 entity("percnt") -> 16#00025;
1651 entity("period") -> 16#0002E;
1652 entity("permil") -> 16#02030;
1653 entity("perp") -> 16#022A5;
1654 entity("pertenk") -> 16#02031;
1655 entity("pfr") -> 16#1D52D;
1656 entity("phi") -> 16#003C6;
1657 entity("phiv") -> 16#003D5;
1658 entity("phmmat") -> 16#02133;
1659 entity("phone") -> 16#0260E;
1660 entity("pi") -> 16#003C0;
1661 entity("pitchfork") -> 16#022D4;
1662 entity("piv") -> 16#003D6;
1663 entity("planck") -> 16#0210F;
1664 entity("planckh") -> 16#0210E;
1665 entity("plankv") -> 16#0210F;
1666 entity("plus") -> 16#0002B;
1667 entity("plusacir") -> 16#02A23;
1668 entity("plusb") -> 16#0229E;
1669 entity("pluscir") -> 16#02A22;
1670 entity("plusdo") -> 16#02214;
1671 entity("plusdu") -> 16#02A25;
1672 entity("pluse") -> 16#02A72;
1673 entity("plusmn") -> 16#000B1;
1674 entity("plussim") -> 16#02A26;
1675 entity("plustwo") -> 16#02A27;
1676 entity("pm") -> 16#000B1;
1677 entity("pointint") -> 16#02A15;
1678 entity("popf") -> 16#1D561;
1679 entity("pound") -> 16#000A3;
1680 entity("pr") -> 16#0227A;
1681 entity("prE") -> 16#02AB3;
1682 entity("prap") -> 16#02AB7;
1683 entity("prcue") -> 16#0227C;
1684 entity("pre") -> 16#02AAF;
1685 entity("prec") -> 16#0227A;
1686 entity("precapprox") -> 16#02AB7;
1687 entity("preccurlyeq") -> 16#0227C;
1688 entity("preceq") -> 16#02AAF;
1689 entity("precnapprox") -> 16#02AB9;
1690 entity("precneqq") -> 16#02AB5;
1691 entity("precnsim") -> 16#022E8;
1692 entity("precsim") -> 16#0227E;
1693 entity("prime") -> 16#02032;
1694 entity("primes") -> 16#02119;
1695 entity("prnE") -> 16#02AB5;
1696 entity("prnap") -> 16#02AB9;
1697 entity("prnsim") -> 16#022E8;
1698 entity("prod") -> 16#0220F;
1699 entity("profalar") -> 16#0232E;
1700 entity("profline") -> 16#02312;
1701 entity("profsurf") -> 16#02313;
1702 entity("prop") -> 16#0221D;
1703 entity("propto") -> 16#0221D;
1704 entity("prsim") -> 16#0227E;
1705 entity("prurel") -> 16#022B0;
1706 entity("pscr") -> 16#1D4C5;
1707 entity("psi") -> 16#003C8;
1708 entity("puncsp") -> 16#02008;
1709 entity("qfr") -> 16#1D52E;
1710 entity("qint") -> 16#02A0C;
1711 entity("qopf") -> 16#1D562;
1712 entity("qprime") -> 16#02057;
1713 entity("qscr") -> 16#1D4C6;
1714 entity("quaternions") -> 16#0210D;
1715 entity("quatint") -> 16#02A16;
1716 entity("quest") -> 16#0003F;
1717 entity("questeq") -> 16#0225F;
1718 entity("quot") -> 16#00022;
1719 entity("rAarr") -> 16#021DB;
1720 entity("rArr") -> 16#021D2;
1721 entity("rAtail") -> 16#0291C;
1722 entity("rBarr") -> 16#0290F;
1723 entity("rHar") -> 16#02964;
1724 entity("race") -> [16#0223D, 16#00331];
1725 entity("racute") -> 16#00155;
1726 entity("radic") -> 16#0221A;
1727 entity("raemptyv") -> 16#029B3;
1728 entity("rang") -> 16#027E9;
1729 entity("rangd") -> 16#02992;
1730 entity("range") -> 16#029A5;
1731 entity("rangle") -> 16#027E9;
1732 entity("raquo") -> 16#000BB;
1733 entity("rarr") -> 16#02192;
1734 entity("rarrap") -> 16#02975;
1735 entity("rarrb") -> 16#021E5;
1736 entity("rarrbfs") -> 16#02920;
1737 entity("rarrc") -> 16#02933;
1738 entity("rarrfs") -> 16#0291E;
1739 entity("rarrhk") -> 16#021AA;
1740 entity("rarrlp") -> 16#021AC;
1741 entity("rarrpl") -> 16#02945;
1742 entity("rarrsim") -> 16#02974;
1743 entity("rarrtl") -> 16#021A3;
1744 entity("rarrw") -> 16#0219D;
1745 entity("ratail") -> 16#0291A;
1746 entity("ratio") -> 16#02236;
1747 entity("rationals") -> 16#0211A;
1748 entity("rbarr") -> 16#0290D;
1749 entity("rbbrk") -> 16#02773;
1750 entity("rbrace") -> 16#0007D;
1751 entity("rbrack") -> 16#0005D;
1752 entity("rbrke") -> 16#0298C;
1753 entity("rbrksld") -> 16#0298E;
1754 entity("rbrkslu") -> 16#02990;
1755 entity("rcaron") -> 16#00159;
1756 entity("rcedil") -> 16#00157;
1757 entity("rceil") -> 16#02309;
1758 entity("rcub") -> 16#0007D;
1759 entity("rcy") -> 16#00440;
1760 entity("rdca") -> 16#02937;
1761 entity("rdldhar") -> 16#02969;
1762 entity("rdquo") -> 16#0201D;
1763 entity("rdquor") -> 16#0201D;
1764 entity("rdsh") -> 16#021B3;
1765 entity("real") -> 16#0211C;
1766 entity("realine") -> 16#0211B;
1767 entity("realpart") -> 16#0211C;
1768 entity("reals") -> 16#0211D;
1769 entity("rect") -> 16#025AD;
1770 entity("reg") -> 16#000AE;
1771 entity("rfisht") -> 16#0297D;
1772 entity("rfloor") -> 16#0230B;
1773 entity("rfr") -> 16#1D52F;
1774 entity("rhard") -> 16#021C1;
1775 entity("rharu") -> 16#021C0;
1776 entity("rharul") -> 16#0296C;
1777 entity("rho") -> 16#003C1;
1778 entity("rhov") -> 16#003F1;
1779 entity("rightarrow") -> 16#02192;
1780 entity("rightarrowtail") -> 16#021A3;
1781 entity("rightharpoondown") -> 16#021C1;
1782 entity("rightharpoonup") -> 16#021C0;
1783 entity("rightleftarrows") -> 16#021C4;
1784 entity("rightleftharpoons") -> 16#021CC;
1785 entity("rightrightarrows") -> 16#021C9;
1786 entity("rightsquigarrow") -> 16#0219D;
1787 entity("rightthreetimes") -> 16#022CC;
1788 entity("ring") -> 16#002DA;
1789 entity("risingdotseq") -> 16#02253;
1790 entity("rlarr") -> 16#021C4;
1791 entity("rlhar") -> 16#021CC;
1792 entity("rlm") -> 16#0200F;
1793 entity("rmoust") -> 16#023B1;
1794 entity("rmoustache") -> 16#023B1;
1795 entity("rnmid") -> 16#02AEE;
1796 entity("roang") -> 16#027ED;
1797 entity("roarr") -> 16#021FE;
1798 entity("robrk") -> 16#027E7;
1799 entity("ropar") -> 16#02986;
1800 entity("ropf") -> 16#1D563;
1801 entity("roplus") -> 16#02A2E;
1802 entity("rotimes") -> 16#02A35;
1803 entity("rpar") -> 16#00029;
1804 entity("rpargt") -> 16#02994;
1805 entity("rppolint") -> 16#02A12;
1806 entity("rrarr") -> 16#021C9;
1807 entity("rsaquo") -> 16#0203A;
1808 entity("rscr") -> 16#1D4C7;
1809 entity("rsh") -> 16#021B1;
1810 entity("rsqb") -> 16#0005D;
1811 entity("rsquo") -> 16#02019;
1812 entity("rsquor") -> 16#02019;
1813 entity("rthree") -> 16#022CC;
1814 entity("rtimes") -> 16#022CA;
1815 entity("rtri") -> 16#025B9;
1816 entity("rtrie") -> 16#022B5;
1817 entity("rtrif") -> 16#025B8;
1818 entity("rtriltri") -> 16#029CE;
1819 entity("ruluhar") -> 16#02968;
1820 entity("rx") -> 16#0211E;
1821 entity("sacute") -> 16#0015B;
1822 entity("sbquo") -> 16#0201A;
1823 entity("sc") -> 16#0227B;
1824 entity("scE") -> 16#02AB4;
1825 entity("scap") -> 16#02AB8;
1826 entity("scaron") -> 16#00161;
1827 entity("sccue") -> 16#0227D;
1828 entity("sce") -> 16#02AB0;
1829 entity("scedil") -> 16#0015F;
1830 entity("scirc") -> 16#0015D;
1831 entity("scnE") -> 16#02AB6;
1832 entity("scnap") -> 16#02ABA;
1833 entity("scnsim") -> 16#022E9;
1834 entity("scpolint") -> 16#02A13;
1835 entity("scsim") -> 16#0227F;
1836 entity("scy") -> 16#00441;
1837 entity("sdot") -> 16#022C5;
1838 entity("sdotb") -> 16#022A1;
1839 entity("sdote") -> 16#02A66;
1840 entity("seArr") -> 16#021D8;
1841 entity("searhk") -> 16#02925;
1842 entity("searr") -> 16#02198;
1843 entity("searrow") -> 16#02198;
1844 entity("sect") -> 16#000A7;
1845 entity("semi") -> 16#0003B;
1846 entity("seswar") -> 16#02929;
1847 entity("setminus") -> 16#02216;
1848 entity("setmn") -> 16#02216;
1849 entity("sext") -> 16#02736;
1850 entity("sfr") -> 16#1D530;
1851 entity("sfrown") -> 16#02322;
1852 entity("sharp") -> 16#0266F;
1853 entity("shchcy") -> 16#00449;
1854 entity("shcy") -> 16#00448;
1855 entity("shortmid") -> 16#02223;
1856 entity("shortparallel") -> 16#02225;
1857 entity("shy") -> 16#000AD;
1858 entity("sigma") -> 16#003C3;
1859 entity("sigmaf") -> 16#003C2;
1860 entity("sigmav") -> 16#003C2;
1861 entity("sim") -> 16#0223C;
1862 entity("simdot") -> 16#02A6A;
1863 entity("sime") -> 16#02243;
1864 entity("simeq") -> 16#02243;
1865 entity("simg") -> 16#02A9E;
1866 entity("simgE") -> 16#02AA0;
1867 entity("siml") -> 16#02A9D;
1868 entity("simlE") -> 16#02A9F;
1869 entity("simne") -> 16#02246;
1870 entity("simplus") -> 16#02A24;
1871 entity("simrarr") -> 16#02972;
1872 entity("slarr") -> 16#02190;
1873 entity("smallsetminus") -> 16#02216;
1874 entity("smashp") -> 16#02A33;
1875 entity("smeparsl") -> 16#029E4;
1876 entity("smid") -> 16#02223;
1877 entity("smile") -> 16#02323;
1878 entity("smt") -> 16#02AAA;
1879 entity("smte") -> 16#02AAC;
1880 entity("smtes") -> [16#02AAC, 16#0FE00];
1881 entity("softcy") -> 16#0044C;
1882 entity("sol") -> 16#0002F;
1883 entity("solb") -> 16#029C4;
1884 entity("solbar") -> 16#0233F;
1885 entity("sopf") -> 16#1D564;
1886 entity("spades") -> 16#02660;
1887 entity("spadesuit") -> 16#02660;
1888 entity("spar") -> 16#02225;
1889 entity("sqcap") -> 16#02293;
1890 entity("sqcaps") -> [16#02293, 16#0FE00];
1891 entity("sqcup") -> 16#02294;
1892 entity("sqcups") -> [16#02294, 16#0FE00];
1893 entity("sqsub") -> 16#0228F;
1894 entity("sqsube") -> 16#02291;
1895 entity("sqsubset") -> 16#0228F;
1896 entity("sqsubseteq") -> 16#02291;
1897 entity("sqsup") -> 16#02290;
1898 entity("sqsupe") -> 16#02292;
1899 entity("sqsupset") -> 16#02290;
1900 entity("sqsupseteq") -> 16#02292;
1901 entity("squ") -> 16#025A1;
1902 entity("square") -> 16#025A1;
1903 entity("squarf") -> 16#025AA;
1904 entity("squf") -> 16#025AA;
1905 entity("srarr") -> 16#02192;
1906 entity("sscr") -> 16#1D4C8;
1907 entity("ssetmn") -> 16#02216;
1908 entity("ssmile") -> 16#02323;
1909 entity("sstarf") -> 16#022C6;
1910 entity("star") -> 16#02606;
1911 entity("starf") -> 16#02605;
1912 entity("straightepsilon") -> 16#003F5;
1913 entity("straightphi") -> 16#003D5;
1914 entity("strns") -> 16#000AF;
1915 entity("sub") -> 16#02282;
1916 entity("subE") -> 16#02AC5;
1917 entity("subdot") -> 16#02ABD;
1918 entity("sube") -> 16#02286;
1919 entity("subedot") -> 16#02AC3;
1920 entity("submult") -> 16#02AC1;
1921 entity("subnE") -> 16#02ACB;
1922 entity("subne") -> 16#0228A;
1923 entity("subplus") -> 16#02ABF;
1924 entity("subrarr") -> 16#02979;
1925 entity("subset") -> 16#02282;
1926 entity("subseteq") -> 16#02286;
1927 entity("subseteqq") -> 16#02AC5;
1928 entity("subsetneq") -> 16#0228A;
1929 entity("subsetneqq") -> 16#02ACB;
1930 entity("subsim") -> 16#02AC7;
1931 entity("subsub") -> 16#02AD5;
1932 entity("subsup") -> 16#02AD3;
1933 entity("succ") -> 16#0227B;
1934 entity("succapprox") -> 16#02AB8;
1935 entity("succcurlyeq") -> 16#0227D;
1936 entity("succeq") -> 16#02AB0;
1937 entity("succnapprox") -> 16#02ABA;
1938 entity("succneqq") -> 16#02AB6;
1939 entity("succnsim") -> 16#022E9;
1940 entity("succsim") -> 16#0227F;
1941 entity("sum") -> 16#02211;
1942 entity("sung") -> 16#0266A;
1943 entity("sup") -> 16#02283;
1944 entity("sup1") -> 16#000B9;
1945 entity("sup2") -> 16#000B2;
1946 entity("sup3") -> 16#000B3;
1947 entity("supE") -> 16#02AC6;
1948 entity("supdot") -> 16#02ABE;
1949 entity("supdsub") -> 16#02AD8;
1950 entity("supe") -> 16#02287;
1951 entity("supedot") -> 16#02AC4;
1952 entity("suphsol") -> 16#027C9;
1953 entity("suphsub") -> 16#02AD7;
1954 entity("suplarr") -> 16#0297B;
1955 entity("supmult") -> 16#02AC2;
1956 entity("supnE") -> 16#02ACC;
1957 entity("supne") -> 16#0228B;
1958 entity("supplus") -> 16#02AC0;
1959 entity("supset") -> 16#02283;
1960 entity("supseteq") -> 16#02287;
1961 entity("supseteqq") -> 16#02AC6;
1962 entity("supsetneq") -> 16#0228B;
1963 entity("supsetneqq") -> 16#02ACC;
1964 entity("supsim") -> 16#02AC8;
1965 entity("supsub") -> 16#02AD4;
1966 entity("supsup") -> 16#02AD6;
1967 entity("swArr") -> 16#021D9;
1968 entity("swarhk") -> 16#02926;
1969 entity("swarr") -> 16#02199;
1970 entity("swarrow") -> 16#02199;
1971 entity("swnwar") -> 16#0292A;
1972 entity("szlig") -> 16#000DF;
1973 entity("target") -> 16#02316;
1974 entity("tau") -> 16#003C4;
1975 entity("tbrk") -> 16#023B4;
1976 entity("tcaron") -> 16#00165;
1977 entity("tcedil") -> 16#00163;
1978 entity("tcy") -> 16#00442;
1979 entity("tdot") -> 16#020DB;
1980 entity("telrec") -> 16#02315;
1981 entity("tfr") -> 16#1D531;
1982 entity("there4") -> 16#02234;
1983 entity("therefore") -> 16#02234;
1984 entity("theta") -> 16#003B8;
1985 entity("thetasym") -> 16#003D1;
1986 entity("thetav") -> 16#003D1;
1987 entity("thickapprox") -> 16#02248;
1988 entity("thicksim") -> 16#0223C;
1989 entity("thinsp") -> 16#02009;
1990 entity("thkap") -> 16#02248;
1991 entity("thksim") -> 16#0223C;
1992 entity("thorn") -> 16#000FE;
1993 entity("tilde") -> 16#002DC;
1994 entity("times") -> 16#000D7;
1995 entity("timesb") -> 16#022A0;
1996 entity("timesbar") -> 16#02A31;
1997 entity("timesd") -> 16#02A30;
1998 entity("tint") -> 16#0222D;
1999 entity("toea") -> 16#02928;
2000 entity("top") -> 16#022A4;
2001 entity("topbot") -> 16#02336;
2002 entity("topcir") -> 16#02AF1;
2003 entity("topf") -> 16#1D565;
2004 entity("topfork") -> 16#02ADA;
2005 entity("tosa") -> 16#02929;
2006 entity("tprime") -> 16#02034;
2007 entity("trade") -> 16#02122;
2008 entity("triangle") -> 16#025B5;
2009 entity("triangledown") -> 16#025BF;
2010 entity("triangleleft") -> 16#025C3;
2011 entity("trianglelefteq") -> 16#022B4;
2012 entity("triangleq") -> 16#0225C;
2013 entity("triangleright") -> 16#025B9;
2014 entity("trianglerighteq") -> 16#022B5;
2015 entity("tridot") -> 16#025EC;
2016 entity("trie") -> 16#0225C;
2017 entity("triminus") -> 16#02A3A;
2018 entity("triplus") -> 16#02A39;
2019 entity("trisb") -> 16#029CD;
2020 entity("tritime") -> 16#02A3B;
2021 entity("trpezium") -> 16#023E2;
2022 entity("tscr") -> 16#1D4C9;
2023 entity("tscy") -> 16#00446;
2024 entity("tshcy") -> 16#0045B;
2025 entity("tstrok") -> 16#00167;
2026 entity("twixt") -> 16#0226C;
2027 entity("twoheadleftarrow") -> 16#0219E;
2028 entity("twoheadrightarrow") -> 16#021A0;
2029 entity("uArr") -> 16#021D1;
2030 entity("uHar") -> 16#02963;
2031 entity("uacute") -> 16#000FA;
2032 entity("uarr") -> 16#02191;
2033 entity("ubrcy") -> 16#0045E;
2034 entity("ubreve") -> 16#0016D;
2035 entity("ucirc") -> 16#000FB;
2036 entity("ucy") -> 16#00443;
2037 entity("udarr") -> 16#021C5;
2038 entity("udblac") -> 16#00171;
2039 entity("udhar") -> 16#0296E;
2040 entity("ufisht") -> 16#0297E;
2041 entity("ufr") -> 16#1D532;
2042 entity("ugrave") -> 16#000F9;
2043 entity("uharl") -> 16#021BF;
2044 entity("uharr") -> 16#021BE;
2045 entity("uhblk") -> 16#02580;
2046 entity("ulcorn") -> 16#0231C;
2047 entity("ulcorner") -> 16#0231C;
2048 entity("ulcrop") -> 16#0230F;
2049 entity("ultri") -> 16#025F8;
2050 entity("umacr") -> 16#0016B;
2051 entity("uml") -> 16#000A8;
2052 entity("uogon") -> 16#00173;
2053 entity("uopf") -> 16#1D566;
2054 entity("uparrow") -> 16#02191;
2055 entity("updownarrow") -> 16#02195;
2056 entity("upharpoonleft") -> 16#021BF;
2057 entity("upharpoonright") -> 16#021BE;
2058 entity("uplus") -> 16#0228E;
2059 entity("upsi") -> 16#003C5;
2060 entity("upsih") -> 16#003D2;
2061 entity("upsilon") -> 16#003C5;
2062 entity("upuparrows") -> 16#021C8;
2063 entity("urcorn") -> 16#0231D;
2064 entity("urcorner") -> 16#0231D;
2065 entity("urcrop") -> 16#0230E;
2066 entity("uring") -> 16#0016F;
2067 entity("urtri") -> 16#025F9;
2068 entity("uscr") -> 16#1D4CA;
2069 entity("utdot") -> 16#022F0;
2070 entity("utilde") -> 16#00169;
2071 entity("utri") -> 16#025B5;
2072 entity("utrif") -> 16#025B4;
2073 entity("uuarr") -> 16#021C8;
2074 entity("uuml") -> 16#000FC;
2075 entity("uwangle") -> 16#029A7;
2076 entity("vArr") -> 16#021D5;
2077 entity("vBar") -> 16#02AE8;
2078 entity("vBarv") -> 16#02AE9;
2079 entity("vDash") -> 16#022A8;
2080 entity("vangrt") -> 16#0299C;
2081 entity("varepsilon") -> 16#003F5;
2082 entity("varkappa") -> 16#003F0;
2083 entity("varnothing") -> 16#02205;
2084 entity("varphi") -> 16#003D5;
2085 entity("varpi") -> 16#003D6;
2086 entity("varpropto") -> 16#0221D;
2087 entity("varr") -> 16#02195;
2088 entity("varrho") -> 16#003F1;
2089 entity("varsigma") -> 16#003C2;
2090 entity("varsubsetneq") -> [16#0228A, 16#0FE00];
2091 entity("varsubsetneqq") -> [16#02ACB, 16#0FE00];
2092 entity("varsupsetneq") -> [16#0228B, 16#0FE00];
2093 entity("varsupsetneqq") -> [16#02ACC, 16#0FE00];
2094 entity("vartheta") -> 16#003D1;
2095 entity("vartriangleleft") -> 16#022B2;
2096 entity("vartriangleright") -> 16#022B3;
2097 entity("vcy") -> 16#00432;
2098 entity("vdash") -> 16#022A2;
2099 entity("vee") -> 16#02228;
2100 entity("veebar") -> 16#022BB;
2101 entity("veeeq") -> 16#0225A;
2102 entity("vellip") -> 16#022EE;
2103 entity("verbar") -> 16#0007C;
2104 entity("vert") -> 16#0007C;
2105 entity("vfr") -> 16#1D533;
2106 entity("vltri") -> 16#022B2;
2107 entity("vnsub") -> [16#02282, 16#020D2];
2108 entity("vnsup") -> [16#02283, 16#020D2];
2109 entity("vopf") -> 16#1D567;
2110 entity("vprop") -> 16#0221D;
2111 entity("vrtri") -> 16#022B3;
2112 entity("vscr") -> 16#1D4CB;
2113 entity("vsubnE") -> [16#02ACB, 16#0FE00];
2114 entity("vsubne") -> [16#0228A, 16#0FE00];
2115 entity("vsupnE") -> [16#02ACC, 16#0FE00];
2116 entity("vsupne") -> [16#0228B, 16#0FE00];
2117 entity("vzigzag") -> 16#0299A;
2118 entity("wcirc") -> 16#00175;
2119 entity("wedbar") -> 16#02A5F;
2120 entity("wedge") -> 16#02227;
2121 entity("wedgeq") -> 16#02259;
2122 entity("weierp") -> 16#02118;
2123 entity("wfr") -> 16#1D534;
2124 entity("wopf") -> 16#1D568;
2125 entity("wp") -> 16#02118;
2126 entity("wr") -> 16#02240;
2127 entity("wreath") -> 16#02240;
2128 entity("wscr") -> 16#1D4CC;
2129 entity("xcap") -> 16#022C2;
2130 entity("xcirc") -> 16#025EF;
2131 entity("xcup") -> 16#022C3;
2132 entity("xdtri") -> 16#025BD;
2133 entity("xfr") -> 16#1D535;
2134 entity("xhArr") -> 16#027FA;
2135 entity("xharr") -> 16#027F7;
2136 entity("xi") -> 16#003BE;
2137 entity("xlArr") -> 16#027F8;
2138 entity("xlarr") -> 16#027F5;
2139 entity("xmap") -> 16#027FC;
2140 entity("xnis") -> 16#022FB;
2141 entity("xodot") -> 16#02A00;
2142 entity("xopf") -> 16#1D569;
2143 entity("xoplus") -> 16#02A01;
2144 entity("xotime") -> 16#02A02;
2145 entity("xrArr") -> 16#027F9;
2146 entity("xrarr") -> 16#027F6;
2147 entity("xscr") -> 16#1D4CD;
2148 entity("xsqcup") -> 16#02A06;
2149 entity("xuplus") -> 16#02A04;
2150 entity("xutri") -> 16#025B3;
2151 entity("xvee") -> 16#022C1;
2152 entity("xwedge") -> 16#022C0;
2153 entity("yacute") -> 16#000FD;
2154 entity("yacy") -> 16#0044F;
2155 entity("ycirc") -> 16#00177;
2156 entity("ycy") -> 16#0044B;
2157 entity("yen") -> 16#000A5;
2158 entity("yfr") -> 16#1D536;
2159 entity("yicy") -> 16#00457;
2160 entity("yopf") -> 16#1D56A;
2161 entity("yscr") -> 16#1D4CE;
2162 entity("yucy") -> 16#0044E;
2163 entity("yuml") -> 16#000FF;
2164 entity("zacute") -> 16#0017A;
2165 entity("zcaron") -> 16#0017E;
2166 entity("zcy") -> 16#00437;
2167 entity("zdot") -> 16#0017C;
2168 entity("zeetrf") -> 16#02128;
2169 entity("zeta") -> 16#003B6;
2170 entity("zfr") -> 16#1D537;
2171 entity("zhcy") -> 16#00436;
2172 entity("zigrarr") -> 16#021DD;
2173 entity("zopf") -> 16#1D56B;
2174 entity("zscr") -> 16#1D4CF;
2175 entity("zwj") -> 16#0200D;
2176 entity("zwnj") -> 16#0200C;
2177 entity(_) -> undefined.
2178
2179 %%
2180 %% Tests
2181 %%
2182 -ifdef(TEST).
2183 -include_lib("eunit/include/eunit.hrl").
2184
2185 exhaustive_entity_test() ->
2186 T = mochiweb_cover:clause_lookup_table(?MODULE, entity),
2187 [?assertEqual(V, entity(K)) || {K, V} <- T].
2188
2189 charref_test() ->
2190 1234 = charref("#1234"),
2191 255 = charref("#xfF"),
2192 255 = charref(<<"#XFf">>),
2193 38 = charref("amp"),
2194 38 = charref(<<"amp">>),
2195 undefined = charref("not_an_entity"),
2196 undefined = charref("#not_an_entity"),
2197 undefined = charref("#xnot_an_entity"),
2198 ok.
2199
2200 -endif.
+0
-101
deps/mochiweb/src/mochiweb_clock.erl less more
0 %% Copyright (c) 2011-2014, Loïc Hoguin <essen@ninenines.eu>
1 %% Copyright (c) 2015, Robert Kowalski <rok@kowalski.gd>
2 %%
3 %% Permission to use, copy, modify, and/or distribute this software for any
4 %% purpose with or without fee is hereby granted, provided that the above
5 %% copyright notice and this permission notice appear in all copies.
6 %%
7 %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15 %% While a gen_server process runs in the background to update
16 %% the cache of formatted dates every second, all API calls are
17 %% local and directly read from the ETS cache table, providing
18 %% fast time and date computations.
19
20 -module(mochiweb_clock).
21
22 -behaviour(gen_server).
23
24 %% API.
25 -export([start_link/0]).
26 -export([start/0]).
27 -export([stop/0]).
28 -export([rfc1123/0]).
29
30 %% gen_server.
31 -export([init/1]).
32 -export([handle_call/3]).
33 -export([handle_cast/2]).
34 -export([handle_info/2]).
35 -export([terminate/2]).
36 -export([code_change/3]).
37
38 -record(state, {}).
39
40 %% API.
41
42 -spec start_link() -> {ok, pid()}.
43 start_link() ->
44 gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
45
46 -spec start() -> {ok, pid()}.
47 start() ->
48 gen_server:start({local, ?MODULE}, ?MODULE, [], []).
49
50 -spec stop() -> stopped.
51 stop() ->
52 gen_server:call(?MODULE, stop).
53
54 -spec rfc1123() -> string().
55 rfc1123() ->
56 case ets:lookup(?MODULE, rfc1123) of
57 [{rfc1123, Date}] ->
58 Date;
59 [] ->
60 ""
61 end.
62
63 %% gen_server.
64
65 -spec init([]) -> {ok, #state{}}.
66 init([]) ->
67 ?MODULE = ets:new(?MODULE, [named_table, protected, {read_concurrency, true}]),
68 handle_info(update_date, #state{}),
69 timer:send_interval(1000, update_date),
70 {ok, #state{}}.
71
72 -type from() :: {pid(), term()}.
73 -spec handle_call
74 (stop, from(), State) -> {stop, normal, stopped, State}
75 when State::#state{}.
76 handle_call(stop, _From, State) ->
77 {stop, normal, stopped, State};
78 handle_call(_Request, _From, State) ->
79 {reply, ignored, State}.
80
81 -spec handle_cast(_, State) -> {noreply, State} when State::#state{}.
82 handle_cast(_Msg, State) ->
83 {noreply, State}.
84
85 -spec handle_info(any(), State) -> {noreply, State} when State::#state{}.
86 handle_info(update_date, State) ->
87 Date = httpd_util:rfc1123_date(),
88 ets:insert(?MODULE, {rfc1123, Date}),
89 {noreply, State};
90 handle_info(_Info, State) ->
91 {noreply, State}.
92
93 -spec terminate(_, _) -> ok.
94 terminate(_Reason, _State) ->
95 ok.
96
97 -spec code_change(_, State, _) -> {ok, State} when State::#state{}.
98 code_change(_OldVsn, State, _Extra) ->
99 {ok, State}.
100
+0
-349
deps/mochiweb/src/mochiweb_cookies.erl less more
0 %% @author Emad El-Haraty <emad@mochimedia.com>
1 %% @copyright 2007 Mochi Media, Inc.
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 %% @doc HTTP Cookie parsing and generating (RFC 2109, RFC 2965).
22
23 -module(mochiweb_cookies).
24 -export([parse_cookie/1, cookie/3, cookie/2]).
25
26 -define(QUOTE, $\").
27
28 -define(IS_WHITESPACE(C),
29 (C =:= $\s orelse C =:= $\t orelse C =:= $\r orelse C =:= $\n)).
30
31 %% RFC 2616 separators (called tspecials in RFC 2068)
32 -define(IS_SEPARATOR(C),
33 (C < 32 orelse
34 C =:= $\s orelse C =:= $\t orelse
35 C =:= $( orelse C =:= $) orelse C =:= $< orelse C =:= $> orelse
36 C =:= $@ orelse C =:= $, orelse C =:= $; orelse C =:= $: orelse
37 C =:= $\\ orelse C =:= $\" orelse C =:= $/ orelse
38 C =:= $[ orelse C =:= $] orelse C =:= $? orelse C =:= $= orelse
39 C =:= ${ orelse C =:= $})).
40
41 %% @type proplist() = [{Key::string(), Value::string()}].
42 %% @type header() = {Name::string(), Value::string()}.
43 %% @type int_seconds() = integer().
44
45 %% @spec cookie(Key::string(), Value::string()) -> header()
46 %% @doc Short-hand for <code>cookie(Key, Value, [])</code>.
47 cookie(Key, Value) ->
48 cookie(Key, Value, []).
49
50 %% @spec cookie(Key::string(), Value::string(), Options::[Option]) -> header()
51 %% where Option = {max_age, int_seconds()} | {local_time, {date(), time()}}
52 %% | {domain, string()} | {path, string()}
53 %% | {secure, true | false} | {http_only, true | false}
54 %%
55 %% @doc Generate a Set-Cookie header field tuple.
56 cookie(Key, Value, Options) ->
57 Cookie = [any_to_list(Key), "=", quote(Value), "; Version=1"],
58 %% Set-Cookie:
59 %% Comment, Domain, Max-Age, Path, Secure, Version
60 %% Set-Cookie2:
61 %% Comment, CommentURL, Discard, Domain, Max-Age, Path, Port, Secure,
62 %% Version
63 ExpiresPart =
64 case proplists:get_value(max_age, Options) of
65 undefined ->
66 "";
67 RawAge ->
68 When = case proplists:get_value(local_time, Options) of
69 undefined ->
70 calendar:local_time();
71 LocalTime ->
72 LocalTime
73 end,
74 Age = case RawAge < 0 of
75 true ->
76 0;
77 false ->
78 RawAge
79 end,
80 ["; Expires=", age_to_cookie_date(Age, When),
81 "; Max-Age=", quote(Age)]
82 end,
83 SecurePart =
84 case proplists:get_value(secure, Options) of
85 true ->
86 "; Secure";
87 _ ->
88 ""
89 end,
90 DomainPart =
91 case proplists:get_value(domain, Options) of
92 undefined ->
93 "";
94 Domain ->
95 ["; Domain=", quote(Domain)]
96 end,
97 PathPart =
98 case proplists:get_value(path, Options) of
99 undefined ->
100 "";
101 Path ->
102 ["; Path=", quote(Path)]
103 end,
104 HttpOnlyPart =
105 case proplists:get_value(http_only, Options) of
106 true ->
107 "; HttpOnly";
108 _ ->
109 ""
110 end,
111 CookieParts = [Cookie, ExpiresPart, SecurePart, DomainPart, PathPart, HttpOnlyPart],
112 {"Set-Cookie", lists:flatten(CookieParts)}.
113
114
115 %% Every major browser incorrectly handles quoted strings in a
116 %% different and (worse) incompatible manner. Instead of wasting time
117 %% writing redundant code for each browser, we restrict cookies to
118 %% only contain characters that browsers handle compatibly.
119 %%
120 %% By replacing the definition of quote with this, we generate
121 %% RFC-compliant cookies:
122 %%
123 %% quote(V) ->
124 %% Fun = fun(?QUOTE, Acc) -> [$\\, ?QUOTE | Acc];
125 %% (Ch, Acc) -> [Ch | Acc]
126 %% end,
127 %% [?QUOTE | lists:foldr(Fun, [?QUOTE], V)].
128
129 %% Convert to a string and raise an error if quoting is required.
130 quote(V0) ->
131 V = any_to_list(V0),
132 lists:all(fun(Ch) -> Ch =:= $/ orelse not ?IS_SEPARATOR(Ch) end, V)
133 orelse erlang:error({cookie_quoting_required, V}),
134 V.
135
136
137 %% Return a date in the form of: Wdy, DD-Mon-YYYY HH:MM:SS GMT
138 %% See also: rfc2109: 10.1.2
139 rfc2109_cookie_expires_date(LocalTime) ->
140 {{YYYY,MM,DD},{Hour,Min,Sec}} =
141 case calendar:local_time_to_universal_time_dst(LocalTime) of
142 [] ->
143 {Date, {Hour1, Min1, Sec1}} = LocalTime,
144 LocalTime2 = {Date, {Hour1 + 1, Min1, Sec1}},
145 case calendar:local_time_to_universal_time_dst(LocalTime2) of
146 [Gmt] -> Gmt;
147 [_,Gmt] -> Gmt
148 end;
149 [Gmt] -> Gmt;
150 [_,Gmt] -> Gmt
151 end,
152 DayNumber = calendar:day_of_the_week({YYYY,MM,DD}),
153 lists:flatten(
154 io_lib:format("~s, ~2.2.0w-~3.s-~4.4.0w ~2.2.0w:~2.2.0w:~2.2.0w GMT",
155 [httpd_util:day(DayNumber),DD,httpd_util:month(MM),YYYY,Hour,Min,Sec])).
156
157 add_seconds(Secs, LocalTime) ->
158 Greg = calendar:datetime_to_gregorian_seconds(LocalTime),
159 calendar:gregorian_seconds_to_datetime(Greg + Secs).
160
161 age_to_cookie_date(Age, LocalTime) ->
162 rfc2109_cookie_expires_date(add_seconds(Age, LocalTime)).
163
164 %% @spec parse_cookie(string()) -> [{K::string(), V::string()}]
165 %% @doc Parse the contents of a Cookie header field, ignoring cookie
166 %% attributes, and return a simple property list.
167 parse_cookie("") ->
168 [];
169 parse_cookie(Cookie) ->
170 parse_cookie(Cookie, []).
171
172 %% Internal API
173
174 parse_cookie([], Acc) ->
175 lists:reverse(Acc);
176 parse_cookie(String, Acc) ->
177 {{Token, Value}, Rest} = read_pair(String),
178 Acc1 = case Token of
179 "" ->
180 Acc;
181 "$" ++ _ ->
182 Acc;
183 _ ->
184 [{Token, Value} | Acc]
185 end,
186 parse_cookie(Rest, Acc1).
187
188 read_pair(String) ->
189 {Token, Rest} = read_token(skip_whitespace(String)),
190 {Value, Rest1} = read_value(skip_whitespace(Rest)),
191 {{Token, Value}, skip_past_separator(Rest1)}.
192
193 read_value([$= | Value]) ->
194 Value1 = skip_whitespace(Value),
195 case Value1 of
196 [?QUOTE | _] ->
197 read_quoted(Value1);
198 _ ->
199 read_token(Value1)
200 end;
201 read_value(String) ->
202 {"", String}.
203
204 read_quoted([?QUOTE | String]) ->
205 read_quoted(String, []).
206
207 read_quoted([], Acc) ->
208 {lists:reverse(Acc), []};
209 read_quoted([?QUOTE | Rest], Acc) ->
210 {lists:reverse(Acc), Rest};
211 read_quoted([$\\, Any | Rest], Acc) ->
212 read_quoted(Rest, [Any | Acc]);
213 read_quoted([C | Rest], Acc) ->
214 read_quoted(Rest, [C | Acc]).
215
216 skip_whitespace(String) ->
217 F = fun (C) -> ?IS_WHITESPACE(C) end,
218 lists:dropwhile(F, String).
219
220 read_token(String) ->
221 F = fun (C) -> not ?IS_SEPARATOR(C) end,
222 lists:splitwith(F, String).
223
224 skip_past_separator([]) ->
225 [];
226 skip_past_separator([$; | Rest]) ->
227 Rest;
228 skip_past_separator([$, | Rest]) ->
229 Rest;
230 skip_past_separator([_ | Rest]) ->
231 skip_past_separator(Rest).
232
233 any_to_list(V) when is_list(V) ->
234 V;
235 any_to_list(V) when is_atom(V) ->
236 atom_to_list(V);
237 any_to_list(V) when is_binary(V) ->
238 binary_to_list(V);
239 any_to_list(V) when is_integer(V) ->
240 integer_to_list(V).
241
242 %%
243 %% Tests
244 %%
245 -ifdef(TEST).
246 -include_lib("eunit/include/eunit.hrl").
247
248 quote_test() ->
249 %% ?assertError eunit macro is not compatible with coverage module
250 try quote(":wq")
251 catch error:{cookie_quoting_required, ":wq"} -> ok
252 end,
253 ?assertEqual(
254 "foo",
255 quote(foo)),
256 ok.
257
258 parse_cookie_test() ->
259 %% RFC example
260 C1 = "$Version=\"1\"; Customer=\"WILE_E_COYOTE\"; $Path=\"/acme\";
261 Part_Number=\"Rocket_Launcher_0001\"; $Path=\"/acme\";
262 Shipping=\"FedEx\"; $Path=\"/acme\"",
263 ?assertEqual(
264 [{"Customer","WILE_E_COYOTE"},
265 {"Part_Number","Rocket_Launcher_0001"},
266 {"Shipping","FedEx"}],
267 parse_cookie(C1)),
268 %% Potential edge cases
269 ?assertEqual(
270 [{"foo", "x"}],
271 parse_cookie("foo=\"\\x\"")),
272 ?assertEqual(
273 [],
274 parse_cookie("=")),
275 ?assertEqual(
276 [{"foo", ""}, {"bar", ""}],
277 parse_cookie(" foo ; bar ")),
278 ?assertEqual(
279 [{"foo", ""}, {"bar", ""}],
280 parse_cookie("foo=;bar=")),
281 ?assertEqual(
282 [{"foo", "\";"}, {"bar", ""}],
283 parse_cookie("foo = \"\\\";\";bar ")),
284 ?assertEqual(
285 [{"foo", "\";bar"}],
286 parse_cookie("foo=\"\\\";bar")),
287 ?assertEqual(
288 [],
289 parse_cookie([])),
290 ?assertEqual(
291 [{"foo", "bar"}, {"baz", "wibble"}],
292 parse_cookie("foo=bar , baz=wibble ")),
293 ok.
294
295 domain_test() ->
296 ?assertEqual(
297 {"Set-Cookie",
298 "Customer=WILE_E_COYOTE; "
299 "Version=1; "
300 "Domain=acme.com; "
301 "HttpOnly"},
302 cookie("Customer", "WILE_E_COYOTE",
303 [{http_only, true}, {domain, "acme.com"}])),
304 ok.
305
306 local_time_test() ->
307 {"Set-Cookie", S} = cookie("Customer", "WILE_E_COYOTE",
308 [{max_age, 111}, {secure, true}]),
309 ?assertMatch(
310 ["Customer=WILE_E_COYOTE",
311 " Version=1",
312 " Expires=" ++ _,
313 " Max-Age=111",
314 " Secure"],
315 string:tokens(S, ";")),
316 ok.
317
318 cookie_test() ->
319 C1 = {"Set-Cookie",
320 "Customer=WILE_E_COYOTE; "
321 "Version=1; "
322 "Path=/acme"},
323 C1 = cookie("Customer", "WILE_E_COYOTE", [{path, "/acme"}]),
324 C1 = cookie("Customer", "WILE_E_COYOTE",
325 [{path, "/acme"}, {badoption, "negatory"}]),
326 C1 = cookie('Customer', 'WILE_E_COYOTE', [{path, '/acme'}]),
327 C1 = cookie(<<"Customer">>, <<"WILE_E_COYOTE">>, [{path, <<"/acme">>}]),
328
329 {"Set-Cookie","=NoKey; Version=1"} = cookie("", "NoKey", []),
330 {"Set-Cookie","=NoKey; Version=1"} = cookie("", "NoKey"),
331 LocalTime = calendar:universal_time_to_local_time({{2007, 5, 15}, {13, 45, 33}}),
332 C2 = {"Set-Cookie",
333 "Customer=WILE_E_COYOTE; "
334 "Version=1; "
335 "Expires=Tue, 15-May-2007 13:45:33 GMT; "
336 "Max-Age=0"},
337 C2 = cookie("Customer", "WILE_E_COYOTE",
338 [{max_age, -111}, {local_time, LocalTime}]),
339 C3 = {"Set-Cookie",
340 "Customer=WILE_E_COYOTE; "
341 "Version=1; "
342 "Expires=Wed, 16-May-2007 13:45:50 GMT; "
343 "Max-Age=86417"},
344 C3 = cookie("Customer", "WILE_E_COYOTE",
345 [{max_age, 86417}, {local_time, LocalTime}]),
346 ok.
347
348 -endif.
+0
-93
deps/mochiweb/src/mochiweb_cover.erl less more
0 %% @author Bob Ippolito <bob@mochimedia.com>
1 %% @copyright 2010 Mochi Media, Inc.
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 %% @doc Workarounds for various cover deficiencies.
22 -module(mochiweb_cover).
23 -export([get_beam/1, get_abstract_code/1,
24 get_clauses/2, clause_lookup_table/1]).
25 -export([clause_lookup_table/2]).
26
27 %% Internal
28
29 get_beam(Module) ->
30 {Module, Beam, _Path} = code:get_object_code(Module),
31 Beam.
32
33 get_abstract_code(Beam) ->
34 {ok, {_Module,
35 [{abstract_code,
36 {raw_abstract_v1, L}}]}} = beam_lib:chunks(Beam, [abstract_code]),
37 L.
38
39 get_clauses(Function, Code) ->
40 [L] = [Clauses || {function, _, FName, _, Clauses}
41 <- Code, FName =:= Function],
42 L.
43
44 clause_lookup_table(Module, Function) ->
45 clause_lookup_table(
46 get_clauses(Function,
47 get_abstract_code(get_beam(Module)))).
48
49 clause_lookup_table(Clauses) ->
50 lists:foldr(fun clause_fold/2, [], Clauses).
51
52 clause_fold({clause, _,
53 [InTerm],
54 _Guards=[],
55 [OutTerm]},
56 Acc) ->
57 try [{erl_parse:normalise(InTerm), erl_parse:normalise(OutTerm)} | Acc]
58 catch error:_ -> Acc
59 end;
60 clause_fold(_, Acc) ->
61 Acc.
62
63 %%
64 %% Tests
65 %%
66 -ifdef(TEST).
67 -include_lib("eunit/include/eunit.hrl").
68 foo_table(a) -> b;
69 foo_table("a") -> <<"b">>;
70 foo_table(123) -> {4, 3, 2};
71 foo_table([list]) -> [];
72 foo_table([list1, list2]) -> [list1, list2, list3];
73 foo_table(ignored) -> some, code, ignored;
74 foo_table(Var) -> Var.
75
76 foo_table_test() ->
77 T = clause_lookup_table(?MODULE, foo_table),
78 [?assertEqual(V, foo_table(K)) || {K, V} <- T].
79
80 clause_lookup_table_test() ->
81 ?assertEqual(b, foo_table(a)),
82 ?assertEqual(ignored, foo_table(ignored)),
83 ?assertEqual('Var', foo_table('Var')),
84 ?assertEqual(
85 [{a, b},
86 {"a", <<"b">>},
87 {123, {4, 3, 2}},
88 {[list], []},
89 {[list1, list2], [list1, list2, list3]}],
90 clause_lookup_table(?MODULE, foo_table)).
91
92 -endif.
+0
-59
deps/mochiweb/src/mochiweb_echo.erl less more
0 %% @author Bob Ippolito <bob@mochimedia.com>
1 %% @copyright 2007 Mochi Media, Inc.
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 %% @doc Simple and stupid echo server to demo mochiweb_socket_server.
22
23 -module(mochiweb_echo).
24 -author('bob@mochimedia.com').
25 -export([start/0, stop/0, loop/1]).
26
27 stop() ->
28 mochiweb_socket_server:stop(?MODULE).
29
30 start() ->
31 mochiweb_socket_server:start([{link, false} | options()]).
32
33 options() ->
34 [{name, ?MODULE},
35 {port, 6789},
36 {ip, "127.0.0.1"},
37 {max, 1},
38 {loop, {?MODULE, loop}}].
39
40 loop(Socket) ->
41 case mochiweb_socket:recv(Socket, 0, 30000) of
42 {ok, Data} ->
43 case mochiweb_socket:send(Socket, Data) of
44 ok ->
45 loop(Socket);
46 _ ->
47 exit(normal)
48 end;
49 _Other ->
50 exit(normal)
51 end.
52
53 %%
54 %% Tests
55 %%
56 -ifdef(TEST).
57 -include_lib("eunit/include/eunit.hrl").
58 -endif.
+0
-438
deps/mochiweb/src/mochiweb_headers.erl less more
0 %% @author Bob Ippolito <bob@mochimedia.com>
1 %% @copyright 2007 Mochi Media, Inc.
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 %% @doc Case preserving (but case insensitive) HTTP Header dictionary.
22
23 -module(mochiweb_headers).
24 -author('bob@mochimedia.com').
25 -export([empty/0, from_list/1, insert/3, enter/3, get_value/2, lookup/2]).
26 -export([delete_any/2, get_primary_value/2, get_combined_value/2]).
27 -export([default/3, enter_from_list/2, default_from_list/2]).
28 -export([to_list/1, make/1]).
29 -export([from_binary/1]).
30
31 %% @type headers().
32 %% @type key() = atom() | binary() | string().
33 %% @type value() = atom() | binary() | string() | integer().
34
35 %% @spec empty() -> headers()
36 %% @doc Create an empty headers structure.
37 empty() ->
38 gb_trees:empty().
39
40 %% @spec make(headers() | [{key(), value()}]) -> headers()
41 %% @doc Construct a headers() from the given list.
42 make(L) when is_list(L) ->
43 from_list(L);
44 %% assume a non-list is already mochiweb_headers.
45 make(T) ->
46 T.
47
48 %% @spec from_binary(iolist()) -> headers()
49 %% @doc Transforms a raw HTTP header into a mochiweb headers structure.
50 %%
51 %% The given raw HTTP header can be one of the following:
52 %%
53 %% 1) A string or a binary representing a full HTTP header ending with
54 %% double CRLF.
55 %% Examples:
56 %% ```
57 %% "Content-Length: 47\r\nContent-Type: text/plain\r\n\r\n"
58 %% <<"Content-Length: 47\r\nContent-Type: text/plain\r\n\r\n">>'''
59 %%
60 %% 2) A list of binaries or strings where each element represents a raw
61 %% HTTP header line ending with a single CRLF.
62 %% Examples:
63 %% ```
64 %% [<<"Content-Length: 47\r\n">>, <<"Content-Type: text/plain\r\n">>]
65 %% ["Content-Length: 47\r\n", "Content-Type: text/plain\r\n"]
66 %% ["Content-Length: 47\r\n", <<"Content-Type: text/plain\r\n">>]'''
67 %%
68 from_binary(RawHttpHeader) when is_binary(RawHttpHeader) ->
69 from_binary(RawHttpHeader, []);
70 from_binary(RawHttpHeaderList) ->
71 from_binary(list_to_binary([RawHttpHeaderList, "\r\n"])).
72
73 from_binary(RawHttpHeader, Acc) ->
74 case erlang:decode_packet(httph, RawHttpHeader, []) of
75 {ok, {http_header, _, H, _, V}, Rest} ->
76 from_binary(Rest, [{H, V} | Acc]);
77 _ ->
78 make(Acc)
79 end.
80
81 %% @spec from_list([{key(), value()}]) -> headers()
82 %% @doc Construct a headers() from the given list.
83 from_list(List) ->
84 lists:foldl(fun ({K, V}, T) -> insert(K, V, T) end, empty(), List).
85
86 %% @spec enter_from_list([{key(), value()}], headers()) -> headers()
87 %% @doc Insert pairs into the headers, replace any values for existing keys.
88 enter_from_list(List, T) ->
89 lists:foldl(fun ({K, V}, T1) -> enter(K, V, T1) end, T, List).
90
91 %% @spec default_from_list([{key(), value()}], headers()) -> headers()
92 %% @doc Insert pairs into the headers for keys that do not already exist.
93 default_from_list(List, T) ->
94 lists:foldl(fun ({K, V}, T1) -> default(K, V, T1) end, T, List).
95
96 %% @spec to_list(headers()) -> [{key(), string()}]
97 %% @doc Return the contents of the headers. The keys will be the exact key
98 %% that was first inserted (e.g. may be an atom or binary, case is
99 %% preserved).
100 to_list(T) ->
101 F = fun ({K, {array, L}}, Acc) ->
102 L1 = lists:reverse(L),
103 lists:foldl(fun (V, Acc1) -> [{K, V} | Acc1] end, Acc, L1);
104 (Pair, Acc) ->
105 [Pair | Acc]
106 end,
107 lists:reverse(lists:foldl(F, [], gb_trees:values(T))).
108
109 %% @spec get_value(key(), headers()) -> string() | undefined
110 %% @doc Return the value of the given header using a case insensitive search.
111 %% undefined will be returned for keys that are not present.
112 get_value(K, T) ->
113 case lookup(K, T) of
114 {value, {_, V}} ->
115 expand(V);
116 none ->
117 undefined
118 end.
119
120 %% @spec get_primary_value(key(), headers()) -> string() | undefined
121 %% @doc Return the value of the given header up to the first semicolon using
122 %% a case insensitive search. undefined will be returned for keys
123 %% that are not present.
124 get_primary_value(K, T) ->
125 case get_value(K, T) of
126 undefined ->
127 undefined;
128 V ->
129 lists:takewhile(fun (C) -> C =/= $; end, V)
130 end.
131
132 %% @spec get_combined_value(key(), headers()) -> string() | undefined
133 %% @doc Return the value from the given header using a case insensitive search.
134 %% If the value of the header is a comma-separated list where holds values
135 %% are all identical, the identical value will be returned.
136 %% undefined will be returned for keys that are not present or the
137 %% values in the list are not the same.
138 %%
139 %% NOTE: The process isn't designed for a general purpose. If you need
140 %% to access all values in the combined header, please refer to
141 %% '''tokenize_header_value/1'''.
142 %%
143 %% Section 4.2 of the RFC 2616 (HTTP 1.1) describes multiple message-header
144 %% fields with the same field-name may be present in a message if and only
145 %% if the entire field-value for that header field is defined as a
146 %% comma-separated list [i.e., #(values)].
147 get_combined_value(K, T) ->
148 case get_value(K, T) of
149 undefined ->
150 undefined;
151 V ->
152 case sets:to_list(sets:from_list(tokenize_header_value(V))) of
153 [Val] ->
154 Val;
155 _ ->
156 undefined
157 end
158 end.
159
160 %% @spec lookup(key(), headers()) -> {value, {key(), string()}} | none
161 %% @doc Return the case preserved key and value for the given header using
162 %% a case insensitive search. none will be returned for keys that are
163 %% not present.
164 lookup(K, T) ->
165 case gb_trees:lookup(normalize(K), T) of
166 {value, {K0, V}} ->
167 {value, {K0, expand(V)}};
168 none ->
169 none
170 end.
171
172 %% @spec default(key(), value(), headers()) -> headers()
173 %% @doc Insert the pair into the headers if it does not already exist.
174 default(K, V, T) ->
175 K1 = normalize(K),
176 V1 = any_to_list(V),
177 try gb_trees:insert(K1, {K, V1}, T)
178 catch
179 error:{key_exists, _} ->
180 T
181 end.
182
183 %% @spec enter(key(), value(), headers()) -> headers()
184 %% @doc Insert the pair into the headers, replacing any pre-existing key.
185 enter(K, V, T) ->
186 K1 = normalize(K),
187 V1 = any_to_list(V),
188 gb_trees:enter(K1, {K, V1}, T).
189
190 %% @spec insert(key(), value(), headers()) -> headers()
191 %% @doc Insert the pair into the headers, merging with any pre-existing key.
192 %% A merge is done with Value = V0 ++ ", " ++ V1.
193 insert(K, V, T) ->
194 K1 = normalize(K),
195 V1 = any_to_list(V),
196 try gb_trees:insert(K1, {K, V1}, T)
197 catch
198 error:{key_exists, _} ->
199 {K0, V0} = gb_trees:get(K1, T),
200 V2 = merge(K1, V1, V0),
201 gb_trees:update(K1, {K0, V2}, T)
202 end.
203
204 %% @spec delete_any(key(), headers()) -> headers()
205 %% @doc Delete the header corresponding to key if it is present.
206 delete_any(K, T) ->
207 K1 = normalize(K),
208 gb_trees:delete_any(K1, T).
209
210 %% Internal API
211
212 tokenize_header_value(undefined) ->
213 undefined;
214 tokenize_header_value(V) ->
215 reversed_tokens(trim_and_reverse(V, false), [], []).
216
217 trim_and_reverse([S | Rest], Reversed) when S=:=$ ; S=:=$\n; S=:=$\t ->
218 trim_and_reverse(Rest, Reversed);
219 trim_and_reverse(V, false) ->
220 trim_and_reverse(lists:reverse(V), true);
221 trim_and_reverse(V, true) ->
222 V.
223
224 reversed_tokens([], [], Acc) ->
225 Acc;
226 reversed_tokens([], Token, Acc) ->
227 [Token | Acc];
228 reversed_tokens("\"" ++ Rest, [], Acc) ->
229 case extract_quoted_string(Rest, []) of
230 {String, NewRest} ->
231 reversed_tokens(NewRest, [], [String | Acc]);
232 undefined ->
233 undefined
234 end;
235 reversed_tokens("\"" ++ _Rest, _Token, _Acc) ->
236 undefined;
237 reversed_tokens([C | Rest], [], Acc) when C=:=$ ;C=:=$\n;C=:=$\t;C=:=$, ->
238 reversed_tokens(Rest, [], Acc);
239 reversed_tokens([C | Rest], Token, Acc) when C=:=$ ;C=:=$\n;C=:=$\t;C=:=$, ->
240 reversed_tokens(Rest, [], [Token | Acc]);
241 reversed_tokens([C | Rest], Token, Acc) ->
242 reversed_tokens(Rest, [C | Token], Acc);
243 reversed_tokens(_, _, _) ->
244 undefeined.
245
246 extract_quoted_string([], _Acc) ->
247 undefined;
248 extract_quoted_string("\"\\" ++ Rest, Acc) ->
249 extract_quoted_string(Rest, "\"" ++ Acc);
250 extract_quoted_string("\"" ++ Rest, Acc) ->
251 {Acc, Rest};
252 extract_quoted_string([C | Rest], Acc) ->
253 extract_quoted_string(Rest, [C | Acc]).
254
255 expand({array, L}) ->
256 mochiweb_util:join(lists:reverse(L), ", ");
257 expand(V) ->
258 V.
259
260 merge("set-cookie", V1, {array, L}) ->
261 {array, [V1 | L]};
262 merge("set-cookie", V1, V0) ->
263 {array, [V1, V0]};
264 merge(_, V1, V0) ->
265 V0 ++ ", " ++ V1.
266
267 normalize(K) when is_list(K) ->
268 string:to_lower(K);
269 normalize(K) when is_atom(K) ->
270 normalize(atom_to_list(K));
271 normalize(K) when is_binary(K) ->
272 normalize(binary_to_list(K)).
273
274 any_to_list(V) when is_list(V) ->
275 V;
276 any_to_list(V) when is_atom(V) ->
277 atom_to_list(V);
278 any_to_list(V) when is_binary(V) ->
279 binary_to_list(V);
280 any_to_list(V) when is_integer(V) ->
281 integer_to_list(V).
282
283 %%
284 %% Tests.
285 %%
286 -ifdef(TEST).
287 -include_lib("eunit/include/eunit.hrl").
288
289 make_test() ->
290 Identity = make([{hdr, foo}]),
291 ?assertEqual(
292 Identity,
293 make(Identity)).
294
295 enter_from_list_test() ->
296 H = make([{hdr, foo}]),
297 ?assertEqual(
298 [{baz, "wibble"}, {hdr, "foo"}],
299 to_list(enter_from_list([{baz, wibble}], H))),
300 ?assertEqual(
301 [{hdr, "bar"}],
302 to_list(enter_from_list([{hdr, bar}], H))),
303 ok.
304
305 default_from_list_test() ->
306 H = make([{hdr, foo}]),
307 ?assertEqual(
308 [{baz, "wibble"}, {hdr, "foo"}],
309 to_list(default_from_list([{baz, wibble}], H))),
310 ?assertEqual(
311 [{hdr, "foo"}],
312 to_list(default_from_list([{hdr, bar}], H))),
313 ok.
314
315 get_primary_value_test() ->
316 H = make([{hdr, foo}, {baz, <<"wibble;taco">>}]),
317 ?assertEqual(
318 "foo",
319 get_primary_value(hdr, H)),
320 ?assertEqual(
321 undefined,
322 get_primary_value(bar, H)),
323 ?assertEqual(
324 "wibble",
325 get_primary_value(<<"baz">>, H)),
326 ok.
327
328 get_combined_value_test() ->
329 H = make([{hdr, foo}, {baz, <<"wibble,taco">>}, {content_length, "123, 123"},
330 {test, " 123, 123, 123 , 123,123 "},
331 {test2, "456, 123, 123 , 123"},
332 {test3, "123"}, {test4, " 123, "}]),
333 ?assertEqual(
334 "foo",
335 get_combined_value(hdr, H)),
336 ?assertEqual(
337 undefined,
338 get_combined_value(bar, H)),
339 ?assertEqual(
340 undefined,
341 get_combined_value(<<"baz">>, H)),
342 ?assertEqual(
343 "123",
344 get_combined_value(<<"content_length">>, H)),
345 ?assertEqual(
346 "123",
347 get_combined_value(<<"test">>, H)),
348 ?assertEqual(
349 undefined,
350 get_combined_value(<<"test2">>, H)),
351 ?assertEqual(
352 "123",
353 get_combined_value(<<"test3">>, H)),
354 ?assertEqual(
355 "123",
356 get_combined_value(<<"test4">>, H)),
357 ok.
358
359 set_cookie_test() ->
360 H = make([{"set-cookie", foo}, {"set-cookie", bar}, {"set-cookie", baz}]),
361 ?assertEqual(
362 [{"set-cookie", "foo"}, {"set-cookie", "bar"}, {"set-cookie", "baz"}],
363 to_list(H)),
364 ok.
365
366 headers_test() ->
367 H = ?MODULE:make([{hdr, foo}, {"Hdr", "bar"}, {'Hdr', 2}]),
368 [{hdr, "foo, bar, 2"}] = ?MODULE:to_list(H),
369 H1 = ?MODULE:insert(taco, grande, H),
370 [{hdr, "foo, bar, 2"}, {taco, "grande"}] = ?MODULE:to_list(H1),
371 H2 = ?MODULE:make([{"Set-Cookie", "foo"}]),
372 [{"Set-Cookie", "foo"}] = ?MODULE:to_list(H2),
373 H3 = ?MODULE:insert("Set-Cookie", "bar", H2),
374 [{"Set-Cookie", "foo"}, {"Set-Cookie", "bar"}] = ?MODULE:to_list(H3),
375 "foo, bar" = ?MODULE:get_value("set-cookie", H3),
376 {value, {"Set-Cookie", "foo, bar"}} = ?MODULE:lookup("set-cookie", H3),
377 undefined = ?MODULE:get_value("shibby", H3),
378 none = ?MODULE:lookup("shibby", H3),
379 H4 = ?MODULE:insert("content-type",
380 "application/x-www-form-urlencoded; charset=utf8",
381 H3),
382 "application/x-www-form-urlencoded" = ?MODULE:get_primary_value(
383 "content-type", H4),
384 H4 = ?MODULE:delete_any("nonexistent-header", H4),
385 H3 = ?MODULE:delete_any("content-type", H4),
386 HB = <<"Content-Length: 47\r\nContent-Type: text/plain\r\n\r\n">>,
387 H_HB = ?MODULE:from_binary(HB),
388 H_HB = ?MODULE:from_binary(binary_to_list(HB)),
389 "47" = ?MODULE:get_value("Content-Length", H_HB),
390 "text/plain" = ?MODULE:get_value("Content-Type", H_HB),
391 L_H_HB = ?MODULE:to_list(H_HB),
392 2 = length(L_H_HB),
393 true = lists:member({'Content-Length', "47"}, L_H_HB),
394 true = lists:member({'Content-Type', "text/plain"}, L_H_HB),
395 HL = [ <<"Content-Length: 47\r\n">>, <<"Content-Type: text/plain\r\n">> ],
396 HL2 = [ "Content-Length: 47\r\n", <<"Content-Type: text/plain\r\n">> ],
397 HL3 = [ <<"Content-Length: 47\r\n">>, "Content-Type: text/plain\r\n" ],
398 H_HL = ?MODULE:from_binary(HL),
399 H_HL = ?MODULE:from_binary(HL2),
400 H_HL = ?MODULE:from_binary(HL3),
401 "47" = ?MODULE:get_value("Content-Length", H_HL),
402 "text/plain" = ?MODULE:get_value("Content-Type", H_HL),
403 L_H_HL = ?MODULE:to_list(H_HL),
404 2 = length(L_H_HL),
405 true = lists:member({'Content-Length', "47"}, L_H_HL),
406 true = lists:member({'Content-Type', "text/plain"}, L_H_HL),
407 [] = ?MODULE:to_list(?MODULE:from_binary(<<>>)),
408 [] = ?MODULE:to_list(?MODULE:from_binary(<<"">>)),
409 [] = ?MODULE:to_list(?MODULE:from_binary(<<"\r\n">>)),
410 [] = ?MODULE:to_list(?MODULE:from_binary(<<"\r\n\r\n">>)),
411 [] = ?MODULE:to_list(?MODULE:from_binary("")),
412 [] = ?MODULE:to_list(?MODULE:from_binary([<<>>])),
413 [] = ?MODULE:to_list(?MODULE:from_binary([<<"">>])),
414 [] = ?MODULE:to_list(?MODULE:from_binary([<<"\r\n">>])),
415 [] = ?MODULE:to_list(?MODULE:from_binary([<<"\r\n\r\n">>])),
416 ok.
417
418 tokenize_header_value_test() ->
419 ?assertEqual(["a quote in a \"quote\"."],
420 tokenize_header_value("\"a quote in a \\\"quote\\\".\"")),
421 ?assertEqual(["abc"], tokenize_header_value("abc")),
422 ?assertEqual(["abc", "def"], tokenize_header_value("abc def")),
423 ?assertEqual(["abc", "def"], tokenize_header_value("abc , def")),
424 ?assertEqual(["abc", "def"], tokenize_header_value(",abc ,, def,,")),
425 ?assertEqual(["abc def"], tokenize_header_value("\"abc def\" ")),
426 ?assertEqual(["abc, def"], tokenize_header_value("\"abc, def\"")),
427 ?assertEqual(["\\a\\$"], tokenize_header_value("\"\\a\\$\"")),
428 ?assertEqual(["abc def", "foo, bar", "12345", ""],
429 tokenize_header_value("\"abc def\" \"foo, bar\" , 12345, \"\"")),
430 ?assertEqual(undefined,
431 tokenize_header_value(undefined)),
432 ?assertEqual(undefined,
433 tokenize_header_value("umatched quote\"")),
434 ?assertEqual(undefined,
435 tokenize_header_value("\"unmatched quote")).
436
437 -endif.
+0
-815
deps/mochiweb/src/mochiweb_html.erl less more
0 %% @author Bob Ippolito <bob@mochimedia.com>
1 %% @copyright 2007 Mochi Media, Inc.
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 %% @doc Loosely tokenizes and generates parse trees for HTML 4.
22 -module(mochiweb_html).
23 -export([tokens/1, parse/1, parse_tokens/1, to_tokens/1, escape/1,
24 escape_attr/1, to_html/1]).
25 -compile([export_all]).
26 -ifdef(TEST).
27 -export([destack/1, destack/2, is_singleton/1]).
28 -endif.
29
30 %% This is a macro to placate syntax highlighters..
31 -define(QUOTE, $\"). %% $\"
32 -define(SQUOTE, $\'). %% $\'
33 -define(ADV_COL(S, N),
34 S#decoder{column=N+S#decoder.column,
35 offset=N+S#decoder.offset}).
36 -define(INC_COL(S),
37 S#decoder{column=1+S#decoder.column,
38 offset=1+S#decoder.offset}).
39 -define(INC_LINE(S),
40 S#decoder{column=1,
41 line=1+S#decoder.line,
42 offset=1+S#decoder.offset}).
43 -define(INC_CHAR(S, C),
44 case C of
45 $\n ->
46 S#decoder{column=1,
47 line=1+S#decoder.line,
48 offset=1+S#decoder.offset};
49 _ ->
50 S#decoder{column=1+S#decoder.column,
51 offset=1+S#decoder.offset}
52 end).
53
54 -define(IS_WHITESPACE(C),
55 (C =:= $\s orelse C =:= $\t orelse C =:= $\r orelse C =:= $\n)).
56 -define(IS_LITERAL_SAFE(C),
57 ((C >= $A andalso C =< $Z) orelse (C >= $a andalso C =< $z)
58 orelse (C >= $0 andalso C =< $9))).
59 -define(PROBABLE_CLOSE(C),
60 (C =:= $> orelse ?IS_WHITESPACE(C))).
61
62 -record(decoder, {line=1,
63 column=1,
64 offset=0}).
65
66 %% @type html_node() = {string(), [html_attr()], [html_node() | string()]}
67 %% @type html_attr() = {string(), string()}
68 %% @type html_token() = html_data() | start_tag() | end_tag() | inline_html() | html_comment() | html_doctype()
69 %% @type html_data() = {data, string(), Whitespace::boolean()}
70 %% @type start_tag() = {start_tag, Name, [html_attr()], Singleton::boolean()}
71 %% @type end_tag() = {end_tag, Name}
72 %% @type html_comment() = {comment, Comment}
73 %% @type html_doctype() = {doctype, [Doctype]}
74 %% @type inline_html() = {'=', iolist()}
75
76 %% External API.
77
78 %% @spec parse(string() | binary()) -> html_node()
79 %% @doc tokenize and then transform the token stream into a HTML tree.
80 parse(Input) ->
81 parse_tokens(tokens(Input)).
82
83 %% @spec parse_tokens([html_token()]) -> html_node()
84 %% @doc Transform the output of tokens(Doc) into a HTML tree.
85 parse_tokens(Tokens) when is_list(Tokens) ->
86 %% Skip over doctype, processing instructions
87 [{start_tag, Tag, Attrs, false} | Rest] = find_document(Tokens, normal),
88 {Tree, _} = tree(Rest, [norm({Tag, Attrs})]),
89 Tree.
90
91 find_document(Tokens=[{start_tag, _Tag, _Attrs, false} | _Rest], Mode) ->
92 maybe_add_html_tag(Tokens, Mode);
93 find_document([{doctype, [<<"html">>]} | Rest], _Mode) ->
94 find_document(Rest, html5);
95 find_document([_T | Rest], Mode) ->
96 find_document(Rest, Mode);
97 find_document([], _Mode) ->
98 [].
99
100 maybe_add_html_tag(Tokens=[{start_tag, Tag, _Attrs, false} | _], html5)
101 when Tag =/= <<"html">> ->
102 [{start_tag, <<"html">>, [], false} | Tokens];
103 maybe_add_html_tag(Tokens, _Mode) ->
104 Tokens.
105
106 %% @spec tokens(StringOrBinary) -> [html_token()]
107 %% @doc Transform the input UTF-8 HTML into a token stream.
108 tokens(Input) ->
109 tokens(iolist_to_binary(Input), #decoder{}, []).
110
111 %% @spec to_tokens(html_node()) -> [html_token()]
112 %% @doc Convert a html_node() tree to a list of tokens.
113 to_tokens({Tag0}) ->
114 to_tokens({Tag0, [], []});
115 to_tokens(T={'=', _}) ->
116 [T];
117 to_tokens(T={doctype, _}) ->
118 [T];
119 to_tokens(T={comment, _}) ->
120 [T];
121 to_tokens({Tag0, Acc}) ->
122 %% This is only allowed in sub-tags: {p, [{"class", "foo"}]}
123 to_tokens({Tag0, [], Acc});
124 to_tokens({Tag0, Attrs, Acc}) ->
125 Tag = to_tag(Tag0),
126 case is_singleton(Tag) of
127 true ->
128 to_tokens([], [{start_tag, Tag, Attrs, true}]);
129 false ->
130 to_tokens([{Tag, Acc}], [{start_tag, Tag, Attrs, false}])
131 end.
132
133 %% @spec to_html([html_token()] | html_node()) -> iolist()
134 %% @doc Convert a list of html_token() to a HTML document.
135 to_html(Node) when is_tuple(Node) ->
136 to_html(to_tokens(Node));
137 to_html(Tokens) when is_list(Tokens) ->
138 to_html(Tokens, []).
139
140 %% @spec escape(string() | atom() | binary()) -> binary()
141 %% @doc Escape a string such that it's safe for HTML (amp; lt; gt;).
142 escape(B) when is_binary(B) ->
143 escape(binary_to_list(B), []);
144 escape(A) when is_atom(A) ->
145 escape(atom_to_list(A), []);
146 escape(S) when is_list(S) ->
147 escape(S, []).
148
149 %% @spec escape_attr(string() | binary() | atom() | integer() | float()) -> binary()
150 %% @doc Escape a string such that it's safe for HTML attrs
151 %% (amp; lt; gt; quot;).
152 escape_attr(B) when is_binary(B) ->
153 escape_attr(binary_to_list(B), []);
154 escape_attr(A) when is_atom(A) ->
155 escape_attr(atom_to_list(A), []);
156 escape_attr(S) when is_list(S) ->
157 escape_attr(S, []);
158 escape_attr(I) when is_integer(I) ->
159 escape_attr(integer_to_list(I), []);
160 escape_attr(F) when is_float(F) ->
161 escape_attr(mochinum:digits(F), []).
162
163 to_html([], Acc) ->
164 lists:reverse(Acc);
165 to_html([{'=', Content} | Rest], Acc) ->
166 to_html(Rest, [Content | Acc]);
167 to_html([{pi, Bin} | Rest], Acc) ->
168 Open = [<<"<?">>,
169 Bin,
170 <<"?>">>],
171 to_html(Rest, [Open | Acc]);
172 to_html([{pi, Tag, Attrs} | Rest], Acc) ->
173 Open = [<<"<?">>,
174 Tag,
175 attrs_to_html(Attrs, []),
176 <<"?>">>],
177 to_html(Rest, [Open | Acc]);
178 to_html([{comment, Comment} | Rest], Acc) ->
179 to_html(Rest, [[<<"<!--">>, Comment, <<"-->">>] | Acc]);
180 to_html([{doctype, Parts} | Rest], Acc) ->
181 Inside = doctype_to_html(Parts, Acc),
182 to_html(Rest, [[<<"<!DOCTYPE">>, Inside, <<">">>] | Acc]);
183 to_html([{data, Data, _Whitespace} | Rest], Acc) ->
184 to_html(Rest, [escape(Data) | Acc]);
185 to_html([{start_tag, Tag, Attrs, Singleton} | Rest], Acc) ->
186 Open = [<<"<">>,
187 Tag,
188 attrs_to_html(Attrs, []),
189 case Singleton of
190 true -> <<" />">>;
191 false -> <<">">>
192 end],
193 to_html(Rest, [Open | Acc]);
194 to_html([{end_tag, Tag} | Rest], Acc) ->
195 to_html(Rest, [[<<"</">>, Tag, <<">">>] | Acc]).
196
197 doctype_to_html([], Acc) ->
198 lists:reverse(Acc);
199 doctype_to_html([Word | Rest], Acc) ->
200 case lists:all(fun (C) -> ?IS_LITERAL_SAFE(C) end,
201 binary_to_list(iolist_to_binary(Word))) of
202 true ->
203 doctype_to_html(Rest, [[<<" ">>, Word] | Acc]);
204 false ->
205 doctype_to_html(Rest, [[<<" \"">>, escape_attr(Word), ?QUOTE] | Acc])
206 end.
207
208 attrs_to_html([], Acc) ->
209 lists:reverse(Acc);
210 attrs_to_html([{K, V} | Rest], Acc) ->
211 attrs_to_html(Rest,
212 [[<<" ">>, escape(K), <<"=\"">>,
213 escape_attr(V), <<"\"">>] | Acc]).
214
215 escape([], Acc) ->
216 list_to_binary(lists:reverse(Acc));
217 escape("<" ++ Rest, Acc) ->
218 escape(Rest, lists:reverse("&lt;", Acc));
219 escape(">" ++ Rest, Acc) ->
220 escape(Rest, lists:reverse("&gt;", Acc));
221 escape("&" ++ Rest, Acc) ->
222 escape(Rest, lists:reverse("&amp;", Acc));
223 escape([C | Rest], Acc) ->
224 escape(Rest, [C | Acc]).
225
226 escape_attr([], Acc) ->
227 list_to_binary(lists:reverse(Acc));
228 escape_attr("<" ++ Rest, Acc) ->
229 escape_attr(Rest, lists:reverse("&lt;", Acc));
230 escape_attr(">" ++ Rest, Acc) ->
231 escape_attr(Rest, lists:reverse("&gt;", Acc));
232 escape_attr("&" ++ Rest, Acc) ->
233 escape_attr(Rest, lists:reverse("&amp;", Acc));
234 escape_attr([?QUOTE | Rest], Acc) ->
235 escape_attr(Rest, lists:reverse("&quot;", Acc));
236 escape_attr([C | Rest], Acc) ->
237 escape_attr(Rest, [C | Acc]).
238
239 to_tag(A) when is_atom(A) ->
240 norm(atom_to_list(A));
241 to_tag(L) ->
242 norm(L).
243
244 to_tokens([], Acc) ->
245 lists:reverse(Acc);
246 to_tokens([{Tag, []} | Rest], Acc) ->
247 to_tokens(Rest, [{end_tag, to_tag(Tag)} | Acc]);
248 to_tokens([{Tag0, [{T0} | R1]} | Rest], Acc) ->
249 %% Allow {br}
250 to_tokens([{Tag0, [{T0, [], []} | R1]} | Rest], Acc);
251 to_tokens([{Tag0, [T0={'=', _C0} | R1]} | Rest], Acc) ->
252 %% Allow {'=', iolist()}
253 to_tokens([{Tag0, R1} | Rest], [T0 | Acc]);
254 to_tokens([{Tag0, [T0={comment, _C0} | R1]} | Rest], Acc) ->
255 %% Allow {comment, iolist()}
256 to_tokens([{Tag0, R1} | Rest], [T0 | Acc]);
257 to_tokens([{Tag0, [T0={pi, _S0} | R1]} | Rest], Acc) ->
258 %% Allow {pi, binary()}
259 to_tokens([{Tag0, R1} | Rest], [T0 | Acc]);
260 to_tokens([{Tag0, [T0={pi, _S0, _A0} | R1]} | Rest], Acc) ->
261 %% Allow {pi, binary(), list()}
262 to_tokens([{Tag0, R1} | Rest], [T0 | Acc]);
263 to_tokens([{Tag0, [{T0, A0=[{_, _} | _]} | R1]} | Rest], Acc) ->
264 %% Allow {p, [{"class", "foo"}]}
265 to_tokens([{Tag0, [{T0, A0, []} | R1]} | Rest], Acc);
266 to_tokens([{Tag0, [{T0, C0} | R1]} | Rest], Acc) ->
267 %% Allow {p, "content"} and {p, <<"content">>}
268 to_tokens([{Tag0, [{T0, [], C0} | R1]} | Rest], Acc);
269 to_tokens([{Tag0, [{T0, A1, C0} | R1]} | Rest], Acc) when is_binary(C0) ->
270 %% Allow {"p", [{"class", "foo"}], <<"content">>}
271 to_tokens([{Tag0, [{T0, A1, binary_to_list(C0)} | R1]} | Rest], Acc);
272 to_tokens([{Tag0, [{T0, A1, C0=[C | _]} | R1]} | Rest], Acc)
273 when is_integer(C) ->
274 %% Allow {"p", [{"class", "foo"}], "content"}
275 to_tokens([{Tag0, [{T0, A1, [C0]} | R1]} | Rest], Acc);
276 to_tokens([{Tag0, [{T0, A1, C1} | R1]} | Rest], Acc) ->
277 %% Native {"p", [{"class", "foo"}], ["content"]}
278 Tag = to_tag(Tag0),
279 T1 = to_tag(T0),
280 case is_singleton(norm(T1)) of
281 true ->
282 to_tokens([{Tag, R1} | Rest], [{start_tag, T1, A1, true} | Acc]);
283 false ->
284 to_tokens([{T1, C1}, {Tag, R1} | Rest],
285 [{start_tag, T1, A1, false} | Acc])
286 end;
287 to_tokens([{Tag0, [L | R1]} | Rest], Acc) when is_list(L) ->
288 %% List text
289 Tag = to_tag(Tag0),
290 to_tokens([{Tag, R1} | Rest], [{data, iolist_to_binary(L), false} | Acc]);
291 to_tokens([{Tag0, [B | R1]} | Rest], Acc) when is_binary(B) ->
292 %% Binary text
293 Tag = to_tag(Tag0),
294 to_tokens([{Tag, R1} | Rest], [{data, B, false} | Acc]).
295
296 tokens(B, S=#decoder{offset=O}, Acc) ->
297 case B of
298 <<_:O/binary>> ->
299 lists:reverse(Acc);
300 _ ->
301 {Tag, S1} = tokenize(B, S),
302 case parse_flag(Tag) of
303 script ->
304 {Tag2, S2} = tokenize_script(B, S1),
305 tokens(B, S2, [Tag2, Tag | Acc]);
306 textarea ->
307 {Tag2, S2} = tokenize_textarea(B, S1),
308 tokens(B, S2, [Tag2, Tag | Acc]);
309 none ->
310 tokens(B, S1, [Tag | Acc])
311 end
312 end.
313
314 parse_flag({start_tag, B, _, false}) ->
315 case string:to_lower(binary_to_list(B)) of
316 "script" ->
317 script;
318 "textarea" ->
319 textarea;
320 _ ->
321 none
322 end;
323 parse_flag(_) ->
324 none.
325
326 tokenize(B, S=#decoder{offset=O}) ->
327 case B of
328 <<_:O/binary, "<!--", _/binary>> ->
329 tokenize_comment(B, ?ADV_COL(S, 4));
330 <<_:O/binary, "<!doctype", _/binary>> ->
331 tokenize_doctype(B, ?ADV_COL(S, 10));
332 <<_:O/binary, "<!DOCTYPE", _/binary>> ->
333 tokenize_doctype(B, ?ADV_COL(S, 10));
334 <<_:O/binary, "<![CDATA[", _/binary>> ->
335 tokenize_cdata(B, ?ADV_COL(S, 9));
336 <<_:O/binary, "<?php", _/binary>> ->
337 {Body, S1} = raw_qgt(B, ?ADV_COL(S, 2)),
338 {{pi, Body}, S1};
339 <<_:O/binary, "<?", _/binary>> ->
340 {Tag, S1} = tokenize_literal(B, ?ADV_COL(S, 2)),
341 {Attrs, S2} = tokenize_attributes(B, S1),
342 S3 = find_qgt(B, S2),
343 {{pi, Tag, Attrs}, S3};
344 <<_:O/binary, "&", _/binary>> ->
345 tokenize_charref(B, ?INC_COL(S));
346 <<_:O/binary, "</", _/binary>> ->
347 {Tag, S1} = tokenize_literal(B, ?ADV_COL(S, 2)),
348 {S2, _} = find_gt(B, S1),
349 {{end_tag, Tag}, S2};
350 <<_:O/binary, "<", C, _/binary>>
351 when ?IS_WHITESPACE(C); not ?IS_LITERAL_SAFE(C) ->
352 %% This isn't really strict HTML
353 {{data, Data, _Whitespace}, S1} = tokenize_data(B, ?INC_COL(S)),
354 {{data, <<$<, Data/binary>>, false}, S1};
355 <<_:O/binary, "<", _/binary>> ->
356 {Tag, S1} = tokenize_literal(B, ?INC_COL(S)),
357 {Attrs, S2} = tokenize_attributes(B, S1),
358 {S3, HasSlash} = find_gt(B, S2),
359 Singleton = HasSlash orelse is_singleton(Tag),
360 {{start_tag, Tag, Attrs, Singleton}, S3};
361 _ ->
362 tokenize_data(B, S)
363 end.
364
365 tree_data([{data, Data, Whitespace} | Rest], AllWhitespace, Acc) ->
366 tree_data(Rest, (Whitespace andalso AllWhitespace), [Data | Acc]);
367 tree_data(Rest, AllWhitespace, Acc) ->
368 {iolist_to_binary(lists:reverse(Acc)), AllWhitespace, Rest}.
369
370 tree([], Stack) ->
371 {destack(Stack), []};
372 tree([{end_tag, Tag} | Rest], Stack) ->
373 case destack(norm(Tag), Stack) of
374 S when is_list(S) ->
375 tree(Rest, S);
376 Result ->
377 {Result, []}
378 end;
379 tree([{start_tag, Tag, Attrs, true} | Rest], S) ->
380 tree(Rest, append_stack_child(norm({Tag, Attrs}), S));
381 tree([{start_tag, Tag, Attrs, false} | Rest], S) ->
382 tree(Rest, stack(norm({Tag, Attrs}), S));
383 tree([T={pi, _Raw} | Rest], S) ->
384 tree(Rest, append_stack_child(T, S));
385 tree([T={pi, _Tag, _Attrs} | Rest], S) ->
386 tree(Rest, append_stack_child(T, S));
387 tree([T={comment, _Comment} | Rest], S) ->
388 tree(Rest, append_stack_child(T, S));
389 tree(L=[{data, _Data, _Whitespace} | _], S) ->
390 case tree_data(L, true, []) of
391 {_, true, Rest} ->
392 tree(Rest, S);
393 {Data, false, Rest} ->
394 tree(Rest, append_stack_child(Data, S))
395 end;
396 tree([{doctype, _} | Rest], Stack) ->
397 tree(Rest, Stack).
398
399 norm({Tag, Attrs}) ->
400 {norm(Tag), [{norm(K), iolist_to_binary(V)} || {K, V} <- Attrs], []};
401 norm(Tag) when is_binary(Tag) ->
402 Tag;
403 norm(Tag) ->
404 list_to_binary(string:to_lower(Tag)).
405
406 stack(T1={TN, _, _}, Stack=[{TN, _, _} | _Rest])
407 when TN =:= <<"li">> orelse TN =:= <<"option">> ->
408 [T1 | destack(TN, Stack)];
409 stack(T1={TN0, _, _}, Stack=[{TN1, _, _} | _Rest])
410 when (TN0 =:= <<"dd">> orelse TN0 =:= <<"dt">>) andalso
411 (TN1 =:= <<"dd">> orelse TN1 =:= <<"dt">>) ->
412 [T1 | destack(TN1, Stack)];
413 stack(T1, Stack) ->
414 [T1 | Stack].
415
416 append_stack_child(StartTag, [{Name, Attrs, Acc} | Stack]) ->
417 [{Name, Attrs, [StartTag | Acc]} | Stack].
418
419 destack(<<"br">>, Stack) ->
420 %% This is an ugly hack to make dumb_br_test() pass,
421 %% this makes it such that br can never have children.
422 Stack;
423 destack(TagName, Stack) when is_list(Stack) ->
424 F = fun (X) ->
425 case X of
426 {TagName, _, _} ->
427 false;
428 _ ->
429 true
430 end
431 end,
432 case lists:splitwith(F, Stack) of
433 {_, []} ->
434 %% If we're parsing something like XML we might find
435 %% a <link>tag</link> that is normally a singleton
436 %% in HTML but isn't here
437 case {is_singleton(TagName), Stack} of
438 {true, [{T0, A0, Acc0} | Post0]} ->
439 case lists:splitwith(F, Acc0) of
440 {_, []} ->
441 %% Actually was a singleton
442 Stack;
443 {Pre, [{T1, A1, Acc1} | Post1]} ->
444 [{T0, A0, [{T1, A1, Acc1 ++ lists:reverse(Pre)} | Post1]}
445 | Post0]
446 end;
447 _ ->
448 %% No match, no state change
449 Stack
450 end;
451 {_Pre, [_T]} ->
452 %% Unfurl the whole stack, we're done
453 destack(Stack);
454 {Pre, [T, {T0, A0, Acc0} | Post]} ->
455 %% Unfurl up to the tag, then accumulate it
456 [{T0, A0, [destack(Pre ++ [T]) | Acc0]} | Post]
457 end.
458
459 destack([{Tag, Attrs, Acc}]) ->
460 {Tag, Attrs, lists:reverse(Acc)};
461 destack([{T1, A1, Acc1}, {T0, A0, Acc0} | Rest]) ->
462 destack([{T0, A0, [{T1, A1, lists:reverse(Acc1)} | Acc0]} | Rest]).
463
464 is_singleton(<<"br">>) -> true;
465 is_singleton(<<"hr">>) -> true;
466 is_singleton(<<"img">>) -> true;
467 is_singleton(<<"input">>) -> true;
468 is_singleton(<<"base">>) -> true;
469 is_singleton(<<"meta">>) -> true;
470 is_singleton(<<"link">>) -> true;
471 is_singleton(<<"area">>) -> true;
472 is_singleton(<<"param">>) -> true;
473 is_singleton(<<"col">>) -> true;
474 is_singleton(_) -> false.
475
476 tokenize_data(B, S=#decoder{offset=O}) ->
477 tokenize_data(B, S, O, true).
478
479 tokenize_data(B, S=#decoder{offset=O}, Start, Whitespace) ->
480 case B of
481 <<_:O/binary, C, _/binary>> when (C =/= $< andalso C =/= $&) ->
482 tokenize_data(B, ?INC_CHAR(S, C), Start,
483 (Whitespace andalso ?IS_WHITESPACE(C)));
484 _ ->
485 Len = O - Start,
486 <<_:Start/binary, Data:Len/binary, _/binary>> = B,
487 {{data, Data, Whitespace}, S}
488 end.
489
490 tokenize_attributes(B, S) ->
491 tokenize_attributes(B, S, []).
492
493 tokenize_attributes(B, S=#decoder{offset=O}, Acc) ->
494 case B of
495 <<_:O/binary>> ->
496 {lists:reverse(Acc), S};
497 <<_:O/binary, C, _/binary>> when (C =:= $> orelse C =:= $/) ->
498 {lists:reverse(Acc), S};
499 <<_:O/binary, "?>", _/binary>> ->
500 {lists:reverse(Acc), S};
501 <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) ->
502 tokenize_attributes(B, ?INC_CHAR(S, C), Acc);
503 _ ->
504 {Attr, S1} = tokenize_literal(B, S),
505 {Value, S2} = tokenize_attr_value(Attr, B, S1),
506 tokenize_attributes(B, S2, [{Attr, Value} | Acc])
507 end.
508
509 tokenize_attr_value(Attr, B, S) ->
510 S1 = skip_whitespace(B, S),
511 O = S1#decoder.offset,
512 case B of
513 <<_:O/binary, "=", _/binary>> ->
514 S2 = skip_whitespace(B, ?INC_COL(S1)),
515 tokenize_quoted_or_unquoted_attr_value(B, S2);
516 _ ->
517 {Attr, S1}
518 end.
519
520 tokenize_quoted_or_unquoted_attr_value(B, S=#decoder{offset=O}) ->
521 case B of
522 <<_:O/binary>> ->
523 { [], S };
524 <<_:O/binary, Q, _/binary>> when Q =:= ?QUOTE orelse
525 Q =:= ?SQUOTE ->
526 tokenize_quoted_attr_value(B, ?INC_COL(S), [], Q);
527 <<_:O/binary, _/binary>> ->
528 tokenize_unquoted_attr_value(B, S, [])
529 end.
530
531 tokenize_quoted_attr_value(B, S=#decoder{offset=O}, Acc, Q) ->
532 case B of
533 <<_:O/binary>> ->
534 { iolist_to_binary(lists:reverse(Acc)), S };
535 <<_:O/binary, $&, _/binary>> ->
536 {{data, Data, false}, S1} = tokenize_charref(B, ?INC_COL(S)),
537 tokenize_quoted_attr_value(B, S1, [Data|Acc], Q);
538 <<_:O/binary, Q, _/binary>> ->
539 { iolist_to_binary(lists:reverse(Acc)), ?INC_COL(S) };
540 <<_:O/binary, C, _/binary>> ->
541 tokenize_quoted_attr_value(B, ?INC_COL(S), [C|Acc], Q)
542 end.
543
544 tokenize_unquoted_attr_value(B, S=#decoder{offset=O}, Acc) ->
545 case B of
546 <<_:O/binary>> ->
547 { iolist_to_binary(lists:reverse(Acc)), S };
548 <<_:O/binary, $&, _/binary>> ->
549 {{data, Data, false}, S1} = tokenize_charref(B, ?INC_COL(S)),
550 tokenize_unquoted_attr_value(B, S1, [Data|Acc]);
551 <<_:O/binary, $/, $>, _/binary>> ->
552 { iolist_to_binary(lists:reverse(Acc)), S };
553 <<_:O/binary, C, _/binary>> when ?PROBABLE_CLOSE(C) ->
554 { iolist_to_binary(lists:reverse(Acc)), S };
555 <<_:O/binary, C, _/binary>> ->
556 tokenize_unquoted_attr_value(B, ?INC_COL(S), [C|Acc])
557 end.
558
559 skip_whitespace(B, S=#decoder{offset=O}) ->
560 case B of
561 <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) ->
562 skip_whitespace(B, ?INC_CHAR(S, C));
563 _ ->
564 S
565 end.
566
567 tokenize_literal(Bin, S=#decoder{offset=O}) ->
568 case Bin of
569 <<_:O/binary, C, _/binary>> when C =:= $>
570 orelse C =:= $/
571 orelse C =:= $= ->
572 %% Handle case where tokenize_literal would consume
573 %% 0 chars. http://github.com/mochi/mochiweb/pull/13
574 {[C], ?INC_COL(S)};
575 _ ->
576 tokenize_literal(Bin, S, [])
577 end.
578
579 tokenize_literal(Bin, S=#decoder{offset=O}, Acc) ->
580 case Bin of
581 <<_:O/binary, $&, _/binary>> ->
582 {{data, Data, false}, S1} = tokenize_charref(Bin, ?INC_COL(S)),
583 tokenize_literal(Bin, S1, [Data | Acc]);
584 <<_:O/binary, C, _/binary>> when not (?IS_WHITESPACE(C)
585 orelse C =:= $>
586 orelse C =:= $/
587 orelse C =:= $=) ->
588 tokenize_literal(Bin, ?INC_COL(S), [C | Acc]);
589 _ ->
590 {iolist_to_binary(string:to_lower(lists:reverse(Acc))), S}
591 end.
592
593 raw_qgt(Bin, S=#decoder{offset=O}) ->
594 raw_qgt(Bin, S, O).
595
596 raw_qgt(Bin, S=#decoder{offset=O}, Start) ->
597 case Bin of
598 <<_:O/binary, "?>", _/binary>> ->
599 Len = O - Start,
600 <<_:Start/binary, Raw:Len/binary, _/binary>> = Bin,
601 {Raw, ?ADV_COL(S, 2)};
602 <<_:O/binary, C, _/binary>> ->
603 raw_qgt(Bin, ?INC_CHAR(S, C), Start);
604 <<_:O/binary>> ->
605 <<_:Start/binary, Raw/binary>> = Bin,
606 {Raw, S}
607 end.
608
609 find_qgt(Bin, S=#decoder{offset=O}) ->
610 case Bin of
611 <<_:O/binary, "?>", _/binary>> ->
612 ?ADV_COL(S, 2);
613 <<_:O/binary, ">", _/binary>> ->
614 ?ADV_COL(S, 1);
615 <<_:O/binary, "/>", _/binary>> ->
616 ?ADV_COL(S, 2);
617 %% tokenize_attributes takes care of this state:
618 %% <<_:O/binary, C, _/binary>> ->
619 %% find_qgt(Bin, ?INC_CHAR(S, C));
620 <<_:O/binary>> ->
621 S
622 end.
623
624 find_gt(Bin, S) ->
625 find_gt(Bin, S, false).
626
627 find_gt(Bin, S=#decoder{offset=O}, HasSlash) ->
628 case Bin of
629 <<_:O/binary, $/, _/binary>> ->
630 find_gt(Bin, ?INC_COL(S), true);
631 <<_:O/binary, $>, _/binary>> ->
632 {?INC_COL(S), HasSlash};
633 <<_:O/binary, C, _/binary>> ->
634 find_gt(Bin, ?INC_CHAR(S, C), HasSlash);
635 _ ->
636 {S, HasSlash}
637 end.
638
639 tokenize_charref(Bin, S=#decoder{offset=O}) ->
640 try
641 case tokenize_charref_raw(Bin, S, O) of
642 {C1, S1} when C1 >= 16#D800 andalso C1 =< 16#DFFF ->
643 %% Surrogate pair
644 tokeninize_charref_surrogate_pair(Bin, S1, C1);
645 {Unichar, S1} when is_integer(Unichar) ->
646 {{data, mochiutf8:codepoint_to_bytes(Unichar), false},
647 S1};
648 {Unichars, S1} when is_list(Unichars) ->
649 {{data, unicode:characters_to_binary(Unichars), false},
650 S1};
651 {undefined, _} ->
652 throw(invalid_charref)
653 end
654 catch
655 throw:invalid_charref ->
656 {{data, <<"&">>, false}, S}
657 end.
658
659 tokeninize_charref_surrogate_pair(Bin, S=#decoder{offset=O}, C1) ->
660 case Bin of
661 <<_:O/binary, $&, _/binary>> ->
662 case tokenize_charref_raw(Bin, ?INC_COL(S), O + 1) of
663 {C2, S1} when C2 >= 16#D800 andalso C1 =< 16#DFFF ->
664 {{data,
665 unicode:characters_to_binary(
666 <<C1:16, C2:16>>,
667 utf16,
668 utf8),
669 false},
670 S1};
671 _ ->
672 throw(invalid_charref)
673 end;
674 _ ->
675 throw(invalid_charref)
676 end.
677
678 tokenize_charref_raw(Bin, S=#decoder{offset=O}, Start) ->
679 case Bin of
680 <<_:O/binary>> ->
681 throw(invalid_charref);
682 <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C)
683 orelse C =:= ?SQUOTE
684 orelse C =:= ?QUOTE
685 orelse C =:= $/
686 orelse C =:= $> ->
687 throw(invalid_charref);
688 <<_:O/binary, $;, _/binary>> ->
689 Len = O - Start,
690 <<_:Start/binary, Raw:Len/binary, _/binary>> = Bin,
691 {mochiweb_charref:charref(Raw), ?INC_COL(S)};
692 _ ->
693 tokenize_charref_raw(Bin, ?INC_COL(S), Start)
694 end.
695
696 tokenize_doctype(Bin, S) ->
697 tokenize_doctype(Bin, S, []).
698
699 tokenize_doctype(Bin, S=#decoder{offset=O}, Acc) ->
700 case Bin of
701 <<_:O/binary>> ->
702 {{doctype, lists:reverse(Acc)}, S};
703 <<_:O/binary, $>, _/binary>> ->
704 {{doctype, lists:reverse(Acc)}, ?INC_COL(S)};
705 <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) ->
706 tokenize_doctype(Bin, ?INC_CHAR(S, C), Acc);
707 _ ->
708 {Word, S1} = tokenize_word_or_literal(Bin, S),
709 tokenize_doctype(Bin, S1, [Word | Acc])
710 end.
711
712 tokenize_word_or_literal(Bin, S=#decoder{offset=O}) ->
713 case Bin of
714 <<_:O/binary, C, _/binary>> when C =:= ?QUOTE orelse C =:= ?SQUOTE ->
715 tokenize_word(Bin, ?INC_COL(S), C);
716 <<_:O/binary, C, _/binary>> when not ?IS_WHITESPACE(C) ->
717 %% Sanity check for whitespace
718 tokenize_literal(Bin, S)
719 end.
720
721 tokenize_word(Bin, S, Quote) ->
722 tokenize_word(Bin, S, Quote, []).
723
724 tokenize_word(Bin, S=#decoder{offset=O}, Quote, Acc) ->
725 case Bin of
726 <<_:O/binary>> ->
727 {iolist_to_binary(lists:reverse(Acc)), S};
728 <<_:O/binary, Quote, _/binary>> ->
729 {iolist_to_binary(lists:reverse(Acc)), ?INC_COL(S)};
730 <<_:O/binary, $&, _/binary>> ->
731 {{data, Data, false}, S1} = tokenize_charref(Bin, ?INC_COL(S)),
732 tokenize_word(Bin, S1, Quote, [Data | Acc]);
733 <<_:O/binary, C, _/binary>> ->
734 tokenize_word(Bin, ?INC_CHAR(S, C), Quote, [C | Acc])
735 end.
736
737 tokenize_cdata(Bin, S=#decoder{offset=O}) ->
738 tokenize_cdata(Bin, S, O).
739
740 tokenize_cdata(Bin, S=#decoder{offset=O}, Start) ->
741 case Bin of
742 <<_:O/binary, "]]>", _/binary>> ->
743 Len = O - Start,
744 <<_:Start/binary, Raw:Len/binary, _/binary>> = Bin,
745 {{data, Raw, false}, ?ADV_COL(S, 3)};
746 <<_:O/binary, C, _/binary>> ->
747 tokenize_cdata(Bin, ?INC_CHAR(S, C), Start);
748 _ ->
749 <<_:O/binary, Raw/binary>> = Bin,
750 {{data, Raw, false}, S}
751 end.
752
753 tokenize_comment(Bin, S=#decoder{offset=O}) ->
754 tokenize_comment(Bin, S, O).
755
756 tokenize_comment(Bin, S=#decoder{offset=O}, Start) ->
757 case Bin of
758 <<_:O/binary, "-->", _/binary>> ->
759 Len = O - Start,
760 <<_:Start/binary, Raw:Len/binary, _/binary>> = Bin,
761 {{comment, Raw}, ?ADV_COL(S, 3)};
762 <<_:O/binary, C, _/binary>> ->
763 tokenize_comment(Bin, ?INC_CHAR(S, C), Start);
764 <<_:Start/binary, Raw/binary>> ->
765 {{comment, Raw}, S}
766 end.
767
768 tokenize_script(Bin, S=#decoder{offset=O}) ->
769 tokenize_script(Bin, S, O).
770
771 tokenize_script(Bin, S=#decoder{offset=O}, Start) ->
772 case Bin of
773 %% Just a look-ahead, we want the end_tag separately
774 <<_:O/binary, $<, $/, SS, CC, RR, II, PP, TT, ZZ, _/binary>>
775 when (SS =:= $s orelse SS =:= $S) andalso
776 (CC =:= $c orelse CC =:= $C) andalso
777 (RR =:= $r orelse RR =:= $R) andalso
778 (II =:= $i orelse II =:= $I) andalso
779 (PP =:= $p orelse PP =:= $P) andalso
780 (TT=:= $t orelse TT =:= $T) andalso
781 ?PROBABLE_CLOSE(ZZ) ->
782 Len = O - Start,
783 <<_:Start/binary, Raw:Len/binary, _/binary>> = Bin,
784 {{data, Raw, false}, S};
785 <<_:O/binary, C, _/binary>> ->
786 tokenize_script(Bin, ?INC_CHAR(S, C), Start);
787 <<_:Start/binary, Raw/binary>> ->
788 {{data, Raw, false}, S}
789 end.
790
791 tokenize_textarea(Bin, S=#decoder{offset=O}) ->
792 tokenize_textarea(Bin, S, O).
793
794 tokenize_textarea(Bin, S=#decoder{offset=O}, Start) ->
795 case Bin of
796 %% Just a look-ahead, we want the end_tag separately
797 <<_:O/binary, $<, $/, TT, EE, XX, TT2, AA, RR, EE2, AA2, ZZ, _/binary>>
798 when (TT =:= $t orelse TT =:= $T) andalso
799 (EE =:= $e orelse EE =:= $E) andalso
800 (XX =:= $x orelse XX =:= $X) andalso
801 (TT2 =:= $t orelse TT2 =:= $T) andalso
802 (AA =:= $a orelse AA =:= $A) andalso
803 (RR =:= $r orelse RR =:= $R) andalso
804 (EE2 =:= $e orelse EE2 =:= $E) andalso
805 (AA2 =:= $a orelse AA2 =:= $A) andalso
806 ?PROBABLE_CLOSE(ZZ) ->
807 Len = O - Start,
808 <<_:Start/binary, Raw:Len/binary, _/binary>> = Bin,
809 {{data, Raw, false}, S};
810 <<_:O/binary, C, _/binary>> ->
811 tokenize_textarea(Bin, ?INC_CHAR(S, C), Start);
812 <<_:Start/binary, Raw/binary>> ->
813 {{data, Raw, false}, S}
814 end.
+0
-307
deps/mochiweb/src/mochiweb_http.erl less more
0 %% @author Bob Ippolito <bob@mochimedia.com>
1 %% @copyright 2007 Mochi Media, Inc.
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 %% @doc HTTP server.
22
23 -module(mochiweb_http).
24 -author('bob@mochimedia.com').
25 -export([start/1, start_link/1, stop/0, stop/1]).
26 -export([loop/3]).
27 -export([after_response/2, reentry/1]).
28 -export([parse_range_request/1, range_skip_length/2]).
29
30 -define(REQUEST_RECV_TIMEOUT, 300000). %% timeout waiting for request line
31 -define(HEADERS_RECV_TIMEOUT, 30000). %% timeout waiting for headers
32
33 -define(MAX_HEADERS, 1000).
34 -define(DEFAULTS, [{name, ?MODULE},
35 {port, 8888}]).
36
37 -ifdef(gen_tcp_r15b_workaround).
38 r15b_workaround() -> true.
39 -else.
40 r15b_workaround() -> false.
41 -endif.
42
43 parse_options(Options) ->
44 {loop, HttpLoop} = proplists:lookup(loop, Options),
45 Loop = {?MODULE, loop, [HttpLoop]},
46 Options1 = [{loop, Loop} | proplists:delete(loop, Options)],
47 mochilists:set_defaults(?DEFAULTS, Options1).
48
49 stop() ->
50 mochiweb_socket_server:stop(?MODULE).
51
52 stop(Name) ->
53 mochiweb_socket_server:stop(Name).
54
55 %% @spec start(Options) -> ServerRet
56 %% Options = [option()]
57 %% Option = {name, atom()} | {ip, string() | tuple()} | {backlog, integer()}
58 %% | {nodelay, boolean()} | {acceptor_pool_size, integer()}
59 %% | {ssl, boolean()} | {profile_fun, undefined | (Props) -> ok}
60 %% | {link, false} | {recbuf, undefined | non_negative_integer()}
61 %% @doc Start a mochiweb server.
62 %% profile_fun is used to profile accept timing.
63 %% After each accept, if defined, profile_fun is called with a proplist of a subset of the mochiweb_socket_server state and timing information.
64 %% The proplist is as follows: [{name, Name}, {port, Port}, {active_sockets, ActiveSockets}, {timing, Timing}].
65 %% @end
66 start(Options) ->
67 ok = ensure_started(mochiweb_clock),
68 mochiweb_socket_server:start(parse_options(Options)).
69
70 start_link(Options) ->
71 ok = ensure_started(mochiweb_clock),
72 mochiweb_socket_server:start_link(parse_options(Options)).
73
74 ensure_started(M) ->
75 case M:start() of
76 {ok, _Pid} ->
77 ok;
78 {error, {already_started, _Pid}} ->
79 ok
80 end.
81
82 loop(Socket, Opts, Body) ->
83 ok = mochiweb_socket:exit_if_closed(mochiweb_socket:setopts(Socket, [{packet, http}])),
84 request(Socket, Opts, Body).
85
86 request(Socket, Opts, Body) ->
87 ok = mochiweb_socket:exit_if_closed(mochiweb_socket:setopts(Socket, [{active, once}])),
88 receive
89 {Protocol, _, {http_request, Method, Path, Version}} when Protocol == http orelse Protocol == ssl ->
90 ok = mochiweb_socket:exit_if_closed(mochiweb_socket:setopts(Socket, [{packet, httph}])),
91 headers(Socket, Opts, {Method, Path, Version}, [], Body, 0);
92 {Protocol, _, {http_error, "\r\n"}} when Protocol == http orelse Protocol == ssl ->
93 request(Socket, Opts, Body);
94 {Protocol, _, {http_error, "\n"}} when Protocol == http orelse Protocol == ssl ->
95 request(Socket, Opts, Body);
96 {tcp_closed, _} ->
97 mochiweb_socket:close(Socket),
98 exit(normal);
99 {ssl_closed, _} ->
100 mochiweb_socket:close(Socket),
101 exit(normal)
102 after ?REQUEST_RECV_TIMEOUT ->
103 mochiweb_socket:close(Socket),
104 exit(normal)
105 end.
106
107 reentry(Body) ->
108 fun (Req) ->
109 ?MODULE:after_response(Body, Req)
110 end.
111
112 headers(Socket, Opts, Request, Headers, _Body, ?MAX_HEADERS) ->
113 %% Too many headers sent, bad request.
114 ok = mochiweb_socket:exit_if_closed(mochiweb_socket:setopts(Socket, [{packet, raw}])),
115 handle_invalid_request(Socket, Opts, Request, Headers);
116 headers(Socket, Opts, Request, Headers, Body, HeaderCount) ->
117 ok = mochiweb_socket:exit_if_closed(mochiweb_socket:setopts(Socket, [{active, once}])),
118 receive
119 {Protocol, _, http_eoh} when Protocol == http orelse Protocol == ssl ->
120 Req = new_request(Socket, Opts, Request, Headers),
121 call_body(Body, Req),
122 ?MODULE:after_response(Body, Req);
123 {Protocol, _, {http_header, _, Name, _, Value}} when Protocol == http orelse Protocol == ssl ->
124 headers(Socket, Opts, Request, [{Name, Value} | Headers], Body,
125 1 + HeaderCount);
126 {tcp_closed, _} ->
127 mochiweb_socket:close(Socket),
128 exit(normal);
129 {tcp_error, _, emsgsize} = Other ->
130 handle_invalid_msg_request(Other, Socket, Opts, Request, Headers)
131 after ?HEADERS_RECV_TIMEOUT ->
132 mochiweb_socket:close(Socket),
133 exit(normal)
134 end.
135
136 call_body({M, F, A}, Req) ->
137 erlang:apply(M, F, [Req | A]);
138 call_body({M, F}, Req) ->
139 M:F(Req);
140 call_body(Body, Req) ->
141 Body(Req).
142
143 -spec handle_invalid_msg_request(term(), term(), term(), term(), term()) -> no_return().
144 handle_invalid_msg_request(Msg, Socket, Opts, Request, RevHeaders) ->
145 case {Msg, r15b_workaround()} of
146 {{tcp_error,_,emsgsize}, true} ->
147 %% R15B02 returns this then closes the socket, so close and exit
148 mochiweb_socket:close(Socket),
149 exit(normal);
150 _ ->
151 handle_invalid_request(Socket, Opts, Request, RevHeaders)
152 end.
153
154 -spec handle_invalid_request(term(), term(), term(), term()) -> no_return().
155 handle_invalid_request(Socket, Opts, Request, RevHeaders) ->
156 Req = new_request(Socket, Opts, Request, RevHeaders),
157 Req:respond({400, [], []}),
158 mochiweb_socket:close(Socket),
159 exit(normal).
160
161 new_request(Socket, Opts, Request, RevHeaders) ->
162 ok = mochiweb_socket:exit_if_closed(mochiweb_socket:setopts(Socket, [{packet, raw}])),
163 mochiweb:new_request({Socket, Opts, Request, lists:reverse(RevHeaders)}).
164
165 after_response(Body, Req) ->
166 Socket = Req:get(socket),
167 case Req:should_close() of
168 true ->
169 mochiweb_socket:close(Socket),
170 exit(normal);
171 false ->
172 Req:cleanup(),
173 erlang:garbage_collect(),
174 ?MODULE:loop(Socket, mochiweb_request:get(opts, Req), Body)
175 end.
176
177 parse_range_request(RawRange) when is_list(RawRange) ->
178 try
179 "bytes=" ++ RangeString = RawRange,
180 RangeTokens = [string:strip(R) || R <- string:tokens(RangeString, ",")],
181 Ranges = [R || R <- RangeTokens, string:len(R) > 0],
182 lists:map(fun ("-" ++ V) ->
183 {none, list_to_integer(V)};
184 (R) ->
185 case string:tokens(R, "-") of
186 [S1, S2] ->
187 {list_to_integer(S1), list_to_integer(S2)};
188 [S] ->
189 {list_to_integer(S), none}
190 end
191 end,
192 Ranges)
193 catch
194 _:_ ->
195 fail
196 end.
197
198 range_skip_length(Spec, Size) ->
199 case Spec of
200 {none, R} when R =< Size, R >= 0 ->
201 {Size - R, R};
202 {none, _OutOfRange} ->
203 {0, Size};
204 {R, none} when R >= 0, R < Size ->
205 {R, Size - R};
206 {_OutOfRange, none} ->
207 invalid_range;
208 {Start, End} when Start >= 0, Start < Size, Start =< End ->
209 {Start, erlang:min(End + 1, Size) - Start};
210 {_InvalidStart, _InvalidEnd} ->
211 invalid_range
212 end.
213
214 %%
215 %% Tests
216 %%
217 -ifdef(TEST).
218 -include_lib("eunit/include/eunit.hrl").
219
220 range_test() ->
221 %% valid, single ranges
222 ?assertEqual([{20, 30}], parse_range_request("bytes=20-30")),
223 ?assertEqual([{20, none}], parse_range_request("bytes=20-")),
224 ?assertEqual([{none, 20}], parse_range_request("bytes=-20")),
225
226 %% trivial single range
227 ?assertEqual([{0, none}], parse_range_request("bytes=0-")),
228
229 %% invalid, single ranges
230 ?assertEqual(fail, parse_range_request("")),
231 ?assertEqual(fail, parse_range_request("garbage")),
232 ?assertEqual(fail, parse_range_request("bytes=-20-30")),
233
234 %% valid, multiple range
235 ?assertEqual(
236 [{20, 30}, {50, 100}, {110, 200}],
237 parse_range_request("bytes=20-30,50-100,110-200")),
238 ?assertEqual(
239 [{20, none}, {50, 100}, {none, 200}],
240 parse_range_request("bytes=20-,50-100,-200")),
241
242 %% valid, multiple range with whitespace
243 ?assertEqual(
244 [{20, 30}, {50, 100}, {110, 200}],
245 parse_range_request("bytes=20-30, 50-100 , 110-200")),
246
247 %% valid, multiple range with extra commas
248 ?assertEqual(
249 [{20, 30}, {50, 100}, {110, 200}],
250 parse_range_request("bytes=20-30,,50-100,110-200")),
251 ?assertEqual(
252 [{20, 30}, {50, 100}, {110, 200}],
253 parse_range_request("bytes=20-30, ,50-100,,,110-200")),
254
255 %% no ranges
256 ?assertEqual([], parse_range_request("bytes=")),
257 ok.
258
259 range_skip_length_test() ->
260 Body = <<"012345678901234567890123456789012345678901234567890123456789">>,
261 BodySize = byte_size(Body), %% 60
262 BodySize = 60,
263
264 %% these values assume BodySize =:= 60
265 ?assertEqual({1,9}, range_skip_length({1,9}, BodySize)), %% 1-9
266 ?assertEqual({10,10}, range_skip_length({10,19}, BodySize)), %% 10-19
267 ?assertEqual({40, 20}, range_skip_length({none, 20}, BodySize)), %% -20
268 ?assertEqual({30, 30}, range_skip_length({30, none}, BodySize)), %% 30-
269
270 %% valid edge cases for range_skip_length
271 ?assertEqual({BodySize, 0}, range_skip_length({none, 0}, BodySize)),
272 ?assertEqual({0, BodySize}, range_skip_length({none, BodySize}, BodySize)),
273 ?assertEqual({0, BodySize}, range_skip_length({0, none}, BodySize)),
274 ?assertEqual({0, BodySize}, range_skip_length({0, BodySize + 1}, BodySize)),
275 BodySizeLess1 = BodySize - 1,
276 ?assertEqual({BodySizeLess1, 1},
277 range_skip_length({BodySize - 1, none}, BodySize)),
278 ?assertEqual({BodySizeLess1, 1},
279 range_skip_length({BodySize - 1, BodySize+5}, BodySize)),
280 ?assertEqual({BodySizeLess1, 1},
281 range_skip_length({BodySize - 1, BodySize}, BodySize)),
282
283 %% out of range, return whole thing
284 ?assertEqual({0, BodySize},
285 range_skip_length({none, BodySize + 1}, BodySize)),
286 ?assertEqual({0, BodySize},
287 range_skip_length({none, -1}, BodySize)),
288 ?assertEqual({0, BodySize},
289 range_skip_length({0, BodySize + 1}, BodySize)),
290
291 %% invalid ranges
292 ?assertEqual(invalid_range,
293 range_skip_length({-1, 30}, BodySize)),
294 ?assertEqual(invalid_range,
295 range_skip_length({-1, BodySize + 1}, BodySize)),
296 ?assertEqual(invalid_range,
297 range_skip_length({BodySize, 40}, BodySize)),
298 ?assertEqual(invalid_range,
299 range_skip_length({-1, none}, BodySize)),
300 ?assertEqual(invalid_range,
301 range_skip_length({BodySize, none}, BodySize)),
302 ?assertEqual(invalid_range,
303 range_skip_length({BodySize + 1, BodySize + 5}, BodySize)),
304 ok.
305
306 -endif.
+0
-61
deps/mochiweb/src/mochiweb_io.erl less more
0 %% @author Bob Ippolito <bob@mochimedia.com>
1 %% @copyright 2007 Mochi Media, Inc.
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 %% @doc Utilities for dealing with IO devices (open files).
22
23 -module(mochiweb_io).
24 -author('bob@mochimedia.com').
25
26 -export([iodevice_stream/3, iodevice_stream/2]).
27 -export([iodevice_foldl/4, iodevice_foldl/3]).
28 -export([iodevice_size/1]).
29 -define(READ_SIZE, 8192).
30
31 iodevice_foldl(F, Acc, IoDevice) ->
32 iodevice_foldl(F, Acc, IoDevice, ?READ_SIZE).
33
34 iodevice_foldl(F, Acc, IoDevice, BufferSize) ->
35 case file:read(IoDevice, BufferSize) of
36 eof ->
37 Acc;
38 {ok, Data} ->
39 iodevice_foldl(F, F(Data, Acc), IoDevice, BufferSize)
40 end.
41
42 iodevice_stream(Callback, IoDevice) ->
43 iodevice_stream(Callback, IoDevice, ?READ_SIZE).
44
45 iodevice_stream(Callback, IoDevice, BufferSize) ->
46 F = fun (Data, ok) -> Callback(Data) end,
47 ok = iodevice_foldl(F, ok, IoDevice, BufferSize).
48
49 iodevice_size(IoDevice) ->
50 {ok, Size} = file:position(IoDevice, eof),
51 {ok, 0} = file:position(IoDevice, bof),
52 Size.
53
54
55 %%
56 %% Tests
57 %%
58 -ifdef(TEST).
59 -include_lib("eunit/include/eunit.hrl").
60 -endif.
+0
-433
deps/mochiweb/src/mochiweb_mime.erl less more
0 %% @author Bob Ippolito <bob@mochimedia.com>
1 %% @copyright 2007 Mochi Media, Inc.
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 %% @doc Gives a good MIME type guess based on file extension.
22
23 -module(mochiweb_mime).
24 -author('bob@mochimedia.com').
25 -export([from_extension/1]).
26
27 %% @spec from_extension(S::string()) -> string() | undefined
28 %% @doc Given a filename extension (e.g. ".html") return a guess for the MIME
29 %% type such as "text/html". Will return the atom undefined if no good
30 %% guess is available.
31
32 from_extension(".stl") ->
33 "application/SLA";
34 from_extension(".stp") ->
35 "application/STEP";
36 from_extension(".step") ->
37 "application/STEP";
38 from_extension(".dwg") ->
39 "application/acad";
40 from_extension(".ez") ->
41 "application/andrew-inset";
42 from_extension(".ccad") ->
43 "application/clariscad";
44 from_extension(".drw") ->
45 "application/drafting";
46 from_extension(".tsp") ->
47 "application/dsptype";
48 from_extension(".dxf") ->
49 "application/dxf";
50 from_extension(".xls") ->
51 "application/excel";
52 from_extension(".unv") ->
53 "application/i-deas";
54 from_extension(".jar") ->
55 "application/java-archive";
56 from_extension(".hqx") ->
57 "application/mac-binhex40";
58 from_extension(".cpt") ->
59 "application/mac-compactpro";
60 from_extension(".pot") ->
61 "application/vnd.ms-powerpoint";
62 from_extension(".ppt") ->
63 "application/vnd.ms-powerpoint";
64 from_extension(".dms") ->
65 "application/octet-stream";
66 from_extension(".lha") ->
67 "application/octet-stream";
68 from_extension(".lzh") ->
69 "application/octet-stream";
70 from_extension(".oda") ->
71 "application/oda";
72 from_extension(".ogg") ->
73 "application/ogg";
74 from_extension(".ogm") ->
75 "application/ogg";
76 from_extension(".pdf") ->
77 "application/pdf";
78 from_extension(".pgp") ->
79 "application/pgp";
80 from_extension(".ai") ->
81 "application/postscript";
82 from_extension(".eps") ->
83 "application/postscript";
84 from_extension(".ps") ->
85 "application/postscript";
86 from_extension(".prt") ->
87 "application/pro_eng";
88 from_extension(".rtf") ->
89 "application/rtf";
90 from_extension(".smi") ->
91 "application/smil";
92 from_extension(".smil") ->
93 "application/smil";
94 from_extension(".sol") ->
95 "application/solids";
96 from_extension(".vda") ->
97 "application/vda";
98 from_extension(".xlm") ->
99 "application/vnd.ms-excel";
100 from_extension(".cod") ->
101 "application/vnd.rim.cod";
102 from_extension(".pgn") ->
103 "application/x-chess-pgn";
104 from_extension(".cpio") ->
105 "application/x-cpio";
106 from_extension(".csh") ->
107 "application/x-csh";
108 from_extension(".deb") ->
109 "application/x-debian-package";
110 from_extension(".dcr") ->
111 "application/x-director";
112 from_extension(".dir") ->
113 "application/x-director";
114 from_extension(".dxr") ->
115 "application/x-director";
116 from_extension(".gz") ->
117 "application/x-gzip";
118 from_extension(".hdf") ->
119 "application/x-hdf";
120 from_extension(".ipx") ->
121 "application/x-ipix";
122 from_extension(".ips") ->
123 "application/x-ipscript";
124 from_extension(".js") ->
125 "application/x-javascript";
126 from_extension(".skd") ->
127 "application/x-koan";
128 from_extension(".skm") ->
129 "application/x-koan";
130 from_extension(".skp") ->
131 "application/x-koan";
132 from_extension(".skt") ->
133 "application/x-koan";
134 from_extension(".latex") ->
135 "application/x-latex";
136 from_extension(".lsp") ->
137 "application/x-lisp";
138 from_extension(".scm") ->
139 "application/x-lotusscreencam";
140 from_extension(".mif") ->
141 "application/x-mif";
142 from_extension(".com") ->
143 "application/x-msdos-program";
144 from_extension(".exe") ->
145 "application/octet-stream";
146 from_extension(".cdf") ->
147 "application/x-netcdf";
148 from_extension(".nc") ->
149 "application/x-netcdf";
150 from_extension(".pl") ->
151 "application/x-perl";
152 from_extension(".pm") ->
153 "application/x-perl";
154 from_extension(".rar") ->
155 "application/x-rar-compressed";
156 from_extension(".sh") ->
157 "application/x-sh";
158 from_extension(".shar") ->
159 "application/x-shar";
160 from_extension(".swf") ->
161 "application/x-shockwave-flash";
162 from_extension(".sit") ->
163 "application/x-stuffit";
164 from_extension(".sv4cpio") ->
165 "application/x-sv4cpio";
166 from_extension(".sv4crc") ->
167 "application/x-sv4crc";
168 from_extension(".tar.gz") ->
169 "application/x-tar-gz";
170 from_extension(".tgz") ->
171 "application/x-tar-gz";
172 from_extension(".tar") ->
173 "application/x-tar";
174 from_extension(".tcl") ->
175 "application/x-tcl";
176 from_extension(".texi") ->
177 "application/x-texinfo";
178 from_extension(".texinfo") ->
179 "application/x-texinfo";
180 from_extension(".man") ->
181 "application/x-troff-man";
182 from_extension(".me") ->
183 "application/x-troff-me";
184 from_extension(".ms") ->
185 "application/x-troff-ms";
186 from_extension(".roff") ->
187 "application/x-troff";
188 from_extension(".t") ->
189 "application/x-troff";
190 from_extension(".tr") ->
191 "application/x-troff";
192 from_extension(".ustar") ->
193 "application/x-ustar";
194 from_extension(".src") ->
195 "application/x-wais-source";
196 from_extension(".zip") ->
197 "application/zip";
198 from_extension(".tsi") ->
199 "audio/TSP-audio";
200 from_extension(".au") ->
201 "audio/basic";
202 from_extension(".snd") ->
203 "audio/basic";
204 from_extension(".kar") ->
205 "audio/midi";
206 from_extension(".mid") ->
207 "audio/midi";
208 from_extension(".midi") ->
209 "audio/midi";
210 from_extension(".mp2") ->
211 "audio/mpeg";
212 from_extension(".mp3") ->
213 "audio/mpeg";
214 from_extension(".mpga") ->
215 "audio/mpeg";
216 from_extension(".aif") ->
217 "audio/x-aiff";
218 from_extension(".aifc") ->
219 "audio/x-aiff";
220 from_extension(".aiff") ->
221 "audio/x-aiff";
222 from_extension(".m3u") ->
223 "audio/x-mpegurl";
224 from_extension(".wax") ->
225 "audio/x-ms-wax";
226 from_extension(".wma") ->
227 "audio/x-ms-wma";
228 from_extension(".rpm") ->
229 "audio/x-pn-realaudio-plugin";
230 from_extension(".ram") ->
231 "audio/x-pn-realaudio";
232 from_extension(".rm") ->
233 "audio/x-pn-realaudio";
234 from_extension(".ra") ->
235 "audio/x-realaudio";
236 from_extension(".wav") ->
237 "audio/x-wav";
238 from_extension(".pdb") ->
239 "chemical/x-pdb";
240 from_extension(".ras") ->
241 "image/cmu-raster";
242 from_extension(".gif") ->
243 "image/gif";
244 from_extension(".ief") ->
245 "image/ief";
246 from_extension(".jpe") ->
247 "image/jpeg";
248 from_extension(".jpeg") ->
249 "image/jpeg";
250 from_extension(".jpg") ->
251 "image/jpeg";
252 from_extension(".jp2") ->
253 "image/jp2";
254 from_extension(".png") ->
255 "image/png";
256 from_extension(".tif") ->
257 "image/tiff";
258 from_extension(".tiff") ->
259 "image/tiff";
260 from_extension(".pnm") ->
261 "image/x-portable-anymap";
262 from_extension(".pbm") ->
263 "image/x-portable-bitmap";
264 from_extension(".pgm") ->
265 "image/x-portable-graymap";
266 from_extension(".ppm") ->
267 "image/x-portable-pixmap";
268 from_extension(".rgb") ->
269 "image/x-rgb";
270 from_extension(".xbm") ->
271 "image/x-xbitmap";
272 from_extension(".xwd") ->
273 "image/x-xwindowdump";
274 from_extension(".iges") ->
275 "model/iges";
276 from_extension(".igs") ->
277 "model/iges";
278 from_extension(".mesh") ->
279 "model/mesh";
280 from_extension(".") ->
281 "";
282 from_extension(".msh") ->
283 "model/mesh";
284 from_extension(".silo") ->
285 "model/mesh";
286 from_extension(".vrml") ->
287 "model/vrml";
288 from_extension(".wrl") ->
289 "model/vrml";
290 from_extension(".css") ->
291 "text/css";
292 from_extension(".htm") ->
293 "text/html";
294 from_extension(".html") ->
295 "text/html";
296 from_extension(".asc") ->
297 "text/plain";
298 from_extension(".c") ->
299 "text/plain";
300 from_extension(".cc") ->
301 "text/plain";
302 from_extension(".f90") ->
303 "text/plain";
304 from_extension(".f") ->
305 "text/plain";
306 from_extension(".hh") ->
307 "text/plain";
308 from_extension(".m") ->
309 "text/plain";
310 from_extension(".txt") ->
311 "text/plain";
312 from_extension(".rtx") ->
313 "text/richtext";
314 from_extension(".sgm") ->
315 "text/sgml";
316 from_extension(".sgml") ->
317 "text/sgml";
318 from_extension(".tsv") ->
319 "text/tab-separated-values";
320 from_extension(".jad") ->
321 "text/vnd.sun.j2me.app-descriptor";
322 from_extension(".etx") ->
323 "text/x-setext";
324 from_extension(".xml") ->
325 "application/xml";
326 from_extension(".dl") ->
327 "video/dl";
328 from_extension(".fli") ->
329 "video/fli";
330 from_extension(".flv") ->
331 "video/x-flv";
332 from_extension(".gl") ->
333 "video/gl";
334 from_extension(".mp4") ->
335 "video/mp4";
336 from_extension(".mpe") ->
337 "video/mpeg";
338 from_extension(".mpeg") ->
339 "video/mpeg";
340 from_extension(".mpg") ->
341 "video/mpeg";
342 from_extension(".mov") ->
343 "video/quicktime";
344 from_extension(".qt") ->
345 "video/quicktime";
346 from_extension(".viv") ->
347 "video/vnd.vivo";
348 from_extension(".vivo") ->
349 "video/vnd.vivo";
350 from_extension(".asf") ->
351 "video/x-ms-asf";
352 from_extension(".asx") ->
353 "video/x-ms-asx";
354 from_extension(".wmv") ->
355 "video/x-ms-wmv";
356 from_extension(".wmx") ->
357 "video/x-ms-wmx";
358 from_extension(".wvx") ->
359 "video/x-ms-wvx";
360 from_extension(".avi") ->
361 "video/x-msvideo";
362 from_extension(".movie") ->
363 "video/x-sgi-movie";
364 from_extension(".mime") ->
365 "www/mime";
366 from_extension(".ice") ->
367 "x-conference/x-cooltalk";
368 from_extension(".vrm") ->
369 "x-world/x-vrml";
370 from_extension(".spx") ->
371 "audio/ogg";
372 from_extension(".xhtml") ->
373 "application/xhtml+xml";
374 from_extension(".bz2") ->
375 "application/x-bzip2";
376 from_extension(".doc") ->
377 "application/msword";
378 from_extension(".z") ->
379 "application/x-compress";
380 from_extension(".ico") ->
381 "image/x-icon";
382 from_extension(".bmp") ->
383 "image/bmp";
384 from_extension(".m4a") ->
385 "audio/mpeg";
386 from_extension(".csv") ->
387 "text/csv";
388 from_extension(".eot") ->
389 "application/vnd.ms-fontobject";
390 from_extension(".m4v") ->
391 "video/mp4";
392 from_extension(".svg") ->
393 "image/svg+xml";
394 from_extension(".svgz") ->
395 "image/svg+xml";
396 from_extension(".ttc") ->
397 "application/x-font-ttf";
398 from_extension(".ttf") ->
399 "application/x-font-ttf";
400 from_extension(".vcf") ->
401 "text/x-vcard";
402 from_extension(".webm") ->
403 "video/web";
404 from_extension(".webp") ->
405 "image/web";
406 from_extension(".woff") ->
407 "application/x-font-woff";
408 from_extension(".otf") ->
409 "font/opentype";
410 from_extension(_) ->
411 undefined.
412
413 %%
414 %% Tests
415 %%
416 -ifdef(TEST).
417 -include_lib("eunit/include/eunit.hrl").
418
419 exhaustive_from_extension_test() ->
420 T = mochiweb_cover:clause_lookup_table(?MODULE, from_extension),
421 [?assertEqual(V, from_extension(K)) || {K, V} <- T].
422
423 from_extension_test() ->
424 ?assertEqual("text/html",
425 from_extension(".html")),
426 ?assertEqual(undefined,
427 from_extension("")),
428 ?assertEqual(undefined,
429 from_extension(".wtf")),
430 ok.
431
432 -endif.
+0
-890
deps/mochiweb/src/mochiweb_multipart.erl less more
0 %% @author Bob Ippolito <bob@mochimedia.com>
1 %% @copyright 2007 Mochi Media, Inc.
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 %% @doc Utilities for parsing multipart/form-data.
22
23 -module(mochiweb_multipart).
24 -author('bob@mochimedia.com').
25
26 -export([parse_form/1, parse_form/2]).
27 -export([parse_multipart_request/2]).
28 -export([parts_to_body/3, parts_to_multipart_body/4]).
29 -export([default_file_handler/2]).
30
31 -define(CHUNKSIZE, 4096).
32
33 -record(mp, {state, boundary, length, buffer, callback, req}).
34
35 %% TODO: DOCUMENT THIS MODULE.
36 %% @type key() = atom() | string() | binary().
37 %% @type value() = atom() | iolist() | integer().
38 %% @type header() = {key(), value()}.
39 %% @type bodypart() = {Start::integer(), End::integer(), Body::iolist()}.
40 %% @type formfile() = {Name::string(), ContentType::string(), Content::binary()}.
41 %% @type request().
42 %% @type file_handler() = (Filename::string(), ContentType::string()) -> file_handler_callback().
43 %% @type file_handler_callback() = (binary() | eof) -> file_handler_callback() | term().
44
45 %% @spec parts_to_body([bodypart()], ContentType::string(),
46 %% Size::integer()) -> {[header()], iolist()}
47 %% @doc Return {[header()], iolist()} representing the body for the given
48 %% parts, may be a single part or multipart.
49 parts_to_body([{Start, End, Body}], ContentType, Size) ->
50 HeaderList = [{"Content-Type", ContentType},
51 {"Content-Range",
52 ["bytes ",
53 mochiweb_util:make_io(Start), "-", mochiweb_util:make_io(End),
54 "/", mochiweb_util:make_io(Size)]}],
55 {HeaderList, Body};
56 parts_to_body(BodyList, ContentType, Size) when is_list(BodyList) ->
57 parts_to_multipart_body(BodyList, ContentType, Size,
58 mochihex:to_hex(crypto:rand_bytes(8))).
59
60 %% @spec parts_to_multipart_body([bodypart()], ContentType::string(),
61 %% Size::integer(), Boundary::string()) ->
62 %% {[header()], iolist()}
63 %% @doc Return {[header()], iolist()} representing the body for the given
64 %% parts, always a multipart response.
65 parts_to_multipart_body(BodyList, ContentType, Size, Boundary) ->
66 HeaderList = [{"Content-Type",
67 ["multipart/byteranges; ",
68 "boundary=", Boundary]}],
69 MultiPartBody = multipart_body(BodyList, ContentType, Boundary, Size),
70
71 {HeaderList, MultiPartBody}.
72
73 %% @spec multipart_body([bodypart()], ContentType::string(),
74 %% Boundary::string(), Size::integer()) -> iolist()
75 %% @doc Return the representation of a multipart body for the given [bodypart()].
76 multipart_body([], _ContentType, Boundary, _Size) ->
77 ["--", Boundary, "--\r\n"];
78 multipart_body([{Start, End, Body} | BodyList], ContentType, Boundary, Size) ->
79 ["--", Boundary, "\r\n",
80 "Content-Type: ", ContentType, "\r\n",
81 "Content-Range: ",
82 "bytes ", mochiweb_util:make_io(Start), "-", mochiweb_util:make_io(End),
83 "/", mochiweb_util:make_io(Size), "\r\n\r\n",
84 Body, "\r\n"
85 | multipart_body(BodyList, ContentType, Boundary, Size)].
86
87 %% @spec parse_form(request()) -> [{string(), string() | formfile()}]
88 %% @doc Parse a multipart form from the given request using the in-memory
89 %% default_file_handler/2.
90 parse_form(Req) ->
91 parse_form(Req, fun default_file_handler/2).
92
93 %% @spec parse_form(request(), F::file_handler()) -> [{string(), string() | term()}]
94 %% @doc Parse a multipart form from the given request using the given file_handler().
95 parse_form(Req, FileHandler) ->
96 Callback = fun (Next) -> parse_form_outer(Next, FileHandler, []) end,
97 {_, _, Res} = parse_multipart_request(Req, Callback),
98 Res.
99
100 parse_form_outer(eof, _, Acc) ->
101 lists:reverse(Acc);
102 parse_form_outer({headers, H}, FileHandler, State) ->
103 {"form-data", H1} = proplists:get_value("content-disposition", H),
104 Name = proplists:get_value("name", H1),
105 Filename = proplists:get_value("filename", H1),
106 case Filename of
107 undefined ->
108 fun (Next) ->
109 parse_form_value(Next, {Name, []}, FileHandler, State)
110 end;
111 _ ->
112 ContentType = proplists:get_value("content-type", H),
113 Handler = FileHandler(Filename, ContentType),
114 fun (Next) ->
115 parse_form_file(Next, {Name, Handler}, FileHandler, State)
116 end
117 end.
118
119 parse_form_value(body_end, {Name, Acc}, FileHandler, State) ->
120 Value = binary_to_list(iolist_to_binary(lists:reverse(Acc))),
121 State1 = [{Name, Value} | State],
122 fun (Next) -> parse_form_outer(Next, FileHandler, State1) end;
123 parse_form_value({body, Data}, {Name, Acc}, FileHandler, State) ->
124 Acc1 = [Data | Acc],
125 fun (Next) -> parse_form_value(Next, {Name, Acc1}, FileHandler, State) end.
126
127 parse_form_file(body_end, {Name, Handler}, FileHandler, State) ->
128 Value = Handler(eof),
129 State1 = [{Name, Value} | State],
130 fun (Next) -> parse_form_outer(Next, FileHandler, State1) end;
131 parse_form_file({body, Data}, {Name, Handler}, FileHandler, State) ->
132 H1 = Handler(Data),
133 fun (Next) -> parse_form_file(Next, {Name, H1}, FileHandler, State) end.
134
135 default_file_handler(Filename, ContentType) ->
136 default_file_handler_1(Filename, ContentType, []).
137
138 default_file_handler_1(Filename, ContentType, Acc) ->
139 fun(eof) ->
140 Value = iolist_to_binary(lists:reverse(Acc)),
141 {Filename, ContentType, Value};
142 (Next) ->
143 default_file_handler_1(Filename, ContentType, [Next | Acc])
144 end.
145
146 parse_multipart_request(Req, Callback) ->
147 %% TODO: Support chunked?
148 Length = list_to_integer(Req:get_combined_header_value("content-length")),
149 Boundary = iolist_to_binary(
150 get_boundary(Req:get_header_value("content-type"))),
151 Prefix = <<"\r\n--", Boundary/binary>>,
152 BS = byte_size(Boundary),
153 Chunk = read_chunk(Req, Length),
154 Length1 = Length - byte_size(Chunk),
155 <<"--", Boundary:BS/binary, "\r\n", Rest/binary>> = Chunk,
156 feed_mp(headers, flash_multipart_hack(#mp{boundary=Prefix,
157 length=Length1,
158 buffer=Rest,
159 callback=Callback,
160 req=Req})).
161
162 parse_headers(<<>>) ->
163 [];
164 parse_headers(Binary) ->
165 parse_headers(Binary, []).
166
167 parse_headers(Binary, Acc) ->
168 case find_in_binary(<<"\r\n">>, Binary) of
169 {exact, N} ->
170 <<Line:N/binary, "\r\n", Rest/binary>> = Binary,
171 parse_headers(Rest, [split_header(Line) | Acc]);
172 not_found ->
173 lists:reverse([split_header(Binary) | Acc])
174 end.
175
176 split_header(Line) ->
177 {Name, [$: | Value]} = lists:splitwith(fun (C) -> C =/= $: end,
178 binary_to_list(Line)),
179 {string:to_lower(string:strip(Name)),
180 mochiweb_util:parse_header(Value)}.
181
182 read_chunk(Req, Length) when Length > 0 ->
183 case Length of
184 Length when Length < ?CHUNKSIZE ->
185 Req:recv(Length);
186 _ ->
187 Req:recv(?CHUNKSIZE)
188 end.
189
190 read_more(State=#mp{length=Length, buffer=Buffer, req=Req}) ->
191 Data = read_chunk(Req, Length),
192 Buffer1 = <<Buffer/binary, Data/binary>>,
193 flash_multipart_hack(State#mp{length=Length - byte_size(Data),
194 buffer=Buffer1}).
195
196 flash_multipart_hack(State=#mp{length=0, buffer=Buffer, boundary=Prefix}) ->
197 %% http://code.google.com/p/mochiweb/issues/detail?id=22
198 %% Flash doesn't terminate multipart with \r\n properly so we fix it up here
199 PrefixSize = size(Prefix),
200 case size(Buffer) - (2 + PrefixSize) of
201 Seek when Seek >= 0 ->
202 case Buffer of
203 <<_:Seek/binary, Prefix:PrefixSize/binary, "--">> ->
204 Buffer1 = <<Buffer/binary, "\r\n">>,
205 State#mp{buffer=Buffer1};
206 _ ->
207 State
208 end;
209 _ ->
210 State
211 end;
212 flash_multipart_hack(State) ->
213 State.
214
215 feed_mp(headers, State=#mp{buffer=Buffer, callback=Callback}) ->
216 {State1, P} = case find_in_binary(<<"\r\n\r\n">>, Buffer) of
217 {exact, N} ->
218 {State, N};
219 _ ->
220 S1 = read_more(State),
221 %% Assume headers must be less than ?CHUNKSIZE
222 {exact, N} = find_in_binary(<<"\r\n\r\n">>,
223 S1#mp.buffer),
224 {S1, N}
225 end,
226 <<Headers:P/binary, "\r\n\r\n", Rest/binary>> = State1#mp.buffer,
227 NextCallback = Callback({headers, parse_headers(Headers)}),
228 feed_mp(body, State1#mp{buffer=Rest,
229 callback=NextCallback});
230 feed_mp(body, State=#mp{boundary=Prefix, buffer=Buffer, callback=Callback}) ->
231 Boundary = find_boundary(Prefix, Buffer),
232 case Boundary of
233 {end_boundary, Start, Skip} ->
234 <<Data:Start/binary, _:Skip/binary, Rest/binary>> = Buffer,
235 C1 = Callback({body, Data}),
236 C2 = C1(body_end),
237 {State#mp.length, Rest, C2(eof)};
238 {next_boundary, Start, Skip} ->
239 <<Data:Start/binary, _:Skip/binary, Rest/binary>> = Buffer,
240 C1 = Callback({body, Data}),
241 feed_mp(headers, State#mp{callback=C1(body_end),
242 buffer=Rest});
243 {maybe, Start} ->
244 <<Data:Start/binary, Rest/binary>> = Buffer,
245 feed_mp(body, read_more(State#mp{callback=Callback({body, Data}),
246 buffer=Rest}));
247 not_found ->
248 {Data, Rest} = {Buffer, <<>>},
249 feed_mp(body, read_more(State#mp{callback=Callback({body, Data}),
250 buffer=Rest}))
251 end.
252
253 get_boundary(ContentType) ->
254 {"multipart/form-data", Opts} = mochiweb_util:parse_header(ContentType),
255 case proplists:get_value("boundary", Opts) of
256 S when is_list(S) ->
257 S
258 end.
259
260 %% @spec find_in_binary(Pattern::binary(), Data::binary()) ->
261 %% {exact, N} | {partial, N, K} | not_found
262 %% @doc Searches for the given pattern in the given binary.
263 find_in_binary(P, Data) when size(P) > 0 ->
264 PS = size(P),
265 DS = size(Data),
266 case DS - PS of
267 Last when Last < 0 ->
268 partial_find(P, Data, 0, DS);
269 Last ->
270 case binary:match(Data, P) of
271 {Pos, _} -> {exact, Pos};
272 nomatch -> partial_find(P, Data, Last+1, PS-1)
273 end
274 end.
275
276 partial_find(_B, _D, _N, 0) ->
277 not_found;
278 partial_find(B, D, N, K) ->
279 <<B1:K/binary, _/binary>> = B,
280 case D of
281 <<_Skip:N/binary, B1:K/binary>> ->
282 {partial, N, K};
283 _ ->
284 partial_find(B, D, 1 + N, K - 1)
285 end.
286
287 find_boundary(Prefix, Data) ->
288 case find_in_binary(Prefix, Data) of
289 {exact, Skip} ->
290 PrefixSkip = Skip + size(Prefix),
291 case Data of
292 <<_:PrefixSkip/binary, "\r\n", _/binary>> ->
293 {next_boundary, Skip, size(Prefix) + 2};
294 <<_:PrefixSkip/binary, "--\r\n", _/binary>> ->
295 {end_boundary, Skip, size(Prefix) + 4};
296 _ when size(Data) < PrefixSkip + 4 ->
297 %% Underflow
298 {maybe, Skip};
299 _ ->
300 %% False positive
301 not_found
302 end;
303 {partial, Skip, Length} when (Skip + Length) =:= size(Data) ->
304 %% Underflow
305 {maybe, Skip};
306 _ ->
307 not_found
308 end.
309
310 %%
311 %% Tests
312 %%
313 -ifdef(TEST).
314 -include_lib("eunit/include/eunit.hrl").
315
316 ssl_cert_opts() ->
317 EbinDir = filename:dirname(code:which(?MODULE)),
318 CertDir = filename:join([EbinDir, "..", "support", "test-materials"]),
319 CertFile = filename:join(CertDir, "test_ssl_cert.pem"),
320 KeyFile = filename:join(CertDir, "test_ssl_key.pem"),
321 [{certfile, CertFile}, {keyfile, KeyFile}].
322
323 with_socket_server(Transport, ServerFun, ClientFun) ->
324 ServerOpts0 = [{ip, "127.0.0.1"}, {port, 0}, {loop, ServerFun}],
325 ServerOpts = case Transport of
326 plain ->
327 ServerOpts0;
328 ssl ->
329 ServerOpts0 ++ [{ssl, true}, {ssl_opts, ssl_cert_opts()}]
330 end,
331 {ok, Server} = mochiweb_socket_server:start_link(ServerOpts),
332 Port = mochiweb_socket_server:get(Server, port),
333 ClientOpts = [binary, {active, false}],
334 {ok, Client} = case Transport of
335 plain ->
336 gen_tcp:connect("127.0.0.1", Port, ClientOpts);
337 ssl ->
338 ClientOpts1 = [{ssl_imp, new} | ClientOpts],
339 {ok, SslSocket} = ssl:connect("127.0.0.1", Port, ClientOpts1),
340 {ok, {ssl, SslSocket}}
341 end,
342 Res = (catch ClientFun(Client)),
343 mochiweb_socket_server:stop(Server),
344 Res.
345
346 fake_request(Socket, ContentType, Length) ->
347 mochiweb_request:new(Socket,
348 'POST',
349 "/multipart",
350 {1,1},
351 mochiweb_headers:make(
352 [{"content-type", ContentType},
353 {"content-length", Length}])).
354
355 test_callback({body, <<>>}, Rest=[body_end | _]) ->
356 %% When expecting the body_end we might get an empty binary
357 fun (Next) -> test_callback(Next, Rest) end;
358 test_callback({body, Got}, [{body, Expect} | Rest]) when Got =/= Expect ->
359 %% Partial response
360 GotSize = size(Got),
361 <<Got:GotSize/binary, Expect1/binary>> = Expect,
362 fun (Next) -> test_callback(Next, [{body, Expect1} | Rest]) end;
363 test_callback(Got, [Expect | Rest]) ->
364 ?assertEqual(Got, Expect),
365 case Rest of
366 [] ->
367 ok;
368 _ ->
369 fun (Next) -> test_callback(Next, Rest) end
370 end.
371
372 parse3_http_test() ->
373 parse3(plain).
374
375 parse3_https_test() ->
376 parse3(ssl).
377
378 parse3(Transport) ->
379 ContentType = "multipart/form-data; boundary=---------------------------7386909285754635891697677882",
380 BinContent = <<"-----------------------------7386909285754635891697677882\r\nContent-Disposition: form-data; name=\"hidden\"\r\n\r\nmultipart message\r\n-----------------------------7386909285754635891697677882\r\nContent-Disposition: form-data; name=\"file\"; filename=\"test_file.txt\"\r\nContent-Type: text/plain\r\n\r\nWoo multiline text file\n\nLa la la\r\n-----------------------------7386909285754635891697677882--\r\n">>,
381 Expect = [{headers,
382 [{"content-disposition",
383 {"form-data", [{"name", "hidden"}]}}]},
384 {body, <<"multipart message">>},
385 body_end,
386 {headers,
387 [{"content-disposition",
388 {"form-data", [{"name", "file"}, {"filename", "test_file.txt"}]}},
389 {"content-type", {"text/plain", []}}]},
390 {body, <<"Woo multiline text file\n\nLa la la">>},
391 body_end,
392 eof],
393 TestCallback = fun (Next) -> test_callback(Next, Expect) end,
394 ServerFun = fun (Socket, _Opts) ->
395 ok = mochiweb_socket:send(Socket, BinContent),
396 exit(normal)
397 end,
398 ClientFun = fun (Socket) ->
399 Req = fake_request(Socket, ContentType,
400 byte_size(BinContent)),
401 Res = parse_multipart_request(Req, TestCallback),
402 {0, <<>>, ok} = Res,
403 ok
404 end,
405 ok = with_socket_server(Transport, ServerFun, ClientFun),
406 ok.
407
408 parse2_http_test() ->
409 parse2(plain).
410
411 parse2_https_test() ->
412 parse2(ssl).
413
414 parse2(Transport) ->
415 ContentType = "multipart/form-data; boundary=---------------------------6072231407570234361599764024",
416 BinContent = <<"-----------------------------6072231407570234361599764024\r\nContent-Disposition: form-data; name=\"hidden\"\r\n\r\nmultipart message\r\n-----------------------------6072231407570234361599764024\r\nContent-Disposition: form-data; name=\"file\"; filename=\"\"\r\nContent-Type: application/octet-stream\r\n\r\n\r\n-----------------------------6072231407570234361599764024--\r\n">>,
417 Expect = [{headers,
418 [{"content-disposition",
419 {"form-data", [{"name", "hidden"}]}}]},
420 {body, <<"multipart message">>},
421 body_end,
422 {headers,
423 [{"content-disposition",
424 {"form-data", [{"name", "file"}, {"filename", ""}]}},
425 {"content-type", {"application/octet-stream", []}}]},
426 {body, <<>>},
427 body_end,
428 eof],
429 TestCallback = fun (Next) -> test_callback(Next, Expect) end,
430 ServerFun = fun (Socket, _Opts) ->
431 ok = mochiweb_socket:send(Socket, BinContent),
432 exit(normal)
433 end,
434 ClientFun = fun (Socket) ->
435 Req = fake_request(Socket, ContentType,
436 byte_size(BinContent)),
437 Res = parse_multipart_request(Req, TestCallback),
438 {0, <<>>, ok} = Res,
439 ok
440 end,
441 ok = with_socket_server(Transport, ServerFun, ClientFun),
442 ok.
443
444 parse_form_http_test() ->
445 do_parse_form(plain).
446
447 parse_form_https_test() ->
448 do_parse_form(ssl).
449
450 do_parse_form(Transport) ->
451 ContentType = "multipart/form-data; boundary=AaB03x",
452 "AaB03x" = get_boundary(ContentType),
453 Content = mochiweb_util:join(
454 ["--AaB03x",
455 "Content-Disposition: form-data; name=\"submit-name\"",
456 "",
457 "Larry",
458 "--AaB03x",
459 "Content-Disposition: form-data; name=\"files\";"
460 ++ "filename=\"file1.txt\"",
461 "Content-Type: text/plain",
462 "",
463 "... contents of file1.txt ...",
464 "--AaB03x--",
465 ""], "\r\n"),
466 BinContent = iolist_to_binary(Content),
467 ServerFun = fun (Socket, _Opts) ->
468 ok = mochiweb_socket:send(Socket, BinContent),
469 exit(normal)
470 end,
471 ClientFun = fun (Socket) ->
472 Req = fake_request(Socket, ContentType,
473 byte_size(BinContent)),
474 Res = parse_form(Req),
475 [{"submit-name", "Larry"},
476 {"files", {"file1.txt", {"text/plain",[]},
477 <<"... contents of file1.txt ...">>}
478 }] = Res,
479 ok
480 end,
481 ok = with_socket_server(Transport, ServerFun, ClientFun),
482 ok.
483
484 parse_http_test() ->
485 do_parse(plain).
486
487 parse_https_test() ->
488 do_parse(ssl).
489
490 do_parse(Transport) ->
491 ContentType = "multipart/form-data; boundary=AaB03x",
492 "AaB03x" = get_boundary(ContentType),
493 Content = mochiweb_util:join(
494 ["--AaB03x",
495 "Content-Disposition: form-data; name=\"submit-name\"",
496 "",
497 "Larry",
498 "--AaB03x",
499 "Content-Disposition: form-data; name=\"files\";"
500 ++ "filename=\"file1.txt\"",
501 "Content-Type: text/plain",
502 "",
503 "... contents of file1.txt ...",
504 "--AaB03x--",
505 ""], "\r\n"),
506 BinContent = iolist_to_binary(Content),
507 Expect = [{headers,
508 [{"content-disposition",
509 {"form-data", [{"name", "submit-name"}]}}]},
510 {body, <<"Larry">>},
511 body_end,
512 {headers,
513 [{"content-disposition",
514 {"form-data", [{"name", "files"}, {"filename", "file1.txt"}]}},
515 {"content-type", {"text/plain", []}}]},
516 {body, <<"... contents of file1.txt ...">>},
517 body_end,
518 eof],
519 TestCallback = fun (Next) -> test_callback(Next, Expect) end,
520 ServerFun = fun (Socket, _Opts) ->
521 ok = mochiweb_socket:send(Socket, BinContent),
522 exit(normal)
523 end,
524 ClientFun = fun (Socket) ->
525 Req = fake_request(Socket, ContentType,
526 byte_size(BinContent)),
527 Res = parse_multipart_request(Req, TestCallback),
528 {0, <<>>, ok} = Res,
529 ok
530 end,
531 ok = with_socket_server(Transport, ServerFun, ClientFun),
532 ok.
533
534 parse_partial_body_boundary_http_test() ->
535 parse_partial_body_boundary(plain).
536
537 parse_partial_body_boundary_https_test() ->
538 parse_partial_body_boundary(ssl).
539
540 parse_partial_body_boundary(Transport) ->
541 Boundary = string:copies("$", 2048),
542 ContentType = "multipart/form-data; boundary=" ++ Boundary,
543 ?assertEqual(Boundary, get_boundary(ContentType)),
544 Content = mochiweb_util:join(
545 ["--" ++ Boundary,
546 "Content-Disposition: form-data; name=\"submit-name\"",
547 "",
548 "Larry",
549 "--" ++ Boundary,
550 "Content-Disposition: form-data; name=\"files\";"
551 ++ "filename=\"file1.txt\"",
552 "Content-Type: text/plain",
553 "",
554 "... contents of file1.txt ...",
555 "--" ++ Boundary ++ "--",
556 ""], "\r\n"),
557 BinContent = iolist_to_binary(Content),
558 Expect = [{headers,
559 [{"content-disposition",
560 {"form-data", [{"name", "submit-name"}]}}]},
561 {body, <<"Larry">>},
562 body_end,
563 {headers,
564 [{"content-disposition",
565 {"form-data", [{"name", "files"}, {"filename", "file1.txt"}]}},
566 {"content-type", {"text/plain", []}}
567 ]},
568 {body, <<"... contents of file1.txt ...">>},
569 body_end,
570 eof],
571 TestCallback = fun (Next) -> test_callback(Next, Expect) end,
572 ServerFun = fun (Socket, _Opts) ->
573 ok = mochiweb_socket:send(Socket, BinContent),
574 exit(normal)
575 end,
576 ClientFun = fun (Socket) ->
577 Req = fake_request(Socket, ContentType,
578 byte_size(BinContent)),
579 Res = parse_multipart_request(Req, TestCallback),
580 {0, <<>>, ok} = Res,
581 ok
582 end,
583 ok = with_socket_server(Transport, ServerFun, ClientFun),
584 ok.
585
586 parse_large_header_http_test() ->
587 parse_large_header(plain).
588
589 parse_large_header_https_test() ->
590 parse_large_header(ssl).
591
592 parse_large_header(Transport) ->
593 ContentType = "multipart/form-data; boundary=AaB03x",
594 "AaB03x" = get_boundary(ContentType),
595 Content = mochiweb_util:join(
596 ["--AaB03x",
597 "Content-Disposition: form-data; name=\"submit-name\"",
598 "",
599 "Larry",
600 "--AaB03x",
601 "Content-Disposition: form-data; name=\"files\";"
602 ++ "filename=\"file1.txt\"",
603 "Content-Type: text/plain",
604 "x-large-header: " ++ string:copies("%", 4096),
605 "",
606 "... contents of file1.txt ...",
607 "--AaB03x--",
608 ""], "\r\n"),
609 BinContent = iolist_to_binary(Content),
610 Expect = [{headers,
611 [{"content-disposition",
612 {"form-data", [{"name", "submit-name"}]}}]},
613 {body, <<"Larry">>},
614 body_end,
615 {headers,
616 [{"content-disposition",
617 {"form-data", [{"name", "files"}, {"filename", "file1.txt"}]}},
618 {"content-type", {"text/plain", []}},
619 {"x-large-header", {string:copies("%", 4096), []}}
620 ]},
621 {body, <<"... contents of file1.txt ...">>},
622 body_end,
623 eof],
624 TestCallback = fun (Next) -> test_callback(Next, Expect) end,
625 ServerFun = fun (Socket, _Opts) ->
626 ok = mochiweb_socket:send(Socket, BinContent),
627 exit(normal)
628 end,
629 ClientFun = fun (Socket) ->
630 Req = fake_request(Socket, ContentType,
631 byte_size(BinContent)),
632 Res = parse_multipart_request(Req, TestCallback),
633 {0, <<>>, ok} = Res,
634 ok
635 end,
636 ok = with_socket_server(Transport, ServerFun, ClientFun),
637 ok.
638
639 find_boundary_test() ->
640 B = <<"\r\n--X">>,
641 {next_boundary, 0, 7} = find_boundary(B, <<"\r\n--X\r\nRest">>),
642 {next_boundary, 1, 7} = find_boundary(B, <<"!\r\n--X\r\nRest">>),
643 {end_boundary, 0, 9} = find_boundary(B, <<"\r\n--X--\r\nRest">>),
644 {end_boundary, 1, 9} = find_boundary(B, <<"!\r\n--X--\r\nRest">>),
645 not_found = find_boundary(B, <<"--X\r\nRest">>),
646 {maybe, 0} = find_boundary(B, <<"\r\n--X\r">>),
647 {maybe, 1} = find_boundary(B, <<"!\r\n--X\r">>),
648 P = <<"\r\n-----------------------------16037454351082272548568224146">>,
649 B0 = <<55,212,131,77,206,23,216,198,35,87,252,118,252,8,25,211,132,229,
650 182,42,29,188,62,175,247,243,4,4,0,59, 13,10,45,45,45,45,45,45,45,
651 45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,
652 49,54,48,51,55,52,53,52,51,53,49>>,
653 {maybe, 30} = find_boundary(P, B0),
654 not_found = find_boundary(B, <<"\r\n--XJOPKE">>),
655 ok.
656
657 find_in_binary_test() ->
658 {exact, 0} = find_in_binary(<<"foo">>, <<"foobarbaz">>),
659 {exact, 1} = find_in_binary(<<"oo">>, <<"foobarbaz">>),
660 {exact, 8} = find_in_binary(<<"z">>, <<"foobarbaz">>),
661 not_found = find_in_binary(<<"q">>, <<"foobarbaz">>),
662 {partial, 7, 2} = find_in_binary(<<"azul">>, <<"foobarbaz">>),
663 {exact, 0} = find_in_binary(<<"foobarbaz">>, <<"foobarbaz">>),
664 {partial, 0, 3} = find_in_binary(<<"foobar">>, <<"foo">>),
665 {partial, 1, 3} = find_in_binary(<<"foobar">>, <<"afoo">>),
666 ok.
667
668 flash_parse_http_test() ->
669 flash_parse(plain).
670
671 flash_parse_https_test() ->
672 flash_parse(ssl).
673
674 flash_parse(Transport) ->
675 ContentType = "multipart/form-data; boundary=----------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5",
676 "----------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5" = get_boundary(ContentType),
677 BinContent = <<"------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"Filename\"\r\n\r\nhello.txt\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"success_action_status\"\r\n\r\n201\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"file\"; filename=\"hello.txt\"\r\nContent-Type: application/octet-stream\r\n\r\nhello\n\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"Upload\"\r\n\r\nSubmit Query\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5--">>,
678 Expect = [{headers,
679 [{"content-disposition",
680 {"form-data", [{"name", "Filename"}]}}]},
681 {body, <<"hello.txt">>},
682 body_end,
683 {headers,
684 [{"content-disposition",
685 {"form-data", [{"name", "success_action_status"}]}}]},
686 {body, <<"201">>},
687 body_end,
688 {headers,
689 [{"content-disposition",
690 {"form-data", [{"name", "file"}, {"filename", "hello.txt"}]}},
691 {"content-type", {"application/octet-stream", []}}]},
692 {body, <<"hello\n">>},
693 body_end,
694 {headers,
695 [{"content-disposition",
696 {"form-data", [{"name", "Upload"}]}}]},
697 {body, <<"Submit Query">>},
698 body_end,
699 eof],
700 TestCallback = fun (Next) -> test_callback(Next, Expect) end,
701 ServerFun = fun (Socket, _Opts) ->
702 ok = mochiweb_socket:send(Socket, BinContent),
703 exit(normal)
704 end,
705 ClientFun = fun (Socket) ->
706 Req = fake_request(Socket, ContentType,
707 byte_size(BinContent)),
708 Res = parse_multipart_request(Req, TestCallback),
709 {0, <<>>, ok} = Res,
710 ok
711 end,
712 ok = with_socket_server(Transport, ServerFun, ClientFun),
713 ok.
714
715 flash_parse2_http_test() ->
716 flash_parse2(plain).
717
718 flash_parse2_https_test() ->
719 flash_parse2(ssl).
720
721 flash_parse2(Transport) ->
722 ContentType = "multipart/form-data; boundary=----------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5",
723 "----------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5" = get_boundary(ContentType),
724 Chunk = iolist_to_binary(string:copies("%", 4096)),
725 BinContent = <<"------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"Filename\"\r\n\r\nhello.txt\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"success_action_status\"\r\n\r\n201\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"file\"; filename=\"hello.txt\"\r\nContent-Type: application/octet-stream\r\n\r\n", Chunk/binary, "\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"Upload\"\r\n\r\nSubmit Query\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5--">>,
726 Expect = [{headers,
727 [{"content-disposition",
728 {"form-data", [{"name", "Filename"}]}}]},
729 {body, <<"hello.txt">>},
730 body_end,
731 {headers,
732 [{"content-disposition",
733 {"form-data", [{"name", "success_action_status"}]}}]},
734 {body, <<"201">>},
735 body_end,
736 {headers,
737 [{"content-disposition",
738 {"form-data", [{"name", "file"}, {"filename", "hello.txt"}]}},
739 {"content-type", {"application/octet-stream", []}}]},
740 {body, Chunk},
741 body_end,
742 {headers,
743 [{"content-disposition",
744 {"form-data", [{"name", "Upload"}]}}]},
745 {body, <<"Submit Query">>},
746 body_end,
747 eof],
748 TestCallback = fun (Next) -> test_callback(Next, Expect) end,
749 ServerFun = fun (Socket, _Opts) ->
750 ok = mochiweb_socket:send(Socket, BinContent),
751 exit(normal)
752 end,
753 ClientFun = fun (Socket) ->
754 Req = fake_request(Socket, ContentType,
755 byte_size(BinContent)),
756 Res = parse_multipart_request(Req, TestCallback),
757 {0, <<>>, ok} = Res,
758 ok
759 end,
760 ok = with_socket_server(Transport, ServerFun, ClientFun),
761 ok.
762
763 parse_headers_test() ->
764 ?assertEqual([], parse_headers(<<>>)).
765
766 flash_multipart_hack_test() ->
767 Buffer = <<"prefix-">>,
768 Prefix = <<"prefix">>,
769 State = #mp{length=0, buffer=Buffer, boundary=Prefix},
770 ?assertEqual(State,
771 flash_multipart_hack(State)).
772
773 parts_to_body_single_test() ->
774 {HL, B} = parts_to_body([{0, 5, <<"01234">>}],
775 "text/plain",
776 10),
777 [{"Content-Range", Range},
778 {"Content-Type", Type}] = lists:sort(HL),
779 ?assertEqual(
780 <<"bytes 0-5/10">>,
781 iolist_to_binary(Range)),
782 ?assertEqual(
783 <<"text/plain">>,
784 iolist_to_binary(Type)),
785 ?assertEqual(
786 <<"01234">>,
787 iolist_to_binary(B)),
788 ok.
789
790 parts_to_body_multi_test() ->
791 {[{"Content-Type", Type}],
792 _B} = parts_to_body([{0, 5, <<"01234">>}, {5, 10, <<"56789">>}],
793 "text/plain",
794 10),
795 ?assertMatch(
796 <<"multipart/byteranges; boundary=", _/binary>>,
797 iolist_to_binary(Type)),
798 ok.
799
800 parts_to_multipart_body_test() ->
801 {[{"Content-Type", V}], B} = parts_to_multipart_body(
802 [{0, 5, <<"01234">>}, {5, 10, <<"56789">>}],
803 "text/plain",
804 10,
805 "BOUNDARY"),
806 MB = multipart_body(
807 [{0, 5, <<"01234">>}, {5, 10, <<"56789">>}],
808 "text/plain",
809 "BOUNDARY",
810 10),
811 ?assertEqual(
812 <<"multipart/byteranges; boundary=BOUNDARY">>,
813 iolist_to_binary(V)),
814 ?assertEqual(
815 iolist_to_binary(MB),
816 iolist_to_binary(B)),
817 ok.
818
819 multipart_body_test() ->
820 ?assertEqual(
821 <<"--BOUNDARY--\r\n">>,
822 iolist_to_binary(multipart_body([], "text/plain", "BOUNDARY", 0))),
823 ?assertEqual(
824 <<"--BOUNDARY\r\n"
825 "Content-Type: text/plain\r\n"
826 "Content-Range: bytes 0-5/10\r\n\r\n"
827 "01234\r\n"
828 "--BOUNDARY\r\n"
829 "Content-Type: text/plain\r\n"
830 "Content-Range: bytes 5-10/10\r\n\r\n"
831 "56789\r\n"
832 "--BOUNDARY--\r\n">>,
833 iolist_to_binary(multipart_body([{0, 5, <<"01234">>}, {5, 10, <<"56789">>}],
834 "text/plain",
835 "BOUNDARY",
836 10))),
837 ok.
838
839 %% @todo Move somewhere more appropriate than in the test suite
840
841 multipart_parsing_benchmark_test() ->
842 run_multipart_parsing_benchmark(1).
843
844 run_multipart_parsing_benchmark(0) -> ok;
845 run_multipart_parsing_benchmark(N) ->
846 multipart_parsing_benchmark(),
847 run_multipart_parsing_benchmark(N-1).
848
849 multipart_parsing_benchmark() ->
850 ContentType = "multipart/form-data; boundary=----------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5",
851 Chunk = binary:copy(<<"This Is_%Some=Quite0Long4String2Used9For7BenchmarKing.5">>, 102400),
852 BinContent = <<"------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"Filename\"\r\n\r\nhello.txt\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"success_action_status\"\r\n\r\n201\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"file\"; filename=\"hello.txt\"\r\nContent-Type: application/octet-stream\r\n\r\n", Chunk/binary, "\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"Upload\"\r\n\r\nSubmit Query\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5--">>,
853 Expect = [{headers,
854 [{"content-disposition",
855 {"form-data", [{"name", "Filename"}]}}]},
856 {body, <<"hello.txt">>},
857 body_end,
858 {headers,
859 [{"content-disposition",
860 {"form-data", [{"name", "success_action_status"}]}}]},
861 {body, <<"201">>},
862 body_end,
863 {headers,
864 [{"content-disposition",
865 {"form-data", [{"name", "file"}, {"filename", "hello.txt"}]}},
866 {"content-type", {"application/octet-stream", []}}]},
867 {body, Chunk},
868 body_end,
869 {headers,
870 [{"content-disposition",
871 {"form-data", [{"name", "Upload"}]}}]},
872 {body, <<"Submit Query">>},
873 body_end,
874 eof],
875 TestCallback = fun (Next) -> test_callback(Next, Expect) end,
876 ServerFun = fun (Socket, _Opts) ->
877 ok = mochiweb_socket:send(Socket, BinContent),
878 exit(normal)
879 end,
880 ClientFun = fun (Socket) ->
881 Req = fake_request(Socket, ContentType,
882 byte_size(BinContent)),
883 Res = parse_multipart_request(Req, TestCallback),
884 {0, <<>>, ok} = Res,
885 ok
886 end,
887 ok = with_socket_server(plain, ServerFun, ClientFun),
888 ok.
889 -endif.
+0
-904
deps/mochiweb/src/mochiweb_request.erl less more
0 %% @author Bob Ippolito <bob@mochimedia.com>
1 %% @copyright 2007 Mochi Media, Inc.
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 %% @doc MochiWeb HTTP Request abstraction.
22
23 -module(mochiweb_request).
24 -author('bob@mochimedia.com').
25
26 -include_lib("kernel/include/file.hrl").
27 -include("internal.hrl").
28
29 -define(QUIP, "Any of you quaids got a smint?").
30
31 -export([new/5, new/6]).
32 -export([get_header_value/2, get_primary_header_value/2, get_combined_header_value/2, get/2, dump/1]).
33 -export([send/2, recv/2, recv/3, recv_body/1, recv_body/2, stream_body/4, stream_body/5]).
34 -export([start_response/2, start_response_length/2, start_raw_response/2]).
35 -export([respond/2, ok/2]).
36 -export([not_found/1, not_found/2]).
37 -export([parse_post/1, parse_qs/1]).
38 -export([should_close/1, cleanup/1]).
39 -export([parse_cookie/1, get_cookie_value/2]).
40 -export([serve_file/3, serve_file/4]).
41 -export([accepted_encodings/2]).
42 -export([accepts_content_type/2, accepted_content_types/2]).
43
44 -define(SAVE_QS, mochiweb_request_qs).
45 -define(SAVE_PATH, mochiweb_request_path).
46 -define(SAVE_RECV, mochiweb_request_recv).
47 -define(SAVE_BODY, mochiweb_request_body).
48 -define(SAVE_BODY_LENGTH, mochiweb_request_body_length).
49 -define(SAVE_POST, mochiweb_request_post).
50 -define(SAVE_COOKIE, mochiweb_request_cookie).
51 -define(SAVE_FORCE_CLOSE, mochiweb_request_force_close).
52
53 %% @type key() = atom() | string() | binary()
54 %% @type value() = atom() | string() | binary() | integer()
55 %% @type headers(). A mochiweb_headers structure.
56 %% @type request(). A mochiweb_request parameterized module instance.
57 %% @type response(). A mochiweb_response parameterized module instance.
58 %% @type ioheaders() = headers() | [{key(), value()}].
59
60 % 5 minute default idle timeout
61 -define(IDLE_TIMEOUT, 300000).
62
63 % Maximum recv_body() length of 1MB
64 -define(MAX_RECV_BODY, (1024*1024)).
65
66 %% @spec new(Socket, Method, RawPath, Version, headers()) -> request()
67 %% @doc Create a new request instance.
68 new(Socket, Method, RawPath, Version, Headers) ->
69 new(Socket, [], Method, RawPath, Version, Headers).
70
71 %% @spec new(Socket, Opts, Method, RawPath, Version, headers()) -> request()
72 %% @doc Create a new request instance.
73 new(Socket, Opts, Method, RawPath, Version, Headers) ->
74 {?MODULE, [Socket, Opts, Method, RawPath, Version, Headers]}.
75
76 %% @spec get_header_value(K, request()) -> undefined | Value
77 %% @doc Get the value of a given request header.
78 get_header_value(K, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, Headers]}) ->
79 mochiweb_headers:get_value(K, Headers).
80
81 get_primary_header_value(K, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, Headers]}) ->
82 mochiweb_headers:get_primary_value(K, Headers).
83
84 get_combined_header_value(K, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, Headers]}) ->
85 mochiweb_headers:get_combined_value(K, Headers).
86
87 %% @type field() = socket | scheme | method | raw_path | version | headers | peer | path | body_length | range
88
89 %% @spec get(field(), request()) -> term()
90 %% @doc Return the internal representation of the given field. If
91 %% <code>socket</code> is requested on a HTTPS connection, then
92 %% an ssl socket will be returned as <code>{ssl, SslSocket}</code>.
93 %% You can use <code>SslSocket</code> with the <code>ssl</code>
94 %% application, eg: <code>ssl:peercert(SslSocket)</code>.
95 get(socket, {?MODULE, [Socket, _Opts, _Method, _RawPath, _Version, _Headers]}) ->
96 Socket;
97 get(scheme, {?MODULE, [Socket, _Opts, _Method, _RawPath, _Version, _Headers]}) ->
98 case mochiweb_socket:type(Socket) of
99 plain ->
100 http;
101 ssl ->
102 https
103 end;
104 get(method, {?MODULE, [_Socket, _Opts, Method, _RawPath, _Version, _Headers]}) ->
105 Method;
106 get(raw_path, {?MODULE, [_Socket, _Opts, _Method, RawPath, _Version, _Headers]}) ->
107 RawPath;
108 get(version, {?MODULE, [_Socket, _Opts, _Method, _RawPath, Version, _Headers]}) ->
109 Version;
110 get(headers, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, Headers]}) ->
111 Headers;
112 get(peer, {?MODULE, [Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
113 case mochiweb_socket:peername(Socket) of
114 {ok, {Addr={10, _, _, _}, _Port}} ->
115 case get_header_value("x-forwarded-for", THIS) of
116 undefined ->
117 inet_parse:ntoa(Addr);
118 Hosts ->
119 string:strip(lists:last(string:tokens(Hosts, ",")))
120 end;
121 {ok, {{127, 0, 0, 1}, _Port}} ->
122 case get_header_value("x-forwarded-for", THIS) of
123 undefined ->
124 "127.0.0.1";
125 Hosts ->
126 string:strip(lists:last(string:tokens(Hosts, ",")))
127 end;
128 {ok, {Addr, _Port}} ->
129 inet_parse:ntoa(Addr);
130 {error, enotconn} ->
131 exit(normal)
132 end;
133 get(path, {?MODULE, [_Socket, _Opts, _Method, RawPath, _Version, _Headers]}) ->
134 case erlang:get(?SAVE_PATH) of
135 undefined ->
136 {Path0, _, _} = mochiweb_util:urlsplit_path(RawPath),
137 Path = mochiweb_util:unquote(Path0),
138 put(?SAVE_PATH, Path),
139 Path;
140 Cached ->
141 Cached
142 end;
143 get(body_length, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
144 case erlang:get(?SAVE_BODY_LENGTH) of
145 undefined ->
146 BodyLength = body_length(THIS),
147 put(?SAVE_BODY_LENGTH, {cached, BodyLength}),
148 BodyLength;
149 {cached, Cached} ->
150 Cached
151 end;
152 get(range, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
153 case get_header_value(range, THIS) of
154 undefined ->
155 undefined;
156 RawRange ->
157 mochiweb_http:parse_range_request(RawRange)
158 end;
159 get(opts, {?MODULE, [_Socket, Opts, _Method, _RawPath, _Version, _Headers]}) ->
160 Opts.
161
162 %% @spec dump(request()) -> {mochiweb_request, [{atom(), term()}]}
163 %% @doc Dump the internal representation to a "human readable" set of terms
164 %% for debugging/inspection purposes.
165 dump({?MODULE, [_Socket, Opts, Method, RawPath, Version, Headers]}) ->
166 {?MODULE, [{method, Method},
167 {version, Version},
168 {raw_path, RawPath},
169 {opts, Opts},
170 {headers, mochiweb_headers:to_list(Headers)}]}.
171
172 %% @spec send(iodata(), request()) -> ok
173 %% @doc Send data over the socket.
174 send(Data, {?MODULE, [Socket, _Opts, _Method, _RawPath, _Version, _Headers]}) ->
175 case mochiweb_socket:send(Socket, Data) of
176 ok ->
177 ok;
178 _ ->
179 exit(normal)
180 end.
181
182 %% @spec recv(integer(), request()) -> binary()
183 %% @doc Receive Length bytes from the client as a binary, with the default
184 %% idle timeout.
185 recv(Length, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
186 recv(Length, ?IDLE_TIMEOUT, THIS).
187
188 %% @spec recv(integer(), integer(), request()) -> binary()
189 %% @doc Receive Length bytes from the client as a binary, with the given
190 %% Timeout in msec.
191 recv(Length, Timeout, {?MODULE, [Socket, _Opts, _Method, _RawPath, _Version, _Headers]}) ->
192 case mochiweb_socket:recv(Socket, Length, Timeout) of
193 {ok, Data} ->
194 put(?SAVE_RECV, true),
195 Data;
196 _ ->
197 exit(normal)
198 end.
199
200 %% @spec body_length(request()) -> undefined | chunked | unknown_transfer_encoding | integer()
201 %% @doc Infer body length from transfer-encoding and content-length headers.
202 body_length({?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
203 case get_header_value("transfer-encoding", THIS) of
204 undefined ->
205 case get_combined_header_value("content-length", THIS) of
206 undefined ->
207 undefined;
208 Length ->
209 list_to_integer(Length)
210 end;
211 "chunked" ->
212 chunked;
213 Unknown ->
214 {unknown_transfer_encoding, Unknown}
215 end.
216
217
218 %% @spec recv_body(request()) -> binary()
219 %% @doc Receive the body of the HTTP request (defined by Content-Length).
220 %% Will only receive up to the default max-body length of 1MB.
221 recv_body({?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
222 recv_body(?MAX_RECV_BODY, THIS).
223
224 %% @spec recv_body(integer(), request()) -> binary()
225 %% @doc Receive the body of the HTTP request (defined by Content-Length).
226 %% Will receive up to MaxBody bytes.
227 recv_body(MaxBody, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
228 case erlang:get(?SAVE_BODY) of
229 undefined ->
230 % we could use a sane constant for max chunk size
231 Body = stream_body(?MAX_RECV_BODY, fun
232 ({0, _ChunkedFooter}, {_LengthAcc, BinAcc}) ->
233 iolist_to_binary(lists:reverse(BinAcc));
234 ({Length, Bin}, {LengthAcc, BinAcc}) ->
235 NewLength = Length + LengthAcc,
236 if NewLength > MaxBody ->
237 exit({body_too_large, chunked});
238 true ->
239 {NewLength, [Bin | BinAcc]}
240 end
241 end, {0, []}, MaxBody, THIS),
242 put(?SAVE_BODY, Body),
243 Body;
244 Cached -> Cached
245 end.
246
247 stream_body(MaxChunkSize, ChunkFun, FunState, {?MODULE,[_Socket,_Opts,_Method,_RawPath,_Version,_Headers]}=THIS) ->
248 stream_body(MaxChunkSize, ChunkFun, FunState, undefined, THIS).
249
250 stream_body(MaxChunkSize, ChunkFun, FunState, MaxBodyLength,
251 {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
252 Expect = case get_header_value("expect", THIS) of
253 undefined ->
254 undefined;
255 Value when is_list(Value) ->
256 string:to_lower(Value)
257 end,
258 case Expect of
259 "100-continue" ->
260 _ = start_raw_response({100, gb_trees:empty()}, THIS),
261 ok;
262 _Else ->
263 ok
264 end,
265 case body_length(THIS) of
266 undefined ->
267 undefined;
268 {unknown_transfer_encoding, Unknown} ->
269 exit({unknown_transfer_encoding, Unknown});
270 chunked ->
271 % In this case the MaxBody is actually used to
272 % determine the maximum allowed size of a single
273 % chunk.
274 stream_chunked_body(MaxChunkSize, ChunkFun, FunState, THIS);
275 0 ->
276 <<>>;
277 Length when is_integer(Length) ->
278 case MaxBodyLength of
279 MaxBodyLength when is_integer(MaxBodyLength), MaxBodyLength < Length ->
280 exit({body_too_large, content_length});
281 _ ->
282 stream_unchunked_body(MaxChunkSize,Length, ChunkFun, FunState, THIS)
283 end
284 end.
285
286
287 %% @spec start_response({integer(), ioheaders()}, request()) -> response()
288 %% @doc Start the HTTP response by sending the Code HTTP response and
289 %% ResponseHeaders. The server will set header defaults such as Server
290 %% and Date if not present in ResponseHeaders.
291 start_response({Code, ResponseHeaders}, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
292 start_raw_response({Code, ResponseHeaders}, THIS).
293
294 %% @spec start_raw_response({integer(), headers()}, request()) -> response()
295 %% @doc Start the HTTP response by sending the Code HTTP response and
296 %% ResponseHeaders.
297 start_raw_response({Code, ResponseHeaders}, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
298 {Header, Response} = format_response_header({Code, ResponseHeaders}, THIS),
299 send(Header, THIS),
300 Response.
301
302
303 %% @spec start_response_length({integer(), ioheaders(), integer()}, request()) -> response()
304 %% @doc Start the HTTP response by sending the Code HTTP response and
305 %% ResponseHeaders including a Content-Length of Length. The server
306 %% will set header defaults such as Server
307 %% and Date if not present in ResponseHeaders.
308 start_response_length({Code, ResponseHeaders, Length},
309 {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
310 HResponse = mochiweb_headers:make(ResponseHeaders),
311 HResponse1 = mochiweb_headers:enter("Content-Length", Length, HResponse),
312 start_response({Code, HResponse1}, THIS).
313
314 %% @spec format_response_header({integer(), ioheaders()} | {integer(), ioheaders(), integer()}, request()) -> iolist()
315 %% @doc Format the HTTP response header, including the Code HTTP response and
316 %% ResponseHeaders including an optional Content-Length of Length. The server
317 %% will set header defaults such as Server
318 %% and Date if not present in ResponseHeaders.
319 format_response_header({Code, ResponseHeaders}, {?MODULE, [_Socket, _Opts, _Method, _RawPath, Version, _Headers]}=THIS) ->
320 HResponse = mochiweb_headers:make(ResponseHeaders),
321 HResponse1 = mochiweb_headers:default_from_list(server_headers(), HResponse),
322 HResponse2 = case should_close(THIS) of
323 true ->
324 mochiweb_headers:enter("Connection", "close", HResponse1);
325 false ->
326 HResponse1
327 end,
328 End = [[mochiweb_util:make_io(K), <<": ">>, V, <<"\r\n">>]
329 || {K, V} <- mochiweb_headers:to_list(HResponse2)],
330 Response = mochiweb:new_response({THIS, Code, HResponse2}),
331 {[make_version(Version), make_code(Code), <<"\r\n">> | [End, <<"\r\n">>]], Response};
332 format_response_header({Code, ResponseHeaders, Length},
333 {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
334 HResponse = mochiweb_headers:make(ResponseHeaders),
335 HResponse1 = mochiweb_headers:enter("Content-Length", Length, HResponse),
336 format_response_header({Code, HResponse1}, THIS).
337
338 %% @spec respond({integer(), ioheaders(), iodata() | chunked | {file, IoDevice}}, request()) -> response()
339 %% @doc Start the HTTP response with start_response, and send Body to the
340 %% client (if the get(method) /= 'HEAD'). The Content-Length header
341 %% will be set by the Body length, and the server will insert header
342 %% defaults.
343 respond({Code, ResponseHeaders, {file, IoDevice}},
344 {?MODULE, [_Socket, _Opts, Method, _RawPath, _Version, _Headers]}=THIS) ->
345 Length = mochiweb_io:iodevice_size(IoDevice),
346 Response = start_response_length({Code, ResponseHeaders, Length}, THIS),
347 case Method of
348 'HEAD' ->
349 ok;
350 _ ->
351 mochiweb_io:iodevice_stream(
352 fun (Body) -> send(Body, THIS) end,
353 IoDevice)
354 end,
355 Response;
356 respond({Code, ResponseHeaders, chunked}, {?MODULE, [_Socket, _Opts, Method, _RawPath, Version, _Headers]}=THIS) ->
357 HResponse = mochiweb_headers:make(ResponseHeaders),
358 HResponse1 = case Method of
359 'HEAD' ->
360 %% This is what Google does, http://www.google.com/
361 %% is chunked but HEAD gets Content-Length: 0.
362 %% The RFC is ambiguous so emulating Google is smart.
363 mochiweb_headers:enter("Content-Length", "0",
364 HResponse);
365 _ when Version >= {1, 1} ->
366 %% Only use chunked encoding for HTTP/1.1
367 mochiweb_headers:enter("Transfer-Encoding", "chunked",
368 HResponse);
369 _ ->
370 %% For pre-1.1 clients we send the data as-is
371 %% without a Content-Length header and without
372 %% chunk delimiters. Since the end of the document
373 %% is now ambiguous we must force a close.
374 put(?SAVE_FORCE_CLOSE, true),
375 HResponse
376 end,
377 start_response({Code, HResponse1}, THIS);
378 respond({Code, ResponseHeaders, Body}, {?MODULE, [_Socket, _Opts, Method, _RawPath, _Version, _Headers]}=THIS) ->
379 {Header, Response} = format_response_header({Code, ResponseHeaders, iolist_size(Body)}, THIS),
380 case Method of
381 'HEAD' -> send(Header, THIS);
382 _ -> send([Header, Body], THIS)
383 end,
384 Response.
385
386 %% @spec not_found(request()) -> response()
387 %% @doc Alias for <code>not_found([])</code>.
388 not_found({?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
389 not_found([], THIS).
390
391 %% @spec not_found(ExtraHeaders, request()) -> response()
392 %% @doc Alias for <code>respond({404, [{"Content-Type", "text/plain"}
393 %% | ExtraHeaders], &lt;&lt;"Not found."&gt;&gt;})</code>.
394 not_found(ExtraHeaders, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
395 respond({404, [{"Content-Type", "text/plain"} | ExtraHeaders],
396 <<"Not found.">>}, THIS).
397
398 %% @spec ok({value(), iodata()} | {value(), ioheaders(), iodata() | {file, IoDevice}}, request()) ->
399 %% response()
400 %% @doc respond({200, [{"Content-Type", ContentType} | Headers], Body}).
401 ok({ContentType, Body}, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
402 ok({ContentType, [], Body}, THIS);
403 ok({ContentType, ResponseHeaders, Body}, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
404 HResponse = mochiweb_headers:make(ResponseHeaders),
405 case THIS:get(range) of
406 X when (X =:= undefined orelse X =:= fail) orelse Body =:= chunked ->
407 %% http://code.google.com/p/mochiweb/issues/detail?id=54
408 %% Range header not supported when chunked, return 200 and provide
409 %% full response.
410 HResponse1 = mochiweb_headers:enter("Content-Type", ContentType,
411 HResponse),
412 respond({200, HResponse1, Body}, THIS);
413 Ranges ->
414 {PartList, Size} = range_parts(Body, Ranges),
415 case PartList of
416 [] -> %% no valid ranges
417 HResponse1 = mochiweb_headers:enter("Content-Type",
418 ContentType,
419 HResponse),
420 %% could be 416, for now we'll just return 200
421 respond({200, HResponse1, Body}, THIS);
422 PartList ->
423 {RangeHeaders, RangeBody} =
424 mochiweb_multipart:parts_to_body(PartList, ContentType, Size),
425 HResponse1 = mochiweb_headers:enter_from_list(
426 [{"Accept-Ranges", "bytes"} |
427 RangeHeaders],
428 HResponse),
429 respond({206, HResponse1, RangeBody}, THIS)
430 end
431 end.
432
433 %% @spec should_close(request()) -> bool()
434 %% @doc Return true if the connection must be closed. If false, using
435 %% Keep-Alive should be safe.
436 should_close({?MODULE, [_Socket, _Opts, _Method, _RawPath, Version, _Headers]}=THIS) ->
437 ForceClose = erlang:get(?SAVE_FORCE_CLOSE) =/= undefined,
438 DidNotRecv = erlang:get(?SAVE_RECV) =:= undefined,
439 ForceClose orelse Version < {1, 0}
440 %% Connection: close
441 orelse is_close(get_header_value("connection", THIS))
442 %% HTTP 1.0 requires Connection: Keep-Alive
443 orelse (Version =:= {1, 0}
444 andalso get_header_value("connection", THIS) =/= "Keep-Alive")
445 %% unread data left on the socket, can't safely continue
446 orelse (DidNotRecv
447 andalso get_combined_header_value("content-length", THIS) =/= undefined
448 andalso list_to_integer(get_combined_header_value("content-length", THIS)) > 0)
449 orelse (DidNotRecv
450 andalso get_header_value("transfer-encoding", THIS) =:= "chunked").
451
452 is_close("close") ->
453 true;
454 is_close(S=[_C, _L, _O, _S, _E]) ->
455 string:to_lower(S) =:= "close";
456 is_close(_) ->
457 false.
458
459 %% @spec cleanup(request()) -> ok
460 %% @doc Clean up any junk in the process dictionary, required before continuing
461 %% a Keep-Alive request.
462 cleanup({?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}) ->
463 L = [?SAVE_QS, ?SAVE_PATH, ?SAVE_RECV, ?SAVE_BODY, ?SAVE_BODY_LENGTH,
464 ?SAVE_POST, ?SAVE_COOKIE, ?SAVE_FORCE_CLOSE],
465 lists:foreach(fun(K) ->
466 erase(K)
467 end, L),
468 ok.
469
470 %% @spec parse_qs(request()) -> [{Key::string(), Value::string()}]
471 %% @doc Parse the query string of the URL.
472 parse_qs({?MODULE, [_Socket, _Opts, _Method, RawPath, _Version, _Headers]}) ->
473 case erlang:get(?SAVE_QS) of
474 undefined ->
475 {_, QueryString, _} = mochiweb_util:urlsplit_path(RawPath),
476 Parsed = mochiweb_util:parse_qs(QueryString),
477 put(?SAVE_QS, Parsed),
478 Parsed;
479 Cached ->
480 Cached
481 end.
482
483 %% @spec get_cookie_value(Key::string, request()) -> string() | undefined
484 %% @doc Get the value of the given cookie.
485 get_cookie_value(Key, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
486 proplists:get_value(Key, parse_cookie(THIS)).
487
488 %% @spec parse_cookie(request()) -> [{Key::string(), Value::string()}]
489 %% @doc Parse the cookie header.
490 parse_cookie({?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
491 case erlang:get(?SAVE_COOKIE) of
492 undefined ->
493 Cookies = case get_header_value("cookie", THIS) of
494 undefined ->
495 [];
496 Value ->
497 mochiweb_cookies:parse_cookie(Value)
498 end,
499 put(?SAVE_COOKIE, Cookies),
500 Cookies;
501 Cached ->
502 Cached
503 end.
504
505 %% @spec parse_post(request()) -> [{Key::string(), Value::string()}]
506 %% @doc Parse an application/x-www-form-urlencoded form POST. This
507 %% has the side-effect of calling recv_body().
508 parse_post({?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
509 case erlang:get(?SAVE_POST) of
510 undefined ->
511 Parsed = case recv_body(THIS) of
512 undefined ->
513 [];
514 Binary ->
515 case get_primary_header_value("content-type",THIS) of
516 "application/x-www-form-urlencoded" ++ _ ->
517 mochiweb_util:parse_qs(Binary);
518 _ ->
519 []
520 end
521 end,
522 put(?SAVE_POST, Parsed),
523 Parsed;
524 Cached ->
525 Cached
526 end.
527
528 %% @spec stream_chunked_body(integer(), fun(), term(), request()) -> term()
529 %% @doc The function is called for each chunk.
530 %% Used internally by read_chunked_body.
531 stream_chunked_body(MaxChunkSize, Fun, FunState,
532 {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
533 case read_chunk_length(THIS) of
534 0 ->
535 Fun({0, read_chunk(0, THIS)}, FunState);
536 Length when Length > MaxChunkSize ->
537 NewState = read_sub_chunks(Length, MaxChunkSize, Fun, FunState, THIS),
538 stream_chunked_body(MaxChunkSize, Fun, NewState, THIS);
539 Length ->
540 NewState = Fun({Length, read_chunk(Length, THIS)}, FunState),
541 stream_chunked_body(MaxChunkSize, Fun, NewState, THIS)
542 end.
543
544 stream_unchunked_body(_MaxChunkSize, 0, Fun, FunState, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}) ->
545 Fun({0, <<>>}, FunState);
546 stream_unchunked_body(MaxChunkSize, Length, Fun, FunState,
547 {?MODULE, [_Socket, Opts, _Method, _RawPath, _Version, _Headers]}=THIS) when Length > 0 ->
548 RecBuf = case mochilists:get_value(recbuf, Opts, ?RECBUF_SIZE) of
549 undefined -> %os controlled buffer size
550 MaxChunkSize;
551 Val ->
552 Val
553 end,
554 PktSize=min(Length,RecBuf),
555 Bin = recv(PktSize, THIS),
556 NewState = Fun({PktSize, Bin}, FunState),
557 stream_unchunked_body(MaxChunkSize, Length - PktSize, Fun, NewState, THIS).
558
559 %% @spec read_chunk_length(request()) -> integer()
560 %% @doc Read the length of the next HTTP chunk.
561 read_chunk_length({?MODULE, [Socket, _Opts, _Method, _RawPath, _Version, _Headers]}) ->
562 ok = mochiweb_socket:exit_if_closed(mochiweb_socket:setopts(Socket, [{packet, line}])),
563 case mochiweb_socket:recv(Socket, 0, ?IDLE_TIMEOUT) of
564 {ok, Header} ->
565 ok = mochiweb_socket:exit_if_closed(mochiweb_socket:setopts(Socket, [{packet, raw}])),
566 Splitter = fun (C) ->
567 C =/= $\r andalso C =/= $\n andalso C =/= $
568 end,
569 {Hex, _Rest} = lists:splitwith(Splitter, binary_to_list(Header)),
570 mochihex:to_int(Hex);
571 _ ->
572 exit(normal)
573 end.
574
575 %% @spec read_chunk(integer(), request()) -> Chunk::binary() | [Footer::binary()]
576 %% @doc Read in a HTTP chunk of the given length. If Length is 0, then read the
577 %% HTTP footers (as a list of binaries, since they're nominal).
578 read_chunk(0, {?MODULE, [Socket, _Opts, _Method, _RawPath, _Version, _Headers]}) ->
579 ok = mochiweb_socket:exit_if_closed(mochiweb_socket:setopts(Socket, [{packet, line}])),
580 F = fun (F1, Acc) ->
581 case mochiweb_socket:recv(Socket, 0, ?IDLE_TIMEOUT) of
582 {ok, <<"\r\n">>} ->
583 Acc;
584 {ok, Footer} ->
585 F1(F1, [Footer | Acc]);
586 _ ->
587 exit(normal)
588 end
589 end,
590 Footers = F(F, []),
591 ok = mochiweb_socket:exit_if_closed(mochiweb_socket:setopts(Socket, [{packet, raw}])),
592 put(?SAVE_RECV, true),
593 Footers;
594 read_chunk(Length, {?MODULE, [Socket, _Opts, _Method, _RawPath, _Version, _Headers]}) ->
595 case mochiweb_socket:recv(Socket, 2 + Length, ?IDLE_TIMEOUT) of
596 {ok, <<Chunk:Length/binary, "\r\n">>} ->
597 Chunk;
598 _ ->
599 exit(normal)
600 end.
601
602 read_sub_chunks(Length, MaxChunkSize, Fun, FunState,
603 {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) when Length > MaxChunkSize ->
604 Bin = recv(MaxChunkSize, THIS),
605 NewState = Fun({size(Bin), Bin}, FunState),
606 read_sub_chunks(Length - MaxChunkSize, MaxChunkSize, Fun, NewState, THIS);
607
608 read_sub_chunks(Length, _MaxChunkSize, Fun, FunState,
609 {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
610 Fun({Length, read_chunk(Length, THIS)}, FunState).
611
612 %% @spec serve_file(Path, DocRoot, request()) -> Response
613 %% @doc Serve a file relative to DocRoot.
614 serve_file(Path, DocRoot, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
615 serve_file(Path, DocRoot, [], THIS).
616
617 %% @spec serve_file(Path, DocRoot, ExtraHeaders, request()) -> Response
618 %% @doc Serve a file relative to DocRoot.
619 serve_file(Path, DocRoot, ExtraHeaders, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
620 case mochiweb_util:safe_relative_path(Path) of
621 undefined ->
622 not_found(ExtraHeaders, THIS);
623 RelPath ->
624 FullPath = filename:join([DocRoot, RelPath]),
625 case filelib:is_dir(FullPath) of
626 true ->
627 maybe_redirect(RelPath, FullPath, ExtraHeaders, THIS);
628 false ->
629 maybe_serve_file(FullPath, ExtraHeaders, THIS)
630 end
631 end.
632
633 %% Internal API
634
635 %% This has the same effect as the DirectoryIndex directive in httpd
636 directory_index(FullPath) ->
637 filename:join([FullPath, "index.html"]).
638
639 maybe_redirect([], FullPath, ExtraHeaders, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
640 maybe_serve_file(directory_index(FullPath), ExtraHeaders, THIS);
641
642 maybe_redirect(RelPath, FullPath, ExtraHeaders,
643 {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, Headers]}=THIS) ->
644 case string:right(RelPath, 1) of
645 "/" ->
646 maybe_serve_file(directory_index(FullPath), ExtraHeaders, THIS);
647 _ ->
648 Host = mochiweb_headers:get_value("host", Headers),
649 Location = "http://" ++ Host ++ "/" ++ RelPath ++ "/",
650 LocationBin = list_to_binary(Location),
651 MoreHeaders = [{"Location", Location},
652 {"Content-Type", "text/html"} | ExtraHeaders],
653 Top = <<"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">"
654 "<html><head>"
655 "<title>301 Moved Permanently</title>"
656 "</head><body>"
657 "<h1>Moved Permanently</h1>"
658 "<p>The document has moved <a href=\"">>,
659 Bottom = <<">here</a>.</p></body></html>\n">>,
660 Body = <<Top/binary, LocationBin/binary, Bottom/binary>>,
661 respond({301, MoreHeaders, Body}, THIS)
662 end.
663
664 maybe_serve_file(File, ExtraHeaders, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
665 case file:read_file_info(File) of
666 {ok, FileInfo} ->
667 LastModified = httpd_util:rfc1123_date(FileInfo#file_info.mtime),
668 case get_header_value("if-modified-since", THIS) of
669 LastModified ->
670 respond({304, ExtraHeaders, ""}, THIS);
671 _ ->
672 case file:open(File, [raw, binary]) of
673 {ok, IoDevice} ->
674 ContentType = mochiweb_util:guess_mime(File),
675 Res = ok({ContentType,
676 [{"last-modified", LastModified}
677 | ExtraHeaders],
678 {file, IoDevice}}, THIS),
679 ok = file:close(IoDevice),
680 Res;
681 _ ->
682 not_found(ExtraHeaders, THIS)
683 end
684 end;
685 {error, _} ->
686 not_found(ExtraHeaders, THIS)
687 end.
688
689 server_headers() ->
690 [{"Server", "MochiWeb/1.0 (" ++ ?QUIP ++ ")"},
691 {"Date", mochiweb_clock:rfc1123()}].
692
693 make_code(X) when is_integer(X) ->
694 [integer_to_list(X), [" " | httpd_util:reason_phrase(X)]];
695 make_code(Io) when is_list(Io); is_binary(Io) ->
696 Io.
697
698 make_version({1, 0}) ->
699 <<"HTTP/1.0 ">>;
700 make_version(_) ->
701 <<"HTTP/1.1 ">>.
702
703 range_parts({file, IoDevice}, Ranges) ->
704 Size = mochiweb_io:iodevice_size(IoDevice),
705 F = fun (Spec, Acc) ->
706 case mochiweb_http:range_skip_length(Spec, Size) of
707 invalid_range ->
708 Acc;
709 V ->
710 [V | Acc]
711 end
712 end,
713 LocNums = lists:foldr(F, [], Ranges),
714 {ok, Data} = file:pread(IoDevice, LocNums),
715 Bodies = lists:zipwith(fun ({Skip, Length}, PartialBody) ->
716 case Length of
717 0 ->
718 {Skip, Skip, <<>>};
719 _ ->
720 {Skip, Skip + Length - 1, PartialBody}
721 end
722 end,
723 LocNums, Data),
724 {Bodies, Size};
725 range_parts(Body0, Ranges) ->
726 Body = iolist_to_binary(Body0),
727 Size = size(Body),
728 F = fun(Spec, Acc) ->
729 case mochiweb_http:range_skip_length(Spec, Size) of
730 invalid_range ->
731 Acc;
732 {Skip, Length} ->
733 <<_:Skip/binary, PartialBody:Length/binary, _/binary>> = Body,
734 [{Skip, Skip + Length - 1, PartialBody} | Acc]
735 end
736 end,
737 {lists:foldr(F, [], Ranges), Size}.
738
739 %% @spec accepted_encodings([encoding()], request()) -> [encoding()] | bad_accept_encoding_value
740 %% @type encoding() = string().
741 %%
742 %% @doc Returns a list of encodings accepted by a request. Encodings that are
743 %% not supported by the server will not be included in the return list.
744 %% This list is computed from the "Accept-Encoding" header and
745 %% its elements are ordered, descendingly, according to their Q values.
746 %%
747 %% Section 14.3 of the RFC 2616 (HTTP 1.1) describes the "Accept-Encoding"
748 %% header and the process of determining which server supported encodings
749 %% can be used for encoding the body for the request's response.
750 %%
751 %% Examples
752 %%
753 %% 1) For a missing "Accept-Encoding" header:
754 %% accepted_encodings(["gzip", "identity"]) -> ["identity"]
755 %%
756 %% 2) For an "Accept-Encoding" header with value "gzip, deflate":
757 %% accepted_encodings(["gzip", "identity"]) -> ["gzip", "identity"]
758 %%
759 %% 3) For an "Accept-Encoding" header with value "gzip;q=0.5, deflate":
760 %% accepted_encodings(["gzip", "deflate", "identity"]) ->
761 %% ["deflate", "gzip", "identity"]
762 %%
763 accepted_encodings(SupportedEncodings, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
764 AcceptEncodingHeader = case get_header_value("Accept-Encoding", THIS) of
765 undefined ->
766 "";
767 Value ->
768 Value
769 end,
770 case mochiweb_util:parse_qvalues(AcceptEncodingHeader) of
771 invalid_qvalue_string ->
772 bad_accept_encoding_value;
773 QList ->
774 mochiweb_util:pick_accepted_encodings(
775 QList, SupportedEncodings, "identity"
776 )
777 end.
778
779 %% @spec accepts_content_type(string() | binary(), request()) -> boolean() | bad_accept_header
780 %%
781 %% @doc Determines whether a request accepts a given media type by analyzing its
782 %% "Accept" header.
783 %%
784 %% Examples
785 %%
786 %% 1) For a missing "Accept" header:
787 %% accepts_content_type("application/json") -> true
788 %%
789 %% 2) For an "Accept" header with value "text/plain, application/*":
790 %% accepts_content_type("application/json") -> true
791 %%
792 %% 3) For an "Accept" header with value "text/plain, */*; q=0.0":
793 %% accepts_content_type("application/json") -> false
794 %%
795 %% 4) For an "Accept" header with value "text/plain; q=0.5, */*; q=0.1":
796 %% accepts_content_type("application/json") -> true
797 %%
798 %% 5) For an "Accept" header with value "text/*; q=0.0, */*":
799 %% accepts_content_type("text/plain") -> false
800 %%
801 accepts_content_type(ContentType1, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
802 ContentType = re:replace(ContentType1, "\\s", "", [global, {return, list}]),
803 AcceptHeader = accept_header(THIS),
804 case mochiweb_util:parse_qvalues(AcceptHeader) of
805 invalid_qvalue_string ->
806 bad_accept_header;
807 QList ->
808 [MainType, _SubType] = string:tokens(ContentType, "/"),
809 SuperType = MainType ++ "/*",
810 lists:any(
811 fun({"*/*", Q}) when Q > 0.0 ->
812 true;
813 ({Type, Q}) when Q > 0.0 ->
814 Type =:= ContentType orelse Type =:= SuperType;
815 (_) ->
816 false
817 end,
818 QList
819 ) andalso
820 (not lists:member({ContentType, 0.0}, QList)) andalso
821 (not lists:member({SuperType, 0.0}, QList))
822 end.
823
824 %% @spec accepted_content_types([string() | binary()], request()) -> [string()] | bad_accept_header
825 %%
826 %% @doc Filters which of the given media types this request accepts. This filtering
827 %% is performed by analyzing the "Accept" header. The returned list is sorted
828 %% according to the preferences specified in the "Accept" header (higher Q values
829 %% first). If two or more types have the same preference (Q value), they're order
830 %% in the returned list is the same as they're order in the input list.
831 %%
832 %% Examples
833 %%
834 %% 1) For a missing "Accept" header:
835 %% accepted_content_types(["text/html", "application/json"]) ->
836 %% ["text/html", "application/json"]
837 %%
838 %% 2) For an "Accept" header with value "text/html, application/*":
839 %% accepted_content_types(["application/json", "text/html"]) ->
840 %% ["application/json", "text/html"]
841 %%
842 %% 3) For an "Accept" header with value "text/html, */*; q=0.0":
843 %% accepted_content_types(["text/html", "application/json"]) ->
844 %% ["text/html"]
845 %%
846 %% 4) For an "Accept" header with value "text/html; q=0.5, */*; q=0.1":
847 %% accepts_content_types(["application/json", "text/html"]) ->
848 %% ["text/html", "application/json"]
849 %%
850 accepted_content_types(Types1, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
851 Types = lists:map(
852 fun(T) -> re:replace(T, "\\s", "", [global, {return, list}]) end,
853 Types1),
854 AcceptHeader = accept_header(THIS),
855 case mochiweb_util:parse_qvalues(AcceptHeader) of
856 invalid_qvalue_string ->
857 bad_accept_header;
858 QList ->
859 TypesQ = lists:foldr(
860 fun(T, Acc) ->
861 case proplists:get_value(T, QList) of
862 undefined ->
863 [MainType, _SubType] = string:tokens(T, "/"),
864 case proplists:get_value(MainType ++ "/*", QList) of
865 undefined ->
866 case proplists:get_value("*/*", QList) of
867 Q when is_float(Q), Q > 0.0 ->
868 [{Q, T} | Acc];
869 _ ->
870 Acc
871 end;
872 Q when Q > 0.0 ->
873 [{Q, T} | Acc];
874 _ ->
875 Acc
876 end;
877 Q when Q > 0.0 ->
878 [{Q, T} | Acc];
879 _ ->
880 Acc
881 end
882 end,
883 [], Types),
884 % Note: Stable sort. If 2 types have the same Q value we leave them in the
885 % same order as in the input list.
886 SortFun = fun({Q1, _}, {Q2, _}) -> Q1 >= Q2 end,
887 [Type || {_Q, Type} <- lists:sort(SortFun, TypesQ)]
888 end.
889
890 accept_header({?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) ->
891 case get_header_value("Accept", THIS) of
892 undefined ->
893 "*/*";
894 Value ->
895 Value
896 end.
897
898 %%
899 %% Tests
900 %%
901 -ifdef(TEST).
902 -include_lib("eunit/include/eunit.hrl").
903 -endif.
+0
-90
deps/mochiweb/src/mochiweb_response.erl less more
0 %% @author Bob Ippolito <bob@mochimedia.com>
1 %% @copyright 2007 Mochi Media, Inc.
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 %% @doc Response abstraction.
22
23 -module(mochiweb_response).
24 -author('bob@mochimedia.com').
25
26 -define(QUIP, "Any of you quaids got a smint?").
27
28 -export([new/3, get_header_value/2, get/2, dump/1]).
29 -export([send/2, write_chunk/2]).
30
31 %% @type response(). A mochiweb_response parameterized module instance.
32
33 %% @spec new(Request, Code, Headers) -> response()
34 %% @doc Create a new mochiweb_response instance.
35 new(Request, Code, Headers) ->
36 {?MODULE, [Request, Code, Headers]}.
37
38 %% @spec get_header_value(string() | atom() | binary(), response()) ->
39 %% string() | undefined
40 %% @doc Get the value of the given response header.
41 get_header_value(K, {?MODULE, [_Request, _Code, Headers]}) ->
42 mochiweb_headers:get_value(K, Headers).
43
44 %% @spec get(request | code | headers, response()) -> term()
45 %% @doc Return the internal representation of the given field.
46 get(request, {?MODULE, [Request, _Code, _Headers]}) ->
47 Request;
48 get(code, {?MODULE, [_Request, Code, _Headers]}) ->
49 Code;
50 get(headers, {?MODULE, [_Request, _Code, Headers]}) ->
51 Headers.
52
53 %% @spec dump(response()) -> {mochiweb_request, [{atom(), term()}]}
54 %% @doc Dump the internal representation to a "human readable" set of terms
55 %% for debugging/inspection purposes.
56 dump({?MODULE, [Request, Code, Headers]}) ->
57 [{request, Request:dump()},
58 {code, Code},
59 {headers, mochiweb_headers:to_list(Headers)}].
60
61 %% @spec send(iodata(), response()) -> ok
62 %% @doc Send data over the socket if the method is not HEAD.
63 send(Data, {?MODULE, [Request, _Code, _Headers]}) ->
64 case Request:get(method) of
65 'HEAD' ->
66 ok;
67 _ ->
68 Request:send(Data)
69 end.
70
71 %% @spec write_chunk(iodata(), response()) -> ok
72 %% @doc Write a chunk of a HTTP chunked response. If Data is zero length,
73 %% then the chunked response will be finished.
74 write_chunk(Data, {?MODULE, [Request, _Code, _Headers]}=THIS) ->
75 case Request:get(version) of
76 Version when Version >= {1, 1} ->
77 Length = iolist_size(Data),
78 send([io_lib:format("~.16b\r\n", [Length]), Data, <<"\r\n">>], THIS);
79 _ ->
80 send(Data, THIS)
81 end.
82
83
84 %%
85 %% Tests
86 %%
87 -ifdef(TEST).
88 -include_lib("eunit/include/eunit.hrl").
89 -endif.
+0
-229
deps/mochiweb/src/mochiweb_session.erl less more
0 %% @author Asier Azkuenaga Batiz <asier@zebixe.com>
1 %% @copyright 2013 Mochi Media, Inc.
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 %% @doc HTTP Cookie session. Note that the expiration time travels unencrypted
22 %% as far as this module is concerned. In order to achieve more security,
23 %% it is advised to use https.
24 %% Based on the paper
25 %% <a href="http://www.cse.msu.edu/~alexliu/publications/Cookie/cookie.pdf">
26 %% "A Secure Cookie Protocol"</a>.
27 %% This module is only supported on R15B02 and later, the AES CFB mode is not
28 %% available in earlier releases of crypto.
29 -module(mochiweb_session).
30 -export([generate_session_data/4, generate_session_cookie/4,
31 check_session_cookie/4]).
32
33 -export_types([expiration_time/0]).
34 -type expiration_time() :: integer().
35 -type key_fun() :: fun((string()) -> iolist()).
36
37 %% TODO: Import this from elsewhere after attribute types refactor.
38 -type header() :: {string(), string()}.
39
40 %% @doc Generates a secure encrypted binary convining all the parameters. The
41 %% expiration time must be a 32-bit integer.
42 -spec generate_session_data(
43 ExpirationTime :: expiration_time(),
44 Data :: iolist(),
45 FSessionKey :: key_fun(),
46 ServerKey :: iolist()) -> binary().
47 generate_session_data(ExpirationTime, Data, FSessionKey, ServerKey)
48 when is_integer(ExpirationTime), is_function(FSessionKey)->
49 BData = ensure_binary(Data),
50 ExpTime = integer_to_list(ExpirationTime),
51 Key = gen_key(ExpTime, ServerKey),
52 Hmac = gen_hmac(ExpTime, BData, FSessionKey(ExpTime), Key),
53 EData = encrypt_data(BData, Key),
54 mochiweb_base64url:encode(
55 <<ExpirationTime:32/integer, Hmac/binary, EData/binary>>).
56
57 %% @doc Convenience wrapper for generate_session_data that returns a
58 %% mochiweb cookie with "id" as the key, a max_age of 20000 seconds,
59 %% and the current local time as local time.
60 -spec generate_session_cookie(
61 ExpirationTime :: expiration_time(),
62 Data :: iolist(),
63 FSessionKey :: key_fun(),
64 ServerKey :: iolist()) -> header().
65 generate_session_cookie(ExpirationTime, Data, FSessionKey, ServerKey)
66 when is_integer(ExpirationTime), is_function(FSessionKey)->
67 CookieData = generate_session_data(ExpirationTime, Data,
68 FSessionKey, ServerKey),
69 mochiweb_cookies:cookie("id", CookieData,
70 [{max_age, 20000},
71 {local_time,
72 calendar:universal_time_to_local_time(
73 calendar:universal_time())}]).
74
75 %% TODO: This return type is messy to express in the type system.
76 -spec check_session_cookie(
77 ECookie :: binary(),
78 ExpirationTime :: string(),
79 FSessionKey :: key_fun(),
80 ServerKey :: iolist()) ->
81 {Success :: boolean(),
82 ExpTimeAndData :: [integer() | binary()]}.
83 check_session_cookie(ECookie, ExpirationTime, FSessionKey, ServerKey)
84 when is_binary(ECookie), is_integer(ExpirationTime),
85 is_function(FSessionKey) ->
86 case mochiweb_base64url:decode(ECookie) of
87 <<ExpirationTime1:32/integer, BHmac:20/binary, EData/binary>> ->
88 ETString = integer_to_list(ExpirationTime1),
89 Key = gen_key(ETString, ServerKey),
90 Data = decrypt_data(EData, Key),
91 Hmac2 = gen_hmac(ETString,
92 Data,
93 FSessionKey(ETString),
94 Key),
95 {ExpirationTime1 >= ExpirationTime andalso eq(Hmac2, BHmac),
96 [ExpirationTime1, binary_to_list(Data)]};
97 _ ->
98 {false, []}
99 end;
100 check_session_cookie(_ECookie, _ExpirationTime, _FSessionKey, _ServerKey) ->
101 {false, []}.
102
103 %% 'Constant' time =:= operator for binary, to mitigate timing attacks.
104 -spec eq(binary(), binary()) -> boolean().
105 eq(A, B) when is_binary(A) andalso is_binary(B) ->
106 eq(A, B, 0).
107
108 eq(<<A, As/binary>>, <<B, Bs/binary>>, Acc) ->
109 eq(As, Bs, Acc bor (A bxor B));
110 eq(<<>>, <<>>, 0) ->
111 true;
112 eq(_As, _Bs, _Acc) ->
113 false.
114
115 -spec ensure_binary(iolist()) -> binary().
116 ensure_binary(B) when is_binary(B) ->
117 B;
118 ensure_binary(L) when is_list(L) ->
119 iolist_to_binary(L).
120
121 -ifdef(crypto_compatibility).
122 -spec encrypt_data(binary(), binary()) -> binary().
123 encrypt_data(Data, Key) ->
124 IV = crypto:rand_bytes(16),
125 Crypt = crypto:aes_cfb_128_encrypt(Key, IV, Data),
126 <<IV/binary, Crypt/binary>>.
127
128 -spec decrypt_data(binary(), binary()) -> binary().
129 decrypt_data(<<IV:16/binary, Crypt/binary>>, Key) ->
130 crypto:aes_cfb_128_decrypt(Key, IV, Crypt).
131
132 -spec gen_key(iolist(), iolist()) -> binary().
133 gen_key(ExpirationTime, ServerKey)->
134 crypto:md5_mac(ServerKey, [ExpirationTime]).
135
136 -spec gen_hmac(iolist(), binary(), iolist(), binary()) -> binary().
137 gen_hmac(ExpirationTime, Data, SessionKey, Key) ->
138 crypto:sha_mac(Key, [ExpirationTime, Data, SessionKey]).
139
140 -else.
141 -spec encrypt_data(binary(), binary()) -> binary().
142 encrypt_data(Data, Key) ->
143 IV = crypto:rand_bytes(16),
144 Crypt = crypto:block_encrypt(aes_cfb128, Key, IV, Data),
145 <<IV/binary, Crypt/binary>>.
146
147 -spec decrypt_data(binary(), binary()) -> binary().
148 decrypt_data(<<IV:16/binary, Crypt/binary>>, Key) ->
149 crypto:block_decrypt(aes_cfb128, Key, IV, Crypt).
150
151 -spec gen_key(iolist(), iolist()) -> binary().
152 gen_key(ExpirationTime, ServerKey)->
153 crypto:hmac(md5, ServerKey, [ExpirationTime]).
154
155 -spec gen_hmac(iolist(), binary(), iolist(), binary()) -> binary().
156 gen_hmac(ExpirationTime, Data, SessionKey, Key) ->
157 crypto:hmac(sha, Key, [ExpirationTime, Data, SessionKey]).
158
159 -endif.
160
161 -ifdef(TEST).
162 -include_lib("eunit/include/eunit.hrl").
163
164 generate_check_session_cookie_test_() ->
165 {setup,
166 fun setup_server_key/0,
167 fun generate_check_session_cookie/1}.
168
169 setup_server_key() ->
170 crypto:start(),
171 ["adfasdfasfs",30000].
172
173 generate_check_session_cookie([ServerKey, TS]) ->
174 Id = fun (A) -> A end,
175 TSFuture = TS + 1000,
176 TSPast = TS - 1,
177 [?_assertEqual(
178 {true, [TSFuture, "alice"]},
179 check_session_cookie(
180 generate_session_data(TSFuture, "alice", Id, ServerKey),
181 TS, Id, ServerKey)),
182 ?_assertEqual(
183 {true, [TSFuture, "alice and"]},
184 check_session_cookie(
185 generate_session_data(TSFuture, "alice and", Id, ServerKey),
186 TS, Id, ServerKey)),
187 ?_assertEqual(
188 {true, [TSFuture, "alice and"]},
189 check_session_cookie(
190 generate_session_data(TSFuture, "alice and", Id, ServerKey),
191 TS, Id,ServerKey)),
192 ?_assertEqual(
193 {true, [TSFuture, "alice and bob"]},
194 check_session_cookie(
195 generate_session_data(TSFuture, "alice and bob",
196 Id, ServerKey),
197 TS, Id, ServerKey)),
198 ?_assertEqual(
199 {true, [TSFuture, "alice jlkjfkjsdfg sdkfjgldsjgl"]},
200 check_session_cookie(
201 generate_session_data(TSFuture, "alice jlkjfkjsdfg sdkfjgldsjgl",
202 Id, ServerKey),
203 TS, Id, ServerKey)),
204 ?_assertEqual(
205 {true, [TSFuture, "alice .'¡'ç+-$%/(&\""]},
206 check_session_cookie(
207 generate_session_data(TSFuture, "alice .'¡'ç+-$%/(&\""
208 ,Id, ServerKey),
209 TS, Id, ServerKey)),
210 ?_assertEqual(
211 {true,[TSFuture,"alice456689875"]},
212 check_session_cookie(
213 generate_session_data(TSFuture, ["alice","456689875"],
214 Id, ServerKey),
215 TS, Id, ServerKey)),
216 ?_assertError(
217 function_clause,
218 check_session_cookie(
219 generate_session_data(TSFuture, {tuple,one},
220 Id, ServerKey),
221 TS, Id,ServerKey)),
222 ?_assertEqual(
223 {false, [TSPast, "bob"]},
224 check_session_cookie(
225 generate_session_data(TSPast, "bob", Id,ServerKey),
226 TS, Id, ServerKey))
227 ].
228 -endif.
+0
-148
deps/mochiweb/src/mochiweb_socket.erl less more
0 %% @copyright 2010 Mochi Media, Inc.
1
2 %% @doc MochiWeb socket - wrapper for plain and ssl sockets.
3
4 -module(mochiweb_socket).
5
6 -export([listen/4,
7 accept/1, transport_accept/1, finish_accept/1,
8 recv/3, send/2, close/1, port/1, peername/1,
9 setopts/2, getopts/2, type/1, exit_if_closed/1]).
10
11 -define(ACCEPT_TIMEOUT, 2000).
12 -define(SSL_TIMEOUT, 10000).
13 -define(SSL_HANDSHAKE_TIMEOUT, 20000).
14
15
16 listen(Ssl, Port, Opts, SslOpts) ->
17 case Ssl of
18 true ->
19 Opts1 = add_unbroken_ciphers_default(Opts ++ SslOpts),
20 Opts2 = add_safe_protocol_versions(Opts1),
21 case ssl:listen(Port, Opts2) of
22 {ok, ListenSocket} ->
23 {ok, {ssl, ListenSocket}};
24 {error, _} = Err ->
25 Err
26 end;
27 false ->
28 gen_tcp:listen(Port, Opts)
29 end.
30
31 add_unbroken_ciphers_default(Opts) ->
32 Default = filter_unsecure_cipher_suites(ssl:cipher_suites()),
33 Ciphers = filter_broken_cipher_suites(proplists:get_value(ciphers, Opts, Default)),
34 [{ciphers, Ciphers} | proplists:delete(ciphers, Opts)].
35
36 filter_broken_cipher_suites(Ciphers) ->
37 case proplists:get_value(ssl_app, ssl:versions()) of
38 "5.3" ++ _ ->
39 lists:filter(fun(Suite) ->
40 string:left(atom_to_list(element(1, Suite)), 4) =/= "ecdh"
41 end, Ciphers);
42 _ ->
43 Ciphers
44 end.
45
46 filter_unsecure_cipher_suites(Ciphers) ->
47 lists:filter(fun
48 ({_,des_cbc,_}) -> false;
49 ({_,_,md5}) -> false;
50 (_) -> true
51 end,
52 Ciphers).
53
54 add_safe_protocol_versions(Opts) ->
55 case proplists:is_defined(versions, Opts) of
56 true ->
57 Opts;
58 false ->
59 Versions = filter_unsafe_protcol_versions(proplists:get_value(available, ssl:versions())),
60 [{versions, Versions} | Opts]
61 end.
62
63 filter_unsafe_protcol_versions(Versions) ->
64 lists:filter(fun
65 (sslv3) -> false;
66 (_) -> true
67 end,
68 Versions).
69
70 %% Provided for backwards compatibility only
71 accept(ListenSocket) ->
72 case transport_accept(ListenSocket) of
73 {ok, Socket} ->
74 finish_accept(Socket);
75 {error, _} = Err ->
76 Err
77 end.
78
79 transport_accept({ssl, ListenSocket}) ->
80 case ssl:transport_accept(ListenSocket, ?SSL_TIMEOUT) of
81 {ok, Socket} ->
82 {ok, {ssl, Socket}};
83 {error, _} = Err ->
84 Err
85 end;
86 transport_accept(ListenSocket) ->
87 gen_tcp:accept(ListenSocket, ?ACCEPT_TIMEOUT).
88
89 finish_accept({ssl, Socket}) ->
90 case ssl:ssl_accept(Socket, ?SSL_HANDSHAKE_TIMEOUT) of
91 ok ->
92 {ok, {ssl, Socket}};
93 {error, _} = Err ->
94 Err
95 end;
96 finish_accept(Socket) ->
97 {ok, Socket}.
98
99 recv({ssl, Socket}, Length, Timeout) ->
100 ssl:recv(Socket, Length, Timeout);
101 recv(Socket, Length, Timeout) ->
102 gen_tcp:recv(Socket, Length, Timeout).
103
104 send({ssl, Socket}, Data) ->
105 ssl:send(Socket, Data);
106 send(Socket, Data) ->
107 gen_tcp:send(Socket, Data).
108
109 close({ssl, Socket}) ->
110 ssl:close(Socket);
111 close(Socket) ->
112 gen_tcp:close(Socket).
113
114 port({ssl, Socket}) ->
115 case ssl:sockname(Socket) of
116 {ok, {_, Port}} ->
117 {ok, Port};
118 {error, _} = Err ->
119 Err
120 end;
121 port(Socket) ->
122 inet:port(Socket).
123
124 peername({ssl, Socket}) ->
125 ssl:peername(Socket);
126 peername(Socket) ->
127 inet:peername(Socket).
128
129 setopts({ssl, Socket}, Opts) ->
130 ssl:setopts(Socket, Opts);
131 setopts(Socket, Opts) ->
132 inet:setopts(Socket, Opts).
133
134 getopts({ssl, Socket}, Opts) ->
135 ssl:getopts(Socket, Opts);
136 getopts(Socket, Opts) ->
137 inet:getopts(Socket, Opts).
138
139 type({ssl, _}) ->
140 ssl;
141 type(_) ->
142 plain.
143
144 exit_if_closed({error, closed}) ->
145 exit(normal);
146 exit_if_closed(Res) ->
147 Res.
+0
-394
deps/mochiweb/src/mochiweb_socket_server.erl less more
0 %% @author Bob Ippolito <bob@mochimedia.com>
1 %% @copyright 2007 Mochi Media, Inc.
2
3 %% @doc MochiWeb socket server.
4
5 -module(mochiweb_socket_server).
6 -author('bob@mochimedia.com').
7 -behaviour(gen_server).
8
9 -include("internal.hrl").
10
11 -export([start/1, start_link/1, stop/1]).
12 -export([init/1, handle_call/3, handle_cast/2, terminate/2, code_change/3,
13 handle_info/2]).
14 -export([get/2, set/3]).
15
16 -record(mochiweb_socket_server,
17 {port,
18 loop,
19 name=undefined,
20 max=2048,
21 ip=any,
22 listen=null,
23 nodelay=false,
24 recbuf=?RECBUF_SIZE,
25 backlog=128,
26 active_sockets=0,
27 acceptor_pool_size=16,
28 ssl=false,
29 ssl_opts=[{ssl_imp, new}],
30 acceptor_pool=sets:new(),
31 profile_fun=undefined}).
32
33 -define(is_old_state(State), not is_record(State, mochiweb_socket_server)).
34
35 start_link(Options) ->
36 start_server(start_link, parse_options(Options)).
37
38 start(Options) ->
39 case lists:keytake(link, 1, Options) of
40 {value, {_Key, false}, Options1} ->
41 start_server(start, parse_options(Options1));
42 _ ->
43 %% TODO: https://github.com/mochi/mochiweb/issues/58
44 %% [X] Phase 1: Add new APIs (Sep 2011)
45 %% [_] Phase 2: Add deprecation warning
46 %% [_] Phase 3: Change default to {link, false} and ignore link
47 %% [_] Phase 4: Add deprecation warning for {link, _} option
48 %% [_] Phase 5: Remove support for {link, _} option
49 start_link(Options)
50 end.
51
52 get(Name, Property) ->
53 gen_server:call(Name, {get, Property}).
54
55 set(Name, profile_fun, Fun) ->
56 gen_server:cast(Name, {set, profile_fun, Fun});
57 set(Name, Property, _Value) ->
58 error_logger:info_msg("?MODULE:set for ~p with ~p not implemented~n",
59 [Name, Property]).
60
61 stop(Name) when is_atom(Name) orelse is_pid(Name) ->
62 gen_server:call(Name, stop);
63 stop({Scope, Name}) when Scope =:= local orelse Scope =:= global ->
64 stop(Name);
65 stop(Options) ->
66 State = parse_options(Options),
67 stop(State#mochiweb_socket_server.name).
68
69 %% Internal API
70
71 parse_options(State=#mochiweb_socket_server{}) ->
72 State;
73 parse_options(Options) ->
74 parse_options(Options, #mochiweb_socket_server{}).
75
76 parse_options([], State=#mochiweb_socket_server{acceptor_pool_size=PoolSize,
77 max=Max}) ->
78 case Max < PoolSize of
79 true ->
80 error_logger:info_report([{warning, "max is set lower than acceptor_pool_size"},
81 {max, Max},
82 {acceptor_pool_size, PoolSize}]);
83 false ->
84 ok
85 end,
86 State;
87 parse_options([{name, L} | Rest], State) when is_list(L) ->
88 Name = {local, list_to_atom(L)},
89 parse_options(Rest, State#mochiweb_socket_server{name=Name});
90 parse_options([{name, A} | Rest], State) when A =:= undefined ->
91 parse_options(Rest, State#mochiweb_socket_server{name=A});
92 parse_options([{name, A} | Rest], State) when is_atom(A) ->
93 Name = {local, A},
94 parse_options(Rest, State#mochiweb_socket_server{name=Name});
95 parse_options([{name, Name} | Rest], State) ->
96 parse_options(Rest, State#mochiweb_socket_server{name=Name});
97 parse_options([{port, L} | Rest], State) when is_list(L) ->
98 Port = list_to_integer(L),
99 parse_options(Rest, State#mochiweb_socket_server{port=Port});
100 parse_options([{port, Port} | Rest], State) ->
101 parse_options(Rest, State#mochiweb_socket_server{port=Port});
102 parse_options([{ip, Ip} | Rest], State) ->
103 ParsedIp = case Ip of
104 any ->
105 any;
106 Ip when is_tuple(Ip) ->
107 Ip;
108 Ip when is_list(Ip) ->
109 {ok, IpTuple} = inet_parse:address(Ip),
110 IpTuple
111 end,
112 parse_options(Rest, State#mochiweb_socket_server{ip=ParsedIp});
113 parse_options([{loop, Loop} | Rest], State) ->
114 parse_options(Rest, State#mochiweb_socket_server{loop=Loop});
115 parse_options([{backlog, Backlog} | Rest], State) ->
116 parse_options(Rest, State#mochiweb_socket_server{backlog=Backlog});
117 parse_options([{nodelay, NoDelay} | Rest], State) ->
118 parse_options(Rest, State#mochiweb_socket_server{nodelay=NoDelay});
119 parse_options([{recbuf, RecBuf} | Rest], State) when is_integer(RecBuf) orelse
120 RecBuf == undefined ->
121 %% XXX: `recbuf' value which is passed to `gen_tcp'
122 %% and value reported by `inet:getopts(P, [recbuf])' may
123 %% differ. They depends on underlying OS. From linux mans:
124 %%
125 %% The kernel doubles this value (to allow space for
126 %% bookkeeping overhead) when it is set using setsockopt(2),
127 %% and this doubled value is returned by getsockopt(2).
128 %%
129 %% See: man 7 socket | grep SO_RCVBUF
130 %%
131 %% In case undefined is passed instead of the default buffer
132 %% size ?RECBUF_SIZE, no size is set and the OS can control it dynamically
133 parse_options(Rest, State#mochiweb_socket_server{recbuf=RecBuf});
134 parse_options([{acceptor_pool_size, Max} | Rest], State) ->
135 MaxInt = ensure_int(Max),
136 parse_options(Rest,
137 State#mochiweb_socket_server{acceptor_pool_size=MaxInt});
138 parse_options([{max, Max} | Rest], State) ->
139 MaxInt = ensure_int(Max),
140 parse_options(Rest, State#mochiweb_socket_server{max=MaxInt});
141 parse_options([{ssl, Ssl} | Rest], State) when is_boolean(Ssl) ->
142 parse_options(Rest, State#mochiweb_socket_server{ssl=Ssl});
143 parse_options([{ssl_opts, SslOpts} | Rest], State) when is_list(SslOpts) ->
144 SslOpts1 = [{ssl_imp, new} | proplists:delete(ssl_imp, SslOpts)],
145 parse_options(Rest, State#mochiweb_socket_server{ssl_opts=SslOpts1});
146 parse_options([{profile_fun, ProfileFun} | Rest], State) when is_function(ProfileFun) ->
147 parse_options(Rest, State#mochiweb_socket_server{profile_fun=ProfileFun}).
148
149
150 start_server(F, State=#mochiweb_socket_server{ssl=Ssl, name=Name}) ->
151 ok = prep_ssl(Ssl),
152 case Name of
153 undefined ->
154 gen_server:F(?MODULE, State, []);
155 _ ->
156 gen_server:F(Name, ?MODULE, State, [])
157 end.
158
159 prep_ssl(true) ->
160 ok = mochiweb:ensure_started(crypto),
161 ok = mochiweb:ensure_started(asn1),
162 ok = mochiweb:ensure_started(public_key),
163 ok = mochiweb:ensure_started(ssl);
164 prep_ssl(false) ->
165 ok.
166
167 ensure_int(N) when is_integer(N) ->
168 N;
169 ensure_int(S) when is_list(S) ->
170 list_to_integer(S).
171
172 ipv6_supported() ->
173 case (catch inet:getaddr("localhost", inet6)) of
174 {ok, _Addr} ->
175 true;
176 {error, _} ->
177 false
178 end.
179
180 init(State=#mochiweb_socket_server{ip=Ip, port=Port, backlog=Backlog,
181 nodelay=NoDelay, recbuf=RecBuf}) ->
182 process_flag(trap_exit, true),
183
184 BaseOpts = [binary,
185 {reuseaddr, true},
186 {packet, 0},
187 {backlog, Backlog},
188 {exit_on_close, false},
189 {active, false},
190 {nodelay, NoDelay}],
191 Opts = case Ip of
192 any ->
193 case ipv6_supported() of % IPv4, and IPv6 if supported
194 true -> [inet, inet6 | BaseOpts];
195 _ -> BaseOpts
196 end;
197 {_, _, _, _} -> % IPv4
198 [inet, {ip, Ip} | BaseOpts];
199 {_, _, _, _, _, _, _, _} -> % IPv6
200 [inet6, {ip, Ip} | BaseOpts]
201 end,
202 OptsBuf=case RecBuf of
203 undefined ->
204 Opts;
205 _ ->
206 [{recbuf, RecBuf}|Opts]
207 end,
208 listen(Port, OptsBuf, State).
209
210 new_acceptor_pool(State=#mochiweb_socket_server{acceptor_pool_size=Size}) ->
211 lists:foldl(fun (_, S) -> new_acceptor(S) end, State, lists:seq(1, Size)).
212
213 new_acceptor(State=#mochiweb_socket_server{acceptor_pool=Pool,
214 recbuf=RecBuf,
215 loop=Loop,
216 listen=Listen}) ->
217 LoopOpts = [{recbuf, RecBuf}],
218 Pid = mochiweb_acceptor:start_link(self(), Listen, Loop, LoopOpts),
219 State#mochiweb_socket_server{
220 acceptor_pool=sets:add_element(Pid, Pool)}.
221
222 listen(Port, Opts, State=#mochiweb_socket_server{ssl=Ssl, ssl_opts=SslOpts}) ->
223 case mochiweb_socket:listen(Ssl, Port, Opts, SslOpts) of
224 {ok, Listen} ->
225 {ok, ListenPort} = mochiweb_socket:port(Listen),
226 {ok, new_acceptor_pool(State#mochiweb_socket_server{
227 listen=Listen,
228 port=ListenPort})};
229 {error, Reason} ->
230 {stop, Reason}
231 end.
232
233 do_get(port, #mochiweb_socket_server{port=Port}) ->
234 Port;
235 do_get(waiting_acceptors, #mochiweb_socket_server{acceptor_pool=Pool}) ->
236 sets:size(Pool);
237 do_get(active_sockets, #mochiweb_socket_server{active_sockets=ActiveSockets}) ->
238 ActiveSockets.
239
240
241 state_to_proplist(#mochiweb_socket_server{name=Name,
242 port=Port,
243 active_sockets=ActiveSockets}) ->
244 [{name, Name}, {port, Port}, {active_sockets, ActiveSockets}].
245
246 upgrade_state(State = #mochiweb_socket_server{}) ->
247 State;
248 upgrade_state({mochiweb_socket_server, Port, Loop, Name,
249 Max, IP, Listen, NoDelay, Backlog, ActiveSockets,
250 AcceptorPoolSize, SSL, SSL_opts,
251 AcceptorPool}) ->
252 #mochiweb_socket_server{port=Port, loop=Loop, name=Name, max=Max, ip=IP,
253 listen=Listen, nodelay=NoDelay, backlog=Backlog,
254 active_sockets=ActiveSockets,
255 acceptor_pool_size=AcceptorPoolSize,
256 ssl=SSL,
257 ssl_opts=SSL_opts,
258 acceptor_pool=AcceptorPool}.
259
260 handle_call(Req, From, State) when ?is_old_state(State) ->
261 handle_call(Req, From, upgrade_state(State));
262 handle_call({get, Property}, _From, State) ->
263 Res = do_get(Property, State),
264 {reply, Res, State};
265 handle_call(stop, _From, State) ->
266 {stop, normal, ok, State};
267 handle_call(_Message, _From, State) ->
268 Res = error,
269 {reply, Res, State}.
270
271
272 handle_cast(Req, State) when ?is_old_state(State) ->
273 handle_cast(Req, upgrade_state(State));
274 handle_cast({accepted, Pid, Timing},
275 State=#mochiweb_socket_server{active_sockets=ActiveSockets}) ->
276 State1 = State#mochiweb_socket_server{active_sockets=1 + ActiveSockets},
277 case State#mochiweb_socket_server.profile_fun of
278 undefined ->
279 undefined;
280 F when is_function(F) ->
281 catch F([{timing, Timing} | state_to_proplist(State1)])
282 end,
283 {noreply, recycle_acceptor(Pid, State1)};
284 handle_cast({set, profile_fun, ProfileFun}, State) ->
285 State1 = case ProfileFun of
286 ProfileFun when is_function(ProfileFun); ProfileFun =:= undefined ->
287 State#mochiweb_socket_server{profile_fun=ProfileFun};
288 _ ->
289 State
290 end,
291 {noreply, State1}.
292
293
294 terminate(Reason, State) when ?is_old_state(State) ->
295 terminate(Reason, upgrade_state(State));
296 terminate(_Reason, #mochiweb_socket_server{listen=Listen}) ->
297 mochiweb_socket:close(Listen).
298
299 code_change(_OldVsn, State, _Extra) ->
300 State.
301
302 recycle_acceptor(Pid, State=#mochiweb_socket_server{
303 acceptor_pool=Pool,
304 acceptor_pool_size=PoolSize,
305 max=Max,
306 active_sockets=ActiveSockets}) ->
307 %% A socket is considered to be active from immediately after it
308 %% has been accepted (see the {accepted, Pid, Timing} cast above).
309 %% This function will be called when an acceptor is transitioning
310 %% to an active socket, or when either type of Pid dies. An acceptor
311 %% Pid will always be in the acceptor_pool set, and an active socket
312 %% will be in that set during the transition but not afterwards.
313 Pool1 = sets:del_element(Pid, Pool),
314 NewSize = sets:size(Pool1),
315 ActiveSockets1 = case NewSize =:= sets:size(Pool) of
316 %% Pid has died and it is not in the acceptor set,
317 %% it must be an active socket.
318 true -> max(0, ActiveSockets - 1);
319 false -> ActiveSockets
320 end,
321 State1 = State#mochiweb_socket_server{
322 acceptor_pool=Pool1,
323 active_sockets=ActiveSockets1},
324 %% Spawn a new acceptor only if it will not overrun the maximum socket
325 %% count or the maximum pool size.
326 case NewSize + ActiveSockets1 < Max andalso NewSize < PoolSize of
327 true -> new_acceptor(State1);
328 false -> State1
329 end.
330
331 handle_info(Msg, State) when ?is_old_state(State) ->
332 handle_info(Msg, upgrade_state(State));
333 handle_info({'EXIT', Pid, normal}, State) ->
334 {noreply, recycle_acceptor(Pid, State)};
335 handle_info({'EXIT', Pid, Reason},
336 State=#mochiweb_socket_server{acceptor_pool=Pool}) ->
337 case sets:is_element(Pid, Pool) of
338 true ->
339 %% If there was an unexpected error accepting, log and sleep.
340 error_logger:error_report({?MODULE, ?LINE,
341 {acceptor_error, Reason}}),
342 timer:sleep(100);
343 false ->
344 ok
345 end,
346 {noreply, recycle_acceptor(Pid, State)};
347
348 % this is what release_handler needs to get a list of modules,
349 % since our supervisor modules list is set to 'dynamic'
350 % see sasl-2.1.9.2/src/release_handler_1.erl get_dynamic_mods
351 handle_info({From, Tag, get_modules}, State = #mochiweb_socket_server{name={local,Mod}}) ->
352 From ! {element(2,Tag), [Mod]},
353 {noreply, State};
354
355 % If for some reason we can't get the module name, send empty list to avoid release_handler timeout:
356 handle_info({From, Tag, get_modules}, State) ->
357 error_logger:info_msg("mochiweb_socket_server replying to dynamic modules request as '[]'~n",[]),
358 From ! {element(2,Tag), []},
359 {noreply, State};
360
361 handle_info(Info, State) ->
362 error_logger:info_report([{'INFO', Info}, {'State', State}]),
363 {noreply, State}.
364
365
366
367 %%
368 %% Tests
369 %%
370 -ifdef(TEST).
371 -include_lib("eunit/include/eunit.hrl").
372
373 upgrade_state_test() ->
374 OldState = {mochiweb_socket_server,
375 port, loop, name,
376 max, ip, listen,
377 nodelay, backlog,
378 active_sockets,
379 acceptor_pool_size,
380 ssl, ssl_opts, acceptor_pool},
381 State = upgrade_state(OldState),
382 CmpState = #mochiweb_socket_server{port=port, loop=loop,
383 name=name, max=max, ip=ip,
384 listen=listen, nodelay=nodelay,
385 backlog=backlog,
386 active_sockets=active_sockets,
387 acceptor_pool_size=acceptor_pool_size,
388 ssl=ssl, ssl_opts=ssl_opts,
389 acceptor_pool=acceptor_pool,
390 profile_fun=undefined},
391 ?assertEqual(CmpState, State).
392
393 -endif.
+0
-993
deps/mochiweb/src/mochiweb_util.erl less more
0 %% @author Bob Ippolito <bob@mochimedia.com>
1 %% @copyright 2007 Mochi Media, Inc.
2
3 %% @doc Utilities for parsing and quoting.
4
5 -module(mochiweb_util).
6 -author('bob@mochimedia.com').
7 -export([join/2, quote_plus/1, urlencode/1, parse_qs/1, unquote/1]).
8 -export([path_split/1]).
9 -export([urlsplit/1, urlsplit_path/1, urlunsplit/1, urlunsplit_path/1]).
10 -export([guess_mime/1, parse_header/1]).
11 -export([shell_quote/1, cmd/1, cmd_string/1, cmd_port/2, cmd_status/1, cmd_status/2]).
12 -export([record_to_proplist/2, record_to_proplist/3]).
13 -export([safe_relative_path/1, partition/2]).
14 -export([parse_qvalues/1, pick_accepted_encodings/3]).
15 -export([make_io/1]).
16
17 -define(PERCENT, 37). % $\%
18 -define(FULLSTOP, 46). % $\.
19 -define(IS_HEX(C), ((C >= $0 andalso C =< $9) orelse
20 (C >= $a andalso C =< $f) orelse
21 (C >= $A andalso C =< $F))).
22 -define(QS_SAFE(C), ((C >= $a andalso C =< $z) orelse
23 (C >= $A andalso C =< $Z) orelse
24 (C >= $0 andalso C =< $9) orelse
25 (C =:= ?FULLSTOP orelse C =:= $- orelse C =:= $~ orelse
26 C =:= $_))).
27
28 hexdigit(C) when C < 10 -> $0 + C;
29 hexdigit(C) when C < 16 -> $A + (C - 10).
30
31 unhexdigit(C) when C >= $0, C =< $9 -> C - $0;
32 unhexdigit(C) when C >= $a, C =< $f -> C - $a + 10;
33 unhexdigit(C) when C >= $A, C =< $F -> C - $A + 10.
34
35 %% @spec partition(String, Sep) -> {String, [], []} | {Prefix, Sep, Postfix}
36 %% @doc Inspired by Python 2.5's str.partition:
37 %% partition("foo/bar", "/") = {"foo", "/", "bar"},
38 %% partition("foo", "/") = {"foo", "", ""}.
39 partition(String, Sep) ->
40 case partition(String, Sep, []) of
41 undefined ->
42 {String, "", ""};
43 Result ->
44 Result
45 end.
46
47 partition("", _Sep, _Acc) ->
48 undefined;
49 partition(S, Sep, Acc) ->
50 case partition2(S, Sep) of
51 undefined ->
52 [C | Rest] = S,
53 partition(Rest, Sep, [C | Acc]);
54 Rest ->
55 {lists:reverse(Acc), Sep, Rest}
56 end.
57
58 partition2(Rest, "") ->
59 Rest;
60 partition2([C | R1], [C | R2]) ->
61 partition2(R1, R2);
62 partition2(_S, _Sep) ->
63 undefined.
64
65
66
67 %% @spec safe_relative_path(string()) -> string() | undefined
68 %% @doc Return the reduced version of a relative path or undefined if it
69 %% is not safe. safe relative paths can be joined with an absolute path
70 %% and will result in a subdirectory of the absolute path. Safe paths
71 %% never contain a backslash character.
72 safe_relative_path("/" ++ _) ->
73 undefined;
74 safe_relative_path(P) ->
75 case string:chr(P, $\\) of
76 0 ->
77 safe_relative_path(P, []);
78 _ ->
79 undefined
80 end.
81
82 safe_relative_path("", Acc) ->
83 case Acc of
84 [] ->
85 "";
86 _ ->
87 string:join(lists:reverse(Acc), "/")
88 end;
89 safe_relative_path(P, Acc) ->
90 case partition(P, "/") of
91 {"", "/", _} ->
92 %% /foo or foo//bar
93 undefined;
94 {"..", _, _} when Acc =:= [] ->
95 undefined;
96 {"..", _, Rest} ->
97 safe_relative_path(Rest, tl(Acc));
98 {Part, "/", ""} ->
99 safe_relative_path("", ["", Part | Acc]);
100 {Part, _, Rest} ->
101 safe_relative_path(Rest, [Part | Acc])
102 end.
103
104 %% @spec shell_quote(string()) -> string()
105 %% @doc Quote a string according to UNIX shell quoting rules, returns a string
106 %% surrounded by double quotes.
107 shell_quote(L) ->
108 shell_quote(L, [$\"]).
109
110 %% @spec cmd_port([string()], Options) -> port()
111 %% @doc open_port({spawn, mochiweb_util:cmd_string(Argv)}, Options).
112 cmd_port(Argv, Options) ->
113 open_port({spawn, cmd_string(Argv)}, Options).
114
115 %% @spec cmd([string()]) -> string()
116 %% @doc os:cmd(cmd_string(Argv)).
117 cmd(Argv) ->
118 os:cmd(cmd_string(Argv)).
119
120 %% @spec cmd_string([string()]) -> string()
121 %% @doc Create a shell quoted command string from a list of arguments.
122 cmd_string(Argv) ->
123 string:join([shell_quote(X) || X <- Argv], " ").
124
125 %% @spec cmd_status([string()]) -> {ExitStatus::integer(), Stdout::binary()}
126 %% @doc Accumulate the output and exit status from the given application,
127 %% will be spawned with cmd_port/2.
128 cmd_status(Argv) ->
129 cmd_status(Argv, []).
130
131 %% @spec cmd_status([string()], [atom()]) -> {ExitStatus::integer(), Stdout::binary()}
132 %% @doc Accumulate the output and exit status from the given application,
133 %% will be spawned with cmd_port/2.
134 cmd_status(Argv, Options) ->
135 Port = cmd_port(Argv, [exit_status, stderr_to_stdout,
136 use_stdio, binary | Options]),
137 try cmd_loop(Port, [])
138 after catch port_close(Port)
139 end.
140
141 %% @spec cmd_loop(port(), list()) -> {ExitStatus::integer(), Stdout::binary()}
142 %% @doc Accumulate the output and exit status from a port.
143 cmd_loop(Port, Acc) ->
144 receive
145 {Port, {exit_status, Status}} ->
146 {Status, iolist_to_binary(lists:reverse(Acc))};
147 {Port, {data, Data}} ->
148 cmd_loop(Port, [Data | Acc])
149 end.
150
151 %% @spec join([iolist()], iolist()) -> iolist()
152 %% @doc Join a list of strings or binaries together with the given separator
153 %% string or char or binary. The output is flattened, but may be an
154 %% iolist() instead of a string() if any of the inputs are binary().
155 join([], _Separator) ->
156 [];
157 join([S], _Separator) ->
158 lists:flatten(S);
159 join(Strings, Separator) ->
160 lists:flatten(revjoin(lists:reverse(Strings), Separator, [])).
161
162 revjoin([], _Separator, Acc) ->
163 Acc;
164 revjoin([S | Rest], Separator, []) ->
165 revjoin(Rest, Separator, [S]);
166 revjoin([S | Rest], Separator, Acc) ->
167 revjoin(Rest, Separator, [S, Separator | Acc]).
168
169 %% @spec quote_plus(atom() | integer() | float() | string() | binary()) -> string()
170 %% @doc URL safe encoding of the given term.
171 quote_plus(Atom) when is_atom(Atom) ->
172 quote_plus(atom_to_list(Atom));
173 quote_plus(Int) when is_integer(Int) ->
174 quote_plus(integer_to_list(Int));
175 quote_plus(Binary) when is_binary(Binary) ->
176 quote_plus(binary_to_list(Binary));
177 quote_plus(Float) when is_float(Float) ->
178 quote_plus(mochinum:digits(Float));
179 quote_plus(String) ->
180 quote_plus(String, []).
181
182 quote_plus([], Acc) ->
183 lists:reverse(Acc);
184 quote_plus([C | Rest], Acc) when ?QS_SAFE(C) ->
185 quote_plus(Rest, [C | Acc]);
186 quote_plus([$\s | Rest], Acc) ->
187 quote_plus(Rest, [$+ | Acc]);
188 quote_plus([C | Rest], Acc) ->
189 <<Hi:4, Lo:4>> = <<C>>,
190 quote_plus(Rest, [hexdigit(Lo), hexdigit(Hi), ?PERCENT | Acc]).
191
192 %% @spec urlencode([{Key, Value}]) -> string()
193 %% @doc URL encode the property list.
194 urlencode(Props) ->
195 Pairs = lists:foldr(
196 fun ({K, V}, Acc) ->
197 [quote_plus(K) ++ "=" ++ quote_plus(V) | Acc]
198 end, [], Props),
199 string:join(Pairs, "&").
200
201 %% @spec parse_qs(string() | binary()) -> [{Key, Value}]
202 %% @doc Parse a query string or application/x-www-form-urlencoded.
203 parse_qs(Binary) when is_binary(Binary) ->
204 parse_qs(binary_to_list(Binary));
205 parse_qs(String) ->
206 parse_qs(String, []).
207
208 parse_qs([], Acc) ->
209 lists:reverse(Acc);
210 parse_qs(String, Acc) ->
211 {Key, Rest} = parse_qs_key(String),
212 {Value, Rest1} = parse_qs_value(Rest),
213 parse_qs(Rest1, [{Key, Value} | Acc]).
214
215 parse_qs_key(String) ->
216 parse_qs_key(String, []).
217
218 parse_qs_key([], Acc) ->
219 {qs_revdecode(Acc), ""};
220 parse_qs_key([$= | Rest], Acc) ->
221 {qs_revdecode(Acc), Rest};
222 parse_qs_key(Rest=[$; | _], Acc) ->
223 {qs_revdecode(Acc), Rest};
224 parse_qs_key(Rest=[$& | _], Acc) ->
225 {qs_revdecode(Acc), Rest};
226 parse_qs_key([C | Rest], Acc) ->
227 parse_qs_key(Rest, [C | Acc]).
228
229 parse_qs_value(String) ->
230 parse_qs_value(String, []).
231
232 parse_qs_value([], Acc) ->
233 {qs_revdecode(Acc), ""};
234 parse_qs_value([$; | Rest], Acc) ->
235 {qs_revdecode(Acc), Rest};
236 parse_qs_value([$& | Rest], Acc) ->
237 {qs_revdecode(Acc), Rest};
238 parse_qs_value([C | Rest], Acc) ->
239 parse_qs_value(Rest, [C | Acc]).
240
241 %% @spec unquote(string() | binary()) -> string()
242 %% @doc Unquote a URL encoded string.
243 unquote(Binary) when is_binary(Binary) ->
244 unquote(binary_to_list(Binary));
245 unquote(String) ->
246 qs_revdecode(lists:reverse(String)).
247
248 qs_revdecode(S) ->
249 qs_revdecode(S, []).
250
251 qs_revdecode([], Acc) ->
252 Acc;
253 qs_revdecode([$+ | Rest], Acc) ->
254 qs_revdecode(Rest, [$\s | Acc]);
255 qs_revdecode([Lo, Hi, ?PERCENT | Rest], Acc) when ?IS_HEX(Lo), ?IS_HEX(Hi) ->
256 qs_revdecode(Rest, [(unhexdigit(Lo) bor (unhexdigit(Hi) bsl 4)) | Acc]);
257 qs_revdecode([C | Rest], Acc) ->
258 qs_revdecode(Rest, [C | Acc]).
259
260 %% @spec urlsplit(Url) -> {Scheme, Netloc, Path, Query, Fragment}
261 %% @doc Return a 5-tuple, does not expand % escapes. Only supports HTTP style
262 %% URLs.
263 urlsplit(Url) ->
264 {Scheme, Url1} = urlsplit_scheme(Url),
265 {Netloc, Url2} = urlsplit_netloc(Url1),
266 {Path, Query, Fragment} = urlsplit_path(Url2),
267 {Scheme, Netloc, Path, Query, Fragment}.
268
269 urlsplit_scheme(Url) ->
270 case urlsplit_scheme(Url, []) of
271 no_scheme ->
272 {"", Url};
273 Res ->
274 Res
275 end.
276
277 urlsplit_scheme([C | Rest], Acc) when ((C >= $a andalso C =< $z) orelse
278 (C >= $A andalso C =< $Z) orelse
279 (C >= $0 andalso C =< $9) orelse
280 C =:= $+ orelse C =:= $- orelse
281 C =:= $.) ->
282 urlsplit_scheme(Rest, [C | Acc]);
283 urlsplit_scheme([$: | Rest], Acc=[_ | _]) ->
284 {string:to_lower(lists:reverse(Acc)), Rest};
285 urlsplit_scheme(_Rest, _Acc) ->
286 no_scheme.
287
288 urlsplit_netloc("//" ++ Rest) ->
289 urlsplit_netloc(Rest, []);
290 urlsplit_netloc(Path) ->
291 {"", Path}.
292
293 urlsplit_netloc("", Acc) ->
294 {lists:reverse(Acc), ""};
295 urlsplit_netloc(Rest=[C | _], Acc) when C =:= $/; C =:= $?; C =:= $# ->
296 {lists:reverse(Acc), Rest};
297 urlsplit_netloc([C | Rest], Acc) ->
298 urlsplit_netloc(Rest, [C | Acc]).
299
300
301 %% @spec path_split(string()) -> {Part, Rest}
302 %% @doc Split a path starting from the left, as in URL traversal.
303 %% path_split("foo/bar") = {"foo", "bar"},
304 %% path_split("/foo/bar") = {"", "foo/bar"}.
305 path_split(S) ->
306 path_split(S, []).
307
308 path_split("", Acc) ->
309 {lists:reverse(Acc), ""};
310 path_split("/" ++ Rest, Acc) ->
311 {lists:reverse(Acc), Rest};
312 path_split([C | Rest], Acc) ->
313 path_split(Rest, [C | Acc]).
314
315
316 %% @spec urlunsplit({Scheme, Netloc, Path, Query, Fragment}) -> string()
317 %% @doc Assemble a URL from the 5-tuple. Path must be absolute.
318 urlunsplit({Scheme, Netloc, Path, Query, Fragment}) ->
319 lists:flatten([case Scheme of "" -> ""; _ -> [Scheme, "://"] end,
320 Netloc,
321 urlunsplit_path({Path, Query, Fragment})]).
322
323 %% @spec urlunsplit_path({Path, Query, Fragment}) -> string()
324 %% @doc Assemble a URL path from the 3-tuple.
325 urlunsplit_path({Path, Query, Fragment}) ->
326 lists:flatten([Path,
327 case Query of "" -> ""; _ -> [$? | Query] end,
328 case Fragment of "" -> ""; _ -> [$# | Fragment] end]).
329
330 %% @spec urlsplit_path(Url) -> {Path, Query, Fragment}
331 %% @doc Return a 3-tuple, does not expand % escapes. Only supports HTTP style
332 %% paths.
333 urlsplit_path(Path) ->
334 urlsplit_path(Path, []).
335
336 urlsplit_path("", Acc) ->
337 {lists:reverse(Acc), "", ""};
338 urlsplit_path("?" ++ Rest, Acc) ->
339 {Query, Fragment} = urlsplit_query(Rest),
340 {lists:reverse(Acc), Query, Fragment};
341 urlsplit_path("#" ++ Rest, Acc) ->
342 {lists:reverse(Acc), "", Rest};
343 urlsplit_path([C | Rest], Acc) ->
344 urlsplit_path(Rest, [C | Acc]).
345
346 urlsplit_query(Query) ->
347 urlsplit_query(Query, []).
348
349 urlsplit_query("", Acc) ->
350 {lists:reverse(Acc), ""};
351 urlsplit_query("#" ++ Rest, Acc) ->
352 {lists:reverse(Acc), Rest};
353 urlsplit_query([C | Rest], Acc) ->
354 urlsplit_query(Rest, [C | Acc]).
355
356 %% @spec guess_mime(string()) -> string()
357 %% @doc Guess the mime type of a file by the extension of its filename.
358 guess_mime(File) ->
359 case filename:basename(File) of
360 "crossdomain.xml" ->
361 "text/x-cross-domain-policy";
362 Name ->
363 case mochiweb_mime:from_extension(filename:extension(Name)) of
364 undefined ->
365 "text/plain";
366 Mime ->
367 Mime
368 end
369 end.
370
371 %% @spec parse_header(string()) -> {Type, [{K, V}]}
372 %% @doc Parse a Content-Type like header, return the main Content-Type
373 %% and a property list of options.
374 parse_header(String) ->
375 %% TODO: This is exactly as broken as Python's cgi module.
376 %% Should parse properly like mochiweb_cookies.
377 [Type | Parts] = [string:strip(S) || S <- string:tokens(String, ";")],
378 F = fun (S, Acc) ->
379 case lists:splitwith(fun (C) -> C =/= $= end, S) of
380 {"", _} ->
381 %% Skip anything with no name
382 Acc;
383 {_, ""} ->
384 %% Skip anything with no value
385 Acc;
386 {Name, [$\= | Value]} ->
387 [{string:to_lower(string:strip(Name)),
388 unquote_header(string:strip(Value))} | Acc]
389 end
390 end,
391 {string:to_lower(Type),
392 lists:foldr(F, [], Parts)}.
393
394 unquote_header("\"" ++ Rest) ->
395 unquote_header(Rest, []);
396 unquote_header(S) ->
397 S.
398
399 unquote_header("", Acc) ->
400 lists:reverse(Acc);
401 unquote_header("\"", Acc) ->
402 lists:reverse(Acc);
403 unquote_header([$\\, C | Rest], Acc) ->
404 unquote_header(Rest, [C | Acc]);
405 unquote_header([C | Rest], Acc) ->
406 unquote_header(Rest, [C | Acc]).
407
408 %% @spec record_to_proplist(Record, Fields) -> proplist()
409 %% @doc calls record_to_proplist/3 with a default TypeKey of '__record'
410 record_to_proplist(Record, Fields) ->
411 record_to_proplist(Record, Fields, '__record').
412
413 %% @spec record_to_proplist(Record, Fields, TypeKey) -> proplist()
414 %% @doc Return a proplist of the given Record with each field in the
415 %% Fields list set as a key with the corresponding value in the Record.
416 %% TypeKey is the key that is used to store the record type
417 %% Fields should be obtained by calling record_info(fields, record_type)
418 %% where record_type is the record type of Record
419 record_to_proplist(Record, Fields, TypeKey)
420 when tuple_size(Record) - 1 =:= length(Fields) ->
421 lists:zip([TypeKey | Fields], tuple_to_list(Record)).
422
423
424 shell_quote([], Acc) ->
425 lists:reverse([$\" | Acc]);
426 shell_quote([C | Rest], Acc) when C =:= $\" orelse C =:= $\` orelse
427 C =:= $\\ orelse C =:= $\$ ->
428 shell_quote(Rest, [C, $\\ | Acc]);
429 shell_quote([C | Rest], Acc) ->
430 shell_quote(Rest, [C | Acc]).
431
432 %% @spec parse_qvalues(string()) -> [qvalue()] | invalid_qvalue_string
433 %% @type qvalue() = {media_type() | encoding() , float()}.
434 %% @type media_type() = string().
435 %% @type encoding() = string().
436 %%
437 %% @doc Parses a list (given as a string) of elements with Q values associated
438 %% to them. Elements are separated by commas and each element is separated
439 %% from its Q value by a semicolon. Q values are optional but when missing
440 %% the value of an element is considered as 1.0. A Q value is always in the
441 %% range [0.0, 1.0]. A Q value list is used for example as the value of the
442 %% HTTP "Accept" and "Accept-Encoding" headers.
443 %%
444 %% Q values are described in section 2.9 of the RFC 2616 (HTTP 1.1).
445 %%
446 %% Example:
447 %%
448 %% parse_qvalues("gzip; q=0.5, deflate, identity;q=0.0") ->
449 %% [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 0.0}]
450 %%
451 parse_qvalues(QValuesStr) ->
452 try
453 lists:map(
454 fun(Pair) ->
455 [Type | Params] = string:tokens(Pair, ";"),
456 NormParams = normalize_media_params(Params),
457 {Q, NonQParams} = extract_q(NormParams),
458 {string:join([string:strip(Type) | NonQParams], ";"), Q}
459 end,
460 string:tokens(string:to_lower(QValuesStr), ",")
461 )
462 catch
463 _Type:_Error ->
464 invalid_qvalue_string
465 end.
466
467 normalize_media_params(Params) ->
468 {ok, Re} = re:compile("\\s"),
469 normalize_media_params(Re, Params, []).
470
471 normalize_media_params(_Re, [], Acc) ->
472 lists:reverse(Acc);
473 normalize_media_params(Re, [Param | Rest], Acc) ->
474 NormParam = re:replace(Param, Re, "", [global, {return, list}]),
475 normalize_media_params(Re, Rest, [NormParam | Acc]).
476
477 extract_q(NormParams) ->
478 {ok, KVRe} = re:compile("^([^=]+)=([^=]+)$"),
479 {ok, QRe} = re:compile("^((?:0|1)(?:\\.\\d{1,3})?)$"),
480 extract_q(KVRe, QRe, NormParams, []).
481
482 extract_q(_KVRe, _QRe, [], Acc) ->
483 {1.0, lists:reverse(Acc)};
484 extract_q(KVRe, QRe, [Param | Rest], Acc) ->
485 case re:run(Param, KVRe, [{capture, [1, 2], list}]) of
486 {match, [Name, Value]} ->
487 case Name of
488 "q" ->
489 {match, [Q]} = re:run(Value, QRe, [{capture, [1], list}]),
490 QVal = case Q of
491 "0" ->
492 0.0;
493 "1" ->
494 1.0;
495 Else ->
496 list_to_float(Else)
497 end,
498 case QVal < 0.0 orelse QVal > 1.0 of
499 false ->
500 {QVal, lists:reverse(Acc) ++ Rest}
501 end;
502 _ ->
503 extract_q(KVRe, QRe, Rest, [Param | Acc])
504 end
505 end.
506
507 %% @spec pick_accepted_encodings([qvalue()], [encoding()], encoding()) ->
508 %% [encoding()]
509 %%
510 %% @doc Determines which encodings specified in the given Q values list are
511 %% valid according to a list of supported encodings and a default encoding.
512 %%
513 %% The returned list of encodings is sorted, descendingly, according to the
514 %% Q values of the given list. The last element of this list is the given
515 %% default encoding unless this encoding is explicitily or implicitily
516 %% marked with a Q value of 0.0 in the given Q values list.
517 %% Note: encodings with the same Q value are kept in the same order as
518 %% found in the input Q values list.
519 %%
520 %% This encoding picking process is described in section 14.3 of the
521 %% RFC 2616 (HTTP 1.1).
522 %%
523 %% Example:
524 %%
525 %% pick_accepted_encodings(
526 %% [{"gzip", 0.5}, {"deflate", 1.0}],
527 %% ["gzip", "identity"],
528 %% "identity"
529 %% ) ->
530 %% ["gzip", "identity"]
531 %%
532 pick_accepted_encodings(AcceptedEncs, SupportedEncs, DefaultEnc) ->
533 SortedQList = lists:reverse(
534 lists:sort(fun({_, Q1}, {_, Q2}) -> Q1 < Q2 end, AcceptedEncs)
535 ),
536 {Accepted, Refused} = lists:foldr(
537 fun({E, Q}, {A, R}) ->
538 case Q > 0.0 of
539 true ->
540 {[E | A], R};
541 false ->
542 {A, [E | R]}
543 end
544 end,
545 {[], []},
546 SortedQList
547 ),
548 Refused1 = lists:foldr(
549 fun(Enc, Acc) ->
550 case Enc of
551 "*" ->
552 lists:subtract(SupportedEncs, Accepted) ++ Acc;
553 _ ->
554 [Enc | Acc]
555 end
556 end,
557 [],
558 Refused
559 ),
560 Accepted1 = lists:foldr(
561 fun(Enc, Acc) ->
562 case Enc of
563 "*" ->
564 lists:subtract(SupportedEncs, Accepted ++ Refused1) ++ Acc;
565 _ ->
566 [Enc | Acc]
567 end
568 end,
569 [],
570 Accepted
571 ),
572 Accepted2 = case lists:member(DefaultEnc, Accepted1) of
573 true ->
574 Accepted1;
575 false ->
576 Accepted1 ++ [DefaultEnc]
577 end,
578 [E || E <- Accepted2, lists:member(E, SupportedEncs),
579 not lists:member(E, Refused1)].
580
581 make_io(Atom) when is_atom(Atom) ->
582 atom_to_list(Atom);
583 make_io(Integer) when is_integer(Integer) ->
584 integer_to_list(Integer);
585 make_io(Io) when is_list(Io); is_binary(Io) ->
586 Io.
587
588 %%
589 %% Tests
590 %%
591 -ifdef(TEST).
592 -include_lib("eunit/include/eunit.hrl").
593
594 make_io_test() ->
595 ?assertEqual(
596 <<"atom">>,
597 iolist_to_binary(make_io(atom))),
598 ?assertEqual(
599 <<"20">>,
600 iolist_to_binary(make_io(20))),
601 ?assertEqual(
602 <<"list">>,
603 iolist_to_binary(make_io("list"))),
604 ?assertEqual(
605 <<"binary">>,
606 iolist_to_binary(make_io(<<"binary">>))),
607 ok.
608
609 -record(test_record, {field1=f1, field2=f2}).
610 record_to_proplist_test() ->
611 ?assertEqual(
612 [{'__record', test_record},
613 {field1, f1},
614 {field2, f2}],
615 record_to_proplist(#test_record{}, record_info(fields, test_record))),
616 ?assertEqual(
617 [{'typekey', test_record},
618 {field1, f1},
619 {field2, f2}],
620 record_to_proplist(#test_record{},
621 record_info(fields, test_record),
622 typekey)),
623 ok.
624
625 shell_quote_test() ->
626 ?assertEqual(
627 "\"foo \\$bar\\\"\\`' baz\"",
628 shell_quote("foo $bar\"`' baz")),
629 ok.
630
631 cmd_port_test_spool(Port, Acc) ->
632 receive
633 {Port, eof} ->
634 Acc;
635 {Port, {data, {eol, Data}}} ->
636 cmd_port_test_spool(Port, ["\n", Data | Acc]);
637 {Port, Unknown} ->
638 throw({unknown, Unknown})
639 after 1000 ->
640 throw(timeout)
641 end.
642
643 cmd_port_test() ->
644 Port = cmd_port(["echo", "$bling$ `word`!"],
645 [eof, stream, {line, 4096}]),
646 Res = try lists:append(lists:reverse(cmd_port_test_spool(Port, [])))
647 after catch port_close(Port)
648 end,
649 self() ! {Port, wtf},
650 try cmd_port_test_spool(Port, [])
651 catch throw:{unknown, wtf} -> ok
652 end,
653 try cmd_port_test_spool(Port, [])
654 catch throw:timeout -> ok
655 end,
656 ?assertEqual(
657 "$bling$ `word`!\n",
658 Res).
659
660 cmd_test() ->
661 ?assertEqual(
662 "$bling$ `word`!\n",
663 cmd(["echo", "$bling$ `word`!"])),
664 ok.
665
666 cmd_string_test() ->
667 ?assertEqual(
668 "\"echo\" \"\\$bling\\$ \\`word\\`!\"",
669 cmd_string(["echo", "$bling$ `word`!"])),
670 ok.
671
672 cmd_status_test() ->
673 ?assertEqual(
674 {0, <<"$bling$ `word`!\n">>},
675 cmd_status(["echo", "$bling$ `word`!"])),
676 ok.
677
678
679 parse_header_test() ->
680 ?assertEqual(
681 {"multipart/form-data", [{"boundary", "AaB03x"}]},
682 parse_header("multipart/form-data; boundary=AaB03x")),
683 %% This tests (currently) intentionally broken behavior
684 ?assertEqual(
685 {"multipart/form-data",
686 [{"b", ""},
687 {"cgi", "is"},
688 {"broken", "true\"e"}]},
689 parse_header("multipart/form-data;b=;cgi=\"i\\s;broken=true\"e;=z;z")),
690 ok.
691
692 guess_mime_test() ->
693 ?assertEqual("text/plain", guess_mime("")),
694 ?assertEqual("text/plain", guess_mime(".text")),
695 ?assertEqual("application/zip", guess_mime(".zip")),
696 ?assertEqual("application/zip", guess_mime("x.zip")),
697 ?assertEqual("text/html", guess_mime("x.html")),
698 ?assertEqual("application/xhtml+xml", guess_mime("x.xhtml")),
699 ?assertEqual("text/x-cross-domain-policy", guess_mime("crossdomain.xml")),
700 ?assertEqual("text/x-cross-domain-policy", guess_mime("www/crossdomain.xml")),
701 ok.
702
703 path_split_test() ->
704 {"", "foo/bar"} = path_split("/foo/bar"),
705 {"foo", "bar"} = path_split("foo/bar"),
706 {"bar", ""} = path_split("bar"),
707 ok.
708
709 urlsplit_test() ->
710 {"", "", "/foo", "", "bar?baz"} = urlsplit("/foo#bar?baz"),
711 {"http", "host:port", "/foo", "", "bar?baz"} =
712 urlsplit("http://host:port/foo#bar?baz"),
713 {"http", "host", "", "", ""} = urlsplit("http://host"),
714 {"", "", "/wiki/Category:Fruit", "", ""} =
715 urlsplit("/wiki/Category:Fruit"),
716 ok.
717
718 urlsplit_path_test() ->
719 {"/foo/bar", "", ""} = urlsplit_path("/foo/bar"),
720 {"/foo", "baz", ""} = urlsplit_path("/foo?baz"),
721 {"/foo", "", "bar?baz"} = urlsplit_path("/foo#bar?baz"),
722 {"/foo", "", "bar?baz#wibble"} = urlsplit_path("/foo#bar?baz#wibble"),
723 {"/foo", "bar", "baz"} = urlsplit_path("/foo?bar#baz"),
724 {"/foo", "bar?baz", "baz"} = urlsplit_path("/foo?bar?baz#baz"),
725 ok.
726
727 urlunsplit_test() ->
728 "/foo#bar?baz" = urlunsplit({"", "", "/foo", "", "bar?baz"}),
729 "http://host:port/foo#bar?baz" =
730 urlunsplit({"http", "host:port", "/foo", "", "bar?baz"}),
731 ok.
732
733 urlunsplit_path_test() ->
734 "/foo/bar" = urlunsplit_path({"/foo/bar", "", ""}),
735 "/foo?baz" = urlunsplit_path({"/foo", "baz", ""}),
736 "/foo#bar?baz" = urlunsplit_path({"/foo", "", "bar?baz"}),
737 "/foo#bar?baz#wibble" = urlunsplit_path({"/foo", "", "bar?baz#wibble"}),
738 "/foo?bar#baz" = urlunsplit_path({"/foo", "bar", "baz"}),
739 "/foo?bar?baz#baz" = urlunsplit_path({"/foo", "bar?baz", "baz"}),
740 ok.
741
742 join_test() ->
743 ?assertEqual("foo,bar,baz",
744 join(["foo", "bar", "baz"], $,)),
745 ?assertEqual("foo,bar,baz",
746 join(["foo", "bar", "baz"], ",")),
747 ?assertEqual("foo bar",
748 join([["foo", " bar"]], ",")),
749 ?assertEqual("foo bar,baz",
750 join([["foo", " bar"], "baz"], ",")),
751 ?assertEqual("foo",
752 join(["foo"], ",")),
753 ?assertEqual("foobarbaz",
754 join(["foo", "bar", "baz"], "")),
755 ?assertEqual("foo" ++ [<<>>] ++ "bar" ++ [<<>>] ++ "baz",
756 join(["foo", "bar", "baz"], <<>>)),
757 ?assertEqual("foobar" ++ [<<"baz">>],
758 join(["foo", "bar", <<"baz">>], "")),
759 ?assertEqual("",
760 join([], "any")),
761 ok.
762
763 quote_plus_test() ->
764 "foo" = quote_plus(foo),
765 "1" = quote_plus(1),
766 "1.1" = quote_plus(1.1),
767 "foo" = quote_plus("foo"),
768 "foo+bar" = quote_plus("foo bar"),
769 "foo%0A" = quote_plus("foo\n"),
770 "foo%0A" = quote_plus("foo\n"),
771 "foo%3B%26%3D" = quote_plus("foo;&="),
772 "foo%3B%26%3D" = quote_plus(<<"foo;&=">>),
773 ok.
774
775 unquote_test() ->
776 ?assertEqual("foo bar",
777 unquote("foo+bar")),
778 ?assertEqual("foo bar",
779 unquote("foo%20bar")),
780 ?assertEqual("foo\r\n",
781 unquote("foo%0D%0A")),
782 ?assertEqual("foo\r\n",
783 unquote(<<"foo%0D%0A">>)),
784 ok.
785
786 urlencode_test() ->
787 "foo=bar&baz=wibble+%0D%0A&z=1" = urlencode([{foo, "bar"},
788 {"baz", "wibble \r\n"},
789 {z, 1}]),
790 ok.
791
792 parse_qs_test() ->
793 ?assertEqual(
794 [{"foo", "bar"}, {"baz", "wibble \r\n"}, {"z", "1"}],
795 parse_qs("foo=bar&baz=wibble+%0D%0a&z=1")),
796 ?assertEqual(
797 [{"", "bar"}, {"baz", "wibble \r\n"}, {"z", ""}],
798 parse_qs("=bar&baz=wibble+%0D%0a&z=")),
799 ?assertEqual(
800 [{"foo", "bar"}, {"baz", "wibble \r\n"}, {"z", "1"}],
801 parse_qs(<<"foo=bar&baz=wibble+%0D%0a&z=1">>)),
802 ?assertEqual(
803 [],
804 parse_qs("")),
805 ?assertEqual(
806 [{"foo", ""}, {"bar", ""}, {"baz", ""}],
807 parse_qs("foo;bar&baz")),
808 ok.
809
810 partition_test() ->
811 {"foo", "", ""} = partition("foo", "/"),
812 {"foo", "/", "bar"} = partition("foo/bar", "/"),
813 {"foo", "/", ""} = partition("foo/", "/"),
814 {"", "/", "bar"} = partition("/bar", "/"),
815 {"f", "oo/ba", "r"} = partition("foo/bar", "oo/ba"),
816 ok.
817
818 safe_relative_path_test() ->
819 "foo" = safe_relative_path("foo"),
820 "foo/" = safe_relative_path("foo/"),
821 "foo" = safe_relative_path("foo/bar/.."),
822 "bar" = safe_relative_path("foo/../bar"),
823 "bar/" = safe_relative_path("foo/../bar/"),
824 "" = safe_relative_path("foo/.."),
825 "" = safe_relative_path("foo/../"),
826 undefined = safe_relative_path("/foo"),
827 undefined = safe_relative_path("../foo"),
828 undefined = safe_relative_path("foo/../.."),
829 undefined = safe_relative_path("foo//"),
830 undefined = safe_relative_path("foo\\bar"),
831 ok.
832
833 parse_qvalues_test() ->
834 [] = parse_qvalues(""),
835 [{"identity", 0.0}] = parse_qvalues("identity;q=0"),
836 [{"identity", 0.0}] = parse_qvalues("identity ;q=0"),
837 [{"identity", 0.0}] = parse_qvalues(" identity; q =0 "),
838 [{"identity", 0.0}] = parse_qvalues("identity ; q = 0"),
839 [{"identity", 0.0}] = parse_qvalues("identity ; q= 0.0"),
840 [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues(
841 "gzip,deflate,identity;q=0.0"
842 ),
843 [{"deflate", 1.0}, {"gzip", 1.0}, {"identity", 0.0}] = parse_qvalues(
844 "deflate,gzip,identity;q=0.0"
845 ),
846 [{"gzip", 1.0}, {"deflate", 1.0}, {"gzip", 1.0}, {"identity", 0.0}] =
847 parse_qvalues("gzip,deflate,gzip,identity;q=0"),
848 [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues(
849 "gzip, deflate , identity; q=0.0"
850 ),
851 [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues(
852 "gzip; q=1, deflate;q=1.0, identity;q=0.0"
853 ),
854 [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues(
855 "gzip; q=0.5, deflate;q=1.0, identity;q=0"
856 ),
857 [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues(
858 "gzip; q=0.5, deflate , identity;q=0.0"
859 ),
860 [{"gzip", 0.5}, {"deflate", 0.8}, {"identity", 0.0}] = parse_qvalues(
861 "gzip; q=0.5, deflate;q=0.8, identity;q=0.0"
862 ),
863 [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 1.0}] = parse_qvalues(
864 "gzip; q=0.5,deflate,identity"
865 ),
866 [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 1.0}, {"identity", 1.0}] =
867 parse_qvalues("gzip; q=0.5,deflate,identity, identity "),
868 [{"text/html;level=1", 1.0}, {"text/plain", 0.5}] =
869 parse_qvalues("text/html;level=1, text/plain;q=0.5"),
870 [{"text/html;level=1", 0.3}, {"text/plain", 1.0}] =
871 parse_qvalues("text/html;level=1;q=0.3, text/plain"),
872 [{"text/html;level=1", 0.3}, {"text/plain", 1.0}] =
873 parse_qvalues("text/html; level = 1; q = 0.3, text/plain"),
874 [{"text/html;level=1", 0.3}, {"text/plain", 1.0}] =
875 parse_qvalues("text/html;q=0.3;level=1, text/plain"),
876 invalid_qvalue_string = parse_qvalues("gzip; q=1.1, deflate"),
877 invalid_qvalue_string = parse_qvalues("gzip; q=0.5, deflate;q=2"),
878 invalid_qvalue_string = parse_qvalues("gzip, deflate;q=AB"),
879 invalid_qvalue_string = parse_qvalues("gzip; q=2.1, deflate"),
880 invalid_qvalue_string = parse_qvalues("gzip; q=0.1234, deflate"),
881 invalid_qvalue_string = parse_qvalues("text/html;level=1;q=0.3, text/html;level"),
882 ok.
883
884 pick_accepted_encodings_test() ->
885 ["identity"] = pick_accepted_encodings(
886 [],
887 ["gzip", "identity"],
888 "identity"
889 ),
890 ["gzip", "identity"] = pick_accepted_encodings(
891 [{"gzip", 1.0}],
892 ["gzip", "identity"],
893 "identity"
894 ),
895 ["identity"] = pick_accepted_encodings(
896 [{"gzip", 0.0}],
897 ["gzip", "identity"],
898 "identity"
899 ),
900 ["gzip", "identity"] = pick_accepted_encodings(
901 [{"gzip", 1.0}, {"deflate", 1.0}],
902 ["gzip", "identity"],
903 "identity"
904 ),
905 ["gzip", "identity"] = pick_accepted_encodings(
906 [{"gzip", 0.5}, {"deflate", 1.0}],
907 ["gzip", "identity"],
908 "identity"
909 ),
910 ["identity"] = pick_accepted_encodings(
911 [{"gzip", 0.0}, {"deflate", 0.0}],
912 ["gzip", "identity"],
913 "identity"
914 ),
915 ["gzip"] = pick_accepted_encodings(
916 [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}],
917 ["gzip", "identity"],
918 "identity"
919 ),
920 ["gzip", "deflate", "identity"] = pick_accepted_encodings(
921 [{"gzip", 1.0}, {"deflate", 1.0}],
922 ["gzip", "deflate", "identity"],
923 "identity"
924 ),
925 ["gzip", "deflate"] = pick_accepted_encodings(
926 [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}],
927 ["gzip", "deflate", "identity"],
928 "identity"
929 ),
930 ["deflate", "gzip", "identity"] = pick_accepted_encodings(
931 [{"gzip", 0.2}, {"deflate", 1.0}],
932 ["gzip", "deflate", "identity"],
933 "identity"
934 ),
935 ["deflate", "deflate", "gzip", "identity"] = pick_accepted_encodings(
936 [{"gzip", 0.2}, {"deflate", 1.0}, {"deflate", 1.0}],
937 ["gzip", "deflate", "identity"],
938 "identity"
939 ),
940 ["deflate", "gzip", "gzip", "identity"] = pick_accepted_encodings(
941 [{"gzip", 0.2}, {"deflate", 1.0}, {"gzip", 1.0}],
942 ["gzip", "deflate", "identity"],
943 "identity"
944 ),
945 ["gzip", "deflate", "gzip", "identity"] = pick_accepted_encodings(
946 [{"gzip", 0.2}, {"deflate", 0.9}, {"gzip", 1.0}],
947 ["gzip", "deflate", "identity"],
948 "identity"
949 ),
950 [] = pick_accepted_encodings(
951 [{"*", 0.0}],
952 ["gzip", "deflate", "identity"],
953 "identity"
954 ),
955 ["gzip", "deflate", "identity"] = pick_accepted_encodings(
956 [{"*", 1.0}],
957 ["gzip", "deflate", "identity"],
958 "identity"
959 ),
960 ["gzip", "deflate", "identity"] = pick_accepted_encodings(
961 [{"*", 0.6}],
962 ["gzip", "deflate", "identity"],
963 "identity"
964 ),
965 ["gzip"] = pick_accepted_encodings(
966 [{"gzip", 1.0}, {"*", 0.0}],
967 ["gzip", "deflate", "identity"],
968 "identity"
969 ),
970 ["gzip", "deflate"] = pick_accepted_encodings(
971 [{"gzip", 1.0}, {"deflate", 0.6}, {"*", 0.0}],
972 ["gzip", "deflate", "identity"],
973 "identity"
974 ),
975 ["deflate", "gzip"] = pick_accepted_encodings(
976 [{"gzip", 0.5}, {"deflate", 1.0}, {"*", 0.0}],
977 ["gzip", "deflate", "identity"],
978 "identity"
979 ),
980 ["gzip", "identity"] = pick_accepted_encodings(
981 [{"deflate", 0.0}, {"*", 1.0}],
982 ["gzip", "deflate", "identity"],
983 "identity"
984 ),
985 ["gzip", "identity"] = pick_accepted_encodings(
986 [{"*", 1.0}, {"deflate", 0.0}],
987 ["gzip", "deflate", "identity"],
988 "identity"
989 ),
990 ok.
991
992 -endif.
+0
-281
deps/mochiweb/src/mochiweb_websocket.erl less more
0 -module(mochiweb_websocket).
1 -author('lukasz.lalik@zadane.pl').
2
3 %% The MIT License (MIT)
4
5 %% Copyright (c) 2012 Zadane.pl sp. z o.o.
6
7 %% Permission is hereby granted, free of charge, to any person obtaining a copy
8 %% of this software and associated documentation files (the "Software"), to deal
9 %% in the Software without restriction, including without limitation the rights
10 %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 %% copies of the Software, and to permit persons to whom the Software is
12 %% furnished to do so, subject to the following conditions:
13
14 %% The above copyright notice and this permission notice shall be included in
15 %% all copies or substantial portions of the Software.
16
17 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 %% THE SOFTWARE.
24
25 %% @doc Websockets module for Mochiweb. Based on Misultin websockets module.
26
27 -export([loop/5, upgrade_connection/2, request/5]).
28 -export([send/3]).
29 -ifdef(TEST).
30 -compile(export_all).
31 -endif.
32
33 loop(Socket, Body, State, WsVersion, ReplyChannel) ->
34 ok = mochiweb_socket:exit_if_closed(mochiweb_socket:setopts(Socket, [{packet, 0}, {active, once}])),
35 proc_lib:hibernate(?MODULE, request,
36 [Socket, Body, State, WsVersion, ReplyChannel]).
37
38 request(Socket, Body, State, WsVersion, ReplyChannel) ->
39 receive
40 {tcp_closed, _} ->
41 mochiweb_socket:close(Socket),
42 exit(normal);
43 {ssl_closed, _} ->
44 mochiweb_socket:close(Socket),
45 exit(normal);
46 {tcp_error, _, _} ->
47 mochiweb_socket:close(Socket),
48 exit(normal);
49 {Proto, _, WsFrames} when Proto =:= tcp orelse Proto =:= ssl ->
50 case parse_frames(WsVersion, WsFrames, Socket) of
51 close ->
52 mochiweb_socket:close(Socket),
53 exit(normal);
54 error ->
55 mochiweb_socket:close(Socket),
56 exit(normal);
57 Payload ->
58 NewState = call_body(Body, Payload, State, ReplyChannel),
59 loop(Socket, Body, NewState, WsVersion, ReplyChannel)
60 end;
61 _ ->
62 mochiweb_socket:close(Socket),
63 exit(normal)
64 end.
65
66 call_body({M, F, A}, Payload, State, ReplyChannel) ->
67 erlang:apply(M, F, [Payload, State, ReplyChannel | A]);
68 call_body({M, F}, Payload, State, ReplyChannel) ->
69 M:F(Payload, State, ReplyChannel);
70 call_body(Body, Payload, State, ReplyChannel) ->
71 Body(Payload, State, ReplyChannel).
72
73 send(Socket, Payload, hybi) ->
74 Prefix = <<1:1, 0:3, 1:4, (payload_length(iolist_size(Payload)))/binary>>,
75 mochiweb_socket:send(Socket, [Prefix, Payload]);
76 send(Socket, Payload, hixie) ->
77 mochiweb_socket:send(Socket, [0, Payload, 255]).
78
79 upgrade_connection(Req, Body) ->
80 case make_handshake(Req) of
81 {Version, Response} ->
82 Req:respond(Response),
83 Socket = Req:get(socket),
84 ReplyChannel = fun (Payload) ->
85 ?MODULE:send(Socket, Payload, Version)
86 end,
87 Reentry = fun (State) ->
88 ?MODULE:loop(Socket, Body, State, Version, ReplyChannel)
89 end,
90 {Reentry, ReplyChannel};
91 _ ->
92 mochiweb_socket:close(Req:get(socket)),
93 exit(normal)
94 end.
95
96 make_handshake(Req) ->
97 SecKey = Req:get_header_value("sec-websocket-key"),
98 Sec1Key = Req:get_header_value("Sec-WebSocket-Key1"),
99 Sec2Key = Req:get_header_value("Sec-WebSocket-Key2"),
100 Origin = Req:get_header_value(origin),
101 if SecKey =/= undefined ->
102 hybi_handshake(SecKey);
103 Sec1Key =/= undefined andalso Sec2Key =/= undefined ->
104 Host = Req:get_header_value("Host"),
105 Path = Req:get(path),
106 Body = Req:recv(8),
107 Scheme = scheme(Req),
108 hixie_handshake(Scheme, Host, Path, Sec1Key, Sec2Key, Body, Origin);
109 true ->
110 error
111 end.
112
113 hybi_handshake(SecKey) ->
114 BinKey = list_to_binary(SecKey),
115 Bin = <<BinKey/binary, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11">>,
116 Challenge = base64:encode(crypto:hash(sha, Bin)),
117 Response = {101, [{"Connection", "Upgrade"},
118 {"Upgrade", "websocket"},
119 {"Sec-Websocket-Accept", Challenge}], ""},
120 {hybi, Response}.
121
122 scheme(Req) ->
123 case mochiweb_request:get(scheme, Req) of
124 http ->
125 "ws://";
126 https ->
127 "wss://"
128 end.
129
130 hixie_handshake(Scheme, Host, Path, Key1, Key2, Body, Origin) ->
131 Ikey1 = [D || D <- Key1, $0 =< D, D =< $9],
132 Ikey2 = [D || D <- Key2, $0 =< D, D =< $9],
133 Blank1 = length([D || D <- Key1, D =:= 32]),
134 Blank2 = length([D || D <- Key2, D =:= 32]),
135 Part1 = erlang:list_to_integer(Ikey1) div Blank1,
136 Part2 = erlang:list_to_integer(Ikey2) div Blank2,
137 Ckey = <<Part1:4/big-unsigned-integer-unit:8,
138 Part2:4/big-unsigned-integer-unit:8,
139 Body/binary>>,
140 Challenge = erlang:md5(Ckey),
141 Location = lists:concat([Scheme, Host, Path]),
142 Response = {101, [{"Upgrade", "WebSocket"},
143 {"Connection", "Upgrade"},
144 {"Sec-WebSocket-Origin", Origin},
145 {"Sec-WebSocket-Location", Location}],
146 Challenge},
147 {hixie, Response}.
148
149 parse_frames(hybi, Frames, Socket) ->
150 try parse_hybi_frames(Socket, Frames, []) of
151 Parsed -> process_frames(Parsed, [])
152 catch
153 _:_ -> error
154 end;
155 parse_frames(hixie, Frames, _Socket) ->
156 try parse_hixie_frames(Frames, []) of
157 Payload -> Payload
158 catch
159 _:_ -> error
160 end.
161
162 %%
163 %% Websockets internal functions for RFC6455 and hybi draft
164 %%
165 process_frames([], Acc) ->
166 lists:reverse(Acc);
167 process_frames([{Opcode, Payload} | Rest], Acc) ->
168 case Opcode of
169 8 -> close;
170 _ ->
171 process_frames(Rest, [Payload | Acc])
172 end.
173
174 parse_hybi_frames(_, <<>>, Acc) ->
175 lists:reverse(Acc);
176 parse_hybi_frames(S, <<_Fin:1,
177 _Rsv:3,
178 Opcode:4,
179 _Mask:1,
180 PayloadLen:7,
181 MaskKey:4/binary,
182 Payload:PayloadLen/binary-unit:8,
183 Rest/binary>>,
184 Acc) when PayloadLen < 126 ->
185 Payload2 = hybi_unmask(Payload, MaskKey, <<>>),
186 parse_hybi_frames(S, Rest, [{Opcode, Payload2} | Acc]);
187 parse_hybi_frames(S, <<_Fin:1,
188 _Rsv:3,
189 Opcode:4,
190 _Mask:1,
191 126:7,
192 PayloadLen:16,
193 MaskKey:4/binary,
194 Payload:PayloadLen/binary-unit:8,
195 Rest/binary>>,
196 Acc) ->
197 Payload2 = hybi_unmask(Payload, MaskKey, <<>>),
198 parse_hybi_frames(S, Rest, [{Opcode, Payload2} | Acc]);
199 parse_hybi_frames(Socket, <<_Fin:1,
200 _Rsv:3,
201 _Opcode:4,
202 _Mask:1,
203 126:7,
204 _PayloadLen:16,
205 _MaskKey:4/binary,
206 _/binary-unit:8>> = PartFrame,
207 Acc) ->
208 ok = mochiweb_socket:exit_if_closed(mochiweb_socket:setopts(Socket, [{packet, 0}, {active, once}])),
209 receive
210 {tcp_closed, _} ->
211 mochiweb_socket:close(Socket),
212 exit(normal);
213 {ssl_closed, _} ->
214 mochiweb_socket:close(Socket),
215 exit(normal);
216 {tcp_error, _, _} ->
217 mochiweb_socket:close(Socket),
218 exit(normal);
219 {Proto, _, Continuation} when Proto =:= tcp orelse Proto =:= ssl ->
220 parse_hybi_frames(Socket, <<PartFrame/binary, Continuation/binary>>,
221 Acc);
222 _ ->
223 mochiweb_socket:close(Socket),
224 exit(normal)
225 after
226 5000 ->
227 mochiweb_socket:close(Socket),
228 exit(normal)
229 end;
230 parse_hybi_frames(S, <<_Fin:1,
231 _Rsv:3,
232 Opcode:4,
233 _Mask:1,
234 127:7,
235 0:1,
236 PayloadLen:63,
237 MaskKey:4/binary,
238 Payload:PayloadLen/binary-unit:8,
239 Rest/binary>>,
240 Acc) ->
241 Payload2 = hybi_unmask(Payload, MaskKey, <<>>),
242 parse_hybi_frames(S, Rest, [{Opcode, Payload2} | Acc]).
243
244 %% Unmasks RFC 6455 message
245 hybi_unmask(<<O:32, Rest/bits>>, MaskKey, Acc) ->
246 <<MaskKey2:32>> = MaskKey,
247 hybi_unmask(Rest, MaskKey, <<Acc/binary, (O bxor MaskKey2):32>>);
248 hybi_unmask(<<O:24>>, MaskKey, Acc) ->
249 <<MaskKey2:24, _:8>> = MaskKey,
250 <<Acc/binary, (O bxor MaskKey2):24>>;
251 hybi_unmask(<<O:16>>, MaskKey, Acc) ->
252 <<MaskKey2:16, _:16>> = MaskKey,
253 <<Acc/binary, (O bxor MaskKey2):16>>;
254 hybi_unmask(<<O:8>>, MaskKey, Acc) ->
255 <<MaskKey2:8, _:24>> = MaskKey,
256 <<Acc/binary, (O bxor MaskKey2):8>>;
257 hybi_unmask(<<>>, _MaskKey, Acc) ->
258 Acc.
259
260 payload_length(N) ->
261 case N of
262 N when N =< 125 -> << N >>;
263 N when N =< 16#ffff -> << 126, N:16 >>;
264 N when N =< 16#7fffffffffffffff -> << 127, N:64 >>
265 end.
266
267
268 %%
269 %% Websockets internal functions for hixie-76 websocket version
270 %%
271 parse_hixie_frames(<<>>, Frames) ->
272 lists:reverse(Frames);
273 parse_hixie_frames(<<0, T/binary>>, Frames) ->
274 {Frame, Rest} = parse_hixie(T, <<>>),
275 parse_hixie_frames(Rest, [Frame | Frames]).
276
277 parse_hixie(<<255, Rest/binary>>, Buffer) ->
278 {Buffer, Rest};
279 parse_hixie(<<H, T/binary>>, Buffer) ->
280 parse_hixie(T, <<Buffer/binary, H>>).
+0
-179
deps/mochiweb/src/reloader.erl less more
0 %% @author Matthew Dempsky <matthew@mochimedia.com>
1 %% @copyright 2007 Mochi Media, Inc.
2 %%
3 %% Permission is hereby granted, free of charge, to any person obtaining a
4 %% copy of this software and associated documentation files (the "Software"),
5 %% to deal in the Software without restriction, including without limitation
6 %% the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 %% and/or sell copies of the Software, and to permit persons to whom the
8 %% Software is furnished to do so, subject to the following conditions:
9 %%
10 %% The above copyright notice and this permission notice shall be included in
11 %% all copies or substantial portions of the Software.
12 %%
13 %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 %% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 %% DEALINGS IN THE SOFTWARE.
20
21 %% @doc Erlang module for automatically reloading modified modules
22 %% during development.
23
24 -module(reloader).
25 -author("Matthew Dempsky <matthew@mochimedia.com>").
26
27 -include_lib("kernel/include/file.hrl").
28
29 -behaviour(gen_server).
30 -export([start/0, start_link/0]).
31 -export([stop/0]).
32 -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
33 -export([all_changed/0]).
34 -export([is_changed/1]).
35 -export([reload_modules/1]).
36 -record(state, {last, tref}).
37
38 %% External API
39
40 %% @spec start() -> ServerRet
41 %% @doc Start the reloader.
42 start() ->
43 gen_server:start({local, ?MODULE}, ?MODULE, [], []).
44
45 %% @spec start_link() -> ServerRet
46 %% @doc Start the reloader.
47 start_link() ->
48 gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
49
50 %% @spec stop() -> ok
51 %% @doc Stop the reloader.
52 stop() ->
53 gen_server:call(?MODULE, stop).
54
55 %% gen_server callbacks
56
57 %% @spec init([]) -> {ok, State}
58 %% @doc gen_server init, opens the server in an initial state.
59 init([]) ->
60 {ok, TRef} = timer:send_interval(timer:seconds(1), doit),
61 {ok, #state{last = stamp(), tref = TRef}}.
62
63 %% @spec handle_call(Args, From, State) -> tuple()
64 %% @doc gen_server callback.
65 handle_call(stop, _From, State) ->
66 {stop, shutdown, stopped, State};
67 handle_call(_Req, _From, State) ->
68 {reply, {error, badrequest}, State}.
69
70 %% @spec handle_cast(Cast, State) -> tuple()
71 %% @doc gen_server callback.
72 handle_cast(_Req, State) ->
73 {noreply, State}.
74
75 %% @spec handle_info(Info, State) -> tuple()
76 %% @doc gen_server callback.
77 handle_info(doit, State) ->
78 Now = stamp(),
79 _ = doit(State#state.last, Now),
80 {noreply, State#state{last = Now}};
81 handle_info(_Info, State) ->
82 {noreply, State}.
83
84 %% @spec terminate(Reason, State) -> ok
85 %% @doc gen_server termination callback.
86 terminate(_Reason, State) ->
87 {ok, cancel} = timer:cancel(State#state.tref),
88 ok.
89
90
91 %% @spec code_change(_OldVsn, State, _Extra) -> State
92 %% @doc gen_server code_change callback (trivial).
93 code_change(_Vsn, State, _Extra) ->
94 {ok, State}.
95
96 %% @spec reload_modules([atom()]) -> [{module, atom()} | {error, term()}]
97 %% @doc code:purge/1 and code:load_file/1 the given list of modules in order,
98 %% return the results of code:load_file/1.
99 reload_modules(Modules) ->
100 [begin code:purge(M), code:load_file(M) end || M <- Modules].
101
102 %% @spec all_changed() -> [atom()]
103 %% @doc Return a list of beam modules that have changed.
104 all_changed() ->
105 [M || {M, Fn} <- code:all_loaded(), is_list(Fn), is_changed(M)].
106
107 %% @spec is_changed(atom()) -> boolean()
108 %% @doc true if the loaded module is a beam with a vsn attribute
109 %% and does not match the on-disk beam file, returns false otherwise.
110 is_changed(M) ->
111 try
112 module_vsn(M:module_info()) =/= module_vsn(code:get_object_code(M))
113 catch _:_ ->
114 false
115 end.
116
117 %% Internal API
118
119 module_vsn({M, Beam, _Fn}) ->
120 {ok, {M, Vsn}} = beam_lib:version(Beam),
121 Vsn;
122 module_vsn(L) when is_list(L) ->
123 {_, Attrs} = lists:keyfind(attributes, 1, L),
124 {_, Vsn} = lists:keyfind(vsn, 1, Attrs),
125 Vsn.
126
127 doit(From, To) ->
128 [case file:read_file_info(Filename) of
129 {ok, #file_info{mtime = Mtime}} when Mtime >= From, Mtime < To ->
130 reload(Module);
131 {ok, _} ->
132 unmodified;
133 {error, enoent} ->
134 %% The Erlang compiler deletes existing .beam files if
135 %% recompiling fails. Maybe it's worth spitting out a
136 %% warning here, but I'd want to limit it to just once.
137 gone;
138 {error, Reason} ->
139 io:format("Error reading ~s's file info: ~p~n",
140 [Filename, Reason]),
141 error
142 end || {Module, Filename} <- code:all_loaded(), is_list(Filename)].
143
144 reload(Module) ->
145 io:format("Reloading ~p ...", [Module]),
146 code:purge(Module),
147 case code:load_file(Module) of
148 {module, Module} ->
149 io:format(" ok.~n"),
150 case erlang:function_exported(Module, test, 0) of
151 true ->
152 io:format(" - Calling ~p:test() ...", [Module]),
153 case catch Module:test() of
154 ok ->
155 io:format(" ok.~n"),
156 reload;
157 Reason ->
158 io:format(" fail: ~p.~n", [Reason]),
159 reload_but_test_failed
160 end;
161 false ->
162 reload
163 end;
164 {error, Reason} ->
165 io:format(" fail: ~p.~n", [Reason]),
166 error
167 end.
168
169
170 stamp() ->
171 erlang:localtime().
172
173 %%
174 %% Tests
175 %%
176 -ifdef(TEST).
177 -include_lib("eunit/include/eunit.hrl").
178 -endif.
+0
-24
deps/mochiweb/support/templates/mochiwebapp.template less more
0 %% -*- erlang -*-
1 {variables, [{appid, "mochiwebapp"},
2 {author, "Mochi Media <dev@mochimedia.com>"},
3 {year, "2010"},
4 {version, "0.1"},
5 {port, 8080},
6 {dest, "{{appid}}"}]}.
7 {dir, "{{dest}}"}.
8 {template, "mochiwebapp_skel/src/mochiapp.app.src", "{{dest}}/src/{{appid}}.app.src"}.
9 {template, "mochiwebapp_skel/src/mochiapp.erl", "{{dest}}/src/{{appid}}.erl"}.
10 {template, "mochiwebapp_skel/src/mochiapp_app.erl", "{{dest}}/src/{{appid}}_app.erl"}.
11 {template, "mochiwebapp_skel/src/mochiapp_deps.erl", "{{dest}}/src/{{appid}}_deps.erl"}.
12 {template, "mochiwebapp_skel/src/mochiapp_sup.erl", "{{dest}}/src/{{appid}}_sup.erl"}.
13 {template, "mochiwebapp_skel/src/mochiapp_web.erl", "{{dest}}/src/{{appid}}_web.erl"}.
14 {template, "mochiwebapp_skel/start-dev.sh", "{{dest}}/start-dev.sh"}.
15 {template, "mochiwebapp_skel/bench.sh", "{{dest}}/bench.sh"}.
16 {template, "mochiwebapp_skel/priv/www/index.html", "{{dest}}/priv/www/index.html"}.
17 {file, "../../.gitignore", "{{dest}}/.gitignore"}.
18 {file, "../../Makefile", "{{dest}}/Makefile"}.
19 {file, "mochiwebapp_skel/rebar.config", "{{dest}}/rebar.config"}.
20 {file, "../../rebar", "{{dest}}/rebar"}.
21 {chmod, 8#755, "{{dest}}/rebar"}.
22 {chmod, 8#755, "{{dest}}/start-dev.sh"}.
23 {chmod, 8#755, "{{dest}}/bench.sh"}.
+0
-19
deps/mochiweb/support/templates/mochiwebapp_skel/bench.sh less more
0 #!/bin/sh
1
2 # workaround for rebar mustache template bug
3 DEFAULT_PORT={{port}}
4 HOST=${HOST:-127.0.0.1}
5 PORT=${PORT:-${DEFAULT_PORT}}
6
7 BENCH_RUN="siege -q -c400 -r100 -b http://$HOST:$PORT/hello_world"
8
9 sleep 120
10
11 echo ""
12 echo ""
13 for i in `seq 1 10`;
14 do
15 echo "Running test #$i:"
16 $BENCH_RUN
17 sleep 90
18 done
+0
-8
deps/mochiweb/support/templates/mochiwebapp_skel/priv/www/index.html less more
0 <html>
1 <head>
2 <title>It Worked</title>
3 </head>
4 <body>
5 {{appid}} running.
6 </body>
7 </html>
+0
-7
deps/mochiweb/support/templates/mochiwebapp_skel/rebar.config less more
0 %% -*- erlang -*-
1 {erl_opts, [debug_info]}.
2 {deps, [
3 {mochiweb, ".*",
4 {git, "git://github.com/mochi/mochiweb.git", {branch, "master"}}}]}.
5 {cover_enabled, true}.
6 {eunit_opts, [verbose, {report,{eunit_surefire,[{dir,"."}]}}]}.
+0
-9
deps/mochiweb/support/templates/mochiwebapp_skel/src/mochiapp.app.src less more
0 %% -*- erlang -*-
1 {application, {{appid}},
2 [{description, "{{appid}}"},
3 {vsn, "{{version}}"},
4 {modules, []},
5 {registered, []},
6 {mod, {'{{appid}}_app', []}},
7 {env, []},
8 {applications, [kernel, stdlib, crypto]}]}.
+0
-30
deps/mochiweb/support/templates/mochiwebapp_skel/src/mochiapp.erl less more
0 %% @author {{author}}
1 %% @copyright {{year}} {{author}}
2
3 %% @doc {{appid}}.
4
5 -module({{appid}}).
6 -author("{{author}}").
7 -export([start/0, stop/0]).
8
9 ensure_started(App) ->
10 case application:start(App) of
11 ok ->
12 ok;
13 {error, {already_started, App}} ->
14 ok
15 end.
16
17
18 %% @spec start() -> ok
19 %% @doc Start the {{appid}} server.
20 start() ->
21 {{appid}}_deps:ensure(),
22 ensure_started(crypto),
23 application:start({{appid}}).
24
25
26 %% @spec stop() -> ok
27 %% @doc Stop the {{appid}} server.
28 stop() ->
29 application:stop({{appid}}).
+0
-22
deps/mochiweb/support/templates/mochiwebapp_skel/src/mochiapp_app.erl less more
0 %% @author {{author}}
1 %% @copyright {{appid}} {{author}}
2
3 %% @doc Callbacks for the {{appid}} application.
4
5 -module({{appid}}_app).
6 -author("{{author}}").
7
8 -behaviour(application).
9 -export([start/2,stop/1]).
10
11
12 %% @spec start(_Type, _StartArgs) -> ServerRet
13 %% @doc application start callback for {{appid}}.
14 start(_Type, _StartArgs) ->
15 {{appid}}_deps:ensure(),
16 {{appid}}_sup:start_link().
17
18 %% @spec stop(_State) -> ServerRet
19 %% @doc application stop callback for {{appid}}.
20 stop(_State) ->
21 ok.
+0
-84
deps/mochiweb/support/templates/mochiwebapp_skel/src/mochiapp_deps.erl less more
0 %% @author {{author}}
1 %% @copyright {{year}} {{author}}
2
3 %% @doc Ensure that the relatively-installed dependencies are on the code
4 %% loading path, and locate resources relative
5 %% to this application's path.
6
7 -module({{appid}}_deps).
8 -author("{{author}}").
9
10 -export([ensure/0, ensure/1]).
11 -export([get_base_dir/0, get_base_dir/1]).
12 -export([local_path/1, local_path/2]).
13 -export([deps_on_path/0, new_siblings/1]).
14
15 %% @spec deps_on_path() -> [ProjNameAndVers]
16 %% @doc List of project dependencies on the path.
17 deps_on_path() ->
18 F = fun (X, Acc) ->
19 ProjDir = filename:dirname(X),
20 case {filename:basename(X),
21 filename:basename(filename:dirname(ProjDir))} of
22 {"ebin", "deps"} ->
23 [filename:basename(ProjDir) | Acc];
24 _ ->
25 Acc
26 end
27 end,
28 ordsets:from_list(lists:foldl(F, [], code:get_path())).
29
30 %% @spec new_siblings(Module) -> [Dir]
31 %% @doc Find new siblings paths relative to Module that aren't already on the
32 %% code path.
33 new_siblings(Module) ->
34 Existing = deps_on_path(),
35 SiblingEbin = filelib:wildcard(local_path(["deps", "*", "ebin"], Module)),
36 Siblings = [filename:dirname(X) || X <- SiblingEbin,
37 ordsets:is_element(
38 filename:basename(filename:dirname(X)),
39 Existing) =:= false],
40 lists:filter(fun filelib:is_dir/1,
41 lists:append([[filename:join([X, "ebin"]),
42 filename:join([X, "include"])] ||
43 X <- Siblings])).
44
45
46 %% @spec ensure(Module) -> ok
47 %% @doc Ensure that all ebin and include paths for dependencies
48 %% of the application for Module are on the code path.
49 ensure(Module) ->
50 code:add_paths(new_siblings(Module)),
51 code:clash(),
52 ok.
53
54 %% @spec ensure() -> ok
55 %% @doc Ensure that the ebin and include paths for dependencies of
56 %% this application are on the code path. Equivalent to
57 %% ensure(?Module).
58 ensure() ->
59 ensure(?MODULE).
60
61 %% @spec get_base_dir(Module) -> string()
62 %% @doc Return the application directory for Module. It assumes Module is in
63 %% a standard OTP layout application in the ebin or src directory.
64 get_base_dir(Module) ->
65 {file, Here} = code:is_loaded(Module),
66 filename:dirname(filename:dirname(Here)).
67
68 %% @spec get_base_dir() -> string()
69 %% @doc Return the application directory for this application. Equivalent to
70 %% get_base_dir(?MODULE).
71 get_base_dir() ->
72 get_base_dir(?MODULE).
73
74 %% @spec local_path([string()], Module) -> string()
75 %% @doc Return an application-relative directory from Module's application.
76 local_path(Components, Module) ->
77 filename:join([get_base_dir(Module) | Components]).
78
79 %% @spec local_path(Components) -> string()
80 %% @doc Return an application-relative directory for this application.
81 %% Equivalent to local_path(Components, ?MODULE).
82 local_path(Components) ->
83 local_path(Components, ?MODULE).
+0
-56
deps/mochiweb/support/templates/mochiwebapp_skel/src/mochiapp_sup.erl less more
0 %% @author {{author}}
1 %% @copyright {{year}} {{author}}
2
3 %% @doc Supervisor for the {{appid}} application.
4
5 -module({{appid}}_sup).
6 -author("{{author}}").
7
8 -behaviour(supervisor).
9
10 %% External exports
11 -export([start_link/0, upgrade/0]).
12
13 %% supervisor callbacks
14 -export([init/1]).
15
16 %% @spec start_link() -> ServerRet
17 %% @doc API for starting the supervisor.
18 start_link() ->
19 supervisor:start_link({local, ?MODULE}, ?MODULE, []).
20
21 %% @spec upgrade() -> ok
22 %% @doc Add processes if necessary.
23 upgrade() ->
24 {ok, {_, Specs}} = init([]),
25
26 Old = sets:from_list(
27 [Name || {Name, _, _, _} <- supervisor:which_children(?MODULE)]),
28 New = sets:from_list([Name || {Name, _, _, _, _, _} <- Specs]),
29 Kill = sets:subtract(Old, New),
30
31 sets:fold(fun (Id, ok) ->
32 supervisor:terminate_child(?MODULE, Id),
33 supervisor:delete_child(?MODULE, Id),
34 ok
35 end, ok, Kill),
36
37 [supervisor:start_child(?MODULE, Spec) || Spec <- Specs],
38 ok.
39
40 %% @spec init([]) -> SupervisorTree
41 %% @doc supervisor callback.
42 init([]) ->
43 Web = web_specs({{appid}}_web, {{port}}),
44 Processes = [Web],
45 Strategy = {one_for_one, 10, 10},
46 {ok,
47 {Strategy, lists:flatten(Processes)}}.
48
49 web_specs(Mod, Port) ->
50 WebConfig = [{ip, {0,0,0,0}},
51 {port, Port},
52 {docroot, {{appid}}_deps:local_path(["priv", "www"])}],
53 {Mod,
54 {Mod, start, [WebConfig]},
55 permanent, 5000, worker, dynamic}.
+0
-71
deps/mochiweb/support/templates/mochiwebapp_skel/src/mochiapp_web.erl less more
0 %% @author {{author}}
1 %% @copyright {{year}} {{author}}
2
3 %% @doc Web server for {{appid}}.
4
5 -module({{appid}}_web).
6 -author("{{author}}").
7
8 -export([start/1, stop/0, loop/2]).
9
10 %% External API
11
12 start(Options) ->
13 {DocRoot, Options1} = get_option(docroot, Options),
14 Loop = fun (Req) ->
15 ?MODULE:loop(Req, DocRoot)
16 end,
17 mochiweb_http:start([{name, ?MODULE}, {loop, Loop} | Options1]).
18
19 stop() ->
20 mochiweb_http:stop(?MODULE).
21
22 loop(Req, DocRoot) ->
23 "/" ++ Path = Req:get(path),
24 try
25 case Req:get(method) of
26 Method when Method =:= 'GET'; Method =:= 'HEAD' ->
27 case Path of
28 "hello_world" ->
29 Req:respond({200, [{"Content-Type", "text/plain"}],
30 "Hello world!\n"});
31 _ ->
32 Req:serve_file(Path, DocRoot)
33 end;
34 'POST' ->
35 case Path of
36 _ ->
37 Req:not_found()
38 end;
39 _ ->
40 Req:respond({501, [], []})
41 end
42 catch
43 Type:What ->
44 Report = ["web request failed",
45 {path, Path},
46 {type, Type}, {what, What},
47 {trace, erlang:get_stacktrace()}],
48 error_logger:error_report(Report),
49 Req:respond({500, [{"Content-Type", "text/plain"}],
50 "request failed, sorry\n"})
51 end.
52
53 %% Internal API
54
55 get_option(Option, Options) ->
56 {proplists:get_value(Option, Options), proplists:delete(Option, Options)}.
57
58 %%
59 %% Tests
60 %%
61 -ifdef(TEST).
62 -include_lib("eunit/include/eunit.hrl").
63
64 you_should_write_a_test() ->
65 ?assertEqual(
66 "No, but I will!",
67 "Have you written any tests?"),
68 ok.
69
70 -endif.
+0
-7
deps/mochiweb/support/templates/mochiwebapp_skel/start-dev.sh less more
0 #!/bin/sh
1 exec erl \
2 -pa ebin deps/*/ebin \
3 -boot start_sasl \
4 -sname {{appid}}_dev \
5 -s {{appid}} \
6 -s reloader
+0
-19
deps/mochiweb/support/test-materials/test_ssl_cert.pem less more
0 -----BEGIN CERTIFICATE-----
1 MIIDIDCCAgigAwIBAgIJAJLkNZzERPIUMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV
2 BAMTCWxvY2FsaG9zdDAeFw0xMDAzMTgxOTM5MThaFw0yMDAzMTUxOTM5MThaMBQx
3 EjAQBgNVBAMTCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
4 ggEBAJeUCOZxbmtngF4S5lXckjSDLc+8C+XjMBYBPyy5eKdJY20AQ1s9/hhp3ulI
5 8pAvl+xVo4wQ+iBSvOzcy248Q+Xi6+zjceF7UNRgoYPgtJjKhdwcHV3mvFFrS/fp
6 9ggoAChaJQWDO1OCfUgTWXImhkw+vcDR11OVMAJ/h73dqzJPI9mfq44PTTHfYtgr
7 v4LAQAOlhXIAa2B+a6PlF6sqDqJaW5jLTcERjsBwnRhUGi7JevQzkejujX/vdA+N
8 jRBjKH/KLU5h3Q7wUchvIez0PXWVTCnZjpA9aR4m7YV05nKQfxtGd71czYDYk+j8
9 hd005jetT4ir7JkAWValBybJVksCAwEAAaN1MHMwHQYDVR0OBBYEFJl9s51SnjJt
10 V/wgKWqV5Q6jnv1ZMEQGA1UdIwQ9MDuAFJl9s51SnjJtV/wgKWqV5Q6jnv1ZoRik
11 FjAUMRIwEAYDVQQDEwlsb2NhbGhvc3SCCQCS5DWcxETyFDAMBgNVHRMEBTADAQH/
12 MA0GCSqGSIb3DQEBBQUAA4IBAQB2ldLeLCc+lxK5i0EZquLamMBJwDIjGpT0JMP9
13 b4XQOK2JABIu54BQIZhwcjk3FDJz/uOW5vm8k1kYni8FCjNZAaRZzCUfiUYTbTKL
14 Rq9LuIAODyP2dnTqyKaQOOJHvrx9MRZ3XVecXPS0Tib4aO57vCaAbIkmhtYpTWmw
15 e3t8CAIDVtgvjR6Se0a1JA4LktR7hBu22tDImvCSJn1nVAaHpani6iPBPPdMuMsP
16 TBoeQfj8VpqBUjCStqJGa8ytjDFX73YaxV2mgrtGwPNme1x3YNRR11yTu7tksyMO
17 GrmgxNriqYRchBhNEf72AKF0LR1ByKwfbDB9rIsV00HtCgOp
18 -----END CERTIFICATE-----
+0
-27
deps/mochiweb/support/test-materials/test_ssl_key.pem less more
0 -----BEGIN RSA PRIVATE KEY-----
1 MIIEpAIBAAKCAQEAl5QI5nFua2eAXhLmVdySNIMtz7wL5eMwFgE/LLl4p0ljbQBD
2 Wz3+GGne6UjykC+X7FWjjBD6IFK87NzLbjxD5eLr7ONx4XtQ1GChg+C0mMqF3Bwd
3 Xea8UWtL9+n2CCgAKFolBYM7U4J9SBNZciaGTD69wNHXU5UwAn+Hvd2rMk8j2Z+r
4 jg9NMd9i2Cu/gsBAA6WFcgBrYH5ro+UXqyoOolpbmMtNwRGOwHCdGFQaLsl69DOR
5 6O6Nf+90D42NEGMof8otTmHdDvBRyG8h7PQ9dZVMKdmOkD1pHibthXTmcpB/G0Z3
6 vVzNgNiT6PyF3TTmN61PiKvsmQBZVqUHJslWSwIDAQABAoIBACI8Ky5xHDFh9RpK
7 Rn/KC7OUlTpADKflgizWJ0Cgu2F9L9mkn5HyFHvLHa+u7CootbWJOiEejH/UcBtH
8 WyMQtX0snYCpdkUpJv5wvMoebGu+AjHOn8tfm9T/2O6rhwgckLyMb6QpGbMo28b1
9 p9QiY17BJPZx7qJQJcHKsAvwDwSThlb7MFmWf42LYWlzybpeYQvwpd+UY4I0WXLu
10 /dqJIS9Npq+5Y5vbo2kAEAssb2hSCvhCfHmwFdKmBzlvgOn4qxgZ1iHQgfKI6Z3Y
11 J0573ZgOVTuacn+lewtdg5AaHFcl/zIYEr9SNqRoPNGbPliuv6k6N2EYcufWL5lR
12 sCmmmHECgYEAxm+7OpepGr++K3+O1e1MUhD7vSPkKJrCzNtUxbOi2NWj3FFUSPRU
13 adWhuxvUnZgTcgM1+KuQ0fB2VmxXe9IDcrSFS7PKFGtd2kMs/5mBw4UgDZkOQh+q
14 kDiBEV3HYYJWRq0w3NQ/9Iy1jxxdENHtGmG9aqamHxNtuO608wGW2S8CgYEAw4yG
15 ZyAic0Q/U9V2OHI0MLxLCzuQz17C2wRT1+hBywNZuil5YeTuIt2I46jro6mJmWI2
16 fH4S/geSZzg2RNOIZ28+aK79ab2jWBmMnvFCvaru+odAuser4N9pfAlHZvY0pT+S
17 1zYX3f44ygiio+oosabLC5nWI0zB2gG8pwaJlaUCgYEAgr7poRB+ZlaCCY0RYtjo
18 mYYBKD02vp5BzdKSB3V1zeLuBWM84pjB6b3Nw0fyDig+X7fH3uHEGN+USRs3hSj6
19 BqD01s1OT6fyfbYXNw5A1r+nP+5h26Wbr0zblcKxdQj4qbbBZC8hOJNhqTqqA0Qe
20 MmzF7jiBaiZV/Cyj4x1f9BcCgYEAhjL6SeuTuOctTqs/5pz5lDikh6DpUGcH8qaV
21 o6aRAHHcMhYkZzpk8yh1uUdD7516APmVyvn6rrsjjhLVq4ZAJjwB6HWvE9JBN0TR
22 bILF+sREHUqU8Zn2Ku0nxyfXCKIOnxlx/J/y4TaGYqBqfXNFWiXNUrjQbIlQv/xR
23 K48g/MECgYBZdQlYbMSDmfPCC5cxkdjrkmAl0EgV051PWAi4wR+hLxIMRjHBvAk7
24 IweobkFvT4TICulgroLkYcSa5eOZGxB/DHqcQCbWj3reFV0VpzmTDoFKG54sqBRl
25 vVntGt0pfA40fF17VoS7riAdHF53ippTtsovHEsg5tq5NrBl5uKm2g==
26 -----END RSA PRIVATE KEY-----
2424 See [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md).
2525
2626
27 ## Running Tests
28
29 To run a "fast suite" (a subset of tests):
30
31 make ct-fast
32
33 To run a "slow suite" (a subset of tests that take much longer to run):
34
35 make ct-slow
36
37 To run a particular suite:
38
39 make ct-$suite_name
40
41 for example, to run the `backing_queue` suite:
42
43 make ct-backing_queue
44
45 Finally,
46
47 make tests
48
49 will run all suites.
50
51
2752 ## Contributor Agreement
2853
2954 If you want to contribute a non-trivial change, please submit a signed copy of our
00 PROJECT = rabbit
1 VERSION ?= $(call get_app_version,src/$(PROJECT).app.src)
2
1 PROJECT_DESCRIPTION = RabbitMQ
2 PROJECT_MOD = rabbit
3 PROJECT_REGISTERED = rabbit_amqqueue_sup \
4 rabbit_direct_client_sup \
5 rabbit_log \
6 rabbit_node_monitor \
7 rabbit_router
8
9 define PROJECT_ENV
10 [
11 {tcp_listeners, [5672]},
12 {num_tcp_acceptors, 10},
13 {ssl_listeners, []},
14 {num_ssl_acceptors, 1},
15 {ssl_options, []},
16 {vm_memory_high_watermark, 0.4},
17 {vm_memory_high_watermark_paging_ratio, 0.5},
18 {memory_monitor_interval, 2500},
19 {disk_free_limit, 50000000}, %% 50MB
20 {msg_store_index_module, rabbit_msg_store_ets_index},
21 {backing_queue_module, rabbit_variable_queue},
22 %% 0 ("no limit") would make a better default, but that
23 %% breaks the QPid Java client
24 {frame_max, 131072},
25 {channel_max, 0},
26 {heartbeat, 60},
27 {msg_store_file_size_limit, 16777216},
28 {fhc_write_buffering, true},
29 {fhc_read_buffering, false},
30 {queue_index_max_journal_entries, 32768},
31 {queue_index_embed_msgs_below, 4096},
32 {default_user, <<"guest">>},
33 {default_pass, <<"guest">>},
34 {default_user_tags, [administrator]},
35 {default_vhost, <<"/">>},
36 {default_permissions, [<<".*">>, <<".*">>, <<".*">>]},
37 {loopback_users, [<<"guest">>]},
38 {password_hashing_module, rabbit_password_hashing_sha256},
39 {cluster_nodes, {[], disc}},
40 {server_properties, []},
41 {collect_statistics, none},
42 {collect_statistics_interval, 5000},
43 {mnesia_table_loading_retry_timeout, 30000},
44 {mnesia_table_loading_retry_limit, 10},
45 {auth_mechanisms, ['PLAIN', 'AMQPLAIN']},
46 {auth_backends, [rabbit_auth_backend_internal]},
47 {delegate_count, 16},
48 {trace_vhosts, []},
49 {log_levels, [{connection, info}]},
50 {ssl_cert_login_from, distinguished_name},
51 {ssl_handshake_timeout, 5000},
52 {ssl_allow_poodle_attack, false},
53 {handshake_timeout, 10000},
54 {reverse_dns_lookups, false},
55 {cluster_partition_handling, ignore},
56 {cluster_keepalive_interval, 10000},
57 {tcp_listen_options, [{backlog, 128},
58 {nodelay, true},
59 {linger, {true, 0}},
60 {exit_on_close, false}
61 ]},
62 {halt_on_upgrade_failure, true},
63 {hipe_compile, false},
64 %% see bug 24513 for how this list was created
65 {hipe_modules,
66 [rabbit_reader, rabbit_channel, gen_server2, rabbit_exchange,
67 rabbit_command_assembler, rabbit_framing_amqp_0_9_1, rabbit_basic,
68 rabbit_event, lists, queue, priority_queue, rabbit_router,
69 rabbit_trace, rabbit_misc, rabbit_binary_parser,
70 rabbit_exchange_type_direct, rabbit_guid, rabbit_net,
71 rabbit_amqqueue_process, rabbit_variable_queue,
72 rabbit_binary_generator, rabbit_writer, delegate, gb_sets, lqueue,
73 sets, orddict, rabbit_amqqueue, rabbit_limiter, gb_trees,
74 rabbit_queue_index, rabbit_exchange_decorator, gen, dict, ordsets,
75 file_handle_cache, rabbit_msg_store, array,
76 rabbit_msg_store_ets_index, rabbit_msg_file,
77 rabbit_exchange_type_fanout, rabbit_exchange_type_topic, mnesia,
78 mnesia_lib, rpc, mnesia_tm, qlc, sofs, proplists, credit_flow,
79 pmon, ssl_connection, tls_connection, ssl_record, tls_record,
80 gen_fsm, ssl]},
81 {ssl_apps, [asn1, crypto, public_key, ssl]},
82 %% see rabbitmq-server#114
83 {mirroring_flow_control, true},
84 {mirroring_sync_batch_size, 4096},
85 %% see rabbitmq-server#227 and related tickets.
86 %% msg_store_credit_disc_bound only takes effect when
87 %% messages are persisted to the message store. If messages
88 %% are embedded on the queue index, then modifying this
89 %% setting has no effect because credit_flow is not used when
90 %% writing to the queue index. See the setting
91 %% queue_index_embed_msgs_below above.
92 {msg_store_credit_disc_bound, {4000, 800}},
93 {msg_store_io_batch_size, 4096},
94 %% see rabbitmq-server#143,
95 %% rabbitmq-server#949, rabbitmq-server#1098
96 {credit_flow_default_credit, {400, 200}},
97 %% see rabbitmq-server#248
98 %% and rabbitmq-server#667
99 {channel_operation_timeout, 15000},
100 {config_entry_decoder, [{cipher, aes_cbc256},
101 {hash, sha512},
102 {iterations, 1000},
103 {passphrase, undefined}
104 ]},
105 %% rabbitmq-server-973
106 {queue_explicit_gc_run_operation_threshold, 1000},
107 {lazy_queue_explicit_gc_run_operation_threshold, 1000},
108 {background_gc_enabled, false},
109 {background_gc_target_interval, 60000},
110 {disk_monitor_failure_retries, 10},
111 {disk_monitor_failure_retry_interval, 120000}
112 ]
113 endef
114
115 LOCAL_DEPS = sasl mnesia os_mon xmerl
3116 DEPS = ranch rabbit_common
4 TEST_DEPS = rabbitmq_ct_helpers amqp_client meck proper
117 TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers amqp_client meck proper
5118
6119 define usage_xml_to_erl
7120 $(subst __,_,$(patsubst $(DOCS_DIR)/rabbitmq%.1.xml, src/rabbit_%_usage.erl, $(subst -,_,$(1))))
18131 .DEFAULT_GOAL = all
19132 $(PROJECT).d:: $(EXTRA_SOURCES)
20133
134 DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-test.mk
21135 DEP_PLUGINS = rabbit_common/mk/rabbitmq-build.mk \
136 rabbit_common/mk/rabbitmq-dist.mk \
22137 rabbit_common/mk/rabbitmq-run.mk \
23 rabbit_common/mk/rabbitmq-dist.mk \
138 rabbit_common/mk/rabbitmq-test.mk \
24139 rabbit_common/mk/rabbitmq-tools.mk
25140
26141 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
31146
32147 include rabbitmq-components.mk
33148 include erlang.mk
149
150 SLOW_CT_SUITES := backing_queue \
151 cluster_rename \
152 clustering_management \
153 dynamic_ha \
154 eager_sync \
155 health_check \
156 partitions \
157 priority_queue \
158 queue_master_location \
159 simple_ha
160 FAST_CT_SUITES := $(filter-out $(sort $(SLOW_CT_SUITES)),$(CT_SUITES))
161
162 ct-fast: CT_SUITES = $(FAST_CT_SUITES)
163 ct-slow: CT_SUITES = $(SLOW_CT_SUITES)
34164
35165 # --------------------------------------------------------------------
36166 # Compilation.
71201 opt='--stringparam man.indent.verbatims=0' ; \
72202 xsltproc --novalid $(DOCS_DIR)/examples-to-end.xsl $< > $<.tmp && \
73203 xmlto -vv -o $(DOCS_DIR) $$opt man $< 2>&1 | (grep -v '^Note: Writing' || :) && \
204 awk -F"'u " '/^\.HP / { print $$1; print $$2; next; } { print; }' "$@" > "$@.tmp" && \
205 mv "$@.tmp" "$@" && \
74206 test -f $@ && \
75207 rm $<.tmp
76208
4545
4646 ## Copyright
4747
48 (c) Pivotal Software Inc., 2007-2016.
48 (c) Pivotal Software Inc., 2007-2017.
303303 %%
304304 %% {hipe_compile, true},
305305
306 %% Timeout used when waiting for Mnesia tables in a cluster to
306 %% Number of times to retry while waiting for Mnesia tables in a cluster to
307307 %% become available.
308308 %%
309 %% {mnesia_table_loading_timeout, 30000},
309 %% {mnesia_table_loading_retry_limit, 10},
310
311 %% Time to wait per retry for Mnesia tables in a cluster to become
312 %% available.
313 %%
314 %% {mnesia_table_loading_retry_timeout, 30000},
310315
311316 %% Size in bytes below which to embed messages in the queue index. See
312317 %% http://www.rabbitmq.com/persistence-conf.html
313318 %%
314 %% {queue_index_embed_msgs_below, 4096}
319 %% {queue_index_embed_msgs_below, 4096},
320
321 %% Whether or not to enable background GC.
322 %%
323 %% {background_gc_enabled, true},
324 %%
325 %% Interval (in milliseconds) at which we run background GC.
326 %%
327 %% {background_gc_target_interval, 60000}
315328
316329 ]},
317330
134134 </listitem>
135135 </varlistentry>
136136
137 <varlistentry>
138 <term><cmdsynopsis><command>shutdown</command></cmdsynopsis></term>
139 <listitem>
140 <para>
141 Shuts down the Erlang process on which RabbitMQ is running. The
142 command is blocking and will return after the Erlang process
143 exits. If RabbitMQ fails to stop, it will return a non-zero exit
144 code.
145 </para>
146 <para>
147 Unlike the stop command, the shutdown command:
148 * does not require a pid_file to wait for the Erlang process to exit
149 * if RabbitMQ node is not running, it will return a non-zero exit code
150 </para>
151 <para role="example-prefix">For example:</para>
152 <screen role="example">rabbitmqctl shutdown</screen>
153 <para role="example">
154 This command shuts down the Erlang process on which RabbitMQ is running.
155 </para>
156 </listitem>
157 </varlistentry>
158
137159 <varlistentry id="stop_app">
138160 <term><cmdsynopsis><command>stop_app</command></cmdsynopsis></term>
139161 <listitem>
10341056 <para>
10351057 Certain features of RabbitMQ (such as the federation plugin)
10361058 are controlled by dynamic,
1037 cluster-wide <emphasis>parameters</emphasis>. Each parameter
1038 consists of a component name, a name and a value, and is
1039 associated with a virtual host. The component name and name are
1040 strings, and the value is an Erlang term. Parameters can be
1041 set, cleared and listed. In general you should refer to the
1059 cluster-wide <emphasis>parameters</emphasis>.
1060 There are 2 kinds of parameters: parameters scoped to
1061 a virtual host and global parameters.
1062 Each vhost-scoped parameter
1063 consists of a component name, a name and a value.
1064 The component name and name are
1065 strings, and the value is an Erlang term.
1066 A global parameter consists of a name and value. The name
1067 is a string and the value is an Erlang term.
1068 Parameters can be set, cleared and listed. In general you should refer to the
10421069 documentation for the feature in question to see how to set
10431070 parameters.
10441071 </para>
11171144 <screen role="example">rabbitmqctl list_parameters</screen>
11181145 <para role="example">
11191146 This command lists all parameters in the default virtual host.
1147 </para>
1148 </listitem>
1149 </varlistentry>
1150 <varlistentry>
1151 <term><cmdsynopsis><command>set_global_parameter</command> <arg choice="req"><replaceable>name</replaceable></arg> <arg choice="req"><replaceable>value</replaceable></arg></cmdsynopsis></term>
1152 <listitem>
1153 <para>
1154 Sets a global runtime parameter. This is similar to <command>set_parameter</command>
1155 but the key-value pair isn't tied to a virtual host.
1156 </para>
1157 <variablelist>
1158 <varlistentry>
1159 <term>name</term>
1160 <listitem><para>
1161 The name of the global runtime parameter being set.
1162 </para></listitem>
1163 </varlistentry>
1164 <varlistentry>
1165 <term>value</term>
1166 <listitem><para>
1167 The value for the global runtime parameter, as a
1168 JSON term. In most shells you are very likely to
1169 need to quote this.
1170 </para></listitem>
1171 </varlistentry>
1172 </variablelist>
1173 <para role="example-prefix">For example:</para>
1174 <screen role="example">rabbitmqctl set_global_parameter mqtt_default_vhosts '{"O=client,CN=guest":"/"}'</screen>
1175 <para role="example">
1176 This command sets the global runtime parameter <command>mqtt_default_vhosts</command> to the JSON term <command>{"O=client,CN=guest":"/"}</command>.
1177 </para>
1178 </listitem>
1179 </varlistentry>
1180 <varlistentry>
1181 <term><cmdsynopsis><command>clear_global_parameter</command> <arg choice="req"><replaceable>name</replaceable></arg></cmdsynopsis></term>
1182 <listitem>
1183 <para>
1184 Clears a global runtime parameter. This is similar to <command>clear_global_parameter</command>
1185 but the key-value pair isn't tied to a virtual host.
1186 </para>
1187 <variablelist>
1188 <varlistentry>
1189 <term>name</term>
1190 <listitem><para>
1191 The name of the global runtime parameter being cleared.
1192 </para></listitem>
1193 </varlistentry>
1194 </variablelist>
1195 <para role="example-prefix">For example:</para>
1196 <screen role="example">rabbitmqctl clear_global_parameter mqtt_default_vhosts</screen>
1197 <para role="example">
1198 This command clears the global runtime parameter <command>mqtt_default_vhosts</command>.
1199 </para>
1200 </listitem>
1201 </varlistentry>
1202 <varlistentry>
1203 <term><cmdsynopsis><command>list_global_parameters</command></cmdsynopsis></term>
1204 <listitem>
1205 <para>
1206 Lists all global runtime parameters. This is similar to <command>list_parameters</command>
1207 but the global runtime parameters are not tied to any virtual host.
1208 </para>
1209 <para role="example-prefix">For example:</para>
1210 <screen role="example">rabbitmqctl list_global_parameters</screen>
1211 <para role="example">
1212 This command lists all global parameters.
11201213 </para>
11211214 </listitem>
11221215 </varlistentry>
0 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
11 #
22 # Permission to use, copy, modify, and/or distribute this software for any
33 # purpose with or without fee is hereby granted, provided that the above
1111 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1212 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1313
14 .PHONY: all app apps deps search rel docs install-docs check tests clean distclean help erlang-mk
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
1515
1616 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17
18 ERLANG_MK_VERSION = 2.0.0-pre.2-144-g647ffd1
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
1931
2032 # Core configuration.
2133
2436
2537 PROJECT_VERSION ?= rolling
2638 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
2740
2841 # Verbosity.
2942
8497 rel::
8598 $(verbose) :
8699
100 relup:: deps app
101
87102 check:: tests
88103
89104 clean:: clean-crashdump
101116 help::
102117 $(verbose) printf "%s\n" \
103118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
104 "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
105120 "" \
106121 "Usage: [V=1] $(MAKE) [target]..." \
107122 "" \
149164 core_native_path = $1
150165 endif
151166
152 ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
153 define core_http_get
154 wget --no-check-certificate -O $(1) $(2)|| rm $(1)
155 endef
156 else
157 define core_http_get.erl
158 ssl:start(),
159 inets:start(),
160 case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
161 {ok, {{_, 200, _}, _, Body}} ->
162 case file:write_file("$(1)", Body) of
163 ok -> ok;
164 {error, R1} -> halt(R1)
165 end;
166 {error, R2} ->
167 halt(R2)
168 end,
169 halt(0).
170 endef
171
172 define core_http_get
173 $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
174 endef
175 endif
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
176168
177169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
178170
192184 ERLANG_MK_BUILD_CONFIG ?= build.config
193185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
194186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
195188 erlang-mk:
196189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
197190 ifdef ERLANG_MK_COMMIT
198191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
199192 endif
200193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
201 $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
202195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
203196 rm -rf $(ERLANG_MK_BUILD_DIR)
204197
285278 pkg_apns_repo = https://github.com/inaka/apns4erl
286279 pkg_apns_commit = master
287280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
288289 PACKAGES += azdht
289290 pkg_azdht_name = azdht
290291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
405406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
406407 pkg_bootstrap_commit = master
407408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
408417 PACKAGES += boss_db
409418 pkg_boss_db_name = boss_db
410419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
413422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
414423 pkg_boss_db_commit = master
415424
416 PACKAGES += boss
417 pkg_boss_name = boss
418 pkg_boss_description = Erlang web MVC, now featuring Comet
419 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
420 pkg_boss_fetch = git
421 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
422 pkg_boss_commit = master
423
424425 PACKAGES += brod
425426 pkg_brod_name = brod
426427 pkg_brod_description = Kafka client in Erlang
565566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
566567 pkg_cloudi_service_api_requests_commit = master
567568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
568585 PACKAGES += cloudi_service_db_cassandra_cql
569586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
570587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
573590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
574591 pkg_cloudi_service_db_cassandra_cql_commit = master
575592
576 PACKAGES += cloudi_service_db_cassandra
577 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
579 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
580 pkg_cloudi_service_db_cassandra_fetch = git
581 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
582 pkg_cloudi_service_db_cassandra_commit = master
583
584593 PACKAGES += cloudi_service_db_couchdb
585594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
586595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
637646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
638647 pkg_cloudi_service_db_tokyotyrant_commit = master
639648
640 PACKAGES += cloudi_service_db
641 pkg_cloudi_service_db_name = cloudi_service_db
642 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
643 pkg_cloudi_service_db_homepage = http://cloudi.org/
644 pkg_cloudi_service_db_fetch = git
645 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
646 pkg_cloudi_service_db_commit = master
647
648649 PACKAGES += cloudi_service_filesystem
649650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
650651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
10371038 pkg_edown_repo = https://github.com/uwiger/edown
10381039 pkg_edown_commit = master
10391040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
10401049 PACKAGES += eep_app
10411050 pkg_eep_app_name = eep_app
10421051 pkg_eep_app_description = Embedded Event Processing
10451054 pkg_eep_app_repo = https://github.com/darach/eep-erl
10461055 pkg_eep_app_commit = master
10471056
1048 PACKAGES += eep
1049 pkg_eep_name = eep
1050 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1051 pkg_eep_homepage = https://github.com/virtan/eep
1052 pkg_eep_fetch = git
1053 pkg_eep_repo = https://github.com/virtan/eep
1054 pkg_eep_commit = master
1055
10561057 PACKAGES += efene
10571058 pkg_efene_name = efene
10581059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
12371238 pkg_eqm_repo = https://github.com/loucash/eqm
12381239 pkg_eqm_commit = master
12391240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
12401249 PACKAGES += eredis_pool
12411250 pkg_eredis_pool_name = eredis_pool
12421251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
12451254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
12461255 pkg_eredis_pool_commit = master
12471256
1248 PACKAGES += eredis
1249 pkg_eredis_name = eredis
1250 pkg_eredis_description = Erlang Redis client
1251 pkg_eredis_homepage = https://github.com/wooga/eredis
1252 pkg_eredis_fetch = git
1253 pkg_eredis_repo = https://github.com/wooga/eredis
1254 pkg_eredis_commit = master
1255
12561257 PACKAGES += erl_streams
12571258 pkg_erl_streams_name = erl_streams
12581259 pkg_erl_streams_description = Streams in Erlang
15411542 pkg_etap_repo = https://github.com/ngerakines/etap
15421543 pkg_etap_commit = master
15431544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
15441553 PACKAGES += etest_http
15451554 pkg_etest_http_name = etest_http
15461555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
15491558 pkg_etest_http_repo = https://github.com/wooga/etest_http
15501559 pkg_etest_http_commit = master
15511560
1552 PACKAGES += etest
1553 pkg_etest_name = etest
1554 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1555 pkg_etest_homepage = https://github.com/wooga/etest
1556 pkg_etest_fetch = git
1557 pkg_etest_repo = https://github.com/wooga/etest
1558 pkg_etest_commit = master
1559
15601561 PACKAGES += etoml
15611562 pkg_etoml_name = etoml
15621563 pkg_etoml_description = TOML language erlang parser
15651566 pkg_etoml_repo = https://github.com/kalta/etoml
15661567 pkg_etoml_commit = master
15671568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
15681577 PACKAGES += eunit_formatters
15691578 pkg_eunit_formatters_name = eunit_formatters
15701579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
15731582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
15741583 pkg_eunit_formatters_commit = master
15751584
1576 PACKAGES += eunit
1577 pkg_eunit_name = eunit
1578 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1579 pkg_eunit_homepage = https://github.com/richcarl/eunit
1580 pkg_eunit_fetch = git
1581 pkg_eunit_repo = https://github.com/richcarl/eunit
1582 pkg_eunit_commit = master
1583
15841585 PACKAGES += euthanasia
15851586 pkg_euthanasia_name = euthanasia
15861587 pkg_euthanasia_description = Merciful killer for your Erlang processes
15981599 pkg_evum_commit = master
15991600
16001601 PACKAGES += exec
1601 pkg_exec_name = exec
1602 pkg_exec_name = erlexec
16021603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
16031604 pkg_exec_homepage = http://saleyn.github.com/erlexec
16041605 pkg_exec_fetch = git
17171718 pkg_fn_repo = https://github.com/reiddraper/fn
17181719 pkg_fn_commit = master
17191720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
17201729 PACKAGES += folsom_cowboy
17211730 pkg_folsom_cowboy_name = folsom_cowboy
17221731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
17251734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
17261735 pkg_folsom_cowboy_commit = master
17271736
1728 PACKAGES += folsom
1729 pkg_folsom_name = folsom
1730 pkg_folsom_description = Expose Erlang Events and Metrics
1731 pkg_folsom_homepage = https://github.com/boundary/folsom
1732 pkg_folsom_fetch = git
1733 pkg_folsom_repo = https://github.com/boundary/folsom
1734 pkg_folsom_commit = master
1735
17361737 PACKAGES += folsomite
17371738 pkg_folsomite_name = folsomite
17381739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
20932094 pkg_jesse_repo = https://github.com/for-GET/jesse
20942095 pkg_jesse_commit = master
20952096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
20962105 PACKAGES += jiffy_v
20972106 pkg_jiffy_v_name = jiffy_v
20982107 pkg_jiffy_v_description = JSON validation utility
21012110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
21022111 pkg_jiffy_v_commit = master
21032112
2104 PACKAGES += jiffy
2105 pkg_jiffy_name = jiffy
2106 pkg_jiffy_description = JSON NIFs for Erlang.
2107 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2108 pkg_jiffy_fetch = git
2109 pkg_jiffy_repo = https://github.com/davisp/jiffy
2110 pkg_jiffy_commit = master
2111
21122113 PACKAGES += jobs
21132114 pkg_jobs_name = jobs
21142115 pkg_jobs_description = a Job scheduler for load regulation
21252126 pkg_joxa_repo = https://github.com/joxa/joxa
21262127 pkg_joxa_commit = master
21272128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
21282137 PACKAGES += json_rec
21292138 pkg_json_rec_name = json_rec
21302139 pkg_json_rec_description = JSON to erlang record
21332142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
21342143 pkg_json_rec_commit = master
21352144
2136 PACKAGES += json
2137 pkg_json_name = json
2138 pkg_json_description = a high level json library for erlang (17.0+)
2139 pkg_json_homepage = https://github.com/talentdeficit/json
2140 pkg_json_fetch = git
2141 pkg_json_repo = https://github.com/talentdeficit/json
2142 pkg_json_commit = master
2143
21442145 PACKAGES += jsone
21452146 pkg_jsone_name = jsone
21462147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
21812182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
21822183 pkg_jsx_commit = master
21832184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
21842193 PACKAGES += kafka_protocol
21852194 pkg_kafka_protocol_name = kafka_protocol
21862195 pkg_kafka_protocol_description = Kafka protocol Erlang library
21892198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
21902199 pkg_kafka_protocol_commit = master
21912200
2192 PACKAGES += kafka
2193 pkg_kafka_name = kafka
2194 pkg_kafka_description = Kafka consumer and producer in Erlang
2195 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2196 pkg_kafka_fetch = git
2197 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2198 pkg_kafka_commit = master
2199
22002201 PACKAGES += kai
22012202 pkg_kai_name = kai
22022203 pkg_kai_description = DHT storage by Takeshi Inoue
22932294 pkg_kvs_repo = https://github.com/synrc/kvs
22942295 pkg_kvs_commit = master
22952296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
22962305 PACKAGES += lager_amqp_backend
22972306 pkg_lager_amqp_backend_name = lager_amqp_backend
22982307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
23092318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
23102319 pkg_lager_syslog_commit = master
23112320
2312 PACKAGES += lager
2313 pkg_lager_name = lager
2314 pkg_lager_description = A logging framework for Erlang/OTP.
2315 pkg_lager_homepage = https://github.com/basho/lager
2316 pkg_lager_fetch = git
2317 pkg_lager_repo = https://github.com/basho/lager
2318 pkg_lager_commit = master
2319
23202321 PACKAGES += lambdapad
23212322 pkg_lambdapad_name = lambdapad
23222323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
25732574 pkg_mixer_repo = https://github.com/chef/mixer
25742575 pkg_mixer_commit = master
25752576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
25762585 PACKAGES += mochiweb_xpath
25772586 pkg_mochiweb_xpath_name = mochiweb_xpath
25782587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
25812590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
25822591 pkg_mochiweb_xpath_commit = master
25832592
2584 PACKAGES += mochiweb
2585 pkg_mochiweb_name = mochiweb
2586 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2587 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2588 pkg_mochiweb_fetch = git
2589 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2590 pkg_mochiweb_commit = master
2591
25922593 PACKAGES += mockgyver
25932594 pkg_mockgyver_name = mockgyver
25942595 pkg_mockgyver_description = A mocking library for Erlang
30613062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
30623063 pkg_quickrand_commit = master
30633064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
30643073 PACKAGES += rabbit_exchange_type_riak
30653074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
30663075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
30693078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
30703079 pkg_rabbit_exchange_type_riak_commit = master
30713080
3072 PACKAGES += rabbit
3073 pkg_rabbit_name = rabbit
3074 pkg_rabbit_description = RabbitMQ Server
3075 pkg_rabbit_homepage = https://www.rabbitmq.com/
3076 pkg_rabbit_fetch = git
3077 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3078 pkg_rabbit_commit = master
3079
30803081 PACKAGES += rack
30813082 pkg_rack_name = rack
30823083 pkg_rack_description = Rack handler for erlang
34933494 pkg_smother_repo = https://github.com/ramsay-t/Smother
34943495 pkg_smother_commit = master
34953496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
34963505 PACKAGES += social
34973506 pkg_social_name = social
34983507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
35413550 pkg_stable_repo = https://github.com/dvv/stable
35423551 pkg_stable_commit = master
35433552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
35443561 PACKAGES += statebox_riak
35453562 pkg_statebox_riak_name = statebox_riak
35463563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
35493566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
35503567 pkg_statebox_riak_commit = master
35513568
3552 PACKAGES += statebox
3553 pkg_statebox_name = statebox
3554 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3555 pkg_statebox_homepage = https://github.com/mochi/statebox
3556 pkg_statebox_fetch = git
3557 pkg_statebox_repo = https://github.com/mochi/statebox
3558 pkg_statebox_commit = master
3559
35603569 PACKAGES += statman
35613570 pkg_statman_name = statman
35623571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
37253734 pkg_tirerl_repo = https://github.com/inaka/tirerl
37263735 pkg_tirerl_commit = master
37273736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
37283745 PACKAGES += traffic_tools
37293746 pkg_traffic_tools_name = traffic_tools
37303747 pkg_traffic_tools_description = Simple traffic limiting library
40614078 pkg_zucchini_repo = https://github.com/devinus/zucchini
40624079 pkg_zucchini_commit = master
40634080
4064 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
40654082 # This file is part of erlang.mk and subject to the terms of the ISC License.
40664083
40674084 .PHONY: search
40884105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
40894106 endif
40904107
4091 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
40924109 # This file is part of erlang.mk and subject to the terms of the ISC License.
40934110
4094 .PHONY: distclean-deps
4111 .PHONY: distclean-deps clean-tmp-deps.log
40954112
40964113 # Configuration.
40974114
41104127
41114128 REBAR_DEPS_DIR = $(DEPS_DIR)
41124129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
41134150
41144151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
41154152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
41164153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
41174154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
41184155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
41194157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
41204158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
41214159
41384176
41394177 # Core targets.
41404178
4141 ifdef IS_APP
4142 apps::
4143 else
4144 apps:: $(ALL_APPS_DIRS)
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
41454180 ifeq ($(IS_APP)$(IS_DEP),)
41464181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
41474182 endif
41494184 # Create ebin directory for all apps to make sure Erlang recognizes them
41504185 # as proper OTP applications when using -include_lib. This is a temporary
41514186 # fix, a proper fix would be to compile apps/* in the right order.
4152 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4153 mkdir -p $$dep/ebin || exit $$?; \
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
41544190 done
4155 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
41564196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
41574197 :; \
41584198 else \
41594199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4160 $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
4200 $(MAKE) -C $$dep IS_APP=1; \
41614201 fi \
41624202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
41634207 endif
41644208
41654209 ifneq ($(SKIP_DEPS),)
41664210 deps::
41674211 else
4168 deps:: $(ALL_DEPS_DIRS) apps
4169 ifeq ($(IS_APP)$(IS_DEP),)
4170 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4171 endif
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
41724213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4173 $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
41744215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
41754216 :; \
41764217 else \
41774218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
41784219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4179 $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
4220 $(MAKE) -C $$dep IS_DEP=1; \
41804221 else \
4181 echo "Error: No Makefile to build dependency $$dep."; \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
41824223 exit 2; \
41834224 fi \
41844225 fi \
41924233 # in practice only Makefile is needed so far.
41934234 define dep_autopatch
41944235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
41954237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
41964238 $(call dep_autopatch_erlang_mk,$(1)); \
41974239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4198 if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
41994243 $(call dep_autopatch2,$(1)); \
4200 elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
42014245 $(call dep_autopatch2,$(1)); \
4202 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
42034247 $(call dep_autopatch2,$(1)); \
4204 else \
4205 $(call erlang,$(call dep_autopatch_app.erl,$(1))); \
42064248 fi \
42074249 else \
42084250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
42144256 endef
42154257
42164258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
42174262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
42184263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
42194264 fi; \
42204265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4221 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
42224267 $(call dep_autopatch_fetch_rebar); \
42234268 $(call dep_autopatch_rebar,$(1)); \
42244269 else \
42304275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
42314276 endef
42324277
4233 # Overwrite erlang.mk with the current file by default.
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
42344280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
42354281 define dep_autopatch_erlang_mk
4236 echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \
4237 > $(DEPS_DIR)/$1/erlang.mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
42384287 endef
42394288 else
42404289 define dep_autopatch_erlang_mk
42534302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
42544303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
42554304 cd $(ERLANG_MK_TMP)/rebar; \
4256 git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
42574306 $(MAKE); \
42584307 cd -; \
42594308 fi
42704319 define dep_autopatch_rebar.erl
42714320 application:load(rebar),
42724321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
42734323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
42744324 {ok, Conf0} -> Conf0;
42754325 _ -> []
44234473 [] -> ok;
44244474 _ ->
44254475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4426 PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
44274477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4428 PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L \\"~s\\" -lerl_interface -lei\n",
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
44294479 [code:lib_dir(erl_interface, lib)])),
44304480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
44314481 FilterEnv = fun(Env) ->
44644514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44654515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44664516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4467 [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
44684518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
44694519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
44704520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
44764526 end,
44774527 [PortSpec(S) || S <- PortSpecs]
44784528 end,
4479 Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"),
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
44804530 RunPlugin = fun(Plugin, Step) ->
44814531 case erlang:function_exported(Plugin, Step, 2) of
44824532 false -> ok;
45244574 halt()
45254575 endef
45264576
4527 define dep_autopatch_app.erl
4528 UpdateModules = fun(App) ->
4529 case filelib:is_regular(App) of
4530 false -> ok;
4531 true ->
4532 {ok, [{application, '$(1)', L0}]} = file:consult(App),
4533 Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true,
4534 fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
4535 L = lists:keystore(modules, 1, L0, {modules, Mods}),
4536 ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}]))
4537 end
4538 end,
4539 UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"),
4540 halt()
4541 endef
4542
45434577 define dep_autopatch_appsrc_script.erl
45444578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
45454579 AppSrcScript = AppSrc ++ ".script",
45874621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
45884622 endef
45894623
4590 define dep_fetch_hex.erl
4591 ssl:start(),
4592 inets:start(),
4593 {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
4594 {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
4595 [], [{body_format, binary}]),
4596 {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
4597 {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
4598 ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
4599 halt()
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
46004626 endef
46014627
46024628 # Hex only has a package version. No need to look in the Erlang.mk packages.
46034629 define dep_fetch_hex
4604 $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
46054634 endef
46064635
46074636 define dep_fetch_fail
46314660 $(eval DEP_NAME := $(call dep_name,$1))
46324661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
46334662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4634 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
46354664 exit 17; \
46364665 fi
46374666 $(verbose) mkdir -p $(DEPS_DIR)
46734702 clean:: clean-apps
46744703
46754704 clean-apps:
4676 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4677 $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
46784707 done
46794708
46804709 distclean:: distclean-apps
46814710
46824711 distclean-apps:
4683 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4684 $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
46854714 done
46864715 endif
46874716
47014730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
47024731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
47034732
4704 # External plugins.
4705
4706 DEP_PLUGINS ?=
4707
4708 define core_dep_plugin
4709 -include $(DEPS_DIR)/$(1)
4710
4711 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4712 endef
4713
4714 $(foreach p,$(DEP_PLUGINS),\
4715 $(eval $(if $(findstring /,$p),\
4716 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4717 $(call core_dep_plugin,$p/plugins.mk,$p))))
4718
4719 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4720 # This file is part of erlang.mk and subject to the terms of the ISC License.
4721
4722 # Configuration.
4723
4724 DTL_FULL_PATH ?=
4725 DTL_PATH ?= templates/
4726 DTL_SUFFIX ?= _dtl
4727 DTL_OPTS ?=
4728
4729 # Verbosity.
4730
4731 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
4732 dtl_verbose = $(dtl_verbose_$(V))
4733
4734 # Core targets.
4735
4736 DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl))
4737
4738 ifneq ($(DTL_FILES),)
4739
4740 ifdef DTL_FULL_PATH
4741 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
4742 else
4743 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
4744 endif
4745
4746 ifneq ($(words $(DTL_FILES)),0)
4747 # Rebuild templates when the Makefile changes.
4748 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
4749 @mkdir -p $(ERLANG_MK_TMP)
4750 @if test -f $@; then \
4751 touch $(DTL_FILES); \
4752 fi
4753 @touch $@
4754
4755 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
4756 endif
4757
4758 define erlydtl_compile.erl
4759 [begin
4760 Module0 = case "$(strip $(DTL_FULL_PATH))" of
4761 "" ->
4762 filename:basename(F, ".dtl");
4763 _ ->
4764 "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
4765 re:replace(F2, "/", "_", [{return, list}, global])
4766 end,
4767 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
4768 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
4769 ok -> ok;
4770 {ok, _} -> ok
4771 end
4772 end || F <- string:tokens("$(1)", " ")],
4773 halt().
4774 endef
4775
4776 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
4777 $(if $(strip $?),\
4778 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
4779
4780 endif
4781
4782 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
47834734 # This file is part of erlang.mk and subject to the terms of the ISC License.
47844735
47854736 # Verbosity.
47984749
47994750 define compile_proto.erl
48004751 [begin
4801 Dir = filename:dirname(filename:dirname(F)),
48024752 protobuffs_compile:generate_source(F,
4803 [{output_include_dir, Dir ++ "/include"},
4804 {output_src_dir, Dir ++ "/ebin"}])
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
48054755 end || F <- string:tokens("$(1)", " ")],
48064756 halt().
48074757 endef
48114761 $(if $(strip $?),$(call compile_proto,$?))
48124762 endif
48134763
4814 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
48154765 # This file is part of erlang.mk and subject to the terms of the ISC License.
48164766
48174767 .PHONY: clean-app
48254775 ERLC_EXCLUDE ?=
48264776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
48274777
4778 ERLC_ASN1_OPTS ?=
4779
48284780 ERLC_MIB_OPTS ?=
48294781 COMPILE_MIB_FIRST ?=
48304782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
48744826
48754827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
48764828 define app_file
4877 {application, $(PROJECT), [
4829 {application, '$(PROJECT)', [
48784830 {description, "$(PROJECT_DESCRIPTION)"},
48794831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48804832 {id$(comma)$(space)"$(1)"}$(comma))
48814833 {modules, [$(call comma_list,$(2))]},
48824834 {registered, []},
4883 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48844837 ]}.
48854838 endef
48864839 else
48874840 define app_file
4888 {application, $(PROJECT), [
4841 {application, '$(PROJECT)', [
48894842 {description, "$(PROJECT_DESCRIPTION)"},
48904843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48914844 {id$(comma)$(space)"$(1)"}$(comma))
48924845 {modules, [$(call comma_list,$(2))]},
48934846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
48944847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4895 {mod, {$(PROJECT_MOD), []}}
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48964850 ]}.
48974851 endef
48984852 endif
49024856
49034857 # Source files.
49044858
4905 ERL_FILES = $(sort $(call core_find,src/,*.erl))
4906 CORE_FILES = $(sort $(call core_find,src/,*.core))
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
49074863
49084864 # ASN.1 files.
49094865
49134869
49144870 define compile_asn1
49154871 $(verbose) mkdir -p include/
4916 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
49174873 $(verbose) mv asn1/*.erl src/
49184874 $(verbose) mv asn1/*.hrl include/
49194875 $(verbose) mv asn1/*.asn1db include/
49364892
49374893 # Leex and Yecc files.
49384894
4939 XRL_FILES = $(sort $(call core_find,src/,*.xrl))
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
49404896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
49414897 ERL_FILES += $(XRL_ERL_FILES)
49424898
4943 YRL_FILES = $(sort $(call core_find,src/,*.yrl))
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
49444900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
49454901 ERL_FILES += $(YRL_ERL_FILES)
49464902
49474903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4948 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
49494905
49504906 # Erlang and Core Erlang files.
49514907
49954951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49964952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49974953 (F, Mod, import, {Imp, _}) ->
4998 case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
49994960 false -> ok;
50004961 true -> Add(Mod, Imp)
50014962 end;
50194980 end || F <- ErlFiles],
50204981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
50214982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
50224991 ok = file:write_file("$(1)", [
50234992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
5024 "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n"
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
50254994 ]),
50264995 halt()
50274996 endef
50345003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
50355004 # Rebuild everything when the Makefile changes.
50365005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5037 @mkdir -p $(ERLANG_MK_TMP)
5038 @if test -f $@; then \
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
50395008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
50405009 touch -c $(PROJECT).d; \
50415010 fi
5042 @touch $@
5011 $(verbose) touch $@
50435012
50445013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
50455014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
50465015 endif
50475016
5048 -include $(PROJECT).d
5017 include $(wildcard $(PROJECT).d)
50495018
50505019 ebin/$(PROJECT).app:: ebin/
50515020
50645033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
50655034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
50665035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5067 $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
50685037 > ebin/$(PROJECT).app
50695038 else
50705039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
50885057
50895058 endif
50905059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
50915061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
50925062 # This file is part of erlang.mk and subject to the terms of the ISC License.
50935063
51055075 doc-deps:
51065076 else
51075077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5108 $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5109 endif
5110
5111 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51125082 # This file is part of erlang.mk and subject to the terms of the ISC License.
51135083
51145084 .PHONY: rel-deps
51255095 rel-deps:
51265096 else
51275097 rel-deps: $(ALL_REL_DEPS_DIRS)
5128 $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5129 endif
5130
5131 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51325102 # This file is part of erlang.mk and subject to the terms of the ISC License.
51335103
51345104 .PHONY: test-deps test-dir test-build clean-test-dir
51505120 test-deps:
51515121 else
51525122 test-deps: $(ALL_TEST_DEPS_DIRS)
5153 $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
51545124 endif
51555125
51565126 ifneq ($(wildcard $(TEST_DIR)),)
51835153 endif
51845154 endif
51855155
5186 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51875157 # This file is part of erlang.mk and subject to the terms of the ISC License.
51885158
51895159 .PHONY: rebar.config
52195189 rebar.config:
52205190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
52215191
5222 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
52235193 # This file is part of erlang.mk and subject to the terms of the ISC License.
52245194
5225 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
5226
5227 MAN_INSTALL_PATH ?= /usr/local/share/man
5228 MAN_SECTIONS ?= 3 7
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
52295200
52305201 docs:: asciidoc
52315202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
52325207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
52335210
52345211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
52355212 asciidoc-guide:
52365213 else
5237 asciidoc-guide: distclean-asciidoc doc-deps
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
52385215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
52395216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5240 endif
5241
5242 ifeq ($(wildcard doc/src/manual/*.asciidoc),)
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
52435227 asciidoc-manual:
52445228 else
5245 asciidoc-manual: distclean-asciidoc doc-deps
5246 for f in doc/src/manual/*.asciidoc ; do \
5247 a2x -v -f manpage $$f ; \
5248 done
5249 for s in $(MAN_SECTIONS); do \
5250 mkdir -p doc/man$$s/ ; \
5251 mv doc/src/manual/*.$$s doc/man$$s/ ; \
5252 gzip doc/man$$s/*.$$s ; \
5253 done
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
52545262
52555263 install-docs:: install-asciidoc
52565264
52575265 install-asciidoc: asciidoc-manual
5258 for s in $(MAN_SECTIONS); do \
5259 mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
5260 install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
5261 done
5262 endif
5263
5264 distclean:: distclean-asciidoc
5265
5266 distclean-asciidoc:
5267 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
5268
5269 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
52705276 # This file is part of erlang.mk and subject to the terms of the ISC License.
52715277
52725278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
53235329 define bs_Makefile
53245330 PROJECT = $p
53255331 PROJECT_DESCRIPTION = New project
5326 PROJECT_VERSION = 0.0.1
5332 PROJECT_VERSION = 0.1.0
53275333
53285334 # Whitespace to be used when creating files from templates.
53295335 SP = $(SP)
53335339 define bs_Makefile
53345340 PROJECT = $p
53355341 PROJECT_DESCRIPTION = New project
5336 PROJECT_VERSION = 0.0.1
5342 PROJECT_VERSION = 0.1.0
53375343
53385344 endef
53395345 endif
53415347 define bs_apps_Makefile
53425348 PROJECT = $p
53435349 PROJECT_DESCRIPTION = New project
5344 PROJECT_VERSION = 0.0.1
5350 PROJECT_VERSION = 0.1.0
53455351
53465352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
53475353 endef
53615367 endef
53625368
53635369 define bs_relx_config
5364 {release, {$p_release, "1"}, [$p]}.
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
53655371 {extended_start_script, true}.
53665372 {sys_config, "rel/sys.config"}.
53675373 {vm_args, "rel/vm.args"}.
57125718 ifndef t
57135719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57145720 endif
5715 ifndef tpl_$(t)
5716 $(error Unknown template)
5717 endif
57185721 ifndef n
57195722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57205723 endif
57275730 list-templates:
57285731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
57295732
5730 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
57315734 # This file is part of erlang.mk and subject to the terms of the ISC License.
57325735
57335736 .PHONY: clean-c_src distclean-c_src-env
59625965 $(call render_template,bs_erl_nif,src/$n.erl)
59635966 endif
59645967
5965 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
59665969 # This file is part of erlang.mk and subject to the terms of the ISC License.
59675970
5968 .PHONY: ci ci-setup distclean-kerl
5969
5970 KERL ?= $(CURDIR)/kerl
5971 export KERL
5972
5973 KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
5974
5975 OTP_GIT ?= https://github.com/erlang/otp
5976
5977 CI_INSTALL_DIR ?= $(HOME)/erlang
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
59785973 CI_OTP ?=
5979
5980 ifeq ($(strip $(CI_OTP)),)
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
59815986 ci::
59825987 else
5983 ci:: $(addprefix ci-,$(CI_OTP))
5984
5985 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
59866007
59876008 ci-setup::
6009
6010 ci-extra::
59886011
59896012 ci_verbose_0 = @echo " CI " $(1);
59906013 ci_verbose = $(ci_verbose_$(V))
59916014
59926015 define ci_target
5993 ci-$(1): $(CI_INSTALL_DIR)/$(1)
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
59946018 $(ci_verbose) \
5995 PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
5996 CI_OTP_RELEASE="$(1)" \
5997 CT_OPTS="-label $(1)" \
5998 $(MAKE) clean ci-setup tests
5999 endef
6000
6001 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
60026030
60036031 define ci_otp_target
60046032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
60056033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6006 $(KERL) build git $(OTP_GIT) $(1) $(1)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
60076035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
60086036 endif
60096037 endef
60106038
60116039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
60126040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
60136052 $(KERL):
6014 $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
60156056 $(verbose) chmod +x $(KERL)
60166057
60176058 help::
60286069 $(gen_verbose) rm -rf $(KERL)
60296070 endif
60306071
6031 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
60326073 # This file is part of erlang.mk and subject to the terms of the ISC License.
60336074
60346075 .PHONY: ct apps-ct distclean-ct
60366077 # Configuration.
60376078
60386079 CT_OPTS ?=
6080
60396081 ifneq ($(wildcard $(TEST_DIR)),)
6040 CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6041 else
6042 CT_SUITES ?=
6043 endif
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
60446088
60456089 # Core targets.
60466090
60636107 -noinput \
60646108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
60656109 -dir $(TEST_DIR) \
6066 -logdir $(CURDIR)/logs
6110 -logdir $(CT_LOGS_DIR)
60676111
60686112 ifeq ($(CT_SUITES),)
60696113 ct: $(if $(IS_APP),,apps-ct)
60706114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
60716117 ct: test-build $(if $(IS_APP),,apps-ct)
6072 $(verbose) mkdir -p $(CURDIR)/logs/
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
60736119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
60746121 endif
60756122
60766123 ifneq ($(ALL_APPS_DIRS),)
60976144
60986145 define ct_suite_target
60996146 ct-$(1): test-build
6100 $(verbose) mkdir -p $(CURDIR)/logs/
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
61016148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
61026149 endef
61036150
61046151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
61056152
61066153 distclean-ct:
6107 $(gen_verbose) rm -rf $(CURDIR)/logs/
6108
6109 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61106157 # This file is part of erlang.mk and subject to the terms of the ISC License.
61116158
61126159 .PHONY: plt distclean-plt dialyze
61506197 endef
61516198
61526199 $(DIALYZER_PLT): deps app
6153 $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
61546204
61556205 plt: $(DIALYZER_PLT)
61566206
61646214 endif
61656215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
61666216
6167 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61686218 # This file is part of erlang.mk and subject to the terms of the ISC License.
61696219
61706220 .PHONY: distclean-edoc edoc
61726222 # Configuration.
61736223
61746224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
61756235
61766236 # Core targets.
61776237
6178 ifneq ($(wildcard doc/overview.edoc),)
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
61796239 docs:: edoc
61806240 endif
61816241
61846244 # Plugin-specific targets.
61856245
61866246 edoc: distclean-edoc doc-deps
6187 $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
61886248
61896249 distclean-edoc:
61906250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
61916251
6192 # Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61936253 # This file is part of erlang.mk and subject to the terms of the ISC License.
61946254
6195 .PHONY: distclean-escript escript
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
61966320
61976321 # Configuration.
61986322
61996323 ESCRIPT_NAME ?= $(PROJECT)
62006324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
62016325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
62026327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6203
6204 ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
6205 ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
6206 ESCRIPT_EMU_ARGS ?= -pa . \
6207 -sasl errlog_type error \
6208 -escript main $(ESCRIPT_NAME)
6209 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6210 ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
62116332
62126333 # Core targets.
62136334
62206341
62216342 # Plugin-specific targets.
62226343
6223 # Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
6224 # Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
6225 # Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
6226 # Software may only be used for the great good and the true happiness of all
6227 # sentient beings.
6228
6229 define ESCRIPT_RAW
6230 'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
6231 'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
6232 ' [F || F <- A, not filelib:is_dir(F) ] end,'\
6233 'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
6234 'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
6235 'Ez = fun(Escript) ->'\
6236 ' Static = Files([$(ESCRIPT_STATIC)]),'\
6237 ' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
6238 ' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
6239 ' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
6240 ' {archive, Archive, [memory]},'\
6241 ' {shebang, "$(ESCRIPT_SHEBANG)"},'\
6242 ' {comment, "$(ESCRIPT_COMMENT)"},'\
6243 ' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
6244 ' ]),'\
6245 ' file:change_mode(Escript, 8#755)'\
6246 'end,'\
6247 'Ez("$(ESCRIPT_FILE)"),'\
6248 'halt().'
6249 endef
6250
6251 ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
6252
6253 escript:: distclean-escript deps app
6254 $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
62556360
62566361 distclean-escript:
6257 $(gen_verbose) rm -f $(ESCRIPT_NAME)
6258
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
62596365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6260 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
62616366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
62626367
62636368 .PHONY: eunit apps-eunit
63216426
63226427 ifneq ($(ALL_APPS_DIRS),)
63236428 apps-eunit:
6324 $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done
6325 endif
6326 endif
6327
6328 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
63296436 # This file is part of erlang.mk and subject to the terms of the ISC License.
63306437
6331 .PHONY: relx-rel distclean-relx-rel distclean-relx run
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
63326439
63336440 # Configuration.
63346441
6335 RELX ?= $(CURDIR)/relx
6442 RELX ?= $(ERLANG_MK_TMP)/relx
63366443 RELX_CONFIG ?= $(CURDIR)/relx.config
63376444
63386445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
63396446 RELX_OPTS ?=
63406447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
63416454
63426455 ifeq ($(firstword $(RELX_OPTS)),-o)
63436456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
63506463 ifeq ($(IS_DEP),)
63516464 ifneq ($(wildcard $(RELX_CONFIG)),)
63526465 rel:: relx-rel
6353 endif
6354 endif
6355
6356 distclean:: distclean-relx-rel distclean-relx
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
63576472
63586473 # Plugin-specific targets.
63596474
63626477 $(verbose) chmod +x $(RELX)
63636478
63646479 relx-rel: $(RELX) rel-deps app
6365 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
63666484
63676485 distclean-relx-rel:
63686486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6369
6370 distclean-relx:
6371 $(gen_verbose) rm -rf $(RELX)
63726487
63736488 # Run target.
63746489
63776492 else
63786493
63796494 define get_relx_release.erl
6380 {ok, Config} = file:consult("$(RELX_CONFIG)"),
6381 {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
6382 io:format("~s", [Name]),
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
63836504 halt(0).
63846505 endef
63856506
6386 RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
63876514
63886515 run: all
6389 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
63906517
63916518 help::
63926519 $(verbose) printf "%s\n" "" \
63956522
63966523 endif
63976524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
63986526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6399 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
64006527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
64016528
64026529 .PHONY: shell
64216548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
64226549
64236550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6424 $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
64256552
64266553 shell: build-shell-deps
64276554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
64286555
6429 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
64306571 # This file is part of erlang.mk and subject to the terms of the ISC License.
64316572
64326573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
64376578 tests:: triq
64386579
64396580 define triq_check.erl
6440 code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
64416582 try
64426583 case $(1) of
64436584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
64696610 endif
64706611 endif
64716612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
64726614 # Copyright (c) 2015, Erlang Solutions Ltd.
64736615 # This file is part of erlang.mk and subject to the terms of the ISC License.
64746616
64856627 XREFR ?= $(CURDIR)/xrefr
64866628 export XREFR
64876629
6488 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
64896631
64906632 # Core targets.
64916633
64926634 help::
6493 $(verbose) printf "%s\n" "" \
6494 "Xref targets:" \
6495 " xref Run Xrefr using $XREF_CONFIG as config file if defined"
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
64966638
64976639 distclean:: distclean-xref
64986640
65086650 distclean-xref:
65096651 $(gen_verbose) rm -rf $(XREFR)
65106652
6511 # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
65126655 # This file is part of erlang.mk and subject to the terms of the ISC License.
65136656
65146657 COVER_REPORT_DIR = cover
65176660
65186661 ifdef COVER
65196662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
65206664 # All modules in 'ebin'
65216665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
65226666
65316675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
65326676 endif
65336677 endif
6678 endif
65346679
65356680 # Core targets
65366681
65526697 "Cover targets:" \
65536698 " cover-report Generate a HTML coverage report from previously collected" \
65546699 " cover data." \
6555 " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
65566701 "" \
65576702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
65586703 "target tests additionally generates a HTML coverage report from the combined" \
65656710
65666711 .PHONY: coverdata-clean
65676712 coverdata-clean:
6568 $(gen_verbose) rm -f *.coverdata ct.cover.spec
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
65696714
65706715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
65716721 all.coverdata: $(COVERDATA)
6572 $(gen_verbose) $(ERL) -eval ' \
6573 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
6574 cover:export("$@"), halt(0).'
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
65756723
65766724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
65776725 # empty if you want the coverdata files but not the HTML report.
65896737 # Modules which include eunit.hrl always contain one line without coverage
65906738 # because eunit defines test/0 which is never called. We compensate for this.
65916739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6592 grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
65936741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
65946742
65956743 define cover_report.erl
66246772 endef
66256773
66266774 cover-report:
6627 $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
66286776 $(gen_verbose) $(call erlang,$(cover_report.erl))
66296777
66306778 endif
66316779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
66326839
66336840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
66346841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
66976904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
66986905 endif
66996906 ifndef IS_APP
6700 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
67016908 $(MAKE) -C $$dep $@ \
67026909 IS_APP=1 \
6703 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6704 || exit $$?; \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67056911 done
67066912 endif
6707 $(verbose) for dep in $^ ; do \
6913 $(verbose) set -e; for dep in $^ ; do \
67086914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
67096915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6710 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
67116917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
67126918 $(MAKE) -C $$dep fetch-deps \
67136919 IS_DEP=1 \
6714 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6715 || exit $$?; \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67166921 fi \
67176922 fi \
67186923 done
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is Pivotal Software, Inc.
13 %% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -type callback_result() :: 'ok' | {'stop', any()} | {'become', atom(), args()}.
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is Pivotal Software, Inc.
13 %% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -define(NODE_OPT, "-n").
22 # before the inclusion of erlang.mk leading to the wrong target becoming
33 # the default.
44 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
526 endif
627
728 # --------------------------------------------------------------------
2041 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
2142 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
2243 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
2345 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
2446 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
2547 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
2648 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
2749 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
2851 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
2952 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
3054 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
3155 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
3256 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
3559 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
3660 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
3761 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
3863 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
3964 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
4065 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
7095
7196 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
7297
73 # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
74 # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
75 # needs to add "ranch" as a BUILD_DEPS. The list of projects needing
76 # this workaround are:
77 # o rabbitmq-web-stomp
78 dep_ranch = git https://github.com/ninenines/ranch 1.2.1
98 # Third-party dependencies version pinning.
99 #
100 # We do that in this file, which is copied in all projects, to ensure
101 # all projects use the same versions. It avoids conflicts and makes it
102 # possible to work with rabbitmq-public-umbrella.
103
104 dep_cowboy_commit = 1.0.4
105 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
106 # Last commit of PropEr supporting Erlang R16B03.
107 dep_proper_commit = 735d972758d8bd85b12483626fe1b66450d6a6fe
108 dep_ranch_commit = 1.3.1
109 dep_webmachine_commit = 1.10.8p2
79110
80111 RABBITMQ_COMPONENTS = amqp_client \
81112 rabbit \
82113 rabbit_common \
83114 rabbitmq_amqp1_0 \
84115 rabbitmq_auth_backend_amqp \
116 rabbitmq_auth_backend_cache \
85117 rabbitmq_auth_backend_http \
86118 rabbitmq_auth_backend_ldap \
87119 rabbitmq_auth_mechanism_ssl \
88120 rabbitmq_boot_steps_visualiser \
89121 rabbitmq_clusterer \
122 rabbitmq_cli \
90123 rabbitmq_codegen \
91124 rabbitmq_consistent_hash_exchange \
125 rabbitmq_ct_client_helpers \
92126 rabbitmq_ct_helpers \
93127 rabbitmq_delayed_message_exchange \
94128 rabbitmq_dotnet_client \
97131 rabbitmq_federation_management \
98132 rabbitmq_java_client \
99133 rabbitmq_jms_client \
134 rabbitmq_jms_cts \
100135 rabbitmq_jms_topic_exchange \
101136 rabbitmq_lvc \
102137 rabbitmq_management \
4040
4141 PLUGINS_DIR="${RABBITMQ_HOME}/plugins"
4242
43 # RABBIT_HOME can contain a version number, so default plugins
44 # directory can be hard to find if we want to package some plugin
45 # separately. When RABBITMQ_HOME points to a standard location where
46 # it's usually being installed by package managers, we add
47 # "/usr/lib/rabbitmq/plugins" to plugin search path.
48 case "$RABBITMQ_HOME" in
49 /usr/lib/rabbitmq/*)
50 PLUGINS_DIR="/usr/lib/rabbitmq/plugins:$PLUGINS_DIR"
51 ;;
52 esac
53
4354 CONF_ENV_FILE=${SYS_PREFIX}/etc/rabbitmq/rabbitmq-env.conf
5656 fi
5757 }
5858
59 path_contains_existing_directory() {
60 local path="${1:?}"
61 local dir
62 local rc
63 local IFS="
64 "
65 for dir in $(echo "$path" | tr ':' '\n'); do
66 if [ -d "$dir" ]; then
67 return 0
68 fi
69 done
70 return 1
71 }
72
5973 RABBITMQ_HOME="$(rmq_realpath "${RABBITMQ_SCRIPTS_DIR}/..")"
6074
6175 ## Set defaults
106120 rmq_normalize_path() {
107121 local path=$1
108122
109 # Remove redundant slashes and strip a trailing slash
110 echo "$path" | sed -e 's#/\{2,\}#/#g' -e 's#/$##'
123 # Remove redundant slashes and strip a trailing slash for a
124 # PATH-like vars - ':' is the delimiter
125 echo "$path" | sed -e 's#/\{2,\}#/#g' -e 's#/$##' -e 's#/:#:#g'
111126 }
112127
113128 rmq_normalize_path_var() {
265280 fi
266281 fi
267282
268 if [ -d "${RABBITMQ_PLUGINS_DIR}" ]; then
283 if path_contains_existing_directory "${RABBITMQ_PLUGINS_DIR}" ; then
269284 # RabbitMQ was started with "make run-broker" from its own
270285 # source tree. Take rabbit_common from the plugins directory.
271286 ERL_LIBS="${RABBITMQ_PLUGINS_DIR}:${ERL_LIBS}"
289304 ERL_LIBS="${DEPS_DIR_norm}:${ERL_LIBS}"
290305 fi
291306 else
292 if [ -d "${RABBITMQ_PLUGINS_DIR}" ]; then
307 if path_contains_existing_directory "${RABBITMQ_PLUGINS_DIR}" ; then
293308 # RabbitMQ was started from its install directory. Take
294309 # rabbit_common from the plugins directory.
295310 ERL_LIBS="${RABBITMQ_PLUGINS_DIR}:${ERL_LIBS}"
405405 exit /b
406406
407407 :filter_path
408 set ERL_LIBS=%ERL_LIBS%;%~dps1%~n1%~x1
408 REM Ensure ERL_LIBS begins with valid path
409 IF [%ERL_LIBS%] EQU [] (
410 set ERL_LIBS=%~dps1%~n1%~x1
411 ) else (
412 set ERL_LIBS=%ERL_LIBS%;%~dps1%~n1%~x1
413 )
409414 exit /b
410415
411416 :filter_paths_done
0 #!/bin/sh -e
0 #!/bin/sh
11 ## The contents of this file are subject to the Mozilla Public License
22 ## Version 1.1 (the "License"); you may not use this file except in
33 ## compliance with the License. You may obtain a copy of the License
1111 ## The Original Code is RabbitMQ.
1212 ##
1313 ## The Initial Developer of the Original Code is GoPivotal, Inc.
14 ## Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
15 ##
14 ## Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
15 ##
16
17 set -e
1618
1719 # Get default settings with user overrides for (RABBITMQ_)<var_name>
1820 # Non-empty defaults should be set in rabbitmq-env
105107 [ "x" != "x$RABBITMQ_NODE_PORT" ] && [ "x" != "x$RABBITMQ_NODE_IP_ADDRESS" ] && RABBITMQ_LISTEN_ARG="-rabbit tcp_listeners [{\""${RABBITMQ_NODE_IP_ADDRESS}"\","${RABBITMQ_NODE_PORT}"}]"
106108
107109 # If $RABBITMQ_LOGS is '-', send all log messages to stdout. Likewise
108 # for RABBITMQ_SASL_LOGS. This is particularily useful for Docker
110 # for RABBITMQ_SASL_LOGS. This is particularly useful for Docker
109111 # images.
110112
111113 if [ "$RABBITMQ_LOGS" = '-' ]; then
120122 else
121123 SASL_ERROR_LOGGER=false
122124 RABBIT_SASL_ERROR_LOGGER='{file,"'${RABBITMQ_SASL_LOGS}'"}'
125 fi
126
127 # Bump ETS table limit to 50000
128 if [ "x" = "x$ERL_MAX_ETS_TABLES" ]; then
129 ERL_MAX_ETS_TABLES=50000
123130 fi
124131
125132 # we need to turn off path expansion because some of the vars, notably
150157 ensure_thread_pool_size
151158 check_start_params &&
152159 RABBITMQ_CONFIG_FILE=$RABBITMQ_CONFIG_FILE \
160 ERL_MAX_ETS_TABLES=$ERL_MAX_ETS_TABLES \
153161 exec ${ERL_DIR}erl \
154162 -pa ${RABBITMQ_SERVER_CODE_PATH} ${RABBITMQ_EBIN_ROOT} \
155163 ${RABBITMQ_START_RABBIT} \
226234 # The Erlang VM should ignore SIGINT.
227235 RABBITMQ_SERVER_START_ARGS="${RABBITMQ_SERVER_START_ARGS} ${RABBITMQ_IGNORE_SIGINT_FLAG}"
228236
229 # Signal handlers. They all stop RabbitMQ properly (using
230 # rabbitmqctl stop). Depending on the signal, this script will exit
231 # with a non-zero error code:
237 # Signal handlers. They all stop RabbitMQ properly, using
238 # rabbitmqctl stop. This script will exit with different exit codes:
232239 # SIGHUP SIGTERM SIGTSTP
233 # They are considered a normal process termination, so the script
234 # exits with 0.
240 # Exits 0 since this is considered a normal process termination.
235241 # SIGINT
236 # They are considered an abnormal process termination, the script
237 # exits with the job exit code.
242 # Exits 128 + $signal_number where $signal_number is 2 for SIGINT (see
243 # http://pubs.opengroup.org/onlinepubs/009695399/utilities/kill.html).
244 # This is considered an abnormal process termination. Normally, we
245 # don't need to specify this exit code because the shell propagates it.
246 # Unfortunately, the signal handler doesn't work as expected in Dash,
247 # thus we need to explicitely restate the exit code.
238248 trap "stop_rabbitmq_server; exit 0" HUP TERM TSTP
239 trap "stop_rabbitmq_server" INT
249 trap "stop_rabbitmq_server; exit 130" INT
240250
241251 start_rabbitmq_server "$@" &
252 rabbitmq_server_pid=$!
242253
243254 # Block until RabbitMQ exits or a signal is caught.
244255 # Waits for last command (which is start_rabbitmq_server)
245 wait $!
246 fi
256 #
257 # The "|| true" is here to work around an issue with Dash. Normally
258 # in a Bourne shell, if `wait` is interrupted by a signal, the
259 # signal handlers defined above are executed and the script
260 # terminates with the exit code of `wait` (unless the signal handler
261 # overrides that).
262 # In the case of Dash, it looks like `set -e` (set at the beginning
263 # of this script) gets precedence over signal handling. Therefore,
264 # when `wait` is interrupted, its exit code is non-zero and because
265 # of `set -e`, the script terminates immediately without running the
266 # signal handler. To work around this issue, we use "|| true" to
267 # force that statement to succeed and the signal handler to properly
268 # execute. Because the statement below has an exit code of 0, the
269 # signal handler has to restate the expected exit code.
270 wait $rabbitmq_server_pid || true
271 fi
7171 )
7272
7373 REM If $RABBITMQ_LOGS is '-', send all log messages to stdout. Likewise
74 REM for RABBITMQ_SASL_LOGS. This is particularily useful for Docker
74 REM for RABBITMQ_SASL_LOGS. This is particularly useful for Docker
7575 REM images.
7676
7777 if "!RABBITMQ_LOGS!" == "-" (
9898
9999 if "!RABBITMQ_IO_THREAD_POOL_SIZE!"=="" (
100100 set RABBITMQ_IO_THREAD_POOL_SIZE=64
101 )
101 )
102102
103 rem Bump ETS table limit to 50000
104 if "!ERL_MAX_ETS_TABLES!"=="" (
105 set ERL_MAX_ETS_TABLES=50000
106 )
103107
104108 set ENV_OK=true
105 CALL :check_not_empty "RABBITMQ_BOOT_MODULE" !RABBITMQ_BOOT_MODULE!
109 CALL :check_not_empty "RABBITMQ_BOOT_MODULE" !RABBITMQ_BOOT_MODULE!
106110 CALL :check_not_empty "RABBITMQ_NAME_TYPE" !RABBITMQ_NAME_TYPE!
107111 CALL :check_not_empty "RABBITMQ_NODENAME" !RABBITMQ_NODENAME!
108112
144148 if "%~2"=="" (
145149 ECHO "Error: ENV variable should be defined: %1. Please check rabbitmq-env and rabbitmq-defaults, and !RABBITMQ_CONF_ENV_FILE! script files. Check also your Environment Variables settings"
146150 set ENV_OK=false
147 EXIT /B 78
151 EXIT /B 78
148152 )
149153 EXIT /B 0
150154
104104 )
105105
106106 set ENV_OK=true
107 CALL :check_not_empty "RABBITMQ_BOOT_MODULE" !RABBITMQ_BOOT_MODULE!
107 CALL :check_not_empty "RABBITMQ_BOOT_MODULE" !RABBITMQ_BOOT_MODULE!
108108 CALL :check_not_empty "RABBITMQ_NAME_TYPE" !RABBITMQ_NAME_TYPE!
109109 CALL :check_not_empty "RABBITMQ_NODENAME" !RABBITMQ_NODENAME!
110110
170170
171171 if "!RABBITMQ_SERVICE_RESTART!"=="" (
172172 set RABBITMQ_SERVICE_RESTART=restart
173 )
174
175 rem Bump ETS table limit to 50000
176 if "!ERL_MAX_ETS_TABLES!"=="" (
177 set ERL_MAX_ETS_TABLES=50000
173178 )
174179
175180 set ERLANG_SERVICE_ARGUMENTS= ^
209214 -machine "!ERLANG_SERVICE_MANAGER_PATH!\erl.exe" ^
210215 -env ERL_CRASH_DUMP="!RABBITMQ_BASE:\=/!/erl_crash.dump" ^
211216 -env ERL_LIBS="!ERL_LIBS!" ^
217 -env ERL_MAX_ETS_TABLES="!ERL_MAX_ETS_TABLES!" ^
212218 -workdir "!RABBITMQ_BASE!" ^
213219 -stopaction "rabbit:stop_and_halt()." ^
214220 !RABBITMQ_NAME_TYPE! !RABBITMQ_NODENAME! ^
216222 -comment "Multi-protocol open source messaging broker" ^
217223 -args "!ERLANG_SERVICE_ARGUMENTS!" > NUL
218224
225 if ERRORLEVEL 1 (
226 EXIT /B 1
227 )
228
219229 goto END
220230
221231
233243 if "%~2"=="" (
234244 ECHO "Error: ENV variable should be defined: %1. Please check rabbitmq-env, rabbitmq-default, and !RABBITMQ_CONF_ENV_FILE! script files. Check also your Environment Variables settings"
235245 set ENV_OK=false
236 EXIT /B 78
246 EXIT /B 78
237247 )
238248 EXIT /B 0
239249
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(background_gc).
2424 terminate/2, code_change/3]).
2525
2626 -define(MAX_RATIO, 0.01).
27 -define(IDEAL_INTERVAL, 60000).
2827 -define(MAX_INTERVAL, 240000).
2928
3029 -record(state, {last_interval}).
4443
4544 %%----------------------------------------------------------------------------
4645
47 init([]) -> {ok, interval_gc(#state{last_interval = ?IDEAL_INTERVAL})}.
46 init([]) ->
47 {ok, IdealInterval} = application:get_env(rabbit, background_gc_target_interval),
48 {ok, interval_gc(#state{last_interval = IdealInterval})}.
4849
4950 handle_call(Msg, _From, State) ->
5051 {stop, {unexpected_call, Msg}, {unexpected_call, Msg}, State}.
6465 %%----------------------------------------------------------------------------
6566
6667 interval_gc(State = #state{last_interval = LastInterval}) ->
68 {ok, IdealInterval} = application:get_env(rabbit, background_gc_target_interval),
6769 {ok, Interval} = rabbit_misc:interval_operation(
6870 {?MODULE, gc, []},
69 ?MAX_RATIO, ?MAX_INTERVAL, ?IDEAL_INTERVAL, LastInterval),
71 ?MAX_RATIO, ?MAX_INTERVAL, IdealInterval, LastInterval),
7072 erlang:send_after(Interval, self(), run),
7173 State#state{last_interval = Interval}.
7274
7375 gc() ->
74 [garbage_collect(P) || P <- processes(),
75 {status, waiting} == process_info(P, status)],
76 garbage_collect(), %% since we will never be waiting...
76 Enabled = rabbit_misc:get_env(rabbit, background_gc_enabled, true),
77 case Enabled of
78 true ->
79 [garbage_collect(P) || P <- processes(),
80 {status, waiting} == process_info(P, status)],
81 %% since we will never be waiting...
82 garbage_collect();
83 false ->
84 ok
85 end,
7786 ok.
+0
-269
deps/rabbit/src/delegate.erl less more
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(delegate).
17
18 %% delegate is an alternative way of doing remote calls. Compared to
19 %% the rpc module, it reduces inter-node communication. For example,
20 %% if a message is routed to 1,000 queues on node A and needs to be
21 %% propagated to nodes B and C, it would be nice to avoid doing 2,000
22 %% remote casts to queue processes.
23 %%
24 %% An important issue here is preserving order - we need to make sure
25 %% that messages from a certain channel to a certain queue take a
26 %% consistent route, to prevent them being reordered. In fact all
27 %% AMQP-ish things (such as queue declaration results and basic.get)
28 %% must take the same route as well, to ensure that clients see causal
29 %% ordering correctly. Therefore we have a rather generic mechanism
30 %% here rather than just a message-reflector. That's also why we pick
31 %% the delegate process to use based on a hash of the source pid.
32 %%
33 %% When a function is invoked using delegate:invoke/2, delegate:call/2
34 %% or delegate:cast/2 on a group of pids, the pids are first split
35 %% into local and remote ones. Remote processes are then grouped by
36 %% node. The function is then invoked locally and on every node (using
37 %% gen_server2:multi/4) as many times as there are processes on that
38 %% node, sequentially.
39 %%
40 %% Errors returned when executing functions on remote nodes are re-raised
41 %% in the caller.
42 %%
43 %% RabbitMQ starts a pool of delegate processes on boot. The size of
44 %% the pool is configurable, the aim is to make sure we don't have too
45 %% few delegates and thus limit performance on many-CPU machines.
46
47 -behaviour(gen_server2).
48
49 -export([start_link/1, invoke_no_result/2, invoke/2,
50 monitor/2, demonitor/1, call/2, cast/2]).
51
52 -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
53 terminate/2, code_change/3]).
54
55 -record(state, {node, monitors, name}).
56
57 %%----------------------------------------------------------------------------
58
59 -export_type([monitor_ref/0]).
60
61 -type monitor_ref() :: reference() | {atom(), pid()}.
62 -type fun_or_mfa(A) :: fun ((pid()) -> A) | {atom(), atom(), [any()]}.
63
64 -spec start_link
65 (non_neg_integer()) -> {'ok', pid()} | ignore | {'error', any()}.
66 -spec invoke
67 ( pid(), fun_or_mfa(A)) -> A;
68 ([pid()], fun_or_mfa(A)) -> {[{pid(), A}], [{pid(), term()}]}.
69 -spec invoke_no_result(pid() | [pid()], fun_or_mfa(any())) -> 'ok'.
70 -spec monitor('process', pid()) -> monitor_ref().
71 -spec demonitor(monitor_ref()) -> 'true'.
72
73 -spec call
74 ( pid(), any()) -> any();
75 ([pid()], any()) -> {[{pid(), any()}], [{pid(), term()}]}.
76 -spec cast(pid() | [pid()], any()) -> 'ok'.
77
78 %%----------------------------------------------------------------------------
79
80 -define(HIBERNATE_AFTER_MIN, 1000).
81 -define(DESIRED_HIBERNATE, 10000).
82
83 %%----------------------------------------------------------------------------
84
85 start_link(Num) ->
86 Name = delegate_name(Num),
87 gen_server2:start_link({local, Name}, ?MODULE, [Name], []).
88
89 invoke(Pid, FunOrMFA) when is_pid(Pid) andalso node(Pid) =:= node() ->
90 apply1(FunOrMFA, Pid);
91 invoke(Pid, FunOrMFA) when is_pid(Pid) ->
92 case invoke([Pid], FunOrMFA) of
93 {[{Pid, Result}], []} ->
94 Result;
95 {[], [{Pid, {Class, Reason, StackTrace}}]} ->
96 erlang:raise(Class, Reason, StackTrace)
97 end;
98
99 invoke([], _FunOrMFA) -> %% optimisation
100 {[], []};
101 invoke([Pid], FunOrMFA) when node(Pid) =:= node() -> %% optimisation
102 case safe_invoke(Pid, FunOrMFA) of
103 {ok, _, Result} -> {[{Pid, Result}], []};
104 {error, _, Error} -> {[], [{Pid, Error}]}
105 end;
106 invoke(Pids, FunOrMFA) when is_list(Pids) ->
107 {LocalPids, Grouped} = group_pids_by_node(Pids),
108 %% The use of multi_call is only safe because the timeout is
109 %% infinity, and thus there is no process spawned in order to do
110 %% the sending. Thus calls can't overtake preceding calls/casts.
111 {Replies, BadNodes} =
112 case orddict:fetch_keys(Grouped) of
113 [] -> {[], []};
114 RemoteNodes -> gen_server2:multi_call(
115 RemoteNodes, delegate(self(), RemoteNodes),
116 {invoke, FunOrMFA, Grouped}, infinity)
117 end,
118 BadPids = [{Pid, {exit, {nodedown, BadNode}, []}} ||
119 BadNode <- BadNodes,
120 Pid <- orddict:fetch(BadNode, Grouped)],
121 ResultsNoNode = lists:append([safe_invoke(LocalPids, FunOrMFA) |
122 [Results || {_Node, Results} <- Replies]]),
123 lists:foldl(
124 fun ({ok, Pid, Result}, {Good, Bad}) -> {[{Pid, Result} | Good], Bad};
125 ({error, Pid, Error}, {Good, Bad}) -> {Good, [{Pid, Error} | Bad]}
126 end, {[], BadPids}, ResultsNoNode).
127
128 invoke_no_result(Pid, FunOrMFA) when is_pid(Pid) andalso node(Pid) =:= node() ->
129 _ = safe_invoke(Pid, FunOrMFA), %% we don't care about any error
130 ok;
131 invoke_no_result(Pid, FunOrMFA) when is_pid(Pid) ->
132 invoke_no_result([Pid], FunOrMFA);
133
134 invoke_no_result([], _FunOrMFA) -> %% optimisation
135 ok;
136 invoke_no_result([Pid], FunOrMFA) when node(Pid) =:= node() -> %% optimisation
137 _ = safe_invoke(Pid, FunOrMFA), %% must not die
138 ok;
139 invoke_no_result(Pids, FunOrMFA) when is_list(Pids) ->
140 {LocalPids, Grouped} = group_pids_by_node(Pids),
141 case orddict:fetch_keys(Grouped) of
142 [] -> ok;
143 RemoteNodes -> gen_server2:abcast(
144 RemoteNodes, delegate(self(), RemoteNodes),
145 {invoke, FunOrMFA, Grouped})
146 end,
147 _ = safe_invoke(LocalPids, FunOrMFA), %% must not die
148 ok.
149
150 monitor(process, Pid) when node(Pid) =:= node() ->
151 erlang:monitor(process, Pid);
152 monitor(process, Pid) ->
153 Name = delegate(Pid, [node(Pid)]),
154 gen_server2:cast(Name, {monitor, self(), Pid}),
155 {Name, Pid}.
156
157 demonitor(Ref) when is_reference(Ref) ->
158 erlang:demonitor(Ref);
159 demonitor({Name, Pid}) ->
160 gen_server2:cast(Name, {demonitor, self(), Pid}).
161
162 call(PidOrPids, Msg) ->
163 invoke(PidOrPids, {gen_server2, call, [Msg, infinity]}).
164
165 cast(PidOrPids, Msg) ->
166 invoke_no_result(PidOrPids, {gen_server2, cast, [Msg]}).
167
168 %%----------------------------------------------------------------------------
169
170 group_pids_by_node(Pids) ->
171 LocalNode = node(),
172 lists:foldl(
173 fun (Pid, {Local, Remote}) when node(Pid) =:= LocalNode ->
174 {[Pid | Local], Remote};
175 (Pid, {Local, Remote}) ->
176 {Local,
177 orddict:update(
178 node(Pid), fun (List) -> [Pid | List] end, [Pid], Remote)}
179 end, {[], orddict:new()}, Pids).
180
181 delegate_name(Hash) ->
182 list_to_atom("delegate_" ++ integer_to_list(Hash)).
183
184 delegate(Pid, RemoteNodes) ->
185 case get(delegate) of
186 undefined -> Name = delegate_name(
187 erlang:phash2(Pid,
188 delegate_sup:count(RemoteNodes))),
189 put(delegate, Name),
190 Name;
191 Name -> Name
192 end.
193
194 safe_invoke(Pids, FunOrMFA) when is_list(Pids) ->
195 [safe_invoke(Pid, FunOrMFA) || Pid <- Pids];
196 safe_invoke(Pid, FunOrMFA) when is_pid(Pid) ->
197 try
198 {ok, Pid, apply1(FunOrMFA, Pid)}
199 catch Class:Reason ->
200 {error, Pid, {Class, Reason, erlang:get_stacktrace()}}
201 end.
202
203 apply1({M, F, A}, Arg) -> apply(M, F, [Arg | A]);
204 apply1(Fun, Arg) -> Fun(Arg).
205
206 %%----------------------------------------------------------------------------
207
208 init([Name]) ->
209 {ok, #state{node = node(), monitors = dict:new(), name = Name}, hibernate,
210 {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}.
211
212 handle_call({invoke, FunOrMFA, Grouped}, _From, State = #state{node = Node}) ->
213 {reply, safe_invoke(orddict:fetch(Node, Grouped), FunOrMFA), State,
214 hibernate}.
215
216 handle_cast({monitor, MonitoringPid, Pid},
217 State = #state{monitors = Monitors}) ->
218 Monitors1 = case dict:find(Pid, Monitors) of
219 {ok, {Ref, Pids}} ->
220 Pids1 = gb_sets:add_element(MonitoringPid, Pids),
221 dict:store(Pid, {Ref, Pids1}, Monitors);
222 error ->
223 Ref = erlang:monitor(process, Pid),
224 Pids = gb_sets:singleton(MonitoringPid),
225 dict:store(Pid, {Ref, Pids}, Monitors)
226 end,
227 {noreply, State#state{monitors = Monitors1}, hibernate};
228
229 handle_cast({demonitor, MonitoringPid, Pid},
230 State = #state{monitors = Monitors}) ->
231 Monitors1 = case dict:find(Pid, Monitors) of
232 {ok, {Ref, Pids}} ->
233 Pids1 = gb_sets:del_element(MonitoringPid, Pids),
234 case gb_sets:is_empty(Pids1) of
235 true -> erlang:demonitor(Ref),
236 dict:erase(Pid, Monitors);
237 false -> dict:store(Pid, {Ref, Pids1}, Monitors)
238 end;
239 error ->
240 Monitors
241 end,
242 {noreply, State#state{monitors = Monitors1}, hibernate};
243
244 handle_cast({invoke, FunOrMFA, Grouped}, State = #state{node = Node}) ->
245 _ = safe_invoke(orddict:fetch(Node, Grouped), FunOrMFA),
246 {noreply, State, hibernate}.
247
248 handle_info({'DOWN', Ref, process, Pid, Info},
249 State = #state{monitors = Monitors, name = Name}) ->
250 {noreply,
251 case dict:find(Pid, Monitors) of
252 {ok, {Ref, Pids}} ->
253 Msg = {'DOWN', {Name, Pid}, process, Pid, Info},
254 gb_sets:fold(fun (MonitoringPid, _) -> MonitoringPid ! Msg end,
255 none, Pids),
256 State#state{monitors = dict:erase(Pid, Monitors)};
257 error ->
258 State
259 end, hibernate};
260
261 handle_info(_Info, State) ->
262 {noreply, State, hibernate}.
263
264 terminate(_Reason, _State) ->
265 ok.
266
267 code_change(_OldVsn, State, _Extra) ->
268 {ok, State}.
+0
-55
deps/rabbit/src/delegate_sup.erl less more
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(delegate_sup).
17
18 -behaviour(supervisor).
19
20 -export([start_link/1, count/1]).
21
22 -export([init/1]).
23
24 -define(SERVER, ?MODULE).
25
26 %%----------------------------------------------------------------------------
27
28 -spec start_link(integer()) -> rabbit_types:ok_pid_or_error().
29 -spec count([node()]) -> integer().
30
31 %%----------------------------------------------------------------------------
32
33 start_link(Count) ->
34 supervisor:start_link({local, ?SERVER}, ?MODULE, [Count]).
35
36 count([]) ->
37 1;
38 count([Node | Nodes]) ->
39 try
40 length(supervisor:which_children({?SERVER, Node}))
41 catch exit:{{R, _}, _} when R =:= nodedown; R =:= shutdown ->
42 count(Nodes);
43 exit:{R, _} when R =:= noproc; R =:= normal; R =:= shutdown;
44 R =:= nodedown ->
45 count(Nodes)
46 end.
47
48 %%----------------------------------------------------------------------------
49
50 init([Count]) ->
51 {ok, {{one_for_one, 10, 10},
52 [{Num, {delegate, start_link, [Num]},
53 transient, 16#ffffffff, worker, [delegate]} ||
54 Num <- lists:seq(0, Count - 1)]}}.
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% A dual-index tree.
+0
-1530
deps/rabbit/src/file_handle_cache.erl less more
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(file_handle_cache).
17
18 %% A File Handle Cache
19 %%
20 %% This extends a subset of the functionality of the Erlang file
21 %% module. In the below, we use "file handle" to specifically refer to
22 %% file handles, and "file descriptor" to refer to descriptors which
23 %% are not file handles, e.g. sockets.
24 %%
25 %% Some constraints
26 %% 1) This supports one writer, multiple readers per file. Nothing
27 %% else.
28 %% 2) Do not open the same file from different processes. Bad things
29 %% may happen, especially for writes.
30 %% 3) Writes are all appends. You cannot write to the middle of a
31 %% file, although you can truncate and then append if you want.
32 %% 4) There are read and write buffers. Feel free to use the read_ahead
33 %% mode, but beware of the interaction between that buffer and the write
34 %% buffer.
35 %%
36 %% Some benefits
37 %% 1) You do not have to remember to call sync before close
38 %% 2) Buffering is much more flexible than with the plain file module,
39 %% and you can control when the buffer gets flushed out. This means
40 %% that you can rely on reads-after-writes working, without having to
41 %% call the expensive sync.
42 %% 3) Unnecessary calls to position and sync get optimised out.
43 %% 4) You can find out what your 'real' offset is, and what your
44 %% 'virtual' offset is (i.e. where the hdl really is, and where it
45 %% would be after the write buffer is written out).
46 %%
47 %% There is also a server component which serves to limit the number
48 %% of open file descriptors. This is a hard limit: the server
49 %% component will ensure that clients do not have more file
50 %% descriptors open than it's configured to allow.
51 %%
52 %% On open, the client requests permission from the server to open the
53 %% required number of file handles. The server may ask the client to
54 %% close other file handles that it has open, or it may queue the
55 %% request and ask other clients to close file handles they have open
56 %% in order to satisfy the request. Requests are always satisfied in
57 %% the order they arrive, even if a latter request (for a small number
58 %% of file handles) can be satisfied before an earlier request (for a
59 %% larger number of file handles). On close, the client sends a
60 %% message to the server. These messages allow the server to keep
61 %% track of the number of open handles. The client also keeps a
62 %% gb_tree which is updated on every use of a file handle, mapping the
63 %% time at which the file handle was last used (timestamp) to the
64 %% handle. Thus the smallest key in this tree maps to the file handle
65 %% that has not been used for the longest amount of time. This
66 %% smallest key is included in the messages to the server. As such,
67 %% the server keeps track of when the least recently used file handle
68 %% was used *at the point of the most recent open or close* by each
69 %% client.
70 %%
71 %% Note that this data can go very out of date, by the client using
72 %% the least recently used handle.
73 %%
74 %% When the limit is exceeded (i.e. the number of open file handles is
75 %% at the limit and there are pending 'open' requests), the server
76 %% calculates the average age of the last reported least recently used
77 %% file handle of all the clients. It then tells all the clients to
78 %% close any handles not used for longer than this average, by
79 %% invoking the callback the client registered. The client should
80 %% receive this message and pass it into
81 %% set_maximum_since_use/1. However, it is highly possible this age
82 %% will be greater than the ages of all the handles the client knows
83 %% of because the client has used its file handles in the mean
84 %% time. Thus at this point the client reports to the server the
85 %% current timestamp at which its least recently used file handle was
86 %% last used. The server will check two seconds later that either it
87 %% is back under the limit, in which case all is well again, or if
88 %% not, it will calculate a new average age. Its data will be much
89 %% more recent now, and so it is very likely that when this is
90 %% communicated to the clients, the clients will close file handles.
91 %% (In extreme cases, where it's very likely that all clients have
92 %% used their open handles since they last sent in an update, which
93 %% would mean that the average will never cause any file handles to
94 %% be closed, the server can send out an average age of 0, resulting
95 %% in all available clients closing all their file handles.)
96 %%
97 %% Care is taken to ensure that (a) processes which are blocked
98 %% waiting for file descriptors to become available are not sent
99 %% requests to close file handles; and (b) given it is known how many
100 %% file handles a process has open, when the average age is forced to
101 %% 0, close messages are only sent to enough processes to release the
102 %% correct number of file handles and the list of processes is
103 %% randomly shuffled. This ensures we don't cause processes to
104 %% needlessly close file handles, and ensures that we don't always
105 %% make such requests of the same processes.
106 %%
107 %% The advantage of this scheme is that there is only communication
108 %% from the client to the server on open, close, and when in the
109 %% process of trying to reduce file handle usage. There is no
110 %% communication from the client to the server on normal file handle
111 %% operations. This scheme forms a feed-back loop - the server does
112 %% not care which file handles are closed, just that some are, and it
113 %% checks this repeatedly when over the limit.
114 %%
115 %% Handles which are closed as a result of the server are put into a
116 %% "soft-closed" state in which the handle is closed (data flushed out
117 %% and sync'd first) but the state is maintained. The handle will be
118 %% fully reopened again as soon as needed, thus users of this library
119 %% do not need to worry about their handles being closed by the server
120 %% - reopening them when necessary is handled transparently.
121 %%
122 %% The server also supports obtain, release and transfer. obtain/{0,1}
123 %% blocks until a file descriptor is available, at which point the
124 %% requesting process is considered to 'own' more descriptor(s).
125 %% release/{0,1} is the inverse operation and releases previously obtained
126 %% descriptor(s). transfer/{1,2} transfers ownership of file descriptor(s)
127 %% between processes. It is non-blocking. Obtain has a
128 %% lower limit, set by the ?OBTAIN_LIMIT/1 macro. File handles can use
129 %% the entire limit, but will be evicted by obtain calls up to the
130 %% point at which no more obtain calls can be satisfied by the obtains
131 %% limit. Thus there will always be some capacity available for file
132 %% handles. Processes that use obtain are never asked to return them,
133 %% and they are not managed in any way by the server. It is simply a
134 %% mechanism to ensure that processes that need file descriptors such
135 %% as sockets can do so in such a way that the overall number of open
136 %% file descriptors is managed.
137 %%
138 %% The callers of register_callback/3, obtain, and the argument of
139 %% transfer are monitored, reducing the count of handles in use
140 %% appropriately when the processes terminate.
141
142 -behaviour(gen_server2).
143
144 -export([register_callback/3]).
145 -export([open/3, close/1, read/2, append/2, needs_sync/1, sync/1, position/2,
146 truncate/1, current_virtual_offset/1, current_raw_offset/1, flush/1,
147 copy/3, set_maximum_since_use/1, delete/1, clear/1,
148 open_with_absolute_path/3]).
149 -export([obtain/0, obtain/1, release/0, release/1, transfer/1, transfer/2,
150 set_limit/1, get_limit/0, info_keys/0, with_handle/1, with_handle/2,
151 info/0, info/1, clear_read_cache/0]).
152 -export([ulimit/0]).
153
154 -export([start_link/0, start_link/2, init/1, handle_call/3, handle_cast/2,
155 handle_info/2, terminate/2, code_change/3, prioritise_cast/3]).
156
157 -define(SERVER, ?MODULE).
158 -define(RESERVED_FOR_OTHERS, 100).
159
160 -define(FILE_HANDLES_LIMIT_OTHER, 1024).
161 -define(FILE_HANDLES_CHECK_INTERVAL, 2000).
162
163 -define(OBTAIN_LIMIT(LIMIT), trunc((LIMIT * 0.9) - 2)).
164 -define(CLIENT_ETS_TABLE, file_handle_cache_client).
165 -define(ELDERS_ETS_TABLE, file_handle_cache_elders).
166
167 -include("rabbit.hrl"). % For #amqqueue record definition.
168
169 %%----------------------------------------------------------------------------
170
171 -record(file,
172 { reader_count,
173 has_writer
174 }).
175
176 -record(handle,
177 { hdl,
178 ref,
179 offset,
180 is_dirty,
181 write_buffer_size,
182 write_buffer_size_limit,
183 write_buffer,
184 read_buffer,
185 read_buffer_pos,
186 read_buffer_rem, %% Num of bytes from pos to end
187 read_buffer_size, %% Next size of read buffer to use
188 read_buffer_size_limit, %% Max size of read buffer to use
189 read_buffer_usage, %% Bytes we have read from it, for tuning
190 at_eof,
191 path,
192 mode,
193 options,
194 is_write,
195 is_read,
196 last_used_at
197 }).
198
199 -record(fhc_state,
200 { elders,
201 limit,
202 open_count,
203 open_pending,
204 obtain_limit, %%socket
205 obtain_count_socket,
206 obtain_count_file,
207 obtain_pending_socket,
208 obtain_pending_file,
209 clients,
210 timer_ref,
211 alarm_set,
212 alarm_clear
213 }).
214
215 -record(cstate,
216 { pid,
217 callback,
218 opened,
219 obtained_socket,
220 obtained_file,
221 blocked,
222 pending_closes
223 }).
224
225 -record(pending,
226 { kind,
227 pid,
228 requested,
229 from
230 }).
231
232 %%----------------------------------------------------------------------------
233 %% Specs
234 %%----------------------------------------------------------------------------
235
236 -type ref() :: any().
237 -type ok_or_error() :: 'ok' | {'error', any()}.
238 -type val_or_error(T) :: {'ok', T} | {'error', any()}.
239 -type position() :: ('bof' | 'eof' | non_neg_integer() |
240 {('bof' |'eof'), non_neg_integer()} |
241 {'cur', integer()}).
242 -type offset() :: non_neg_integer().
243
244 -spec register_callback(atom(), atom(), [any()]) -> 'ok'.
245 -spec open
246 (file:filename(), [any()],
247 [{'write_buffer', (non_neg_integer() | 'infinity' | 'unbuffered')} |
248 {'read_buffer', (non_neg_integer() | 'unbuffered')}]) ->
249 val_or_error(ref()).
250 -spec open_with_absolute_path
251 (file:filename(), [any()],
252 [{'write_buffer', (non_neg_integer() | 'infinity' | 'unbuffered')} |
253 {'read_buffer', (non_neg_integer() | 'unbuffered')}]) ->
254 val_or_error(ref()).
255 -spec close(ref()) -> ok_or_error().
256 -spec read
257 (ref(), non_neg_integer()) -> val_or_error([char()] | binary()) | 'eof'.
258 -spec append(ref(), iodata()) -> ok_or_error().
259 -spec sync(ref()) -> ok_or_error().
260 -spec position(ref(), position()) -> val_or_error(offset()).
261 -spec truncate(ref()) -> ok_or_error().
262 -spec current_virtual_offset(ref()) -> val_or_error(offset()).
263 -spec current_raw_offset(ref()) -> val_or_error(offset()).
264 -spec flush(ref()) -> ok_or_error().
265 -spec copy(ref(), ref(), non_neg_integer()) -> val_or_error(non_neg_integer()).
266 -spec delete(ref()) -> ok_or_error().
267 -spec clear(ref()) -> ok_or_error().
268 -spec set_maximum_since_use(non_neg_integer()) -> 'ok'.
269 -spec obtain() -> 'ok'.
270 -spec obtain(non_neg_integer()) -> 'ok'.
271 -spec release() -> 'ok'.
272 -spec release(non_neg_integer()) -> 'ok'.
273 -spec transfer(pid()) -> 'ok'.
274 -spec transfer(pid(), non_neg_integer()) -> 'ok'.
275 -spec with_handle(fun(() -> A)) -> A.
276 -spec with_handle(non_neg_integer(), fun(() -> A)) -> A.
277 -spec set_limit(non_neg_integer()) -> 'ok'.
278 -spec get_limit() -> non_neg_integer().
279 -spec info_keys() -> rabbit_types:info_keys().
280 -spec info() -> rabbit_types:infos().
281 -spec info([atom()]) -> rabbit_types:infos().
282 -spec ulimit() -> 'unknown' | non_neg_integer().
283
284 %%----------------------------------------------------------------------------
285 -define(INFO_KEYS, [total_limit, total_used, sockets_limit, sockets_used]).
286
287 %%----------------------------------------------------------------------------
288 %% Public API
289 %%----------------------------------------------------------------------------
290
291 start_link() ->
292 start_link(fun alarm_handler:set_alarm/1, fun alarm_handler:clear_alarm/1).
293
294 start_link(AlarmSet, AlarmClear) ->
295 gen_server2:start_link({local, ?SERVER}, ?MODULE, [AlarmSet, AlarmClear],
296 [{timeout, infinity}]).
297
298 register_callback(M, F, A)
299 when is_atom(M) andalso is_atom(F) andalso is_list(A) ->
300 gen_server2:cast(?SERVER, {register_callback, self(), {M, F, A}}).
301
302 open(Path, Mode, Options) ->
303 open_with_absolute_path(filename:absname(Path), Mode, Options).
304
305 open_with_absolute_path(Path, Mode, Options) ->
306 File1 = #file { reader_count = RCount, has_writer = HasWriter } =
307 case get({Path, fhc_file}) of
308 File = #file {} -> File;
309 undefined -> #file { reader_count = 0,
310 has_writer = false }
311 end,
312 Mode1 = append_to_write(Mode),
313 IsWriter = is_writer(Mode1),
314 case IsWriter andalso HasWriter of
315 true -> {error, writer_exists};
316 false -> {ok, Ref} = new_closed_handle(Path, Mode1, Options),
317 case get_or_reopen_timed([{Ref, new}]) of
318 {ok, [_Handle1]} ->
319 RCount1 = case is_reader(Mode1) of
320 true -> RCount + 1;
321 false -> RCount
322 end,
323 HasWriter1 = HasWriter orelse IsWriter,
324 put({Path, fhc_file},
325 File1 #file { reader_count = RCount1,
326 has_writer = HasWriter1 }),
327 {ok, Ref};
328 Error ->
329 erase({Ref, fhc_handle}),
330 Error
331 end
332 end.
333
334 close(Ref) ->
335 case erase({Ref, fhc_handle}) of
336 undefined -> ok;
337 Handle -> case hard_close(Handle) of
338 ok -> ok;
339 {Error, Handle1} -> put_handle(Ref, Handle1),
340 Error
341 end
342 end.
343
344 read(Ref, Count) ->
345 with_flushed_handles(
346 [Ref], keep,
347 fun ([#handle { is_read = false }]) ->
348 {error, not_open_for_reading};
349 ([#handle{read_buffer_size_limit = 0,
350 hdl = Hdl, offset = Offset} = Handle]) ->
351 %% The read buffer is disabled. This is just an
352 %% optimization: the clauses below can handle this case.
353 case prim_file_read(Hdl, Count) of
354 {ok, Data} -> {{ok, Data},
355 [Handle#handle{offset = Offset+size(Data)}]};
356 eof -> {eof, [Handle #handle { at_eof = true }]};
357 Error -> {Error, Handle}
358 end;
359 ([Handle = #handle{read_buffer = Buf,
360 read_buffer_pos = BufPos,
361 read_buffer_rem = BufRem,
362 read_buffer_usage = BufUsg,
363 offset = Offset}])
364 when BufRem >= Count ->
365 <<_:BufPos/binary, Res:Count/binary, _/binary>> = Buf,
366 {{ok, Res}, [Handle#handle{offset = Offset + Count,
367 read_buffer_pos = BufPos + Count,
368 read_buffer_rem = BufRem - Count,
369 read_buffer_usage = BufUsg + Count }]};
370 ([Handle0]) ->
371 maybe_reduce_read_cache([Ref]),
372 Handle = #handle{read_buffer = Buf,
373 read_buffer_pos = BufPos,
374 read_buffer_rem = BufRem,
375 read_buffer_size = BufSz,
376 hdl = Hdl,
377 offset = Offset}
378 = tune_read_buffer_limit(Handle0, Count),
379 WantedCount = Count - BufRem,
380 case prim_file_read(Hdl, max(BufSz, WantedCount)) of
381 {ok, Data} ->
382 <<_:BufPos/binary, BufTl/binary>> = Buf,
383 ReadCount = size(Data),
384 case ReadCount < WantedCount of
385 true ->
386 OffSet1 = Offset + BufRem + ReadCount,
387 {{ok, <<BufTl/binary, Data/binary>>},
388 [reset_read_buffer(
389 Handle#handle{offset = OffSet1})]};
390 false ->
391 <<Hd:WantedCount/binary, _/binary>> = Data,
392 OffSet1 = Offset + BufRem + WantedCount,
393 BufRem1 = ReadCount - WantedCount,
394 {{ok, <<BufTl/binary, Hd/binary>>},
395 [Handle#handle{offset = OffSet1,
396 read_buffer = Data,
397 read_buffer_pos = WantedCount,
398 read_buffer_rem = BufRem1,
399 read_buffer_usage = WantedCount}]}
400 end;
401 eof ->
402 {eof, [Handle #handle { at_eof = true }]};
403 Error ->
404 {Error, [reset_read_buffer(Handle)]}
405 end
406 end).
407
408 append(Ref, Data) ->
409 with_handles(
410 [Ref],
411 fun ([#handle { is_write = false }]) ->
412 {error, not_open_for_writing};
413 ([Handle]) ->
414 case maybe_seek(eof, Handle) of
415 {{ok, _Offset}, #handle { hdl = Hdl, offset = Offset,
416 write_buffer_size_limit = 0,
417 at_eof = true } = Handle1} ->
418 Offset1 = Offset + iolist_size(Data),
419 {prim_file_write(Hdl, Data),
420 [Handle1 #handle { is_dirty = true, offset = Offset1 }]};
421 {{ok, _Offset}, #handle { write_buffer = WriteBuffer,
422 write_buffer_size = Size,
423 write_buffer_size_limit = Limit,
424 at_eof = true } = Handle1} ->
425 WriteBuffer1 = [Data | WriteBuffer],
426 Size1 = Size + iolist_size(Data),
427 Handle2 = Handle1 #handle { write_buffer = WriteBuffer1,
428 write_buffer_size = Size1 },
429 case Limit =/= infinity andalso Size1 > Limit of
430 true -> {Result, Handle3} = write_buffer(Handle2),
431 {Result, [Handle3]};
432 false -> {ok, [Handle2]}
433 end;
434 {{error, _} = Error, Handle1} ->
435 {Error, [Handle1]}
436 end
437 end).
438
439 sync(Ref) ->
440 with_flushed_handles(
441 [Ref], keep,
442 fun ([#handle { is_dirty = false, write_buffer = [] }]) ->
443 ok;
444 ([Handle = #handle { hdl = Hdl,
445 is_dirty = true, write_buffer = [] }]) ->
446 case prim_file_sync(Hdl) of
447 ok -> {ok, [Handle #handle { is_dirty = false }]};
448 Error -> {Error, [Handle]}
449 end
450 end).
451
452 needs_sync(Ref) ->
453 %% This must *not* use with_handles/2; see bug 25052
454 case get({Ref, fhc_handle}) of
455 #handle { is_dirty = false, write_buffer = [] } -> false;
456 #handle {} -> true
457 end.
458
459 position(Ref, NewOffset) ->
460 with_flushed_handles(
461 [Ref], keep,
462 fun ([Handle]) -> {Result, Handle1} = maybe_seek(NewOffset, Handle),
463 {Result, [Handle1]}
464 end).
465
466 truncate(Ref) ->
467 with_flushed_handles(
468 [Ref],
469 fun ([Handle1 = #handle { hdl = Hdl }]) ->
470 case prim_file:truncate(Hdl) of
471 ok -> {ok, [Handle1 #handle { at_eof = true }]};
472 Error -> {Error, [Handle1]}
473 end
474 end).
475
476 current_virtual_offset(Ref) ->
477 with_handles([Ref], fun ([#handle { at_eof = true, is_write = true,
478 offset = Offset,
479 write_buffer_size = Size }]) ->
480 {ok, Offset + Size};
481 ([#handle { offset = Offset }]) ->
482 {ok, Offset}
483 end).
484
485 current_raw_offset(Ref) ->
486 with_handles([Ref], fun ([Handle]) -> {ok, Handle #handle.offset} end).
487
488 flush(Ref) ->
489 with_flushed_handles([Ref], fun ([Handle]) -> {ok, [Handle]} end).
490
491 copy(Src, Dest, Count) ->
492 with_flushed_handles(
493 [Src, Dest],
494 fun ([SHandle = #handle { is_read = true, hdl = SHdl, offset = SOffset },
495 DHandle = #handle { is_write = true, hdl = DHdl, offset = DOffset }]
496 ) ->
497 case prim_file:copy(SHdl, DHdl, Count) of
498 {ok, Count1} = Result1 ->
499 {Result1,
500 [SHandle #handle { offset = SOffset + Count1 },
501 DHandle #handle { offset = DOffset + Count1,
502 is_dirty = true }]};
503 Error ->
504 {Error, [SHandle, DHandle]}
505 end;
506 (_Handles) ->
507 {error, incorrect_handle_modes}
508 end).
509
510 delete(Ref) ->
511 case erase({Ref, fhc_handle}) of
512 undefined ->
513 ok;
514 Handle = #handle { path = Path } ->
515 case hard_close(Handle #handle { is_dirty = false,
516 write_buffer = [] }) of
517 ok -> prim_file:delete(Path);
518 {Error, Handle1} -> put_handle(Ref, Handle1),
519 Error
520 end
521 end.
522
523 clear(Ref) ->
524 with_handles(
525 [Ref],
526 fun ([#handle { at_eof = true, write_buffer_size = 0, offset = 0 }]) ->
527 ok;
528 ([Handle]) ->
529 case maybe_seek(bof, Handle#handle{write_buffer = [],
530 write_buffer_size = 0}) of
531 {{ok, 0}, Handle1 = #handle { hdl = Hdl }} ->
532 case prim_file:truncate(Hdl) of
533 ok -> {ok, [Handle1 #handle { at_eof = true }]};
534 Error -> {Error, [Handle1]}
535 end;
536 {{error, _} = Error, Handle1} ->
537 {Error, [Handle1]}
538 end
539 end).
540
541 set_maximum_since_use(MaximumAge) ->
542 Now = time_compat:monotonic_time(),
543 case lists:foldl(
544 fun ({{Ref, fhc_handle},
545 Handle = #handle { hdl = Hdl, last_used_at = Then }}, Rep) ->
546 case Hdl =/= closed andalso
547 time_compat:convert_time_unit(Now - Then,
548 native,
549 micro_seconds)
550 >= MaximumAge of
551 true -> soft_close(Ref, Handle) orelse Rep;
552 false -> Rep
553 end;
554 (_KeyValuePair, Rep) ->
555 Rep
556 end, false, get()) of
557 false -> age_tree_change(), ok;
558 true -> ok
559 end.
560
561 obtain() -> obtain(1).
562 release() -> release(1).
563 transfer(Pid) -> transfer(Pid, 1).
564
565 obtain(Count) -> obtain(Count, socket).
566 release(Count) -> release(Count, socket).
567
568 with_handle(Fun) ->
569 with_handle(1, Fun).
570
571 with_handle(N, Fun) ->
572 ok = obtain(N, file),
573 try Fun()
574 after ok = release(N, file)
575 end.
576
577 obtain(Count, Type) when Count > 0 ->
578 %% If the FHC isn't running, obtains succeed immediately.
579 case whereis(?SERVER) of
580 undefined -> ok;
581 _ -> gen_server2:call(
582 ?SERVER, {obtain, Count, Type, self()}, infinity)
583 end.
584
585 release(Count, Type) when Count > 0 ->
586 gen_server2:cast(?SERVER, {release, Count, Type, self()}).
587
588 transfer(Pid, Count) when Count > 0 ->
589 gen_server2:cast(?SERVER, {transfer, Count, self(), Pid}).
590
591 set_limit(Limit) ->
592 gen_server2:call(?SERVER, {set_limit, Limit}, infinity).
593
594 get_limit() ->
595 gen_server2:call(?SERVER, get_limit, infinity).
596
597 info_keys() -> ?INFO_KEYS.
598
599 info() -> info(?INFO_KEYS).
600 info(Items) -> gen_server2:call(?SERVER, {info, Items}, infinity).
601
602 clear_read_cache() ->
603 case application:get_env(rabbit, fhc_read_buffering) of
604 {ok, true} ->
605 gen_server2:cast(?SERVER, clear_read_cache),
606 clear_vhost_read_cache(rabbit_vhost:list());
607 _ -> %% undefined or {ok, false}
608 ok
609 end.
610
611 clear_vhost_read_cache([]) ->
612 ok;
613 clear_vhost_read_cache([VHost | Rest]) ->
614 clear_queue_read_cache(rabbit_amqqueue:list(VHost)),
615 clear_vhost_read_cache(Rest).
616
617 clear_queue_read_cache([]) ->
618 ok;
619 clear_queue_read_cache([#amqqueue{pid = MPid, slave_pids = SPids} | Rest]) ->
620 %% Limit the action to the current node.
621 Pids = [P || P <- [MPid | SPids], node(P) =:= node()],
622 %% This function is executed in the context of the backing queue
623 %% process because the read buffer is stored in the process
624 %% dictionary.
625 Fun = fun(_, State) ->
626 _ = clear_process_read_cache(),
627 State
628 end,
629 [rabbit_amqqueue:run_backing_queue(Pid, rabbit_variable_queue, Fun)
630 || Pid <- Pids],
631 clear_queue_read_cache(Rest).
632
633 clear_process_read_cache() ->
634 [
635 begin
636 Handle1 = reset_read_buffer(Handle),
637 put({Ref, fhc_handle}, Handle1)
638 end ||
639 {{Ref, fhc_handle}, Handle} <- get(),
640 size(Handle#handle.read_buffer) > 0
641 ].
642
643 %%----------------------------------------------------------------------------
644 %% Internal functions
645 %%----------------------------------------------------------------------------
646
647 prim_file_read(Hdl, Size) ->
648 file_handle_cache_stats:update(
649 io_read, Size, fun() -> prim_file:read(Hdl, Size) end).
650
651 prim_file_write(Hdl, Bytes) ->
652 file_handle_cache_stats:update(
653 io_write, iolist_size(Bytes), fun() -> prim_file:write(Hdl, Bytes) end).
654
655 prim_file_sync(Hdl) ->
656 file_handle_cache_stats:update(io_sync, fun() -> prim_file:sync(Hdl) end).
657
658 prim_file_position(Hdl, NewOffset) ->
659 file_handle_cache_stats:update(
660 io_seek, fun() -> prim_file:position(Hdl, NewOffset) end).
661
662 is_reader(Mode) -> lists:member(read, Mode).
663
664 is_writer(Mode) -> lists:member(write, Mode).
665
666 append_to_write(Mode) ->
667 case lists:member(append, Mode) of
668 true -> [write | Mode -- [append, write]];
669 false -> Mode
670 end.
671
672 with_handles(Refs, Fun) ->
673 with_handles(Refs, reset, Fun).
674
675 with_handles(Refs, ReadBuffer, Fun) ->
676 case get_or_reopen_timed([{Ref, reopen} || Ref <- Refs]) of
677 {ok, Handles0} ->
678 Handles = case ReadBuffer of
679 reset -> [reset_read_buffer(H) || H <- Handles0];
680 keep -> Handles0
681 end,
682 case Fun(Handles) of
683 {Result, Handles1} when is_list(Handles1) ->
684 _ = lists:zipwith(fun put_handle/2, Refs, Handles1),
685 Result;
686 Result ->
687 Result
688 end;
689 Error ->
690 Error
691 end.
692
693 with_flushed_handles(Refs, Fun) ->
694 with_flushed_handles(Refs, reset, Fun).
695
696 with_flushed_handles(Refs, ReadBuffer, Fun) ->
697 with_handles(
698 Refs, ReadBuffer,
699 fun (Handles) ->
700 case lists:foldl(
701 fun (Handle, {ok, HandlesAcc}) ->
702 {Res, Handle1} = write_buffer(Handle),
703 {Res, [Handle1 | HandlesAcc]};
704 (Handle, {Error, HandlesAcc}) ->
705 {Error, [Handle | HandlesAcc]}
706 end, {ok, []}, Handles) of
707 {ok, Handles1} ->
708 Fun(lists:reverse(Handles1));
709 {Error, Handles1} ->
710 {Error, lists:reverse(Handles1)}
711 end
712 end).
713
714 get_or_reopen_timed(RefNewOrReopens) ->
715 file_handle_cache_stats:update(
716 io_file_handle_open_attempt, fun() -> get_or_reopen(RefNewOrReopens) end).
717
718 get_or_reopen(RefNewOrReopens) ->
719 case partition_handles(RefNewOrReopens) of
720 {OpenHdls, []} ->
721 {ok, [Handle || {_Ref, Handle} <- OpenHdls]};
722 {OpenHdls, ClosedHdls} ->
723 Oldest = oldest(get_age_tree(),
724 fun () -> time_compat:monotonic_time() end),
725 case gen_server2:call(?SERVER, {open, self(), length(ClosedHdls),
726 Oldest}, infinity) of
727 ok ->
728 case reopen(ClosedHdls) of
729 {ok, RefHdls} -> sort_handles(RefNewOrReopens,
730 OpenHdls, RefHdls, []);
731 Error -> Error
732 end;
733 close ->
734 [soft_close(Ref, Handle) ||
735 {{Ref, fhc_handle}, Handle = #handle { hdl = Hdl }} <-
736 get(),
737 Hdl =/= closed],
738 get_or_reopen(RefNewOrReopens)
739 end
740 end.
741
742 reopen(ClosedHdls) -> reopen(ClosedHdls, get_age_tree(), []).
743
744 reopen([], Tree, RefHdls) ->
745 put_age_tree(Tree),
746 {ok, lists:reverse(RefHdls)};
747 reopen([{Ref, NewOrReopen, Handle = #handle { hdl = closed,
748 path = Path,
749 mode = Mode0,
750 offset = Offset,
751 last_used_at = undefined }} |
752 RefNewOrReopenHdls] = ToOpen, Tree, RefHdls) ->
753 Mode = case NewOrReopen of
754 new -> Mode0;
755 reopen -> file_handle_cache_stats:update(io_reopen),
756 [read | Mode0]
757 end,
758 case prim_file:open(Path, Mode) of
759 {ok, Hdl} ->
760 Now = time_compat:monotonic_time(),
761 {{ok, _Offset}, Handle1} =
762 maybe_seek(Offset, reset_read_buffer(
763 Handle#handle{hdl = Hdl,
764 offset = 0,
765 last_used_at = Now})),
766 put({Ref, fhc_handle}, Handle1),
767 reopen(RefNewOrReopenHdls, gb_trees:insert({Now, Ref}, true, Tree),
768 [{Ref, Handle1} | RefHdls]);
769 Error ->
770 %% NB: none of the handles in ToOpen are in the age tree
771 Oldest = oldest(Tree, fun () -> undefined end),
772 [gen_server2:cast(?SERVER, {close, self(), Oldest}) || _ <- ToOpen],
773 put_age_tree(Tree),
774 Error
775 end.
776
777 partition_handles(RefNewOrReopens) ->
778 lists:foldr(
779 fun ({Ref, NewOrReopen}, {Open, Closed}) ->
780 case get({Ref, fhc_handle}) of
781 #handle { hdl = closed } = Handle ->
782 {Open, [{Ref, NewOrReopen, Handle} | Closed]};
783 #handle {} = Handle ->
784 {[{Ref, Handle} | Open], Closed}
785 end
786 end, {[], []}, RefNewOrReopens).
787
788 sort_handles([], [], [], Acc) ->
789 {ok, lists:reverse(Acc)};
790 sort_handles([{Ref, _} | RefHdls], [{Ref, Handle} | RefHdlsA], RefHdlsB, Acc) ->
791 sort_handles(RefHdls, RefHdlsA, RefHdlsB, [Handle | Acc]);
792 sort_handles([{Ref, _} | RefHdls], RefHdlsA, [{Ref, Handle} | RefHdlsB], Acc) ->
793 sort_handles(RefHdls, RefHdlsA, RefHdlsB, [Handle | Acc]).
794
795 put_handle(Ref, Handle = #handle { last_used_at = Then }) ->
796 Now = time_compat:monotonic_time(),
797 age_tree_update(Then, Now, Ref),
798 put({Ref, fhc_handle}, Handle #handle { last_used_at = Now }).
799
800 with_age_tree(Fun) -> put_age_tree(Fun(get_age_tree())).
801
802 get_age_tree() ->
803 case get(fhc_age_tree) of
804 undefined -> gb_trees:empty();
805 AgeTree -> AgeTree
806 end.
807
808 put_age_tree(Tree) -> put(fhc_age_tree, Tree).
809
810 age_tree_update(Then, Now, Ref) ->
811 with_age_tree(
812 fun (Tree) ->
813 gb_trees:insert({Now, Ref}, true,
814 gb_trees:delete_any({Then, Ref}, Tree))
815 end).
816
817 age_tree_delete(Then, Ref) ->
818 with_age_tree(
819 fun (Tree) ->
820 Tree1 = gb_trees:delete_any({Then, Ref}, Tree),
821 Oldest = oldest(Tree1, fun () -> undefined end),
822 gen_server2:cast(?SERVER, {close, self(), Oldest}),
823 Tree1
824 end).
825
826 age_tree_change() ->
827 with_age_tree(
828 fun (Tree) ->
829 case gb_trees:is_empty(Tree) of
830 true -> Tree;
831 false -> {{Oldest, _Ref}, _} = gb_trees:smallest(Tree),
832 gen_server2:cast(?SERVER, {update, self(), Oldest}),
833 Tree
834 end
835 end).
836
837 oldest(Tree, DefaultFun) ->
838 case gb_trees:is_empty(Tree) of
839 true -> DefaultFun();
840 false -> {{Oldest, _Ref}, _} = gb_trees:smallest(Tree),
841 Oldest
842 end.
843
844 new_closed_handle(Path, Mode, Options) ->
845 WriteBufferSize =
846 case application:get_env(rabbit, fhc_write_buffering) of
847 {ok, false} -> 0;
848 {ok, true} ->
849 case proplists:get_value(write_buffer, Options, unbuffered) of
850 unbuffered -> 0;
851 infinity -> infinity;
852 N when is_integer(N) -> N
853 end
854 end,
855 ReadBufferSize =
856 case application:get_env(rabbit, fhc_read_buffering) of
857 {ok, false} -> 0;
858 {ok, true} ->
859 case proplists:get_value(read_buffer, Options, unbuffered) of
860 unbuffered -> 0;
861 N2 when is_integer(N2) -> N2
862 end
863 end,
864 Ref = make_ref(),
865 put({Ref, fhc_handle}, #handle { hdl = closed,
866 ref = Ref,
867 offset = 0,
868 is_dirty = false,
869 write_buffer_size = 0,
870 write_buffer_size_limit = WriteBufferSize,
871 write_buffer = [],
872 read_buffer = <<>>,
873 read_buffer_pos = 0,
874 read_buffer_rem = 0,
875 read_buffer_size = ReadBufferSize,
876 read_buffer_size_limit = ReadBufferSize,
877 read_buffer_usage = 0,
878 at_eof = false,
879 path = Path,
880 mode = Mode,
881 options = Options,
882 is_write = is_writer(Mode),
883 is_read = is_reader(Mode),
884 last_used_at = undefined }),
885 {ok, Ref}.
886
887 soft_close(Ref, Handle) ->
888 {Res, Handle1} = soft_close(Handle),
889 case Res of
890 ok -> put({Ref, fhc_handle}, Handle1),
891 true;
892 _ -> put_handle(Ref, Handle1),
893 false
894 end.
895
896 soft_close(Handle = #handle { hdl = closed }) ->
897 {ok, Handle};
898 soft_close(Handle) ->
899 case write_buffer(Handle) of
900 {ok, #handle { hdl = Hdl,
901 ref = Ref,
902 is_dirty = IsDirty,
903 last_used_at = Then } = Handle1 } ->
904 ok = case IsDirty of
905 true -> prim_file_sync(Hdl);
906 false -> ok
907 end,
908 ok = prim_file:close(Hdl),
909 age_tree_delete(Then, Ref),
910 {ok, Handle1 #handle { hdl = closed,
911 is_dirty = false,
912 last_used_at = undefined }};
913 {_Error, _Handle} = Result ->
914 Result
915 end.
916
917 hard_close(Handle) ->
918 case soft_close(Handle) of
919 {ok, #handle { path = Path,
920 is_read = IsReader, is_write = IsWriter }} ->
921 #file { reader_count = RCount, has_writer = HasWriter } = File =
922 get({Path, fhc_file}),
923 RCount1 = case IsReader of
924 true -> RCount - 1;
925 false -> RCount
926 end,
927 HasWriter1 = HasWriter andalso not IsWriter,
928 case RCount1 =:= 0 andalso not HasWriter1 of
929 true -> erase({Path, fhc_file});
930 false -> put({Path, fhc_file},
931 File #file { reader_count = RCount1,
932 has_writer = HasWriter1 })
933 end,
934 ok;
935 {_Error, _Handle} = Result ->
936 Result
937 end.
938
939 maybe_seek(New, Handle = #handle{hdl = Hdl,
940 offset = Old,
941 read_buffer_pos = BufPos,
942 read_buffer_rem = BufRem,
943 at_eof = AtEoF}) ->
944 {AtEoF1, NeedsSeek} = needs_seek(AtEoF, Old, New),
945 case NeedsSeek of
946 true when is_number(New) andalso
947 ((New >= Old andalso New =< BufRem + Old)
948 orelse (New < Old andalso Old - New =< BufPos)) ->
949 Diff = New - Old,
950 {{ok, New}, Handle#handle{offset = New,
951 at_eof = AtEoF1,
952 read_buffer_pos = BufPos + Diff,
953 read_buffer_rem = BufRem - Diff}};
954 true ->
955 case prim_file_position(Hdl, New) of
956 {ok, Offset1} = Result ->
957 {Result, reset_read_buffer(Handle#handle{offset = Offset1,
958 at_eof = AtEoF1})};
959 {error, _} = Error ->
960 {Error, Handle}
961 end;
962 false ->
963 {{ok, Old}, Handle}
964 end.
965
966 needs_seek( AtEoF, _CurOffset, cur ) -> {AtEoF, false};
967 needs_seek( AtEoF, _CurOffset, {cur, 0}) -> {AtEoF, false};
968 needs_seek( true, _CurOffset, eof ) -> {true , false};
969 needs_seek( true, _CurOffset, {eof, 0}) -> {true , false};
970 needs_seek( false, _CurOffset, eof ) -> {true , true };
971 needs_seek( false, _CurOffset, {eof, 0}) -> {true , true };
972 needs_seek( AtEoF, 0, bof ) -> {AtEoF, false};
973 needs_seek( AtEoF, 0, {bof, 0}) -> {AtEoF, false};
974 needs_seek( AtEoF, CurOffset, CurOffset) -> {AtEoF, false};
975 needs_seek( true, CurOffset, {bof, DesiredOffset})
976 when DesiredOffset >= CurOffset ->
977 {true, true};
978 needs_seek( true, _CurOffset, {cur, DesiredOffset})
979 when DesiredOffset > 0 ->
980 {true, true};
981 needs_seek( true, CurOffset, DesiredOffset) %% same as {bof, DO}
982 when is_integer(DesiredOffset) andalso DesiredOffset >= CurOffset ->
983 {true, true};
984 %% because we can't really track size, we could well end up at EoF and not know
985 needs_seek(_AtEoF, _CurOffset, _DesiredOffset) ->
986 {false, true}.
987
988 write_buffer(Handle = #handle { write_buffer = [] }) ->
989 {ok, Handle};
990 write_buffer(Handle = #handle { hdl = Hdl, offset = Offset,
991 write_buffer = WriteBuffer,
992 write_buffer_size = DataSize,
993 at_eof = true }) ->
994 case prim_file_write(Hdl, lists:reverse(WriteBuffer)) of
995 ok ->
996 Offset1 = Offset + DataSize,
997 {ok, Handle #handle { offset = Offset1, is_dirty = true,
998 write_buffer = [], write_buffer_size = 0 }};
999 {error, _} = Error ->
1000 {Error, Handle}
1001 end.
1002
1003 reset_read_buffer(Handle) ->
1004 Handle#handle{read_buffer = <<>>,
1005 read_buffer_pos = 0,
1006 read_buffer_rem = 0}.
1007
1008 %% We come into this function whenever there's been a miss while
1009 %% reading from the buffer - but note that when we first start with a
1010 %% new handle the usage will be 0. Therefore in that case don't take
1011 %% it as meaning the buffer was useless, we just haven't done anything
1012 %% yet!
1013 tune_read_buffer_limit(Handle = #handle{read_buffer_usage = 0}, _Count) ->
1014 Handle;
1015 %% In this head we have been using the buffer but now tried to read
1016 %% outside it. So how did we do? If we used less than the size of the
1017 %% buffer, make the new buffer the size of what we used before, but
1018 %% add one byte (so that next time we can distinguish between getting
1019 %% the buffer size exactly right and actually wanting more). If we
1020 %% read 100% of what we had, then double it for next time, up to the
1021 %% limit that was set when we were created.
1022 tune_read_buffer_limit(Handle = #handle{read_buffer = Buf,
1023 read_buffer_usage = Usg,
1024 read_buffer_size = Sz,
1025 read_buffer_size_limit = Lim}, Count) ->
1026 %% If the buffer is <<>> then we are in the first read after a
1027 %% reset, the read_buffer_usage is the total usage from before the
1028 %% reset. But otherwise we are in a read which read off the end of
1029 %% the buffer, so really the size of this read should be included
1030 %% in the usage.
1031 TotalUsg = case Buf of
1032 <<>> -> Usg;
1033 _ -> Usg + Count
1034 end,
1035 Handle#handle{read_buffer_usage = 0,
1036 read_buffer_size = erlang:min(case TotalUsg < Sz of
1037 true -> Usg + 1;
1038 false -> Usg * 2
1039 end, Lim)}.
1040
1041 maybe_reduce_read_cache(SparedRefs) ->
1042 case rabbit_memory_monitor:memory_use(bytes) of
1043 {_, infinity} -> ok;
1044 {MemUse, MemLimit} when MemUse < MemLimit -> ok;
1045 {MemUse, MemLimit} -> reduce_read_cache(
1046 (MemUse - MemLimit) * 2,
1047 SparedRefs)
1048 end.
1049
1050 reduce_read_cache(MemToFree, SparedRefs) ->
1051 Handles = lists:sort(
1052 fun({_, H1}, {_, H2}) -> H1 < H2 end,
1053 [{R, H} || {{R, fhc_handle}, H} <- get(),
1054 not lists:member(R, SparedRefs)
1055 andalso size(H#handle.read_buffer) > 0]),
1056 FreedMem = lists:foldl(
1057 fun
1058 (_, Freed) when Freed >= MemToFree ->
1059 Freed;
1060 ({Ref, #handle{read_buffer = Buf} = Handle}, Freed) ->
1061 Handle1 = reset_read_buffer(Handle),
1062 put({Ref, fhc_handle}, Handle1),
1063 Freed + size(Buf)
1064 end, 0, Handles),
1065 if
1066 FreedMem < MemToFree andalso SparedRefs =/= [] ->
1067 reduce_read_cache(MemToFree - FreedMem, []);
1068 true ->
1069 ok
1070 end.
1071
1072 infos(Items, State) -> [{Item, i(Item, State)} || Item <- Items].
1073
1074 i(total_limit, #fhc_state{limit = Limit}) -> Limit;
1075 i(total_used, State) -> used(State);
1076 i(sockets_limit, #fhc_state{obtain_limit = Limit}) -> Limit;
1077 i(sockets_used, #fhc_state{obtain_count_socket = Count}) -> Count;
1078 i(Item, _) -> throw({bad_argument, Item}).
1079
1080 used(#fhc_state{open_count = C1,
1081 obtain_count_socket = C2,
1082 obtain_count_file = C3}) -> C1 + C2 + C3.
1083
1084 %%----------------------------------------------------------------------------
1085 %% gen_server2 callbacks
1086 %%----------------------------------------------------------------------------
1087
1088 init([AlarmSet, AlarmClear]) ->
1089 _ = file_handle_cache_stats:init(),
1090 Limit = case application:get_env(file_handles_high_watermark) of
1091 {ok, Watermark} when (is_integer(Watermark) andalso
1092 Watermark > 0) ->
1093 Watermark;
1094 _ ->
1095 case ulimit() of
1096 unknown -> ?FILE_HANDLES_LIMIT_OTHER;
1097 Lim -> lists:max([2, Lim - ?RESERVED_FOR_OTHERS])
1098 end
1099 end,
1100 ObtainLimit = obtain_limit(Limit),
1101 error_logger:info_msg("Limiting to approx ~p file handles (~p sockets)~n",
1102 [Limit, ObtainLimit]),
1103 Clients = ets:new(?CLIENT_ETS_TABLE, [set, private, {keypos, #cstate.pid}]),
1104 Elders = ets:new(?ELDERS_ETS_TABLE, [set, private]),
1105 {ok, #fhc_state { elders = Elders,
1106 limit = Limit,
1107 open_count = 0,
1108 open_pending = pending_new(),
1109 obtain_limit = ObtainLimit,
1110 obtain_count_file = 0,
1111 obtain_pending_file = pending_new(),
1112 obtain_count_socket = 0,
1113 obtain_pending_socket = pending_new(),
1114 clients = Clients,
1115 timer_ref = undefined,
1116 alarm_set = AlarmSet,
1117 alarm_clear = AlarmClear }}.
1118
1119 prioritise_cast(Msg, _Len, _State) ->
1120 case Msg of
1121 {release, _, _, _} -> 5;
1122 _ -> 0
1123 end.
1124
1125 handle_call({open, Pid, Requested, EldestUnusedSince}, From,
1126 State = #fhc_state { open_count = Count,
1127 open_pending = Pending,
1128 elders = Elders,
1129 clients = Clients })
1130 when EldestUnusedSince =/= undefined ->
1131 true = ets:insert(Elders, {Pid, EldestUnusedSince}),
1132 Item = #pending { kind = open,
1133 pid = Pid,
1134 requested = Requested,
1135 from = From },
1136 ok = track_client(Pid, Clients),
1137 case needs_reduce(State #fhc_state { open_count = Count + Requested }) of
1138 true -> case ets:lookup(Clients, Pid) of
1139 [#cstate { opened = 0 }] ->
1140 true = ets:update_element(
1141 Clients, Pid, {#cstate.blocked, true}),
1142 {noreply,
1143 reduce(State #fhc_state {
1144 open_pending = pending_in(Item, Pending) })};
1145 [#cstate { opened = Opened }] ->
1146 true = ets:update_element(
1147 Clients, Pid,
1148 {#cstate.pending_closes, Opened}),
1149 {reply, close, State}
1150 end;
1151 false -> {noreply, run_pending_item(Item, State)}
1152 end;
1153
1154 handle_call({obtain, N, Type, Pid}, From,
1155 State = #fhc_state { clients = Clients }) ->
1156 Count = obtain_state(Type, count, State),
1157 Pending = obtain_state(Type, pending, State),
1158 ok = track_client(Pid, Clients),
1159 Item = #pending { kind = {obtain, Type}, pid = Pid,
1160 requested = N, from = From },
1161 Enqueue = fun () ->
1162 true = ets:update_element(Clients, Pid,
1163 {#cstate.blocked, true}),
1164 set_obtain_state(Type, pending,
1165 pending_in(Item, Pending), State)
1166 end,
1167 {noreply,
1168 case obtain_limit_reached(Type, State) of
1169 true -> Enqueue();
1170 false -> case needs_reduce(
1171 set_obtain_state(Type, count, Count + 1, State)) of
1172 true -> reduce(Enqueue());
1173 false -> adjust_alarm(
1174 State, run_pending_item(Item, State))
1175 end
1176 end};
1177
1178 handle_call({set_limit, Limit}, _From, State) ->
1179 {reply, ok, adjust_alarm(
1180 State, maybe_reduce(
1181 process_pending(
1182 State #fhc_state {
1183 limit = Limit,
1184 obtain_limit = obtain_limit(Limit) })))};
1185
1186 handle_call(get_limit, _From, State = #fhc_state { limit = Limit }) ->
1187 {reply, Limit, State};
1188
1189 handle_call({info, Items}, _From, State) ->
1190 {reply, infos(Items, State), State}.
1191
1192 handle_cast({register_callback, Pid, MFA},
1193 State = #fhc_state { clients = Clients }) ->
1194 ok = track_client(Pid, Clients),
1195 true = ets:update_element(Clients, Pid, {#cstate.callback, MFA}),
1196 {noreply, State};
1197
1198 handle_cast({update, Pid, EldestUnusedSince},
1199 State = #fhc_state { elders = Elders })
1200 when EldestUnusedSince =/= undefined ->
1201 true = ets:insert(Elders, {Pid, EldestUnusedSince}),
1202 %% don't call maybe_reduce from here otherwise we can create a
1203 %% storm of messages
1204 {noreply, State};
1205
1206 handle_cast({release, N, Type, Pid}, State) ->
1207 State1 = process_pending(update_counts({obtain, Type}, Pid, -N, State)),
1208 {noreply, adjust_alarm(State, State1)};
1209
1210 handle_cast({close, Pid, EldestUnusedSince},
1211 State = #fhc_state { elders = Elders, clients = Clients }) ->
1212 true = case EldestUnusedSince of
1213 undefined -> ets:delete(Elders, Pid);
1214 _ -> ets:insert(Elders, {Pid, EldestUnusedSince})
1215 end,
1216 ets:update_counter(Clients, Pid, {#cstate.pending_closes, -1, 0, 0}),
1217 {noreply, adjust_alarm(State, process_pending(
1218 update_counts(open, Pid, -1, State)))};
1219
1220 handle_cast({transfer, N, FromPid, ToPid}, State) ->
1221 ok = track_client(ToPid, State#fhc_state.clients),
1222 {noreply, process_pending(
1223 update_counts({obtain, socket}, ToPid, +N,
1224 update_counts({obtain, socket}, FromPid, -N,
1225 State)))};
1226
1227 handle_cast(clear_read_cache, State) ->
1228 _ = clear_process_read_cache(),
1229 {noreply, State}.
1230
1231 handle_info(check_counts, State) ->
1232 {noreply, maybe_reduce(State #fhc_state { timer_ref = undefined })};
1233
1234 handle_info({'DOWN', _MRef, process, Pid, _Reason},
1235 State = #fhc_state { elders = Elders,
1236 open_count = OpenCount,
1237 open_pending = OpenPending,
1238 obtain_count_file = ObtainCountF,
1239 obtain_count_socket = ObtainCountS,
1240 obtain_pending_file = ObtainPendingF,
1241 obtain_pending_socket = ObtainPendingS,
1242 clients = Clients }) ->
1243 [#cstate { opened = Opened,
1244 obtained_file = ObtainedFile,
1245 obtained_socket = ObtainedSocket}] =
1246 ets:lookup(Clients, Pid),
1247 true = ets:delete(Clients, Pid),
1248 true = ets:delete(Elders, Pid),
1249 Fun = fun (#pending { pid = Pid1 }) -> Pid1 =/= Pid end,
1250 State1 = process_pending(
1251 State #fhc_state {
1252 open_count = OpenCount - Opened,
1253 open_pending = filter_pending(Fun, OpenPending),
1254 obtain_count_file = ObtainCountF - ObtainedFile,
1255 obtain_count_socket = ObtainCountS - ObtainedSocket,
1256 obtain_pending_file = filter_pending(Fun, ObtainPendingF),
1257 obtain_pending_socket = filter_pending(Fun, ObtainPendingS) }),
1258 {noreply, adjust_alarm(State, State1)}.
1259
1260 terminate(_Reason, State = #fhc_state { clients = Clients,
1261 elders = Elders }) ->
1262 ets:delete(Clients),
1263 ets:delete(Elders),
1264 State.
1265
1266 code_change(_OldVsn, State, _Extra) ->
1267 {ok, State}.
1268
1269 %%----------------------------------------------------------------------------
1270 %% pending queue abstraction helpers
1271 %%----------------------------------------------------------------------------
1272
1273 queue_fold(Fun, Init, Q) ->
1274 case queue:out(Q) of
1275 {empty, _Q} -> Init;
1276 {{value, V}, Q1} -> queue_fold(Fun, Fun(V, Init), Q1)
1277 end.
1278
1279 filter_pending(Fun, {Count, Queue}) ->
1280 {Delta, Queue1} =
1281 queue_fold(
1282 fun (Item = #pending { requested = Requested }, {DeltaN, QueueN}) ->
1283 case Fun(Item) of
1284 true -> {DeltaN, queue:in(Item, QueueN)};
1285 false -> {DeltaN - Requested, QueueN}
1286 end
1287 end, {0, queue:new()}, Queue),
1288 {Count + Delta, Queue1}.
1289
1290 pending_new() ->
1291 {0, queue:new()}.
1292
1293 pending_in(Item = #pending { requested = Requested }, {Count, Queue}) ->
1294 {Count + Requested, queue:in(Item, Queue)}.
1295
1296 pending_out({0, _Queue} = Pending) ->
1297 {empty, Pending};
1298 pending_out({N, Queue}) ->
1299 {{value, #pending { requested = Requested }} = Result, Queue1} =
1300 queue:out(Queue),
1301 {Result, {N - Requested, Queue1}}.
1302
1303 pending_count({Count, _Queue}) ->
1304 Count.
1305
1306 %%----------------------------------------------------------------------------
1307 %% server helpers
1308 %%----------------------------------------------------------------------------
1309
1310 obtain_limit(infinity) -> infinity;
1311 obtain_limit(Limit) -> case ?OBTAIN_LIMIT(Limit) of
1312 OLimit when OLimit < 0 -> 0;
1313 OLimit -> OLimit
1314 end.
1315
1316 obtain_limit_reached(socket, State) -> obtain_limit_reached(State);
1317 obtain_limit_reached(file, State) -> needs_reduce(State).
1318
1319 obtain_limit_reached(#fhc_state{obtain_limit = Limit,
1320 obtain_count_socket = Count}) ->
1321 Limit =/= infinity andalso Count >= Limit.
1322
1323 obtain_state(file, count, #fhc_state{obtain_count_file = N}) -> N;
1324 obtain_state(socket, count, #fhc_state{obtain_count_socket = N}) -> N;
1325 obtain_state(file, pending, #fhc_state{obtain_pending_file = N}) -> N;
1326 obtain_state(socket, pending, #fhc_state{obtain_pending_socket = N}) -> N.
1327
1328 set_obtain_state(file, count, N, S) -> S#fhc_state{obtain_count_file = N};
1329 set_obtain_state(socket, count, N, S) -> S#fhc_state{obtain_count_socket = N};
1330 set_obtain_state(file, pending, N, S) -> S#fhc_state{obtain_pending_file = N};
1331 set_obtain_state(socket, pending, N, S) -> S#fhc_state{obtain_pending_socket = N}.
1332
1333 adjust_alarm(OldState = #fhc_state { alarm_set = AlarmSet,
1334 alarm_clear = AlarmClear }, NewState) ->
1335 case {obtain_limit_reached(OldState), obtain_limit_reached(NewState)} of
1336 {false, true} -> AlarmSet({file_descriptor_limit, []});
1337 {true, false} -> AlarmClear(file_descriptor_limit);
1338 _ -> ok
1339 end,
1340 NewState.
1341
1342 process_pending(State = #fhc_state { limit = infinity }) ->
1343 State;
1344 process_pending(State) ->
1345 process_open(process_obtain(socket, process_obtain(file, State))).
1346
1347 process_open(State = #fhc_state { limit = Limit,
1348 open_pending = Pending}) ->
1349 {Pending1, State1} = process_pending(Pending, Limit - used(State), State),
1350 State1 #fhc_state { open_pending = Pending1 }.
1351
1352 process_obtain(socket, State = #fhc_state { limit = Limit,
1353 obtain_limit = ObtainLimit,
1354 open_count = OpenCount,
1355 obtain_count_socket = ObtainCount,
1356 obtain_pending_socket = Pending,
1357 obtain_count_file = ObtainCountF}) ->
1358 Quota = min(ObtainLimit - ObtainCount,
1359 Limit - (OpenCount + ObtainCount + ObtainCountF)),
1360 {Pending1, State1} = process_pending(Pending, Quota, State),
1361 State1#fhc_state{obtain_pending_socket = Pending1};
1362 process_obtain(file, State = #fhc_state { limit = Limit,
1363 open_count = OpenCount,
1364 obtain_count_socket = ObtainCountS,
1365 obtain_count_file = ObtainCountF,
1366 obtain_pending_file = Pending}) ->
1367 Quota = Limit - (OpenCount + ObtainCountS + ObtainCountF),
1368 {Pending1, State1} = process_pending(Pending, Quota, State),
1369 State1#fhc_state{obtain_pending_file = Pending1}.
1370
1371 process_pending(Pending, Quota, State) when Quota =< 0 ->
1372 {Pending, State};
1373 process_pending(Pending, Quota, State) ->
1374 case pending_out(Pending) of
1375 {empty, _Pending} ->
1376 {Pending, State};
1377 {{value, #pending { requested = Requested }}, _Pending1}
1378 when Requested > Quota ->
1379 {Pending, State};
1380 {{value, #pending { requested = Requested } = Item}, Pending1} ->
1381 process_pending(Pending1, Quota - Requested,
1382 run_pending_item(Item, State))
1383 end.
1384
1385 run_pending_item(#pending { kind = Kind,
1386 pid = Pid,
1387 requested = Requested,
1388 from = From },
1389 State = #fhc_state { clients = Clients }) ->
1390 gen_server2:reply(From, ok),
1391 true = ets:update_element(Clients, Pid, {#cstate.blocked, false}),
1392 update_counts(Kind, Pid, Requested, State).
1393
1394 update_counts(open, Pid, Delta,
1395 State = #fhc_state { open_count = OpenCount,
1396 clients = Clients }) ->
1397 ets:update_counter(Clients, Pid, {#cstate.opened, Delta}),
1398 State #fhc_state { open_count = OpenCount + Delta};
1399 update_counts({obtain, file}, Pid, Delta,
1400 State = #fhc_state {obtain_count_file = ObtainCountF,
1401 clients = Clients }) ->
1402 ets:update_counter(Clients, Pid, {#cstate.obtained_file, Delta}),
1403 State #fhc_state { obtain_count_file = ObtainCountF + Delta};
1404 update_counts({obtain, socket}, Pid, Delta,
1405 State = #fhc_state {obtain_count_socket = ObtainCountS,
1406 clients = Clients }) ->
1407 ets:update_counter(Clients, Pid, {#cstate.obtained_socket, Delta}),
1408 State #fhc_state { obtain_count_socket = ObtainCountS + Delta}.
1409
1410 maybe_reduce(State) ->
1411 case needs_reduce(State) of
1412 true -> reduce(State);
1413 false -> State
1414 end.
1415
1416 needs_reduce(#fhc_state { limit = Limit,
1417 open_count = OpenCount,
1418 open_pending = {OpenPending, _},
1419 obtain_limit = ObtainLimit,
1420 obtain_count_socket = ObtainCountS,
1421 obtain_count_file = ObtainCountF,
1422 obtain_pending_file = {ObtainPendingF, _},
1423 obtain_pending_socket = {ObtainPendingS, _} }) ->
1424 Limit =/= infinity
1425 andalso (((OpenCount + ObtainCountS + ObtainCountF) > Limit)
1426 orelse (OpenPending =/= 0)
1427 orelse (ObtainPendingF =/= 0)
1428 orelse (ObtainCountS < ObtainLimit
1429 andalso (ObtainPendingS =/= 0))).
1430
1431 reduce(State = #fhc_state { open_pending = OpenPending,
1432 obtain_pending_file = ObtainPendingFile,
1433 obtain_pending_socket = ObtainPendingSocket,
1434 elders = Elders,
1435 clients = Clients,
1436 timer_ref = TRef }) ->
1437 Now = time_compat:monotonic_time(),
1438 {CStates, Sum, ClientCount} =
1439 ets:foldl(fun ({Pid, Eldest}, {CStatesAcc, SumAcc, CountAcc} = Accs) ->
1440 [#cstate { pending_closes = PendingCloses,
1441 opened = Opened,
1442 blocked = Blocked } = CState] =
1443 ets:lookup(Clients, Pid),
1444 TimeDiff = time_compat:convert_time_unit(
1445 Now - Eldest, native, micro_seconds),
1446 case Blocked orelse PendingCloses =:= Opened of
1447 true -> Accs;
1448 false -> {[CState | CStatesAcc],
1449 SumAcc + TimeDiff,
1450 CountAcc + 1}
1451 end
1452 end, {[], 0, 0}, Elders),
1453 case CStates of
1454 [] -> ok;
1455 _ -> case (Sum / ClientCount) -
1456 (1000 * ?FILE_HANDLES_CHECK_INTERVAL) of
1457 AverageAge when AverageAge > 0 ->
1458 notify_age(CStates, AverageAge);
1459 _ ->
1460 notify_age0(Clients, CStates,
1461 pending_count(OpenPending) +
1462 pending_count(ObtainPendingFile) +
1463 pending_count(ObtainPendingSocket))
1464 end
1465 end,
1466 case TRef of
1467 undefined -> TRef1 = erlang:send_after(
1468 ?FILE_HANDLES_CHECK_INTERVAL, ?SERVER,
1469 check_counts),
1470 State #fhc_state { timer_ref = TRef1 };
1471 _ -> State
1472 end.
1473
1474 notify_age(CStates, AverageAge) ->
1475 lists:foreach(
1476 fun (#cstate { callback = undefined }) -> ok;
1477 (#cstate { callback = {M, F, A} }) -> apply(M, F, A ++ [AverageAge])
1478 end, CStates).
1479
1480 notify_age0(Clients, CStates, Required) ->
1481 case [CState || CState <- CStates, CState#cstate.callback =/= undefined] of
1482 [] -> ok;
1483 Notifications -> S = rand_compat:uniform(length(Notifications)),
1484 {L1, L2} = lists:split(S, Notifications),
1485 notify(Clients, Required, L2 ++ L1)
1486 end.
1487
1488 notify(_Clients, _Required, []) ->
1489 ok;
1490 notify(_Clients, Required, _Notifications) when Required =< 0 ->
1491 ok;
1492 notify(Clients, Required, [#cstate{ pid = Pid,
1493 callback = {M, F, A},
1494 opened = Opened } | Notifications]) ->
1495 apply(M, F, A ++ [0]),
1496 ets:update_element(Clients, Pid, {#cstate.pending_closes, Opened}),
1497 notify(Clients, Required - Opened, Notifications).
1498
1499 track_client(Pid, Clients) ->
1500 case ets:insert_new(Clients, #cstate { pid = Pid,
1501 callback = undefined,
1502 opened = 0,
1503 obtained_file = 0,
1504 obtained_socket = 0,
1505 blocked = false,
1506 pending_closes = 0 }) of
1507 true -> _MRef = erlang:monitor(process, Pid),
1508 ok;
1509 false -> ok
1510 end.
1511
1512
1513 %% To increase the number of file descriptors: on Windows set ERL_MAX_PORTS
1514 %% environment variable, on Linux set `ulimit -n`.
1515 ulimit() ->
1516 case proplists:get_value(max_fds, erlang:system_info(check_io)) of
1517 MaxFds when is_integer(MaxFds) andalso MaxFds > 1 ->
1518 case os:type() of
1519 {win32, _OsName} ->
1520 %% On Windows max_fds is twice the number of open files:
1521 %% https://github.com/yrashk/erlang/blob/e1282325ed75e52a98d5/erts/emulator/sys/win32/sys.c#L2459-2466
1522 MaxFds div 2;
1523 _Any ->
1524 %% For other operating systems trust Erlang.
1525 MaxFds
1526 end;
1527 _ ->
1528 unknown
1529 end.
+0
-66
deps/rabbit/src/file_handle_cache_stats.erl less more
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(file_handle_cache_stats).
17
18 %% stats about read / write operations that go through the fhc.
19
20 -export([init/0, update/3, update/2, update/1, get/0]).
21
22 -define(TABLE, ?MODULE).
23
24 -define(COUNT,
25 [io_reopen, mnesia_ram_tx, mnesia_disk_tx,
26 msg_store_read, msg_store_write,
27 queue_index_journal_write, queue_index_write, queue_index_read]).
28 -define(COUNT_TIME, [io_sync, io_seek, io_file_handle_open_attempt]).
29 -define(COUNT_TIME_BYTES, [io_read, io_write]).
30
31 init() ->
32 ets:new(?TABLE, [public, named_table]),
33 [ets:insert(?TABLE, {{Op, Counter}, 0}) || Op <- ?COUNT_TIME_BYTES,
34 Counter <- [count, bytes, time]],
35 [ets:insert(?TABLE, {{Op, Counter}, 0}) || Op <- ?COUNT_TIME,
36 Counter <- [count, time]],
37 [ets:insert(?TABLE, {{Op, Counter}, 0}) || Op <- ?COUNT,
38 Counter <- [count]].
39
40 update(Op, Bytes, Thunk) ->
41 {Time, Res} = timer_tc(Thunk),
42 ets:update_counter(?TABLE, {Op, count}, 1),
43 ets:update_counter(?TABLE, {Op, bytes}, Bytes),
44 ets:update_counter(?TABLE, {Op, time}, Time),
45 Res.
46
47 update(Op, Thunk) ->
48 {Time, Res} = timer_tc(Thunk),
49 ets:update_counter(?TABLE, {Op, count}, 1),
50 ets:update_counter(?TABLE, {Op, time}, Time),
51 Res.
52
53 update(Op) ->
54 ets:update_counter(?TABLE, {Op, count}, 1),
55 ok.
56
57 get() ->
58 lists:sort(ets:tab2list(?TABLE)).
59
60 timer_tc(Thunk) ->
61 T1 = time_compat:monotonic_time(),
62 Res = Thunk(),
63 T2 = time_compat:monotonic_time(),
64 Diff = time_compat:convert_time_unit(T2 - T1, native, micro_seconds),
65 {Diff, Res}.
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(gatherer).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(gm).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(mnesia_sync).
+0
-111
deps/rabbit/src/rabbit.app.src less more
0 {application, rabbit, %% -*- erlang -*-
1 [{description, "RabbitMQ"},
2 {id, "RabbitMQ"},
3 {vsn, "3.6.6"},
4 {modules, []},
5 {registered, [rabbit_amqqueue_sup,
6 rabbit_log,
7 rabbit_node_monitor,
8 rabbit_router,
9 rabbit_sup,
10 rabbit_direct_client_sup]},
11 {applications, [kernel, stdlib, sasl, mnesia, rabbit_common, ranch, os_mon, xmerl]},
12 %% we also depend on crypto, public_key and ssl but they shouldn't be
13 %% in here as we don't actually want to start it
14 {mod, {rabbit, []}},
15 {env, [{tcp_listeners, [5672]},
16 {num_tcp_acceptors, 10},
17 {ssl_listeners, []},
18 {num_ssl_acceptors, 1},
19 {ssl_options, []},
20 {vm_memory_high_watermark, 0.4},
21 {vm_memory_high_watermark_paging_ratio, 0.5},
22 {memory_monitor_interval, 2500},
23 {disk_free_limit, 50000000}, %% 50MB
24 {msg_store_index_module, rabbit_msg_store_ets_index},
25 {backing_queue_module, rabbit_variable_queue},
26 %% 0 ("no limit") would make a better default, but that
27 %% breaks the QPid Java client
28 {frame_max, 131072},
29 {channel_max, 0},
30 {heartbeat, 60},
31 {msg_store_file_size_limit, 16777216},
32 {fhc_write_buffering, true},
33 {fhc_read_buffering, false},
34 {queue_index_max_journal_entries, 32768},
35 {queue_index_embed_msgs_below, 4096},
36 {default_user, <<"guest">>},
37 {default_pass, <<"guest">>},
38 {default_user_tags, [administrator]},
39 {default_vhost, <<"/">>},
40 {default_permissions, [<<".*">>, <<".*">>, <<".*">>]},
41 {loopback_users, [<<"guest">>]},
42 {password_hashing_module, rabbit_password_hashing_sha256},
43 {cluster_nodes, {[], disc}},
44 {server_properties, []},
45 {collect_statistics, none},
46 {collect_statistics_interval, 5000},
47 {mnesia_table_loading_timeout, 30000},
48 {auth_mechanisms, ['PLAIN', 'AMQPLAIN']},
49 {auth_backends, [rabbit_auth_backend_internal]},
50 {delegate_count, 16},
51 {trace_vhosts, []},
52 {log_levels, [{connection, info}]},
53 {ssl_cert_login_from, distinguished_name},
54 {ssl_handshake_timeout, 5000},
55 {ssl_allow_poodle_attack, false},
56 {handshake_timeout, 10000},
57 {reverse_dns_lookups, false},
58 {cluster_partition_handling, ignore},
59 {cluster_keepalive_interval, 10000},
60 {tcp_listen_options, [{backlog, 128},
61 {nodelay, true},
62 {linger, {true, 0}},
63 {exit_on_close, false}]},
64 {halt_on_upgrade_failure, true},
65 {hipe_compile, false},
66 %% see bug 24513 for how this list was created
67 {hipe_modules,
68 [rabbit_reader, rabbit_channel, gen_server2, rabbit_exchange,
69 rabbit_command_assembler, rabbit_framing_amqp_0_9_1, rabbit_basic,
70 rabbit_event, lists, queue, priority_queue, rabbit_router,
71 rabbit_trace, rabbit_misc, rabbit_binary_parser,
72 rabbit_exchange_type_direct, rabbit_guid, rabbit_net,
73 rabbit_amqqueue_process, rabbit_variable_queue,
74 rabbit_binary_generator, rabbit_writer, delegate, gb_sets, lqueue,
75 sets, orddict, rabbit_amqqueue, rabbit_limiter, gb_trees,
76 rabbit_queue_index, rabbit_exchange_decorator, gen, dict, ordsets,
77 file_handle_cache, rabbit_msg_store, array,
78 rabbit_msg_store_ets_index, rabbit_msg_file,
79 rabbit_exchange_type_fanout, rabbit_exchange_type_topic, mnesia,
80 mnesia_lib, rpc, mnesia_tm, qlc, sofs, proplists, credit_flow,
81 pmon, ssl_connection, tls_connection, ssl_record, tls_record,
82 gen_fsm, ssl]},
83 {ssl_apps, [asn1, crypto, public_key, ssl]},
84 %% see rabbitmq-server#114
85 {mirroring_flow_control, true},
86 {mirroring_sync_batch_size, 4096},
87 %% see rabbitmq-server#227 and related tickets.
88 %% msg_store_credit_disc_bound only takes effect when
89 %% messages are persisted to the message store. If messages
90 %% are embedded on the queue index, then modifying this
91 %% setting has no effect because credit_flow is not used when
92 %% writing to the queue index. See the setting
93 %% queue_index_embed_msgs_below above.
94 {msg_store_credit_disc_bound, {2000, 500}},
95 {msg_store_io_batch_size, 2048},
96 %% see rabbitmq-server#143
97 %% and rabbitmq-server#949
98 {credit_flow_default_credit, {200, 100}},
99 %% see rabbitmq-server#248
100 %% and rabbitmq-server#667
101 {channel_operation_timeout, 15000},
102 {config_entry_decoder, [
103 {cipher, aes_cbc256},
104 {hash, sha512},
105 {iterations, 1000},
106 {passphrase, undefined}
107 ]},
108 %% rabbitmq-server-973
109 {lazy_queue_explicit_gc_run_operation_threshold, 250}
110 ]}]}.
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit).
2121 stop_and_halt/0, await_startup/0, status/0, is_running/0,
2222 is_running/1, environment/0, rotate_logs/1, force_event_refresh/1,
2323 start_fhc/0]).
24 -export([start/2, stop/1, prep_stop/1]).
24 -export([start/2, stop/1]).
2525 -export([start_apps/1, stop_apps/1]).
2626 -export([log_location/1, config_files/0, decrypt_config/2]). %% for testing and mgmt-agent
2727
8383 [rabbit_registry]}},
8484 {requires, external_infrastructure},
8585 {enables, kernel_ready}]}).
86
87 -rabbit_boot_step({rabbit_core_metrics,
88 [{description, "core metrics storage"},
89 {mfa, {rabbit_sup, start_child,
90 [rabbit_metrics]}},
91 {requires, pre_boot},
92 {enables, external_infrastructure}]}).
8693
8794 -rabbit_boot_step({rabbit_event,
8895 [{description, "statistics event manager"},
184191 [{description, "background garbage collection"},
185192 {mfa, {rabbit_sup, start_restartable_child,
186193 [background_gc]}},
194 {enables, networking}]}).
195
196 -rabbit_boot_step({rabbit_core_metrics_gc,
197 [{description, "background core metrics garbage collection"},
198 {mfa, {rabbit_sup, start_restartable_child,
199 [rabbit_core_metrics_gc]}},
187200 {enables, networking}]}).
188201
189202 %%---------------------------------------------------------------------------
347360 use_stdio, out]).
348361
349362 sd_notify_socat(Unit) ->
350 case sd_open_port() of
351 {'EXIT', Exit} ->
352 io:format(standard_error, "Failed to start socat ~p~n", [Exit]),
353 false;
363 try sd_open_port() of
354364 Port ->
355365 Port ! {self(), {command, sd_notify_data()}},
356366 Result = sd_wait_activation(Port, Unit),
357367 port_close(Port),
358368 Result
369 catch
370 Class:Reason ->
371 io:format(standard_error, "Failed to start socat ~p:~p~n", [Class, Reason]),
372 false
359373 end.
360374
361375 sd_current_unit() ->
419433 stop() ->
420434 case whereis(rabbit_boot) of
421435 undefined -> ok;
422 _ -> await_startup(true)
436 _ ->
437 rabbit_log:info("RabbitMQ hasn't finished starting yet. Waiting for startup to finish before stopping..."),
438 wait_for_boot_to_finish()
423439 end,
424 rabbit_log:info("Stopping RabbitMQ~n", []),
440 rabbit_log:info("RabbitMQ is asked to stop...~n", []),
425441 Apps = ?APPS ++ rabbit_plugins:active(),
426442 stop_apps(app_utils:app_dependency_order(Apps, true)),
427 rabbit_log:info("Stopped RabbitMQ application~n", []).
443 rabbit_log:info("Successfully stopped RabbitMQ and its dependencies~n", []).
428444
429445 stop_and_halt() ->
430446 try
431447 stop()
448 catch Type:Reason ->
449 rabbit_log:error("Error trying to stop RabbitMQ: ~p:~p", [Type, Reason]),
450 error({Type, Reason})
432451 after
433 rabbit_log:info("Halting Erlang VM~n", []),
434 %% Also duplicate this information to stderr, so console where
435 %% foreground broker was running (or systemd journal) will
436 %% contain information about graceful termination.
437 io:format(standard_error, "Gracefully halting Erlang VM~n", []),
438 init:stop()
452 %% Enclose all the logging in the try block.
453 %% init:stop() will be called regardless of any errors.
454 try
455 AppsLeft = [ A || {A, _, _} <- application:which_applications() ],
456 rabbit_log:info(
457 lists:flatten(["Halting Erlang VM with the following applications:~n",
458 [" ~p~n" || _ <- AppsLeft]]),
459 AppsLeft),
460 %% Also duplicate this information to stderr, so console where
461 %% foreground broker was running (or systemd journal) will
462 %% contain information about graceful termination.
463 io:format(standard_error, "Gracefully halting Erlang VM~n", [])
464 after
465 init:stop()
466 end
439467 end,
440468 ok.
441469
452480 prompt ->
453481 IoDevice = get_input_iodevice(),
454482 io:setopts(IoDevice, [{echo, false}]),
455 PP = lists:droplast(io:get_line(IoDevice,
483 PP = rabbit_misc:lists_droplast(io:get_line(IoDevice,
456484 "\nPlease enter the passphrase to unlock encrypted "
457485 "configuration entries.\n\nPassphrase: ")),
458486 io:setopts(IoDevice, [{echo, true}]),
554582 decrypt_list(Tail, Algo, [decrypt(Value, Algo)|Acc]).
555583
556584 stop_apps(Apps) ->
585 rabbit_log:info(
586 lists:flatten(["Stopping RabbitMQ applications and their dependencies in the following order: ~n",
587 [" ~p~n" || _ <- Apps]]),
588 lists:reverse(Apps)),
557589 ok = app_utils:stop_applications(
558590 Apps, handle_app_error(error_during_shutdown)),
559591 case lists:member(rabbit, Apps) of
571603 end.
572604
573605 await_startup() ->
574 await_startup(false).
575
576 await_startup(HaveSeenRabbitBoot) ->
577 %% We don't take absence of rabbit_boot as evidence we've started,
578 %% since there's a small window before it is registered.
606 case is_booting() of
607 true -> wait_for_boot_to_finish();
608 false ->
609 case is_running() of
610 true -> ok;
611 false -> wait_for_boot_to_start(),
612 wait_for_boot_to_finish()
613 end
614 end.
615
616 is_booting() ->
617 whereis(rabbit_boot) /= undefined.
618
619 wait_for_boot_to_start() ->
579620 case whereis(rabbit_boot) of
580 undefined -> case HaveSeenRabbitBoot orelse is_running() of
581 true -> ok;
582 false -> timer:sleep(100),
583 await_startup(false)
584 end;
621 undefined -> timer:sleep(100),
622 wait_for_boot_to_start();
623 _ -> ok
624 end.
625
626 wait_for_boot_to_finish() ->
627 case whereis(rabbit_boot) of
628 undefined -> true = is_running(),
629 ok;
585630 _ -> timer:sleep(100),
586 await_startup(true)
631 wait_for_boot_to_finish()
587632 end.
588633
589634 status() ->
689734 Error
690735 end.
691736
692 prep_stop(_State) ->
737 stop(_State) ->
693738 ok = rabbit_alarm:stop(),
694739 ok = case rabbit_mnesia:is_clustered() of
695740 true -> ok;
696741 false -> rabbit_table:clear_ram_only_tables()
697742 end,
698743 ok.
699
700 stop(_) -> ok.
701744
702745 -spec boot_error(term(), not_available | [tuple()]) -> no_return().
703746
770813 {ok, DefaultVHost} = application:get_env(default_vhost),
771814 {ok, [DefaultConfigurePerm, DefaultWritePerm, DefaultReadPerm]} =
772815 application:get_env(default_permissions),
773 ok = rabbit_vhost:add(DefaultVHost),
774 ok = rabbit_auth_backend_internal:add_user(DefaultUser, DefaultPass),
775 ok = rabbit_auth_backend_internal:set_tags(DefaultUser, DefaultTags),
776 ok = rabbit_auth_backend_internal:set_permissions(DefaultUser,
777 DefaultVHost,
778 DefaultConfigurePerm,
779 DefaultWritePerm,
780 DefaultReadPerm),
816
817 DefaultUserBin = rabbit_data_coercion:to_binary(DefaultUser),
818 DefaultPassBin = rabbit_data_coercion:to_binary(DefaultPass),
819 DefaultVHostBin = rabbit_data_coercion:to_binary(DefaultVHost),
820 DefaultConfigurePermBin = rabbit_data_coercion:to_binary(DefaultConfigurePerm),
821 DefaultWritePermBin = rabbit_data_coercion:to_binary(DefaultWritePerm),
822 DefaultReadPermBin = rabbit_data_coercion:to_binary(DefaultReadPerm),
823
824 ok = rabbit_vhost:add(DefaultVHostBin),
825 ok = rabbit_auth_backend_internal:add_user(
826 DefaultUserBin,
827 DefaultPassBin
828 ),
829 ok = rabbit_auth_backend_internal:set_tags(DefaultUserBin,DefaultTags),
830 ok = rabbit_auth_backend_internal:set_permissions(DefaultUserBin,
831 DefaultVHostBin,
832 DefaultConfigurePermBin,
833 DefaultWritePermBin,
834 DefaultReadPermBin),
781835 ok.
782836
783837 %%---------------------------------------------------------------------------
894948 end.
895949
896950 print_banner() ->
897 {ok, Product} = application:get_key(id),
951 {ok, Product} = application:get_key(description),
898952 {ok, Version} = application:get_key(vsn),
899953 io:format("~n ~s ~s. ~s"
900954 "~n ## ## ~s"
11091163 end,
11101164 TestPid = spawn_link(TestFun),
11111165 %% Because we are waiting for the test fun, abuse the
1112 %% 'mnesia_table_loading_timeout' parameter to find a sane timeout
1166 %% 'mnesia_table_loading_retry_timeout' parameter to find a sane timeout
11131167 %% value.
1114 Timeout = rabbit_table:wait_timeout(),
1168 Timeout = rabbit_table:retry_timeout(),
11151169 receive
11161170 fhc_ok -> ok;
11171171 {'EXIT', TestPid, Exception} -> throw({ensure_working_fhc, Exception})
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_access_control).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515 %% There are two types of alarms handled by this module:
1616 %%
1717 %% * per-node resource (disk, memory) alarms for the whole cluster. If any node
18 %% has an alarm, then all publishing should be disabled througout the
18 %% has an alarm, then all publishing should be disabled across the
1919 %% cluster until all alarms clear. When a node sets such an alarm,
2020 %% this information is automatically propagated throughout the cluster.
2121 %% `#alarms.alarmed_nodes' is being used to track this type of alarms.
127127
128128 handle_event({set_alarm, {{resource_limit, Source, Node}, []}}, State) ->
129129 case is_node_alarmed(Source, Node, State) of
130 true -> {ok, State};
131 false -> handle_set_resource_alarm(Source, Node, State)
130 true ->
131 {ok, State};
132 false ->
133 rabbit_event:notify(alarm_set, [{source, Source},
134 {node, Node}]),
135 handle_set_resource_alarm(Source, Node, State)
132136 end;
133137 handle_event({set_alarm, Alarm}, State = #alarms{alarms = Alarms}) ->
134138 case lists:member(Alarm, Alarms) of
140144 handle_event({clear_alarm, {resource_limit, Source, Node}}, State) ->
141145 case is_node_alarmed(Source, Node, State) of
142146 true ->
147 rabbit_event:notify(alarm_cleared, [{source, Source},
148 {node, Node}]),
143149 handle_clear_resource_alarm(Source, Node, State);
144150 false ->
145151 {ok, State}
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_amqqueue_process).
100100 %%----------------------------------------------------------------------------
101101
102102 -define(STATISTICS_KEYS,
103 [name,
103 [messages_ready,
104 messages_unacknowledged,
105 messages,
106 reductions,
107 name,
104108 policy,
105109 exclusive_consumer_pid,
106110 exclusive_consumer_tag,
107 messages_ready,
108 messages_unacknowledged,
109 messages,
110111 consumers,
111112 consumer_utilisation,
112113 memory,
114115 synchronised_slave_pids,
115116 recoverable_slaves,
116117 state,
117 reductions,
118118 garbage_collection
119119 ]).
120120
937937
938938 emit_stats(State, Extra) ->
939939 ExtraKs = [K || {K, _} <- Extra],
940 Infos = [{K, V} || {K, V} <- infos(statistics_keys(), State),
941 not lists:member(K, ExtraKs)],
942 rabbit_event:notify(queue_stats, Extra ++ Infos).
940 [{messages_ready, MR}, {messages_unacknowledged, MU}, {messages, M},
941 {reductions, R}, {name, Name} | Infos] = All
942 = [{K, V} || {K, V} <- infos(statistics_keys(), State),
943 not lists:member(K, ExtraKs)],
944 rabbit_core_metrics:queue_stats(Name, Extra ++ Infos),
945 rabbit_core_metrics:queue_stats(Name, MR, MU, M, R),
946 rabbit_event:notify(queue_stats, Extra ++ All).
943947
944948 emit_consumer_created(ChPid, CTag, Exclusive, AckRequired, QName,
945949 PrefetchCount, Args, Ref) ->
954958 Ref).
955959
956960 emit_consumer_deleted(ChPid, ConsumerTag, QName) ->
961 rabbit_core_metrics:consumer_deleted(ChPid, ConsumerTag, QName),
957962 rabbit_event:notify(consumer_deleted,
958963 [{consumer_tag, ConsumerTag},
959964 {channel, ChPid},
10841089 has_had_consumers = true,
10851090 exclusive_consumer = ExclusiveConsumer},
10861091 ok = maybe_send_reply(ChPid, OkMsg),
1092 QName = qname(State1),
1093 AckRequired = not NoAck,
1094 rabbit_core_metrics:consumer_created(
1095 ChPid, ConsumerTag, ExclusiveConsume, AckRequired, QName,
1096 PrefetchCount, Args),
10871097 emit_consumer_created(ChPid, ConsumerTag, ExclusiveConsume,
1088 not NoAck, qname(State1),
1089 PrefetchCount, Args, none),
1098 AckRequired, QName, PrefetchCount,
1099 Args, none),
10901100 notify_decorators(State1),
10911101 reply(ok, run_message_queue(State1))
10921102 end;
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_amqqueue_sup).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_amqqueue_sup_sup).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_auth_mechanism_amqplain).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_auth_mechanism_cr_demo).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_auth_mechanism_plain).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_autoheal).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_binding).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_channel_sup).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_channel_sup_sup).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_cli).
153153 start_distribution_anon(TriesLeft - 1, Reason)
154154 end.
155155
156 %% Tries to start distribution with random name choosen from limited list of candidates - to
156 %% Tries to start distribution with random name chosen from limited list of candidates - to
157157 %% prevent atom table pollution on target nodes.
158158 start_distribution() ->
159159 rabbit_nodes:ensure_epmd(),
276276 {ok, Value};
277277 _ ->
278278 Names = [ [$', N, $'] || {N, _} <- PresentFlags ],
279 CommaSeparated = string:join(lists:droplast(Names), ", "),
279 CommaSeparated = string:join(rabbit_misc:lists_droplast(Names), ", "),
280280 AndOneMore = lists:last(Names),
281281 Msg = io_lib:format("Options ~s and ~s are mutually exclusive", [CommaSeparated, AndOneMore]),
282282 {error, lists:flatten(Msg)}
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_client_sup).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_connection_helper_sup).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_connection_sup).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_control_main).
3030
3131 -define(COMMANDS,
3232 [stop,
33 shutdown,
3334 stop_app,
3435 start_app,
3536 wait,
6869 {set_parameter, [?VHOST_DEF]},
6970 {clear_parameter, [?VHOST_DEF]},
7071 {list_parameters, [?VHOST_DEF]},
72
73 set_global_parameter,
74 clear_global_parameter,
75 list_global_parameters,
7176
7277 {set_policy, [?VHOST_DEF, ?PRIORITY_DEF, ?APPLY_TO_DEF]},
7378 {clear_policy, [?VHOST_DEF]},
111116 {"Parameters", rabbit_runtime_parameters, list_formatted, info_keys}]).
112117
113118 -define(COMMANDS_NOT_REQUIRING_APP,
114 [stop, stop_app, start_app, wait, reset, force_reset, rotate_logs,
119 [stop, shutdown, stop_app, start_app, wait, reset, force_reset, rotate_logs,
115120 join_cluster, change_cluster_node_type, update_cluster_nodes,
116121 forget_cluster_node, rename_cluster_node, cluster_status, status,
117122 environment, eval, force_boot, help, hipe_compile, encode]).
120125 -define(COMMANDS_WITH_TIMEOUT,
121126 [list_user_permissions, list_policies, list_queues, list_exchanges,
122127 list_bindings, list_connections, list_channels, list_consumers,
123 list_vhosts, list_parameters,
128 list_vhosts, list_parameters, list_global_parameters,
124129 purge_queue,
125130 {node_health_check, 70000}]).
126131
150155 Inform = case Quiet of
151156 true -> fun (_Format, _Args1) -> ok end;
152157 false -> fun (Format, Args1) ->
153 io:format(Format ++ " ...~n", Args1)
158 io:format(Format ++ "~n", Args1)
154159 end
155160 end,
156161 try
264269 action(Command, Node, Args, Opts, Inform)
265270 end.
266271
272 shutdown_node_and_wait_pid_to_stop(Node, Pid, Inform) ->
273 Inform("Shutting down RabbitMQ node ~p running at PID ~s", [Node, Pid]),
274 Res = call(Node, {rabbit, stop_and_halt, []}),
275 case Res of
276 ok ->
277 Inform("Waiting for PID ~s to terminate", [Pid]),
278 wait_for_process_death(Pid),
279 Inform(
280 "RabbitMQ node ~p running at PID ~s successfully shut down",
281 [Node, Pid]);
282 _ -> ok
283 end,
284 Res.
285
286 action(shutdown, Node, [], _Opts, Inform) ->
287 case rpc:call(Node, os, getpid, []) of
288 Pid when is_list(Pid) ->
289 shutdown_node_and_wait_pid_to_stop(Node, Pid, Inform);
290 Error -> Error
291 end;
292
267293 action(stop, Node, Args, _Opts, Inform) ->
268294 Inform("Stopping and halting node ~p", [Node]),
269295 Res = call(Node, {rabbit, stop_and_halt, []}),
276302 Res;
277303
278304 action(stop_app, Node, [], _Opts, Inform) ->
279 Inform("Stopping node ~p", [Node]),
305 Inform("Stopping rabbit application on node ~p", [Node]),
280306 call(Node, {rabbit, stop, []});
281307
282308 action(start_app, Node, [], _Opts, Inform) ->
285311
286312 action(reset, Node, [], _Opts, Inform) ->
287313 Inform("Resetting node ~p", [Node]),
288 require_mnesia_stopped(Node,
314 require_mnesia_stopped(Node,
289315 fun() ->
290316 call(Node, {rabbit_mnesia, reset, []})
291317 end);
292318
293319 action(force_reset, Node, [], _Opts, Inform) ->
294320 Inform("Forcefully resetting node ~p", [Node]),
295 require_mnesia_stopped(Node,
321 require_mnesia_stopped(Node,
296322 fun() ->
297323 call(Node, {rabbit_mnesia, force_reset, []})
298324 end);
304330 false -> disc
305331 end,
306332 Inform("Clustering node ~p with ~p", [Node, ClusterNode]),
307 require_mnesia_stopped(Node,
333 require_mnesia_stopped(Node,
308334 fun() ->
309335 rpc_call(Node, rabbit_mnesia, join_cluster, [ClusterNode, NodeType])
310336 end);
311337
312338 action(change_cluster_node_type, Node, ["ram"], _Opts, Inform) ->
313339 Inform("Turning ~p into a ram node", [Node]),
314 require_mnesia_stopped(Node,
340 require_mnesia_stopped(Node,
315341 fun() ->
316342 rpc_call(Node, rabbit_mnesia, change_cluster_node_type, [ram])
317343 end);
318344 action(change_cluster_node_type, Node, [Type], _Opts, Inform)
319345 when Type =:= "disc" orelse Type =:= "disk" ->
320346 Inform("Turning ~p into a disc node", [Node]),
321 require_mnesia_stopped(Node,
347 require_mnesia_stopped(Node,
322348 fun() ->
323349 rpc_call(Node, rabbit_mnesia, change_cluster_node_type, [disc])
324350 end);
326352 action(update_cluster_nodes, Node, [ClusterNodeS], _Opts, Inform) ->
327353 ClusterNode = list_to_atom(ClusterNodeS),
328354 Inform("Updating cluster nodes for ~p from ~p", [Node, ClusterNode]),
329 require_mnesia_stopped(Node,
355 require_mnesia_stopped(Node,
330356 fun() ->
331357 rpc_call(Node, rabbit_mnesia, update_cluster_nodes, [ClusterNode])
332358 end);
491517 _ -> Arg
492518 end),
493519 Inform("Setting disk free limit on ~p to ~p of total RAM", [Node, Frac]),
494 rpc_call(Node,
495 rabbit_disk_monitor,
496 set_disk_free_limit,
520 rpc_call(Node,
521 rabbit_disk_monitor,
522 set_disk_free_limit,
497523 [{mem_relative, Frac}]);
498524
499525
525551 rpc_call(Node, rabbit_runtime_parameters, clear, [VHostArg,
526552 list_to_binary(Component),
527553 list_to_binary(Key)]);
554
555 action(set_global_parameter, Node, [Key, Value], _Opts, Inform) ->
556 Inform("Setting global runtime parameter ~p to ~p", [Key, Value]),
557 rpc_call(
558 Node, rabbit_runtime_parameters, parse_set_global,
559 [rabbit_data_coercion:to_atom(Key), rabbit_data_coercion:to_binary(Value)]
560 );
561
562 action(clear_global_parameter, Node, [Key], _Opts, Inform) ->
563 Inform("Clearing global runtime parameter ~p", [Key]),
564 rpc_call(
565 Node, rabbit_runtime_parameters, clear_global,
566 [rabbit_data_coercion:to_atom(Key)]
567 );
528568
529569 action(set_policy, Node, [Key, Pattern, Defn], Opts, Inform) ->
530570 Msg = "Setting policy ~p for pattern ~p to ~p with priority ~p",
621661 Inform("Listing runtime parameters", []),
622662 call(Node, {rabbit_runtime_parameters, list_formatted, [VHostArg]},
623663 rabbit_runtime_parameters:info_keys(), Timeout);
664
665 action(list_global_parameters, Node, [], _Opts, Inform, Timeout) ->
666 Inform("Listing global runtime parameters", []),
667 call(Node, {rabbit_runtime_parameters, list_global_formatted, []},
668 rabbit_runtime_parameters:global_info_keys(), Timeout);
624669
625670 action(list_policies, Node, [], Opts, Inform, Timeout) ->
626671 VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)),
938983 escape(L, false) when is_list(L) ->
939984 escape_char(lists:reverse(L), []);
940985 escape(L, true) when is_list(L) ->
941 L.
986 L.
942987
943988 escape_char([$\\ | T], Acc) ->
944989 escape_char(T, [$\\, $\\ | Acc]);
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_control_pbe).
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15 -module(rabbit_core_metrics_gc).
16
17 -record(state, {timer,
18 interval
19 }).
20
21 -spec start_link() -> rabbit_types:ok_pid_or_error().
22
23 -export([start_link/0]).
24 -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
25 code_change/3]).
26
27 start_link() ->
28 gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
29
30 init(_) ->
31 Interval = rabbit_misc:get_env(rabbit, core_metrics_gc_interval, 120000),
32 {ok, start_timer(#state{interval = Interval})}.
33
34 handle_call(test, _From, State) ->
35 {reply, ok, State}.
36
37 handle_cast(_Request, State) ->
38 {noreply, State}.
39
40 handle_info(start_gc, State) ->
41 gc_connections(),
42 gc_channels(),
43 gc_queues(),
44 gc_exchanges(),
45 gc_nodes(),
46 gc_gen_server2(),
47 {noreply, start_timer(State)}.
48
49 terminate(_Reason, #state{timer = TRef}) ->
50 erlang:cancel_timer(TRef),
51 ok.
52
53 code_change(_OldVsn, State, _Extra) ->
54 {ok, State}.
55
56 start_timer(#state{interval = Interval} = St) ->
57 TRef = erlang:send_after(Interval, self(), start_gc),
58 St#state{timer = TRef}.
59
60 gc_connections() ->
61 gc_process(connection_created),
62 gc_process(connection_metrics),
63 gc_process(connection_coarse_metrics).
64
65 gc_channels() ->
66 gc_process(channel_created),
67 gc_process(channel_metrics),
68 gc_process(channel_process_metrics),
69 ok.
70
71 gc_queues() ->
72 Queues = rabbit_amqqueue:list_names(),
73 GbSet = gb_sets:from_list(Queues),
74 gc_entity(queue_metrics, GbSet),
75 gc_entity(queue_coarse_metrics, GbSet),
76 gc_process_and_entity(channel_queue_metrics, GbSet),
77 gc_process_and_entity(consumer_created, GbSet),
78 ExchangeGbSet = gb_sets:from_list(rabbit_exchange:list_names()),
79 gc_process_and_entities(channel_queue_exchange_metrics, GbSet, ExchangeGbSet).
80
81 gc_exchanges() ->
82 Exchanges = rabbit_exchange:list_names(),
83 GbSet = gb_sets:from_list(Exchanges),
84 gc_process_and_entity(channel_exchange_metrics, GbSet).
85
86 gc_nodes() ->
87 Nodes = rabbit_mnesia:cluster_nodes(all),
88 GbSet = gb_sets:from_list(Nodes),
89 gc_entity(node_node_metrics, GbSet).
90
91 gc_gen_server2() ->
92 gc_process(gen_server2_metrics).
93
94 gc_process(Table) ->
95 ets:foldl(fun({Pid = Key, _}, none) ->
96 gc_process(Pid, Table, Key);
97 ({Pid = Key, _, _, _, _}, none) ->
98 gc_process(Pid, Table, Key);
99 ({Pid = Key, _, _, _}, none) ->
100 gc_process(Pid, Table, Key)
101 end, none, Table).
102
103 gc_process(Pid, Table, Key) ->
104 case rabbit_misc:is_process_alive(Pid) of
105 true ->
106 none;
107 false ->
108 ets:delete(Table, Key),
109 none
110 end.
111
112 gc_entity(Table, GbSet) ->
113 ets:foldl(fun({{_, Id} = Key, _}, none) ->
114 gc_entity(Id, Table, Key, GbSet);
115 ({Id = Key, _}, none) ->
116 gc_entity(Id, Table, Key, GbSet);
117 ({Id = Key, _, _}, none) ->
118 gc_entity(Id, Table, Key, GbSet);
119 ({Id = Key, _, _, _, _}, none) ->
120 gc_entity(Id, Table, Key, GbSet)
121 end, none, Table).
122
123 gc_entity(Id, Table, Key, GbSet) ->
124 case gb_sets:is_member(Id, GbSet) of
125 true ->
126 none;
127 false ->
128 ets:delete(Table, Key),
129 none
130 end.
131
132 gc_process_and_entity(Table, GbSet) ->
133 ets:foldl(fun({{Pid, Id} = Key, _, _, _, _, _, _, _}, none)
134 when Table == channel_queue_metrics ->
135 gc_process_and_entity(Id, Pid, Table, Key, GbSet);
136 ({{Pid, Id} = Key, _, _, _, _}, none)
137 when Table == channel_exchange_metrics ->
138 gc_process_and_entity(Id, Pid, Table, Key, GbSet);
139 ({{Id, Pid, _} = Key, _, _, _, _}, none)
140 when Table == consumer_created ->
141 gc_process_and_entity(Id, Pid, Table, Key, GbSet);
142 ({{{Pid, Id}, _} = Key, _, _, _, _}, none) ->
143 gc_process_and_entity(Id, Pid, Table, Key, GbSet)
144 end, none, Table).
145
146 gc_process_and_entity(Id, Pid, Table, Key, GbSet) ->
147 case rabbit_misc:is_process_alive(Pid) andalso gb_sets:is_member(Id, GbSet) of
148 true ->
149 none;
150 false ->
151 ets:delete(Table, Key),
152 none
153 end.
154
155 gc_process_and_entities(Table, QueueGbSet, ExchangeGbSet) ->
156 ets:foldl(fun({{Pid, {Q, X}} = Key, _, _}, none) ->
157 gc_process(Pid, Table, Key),
158 gc_entity(Q, Table, Key, QueueGbSet),
159 gc_entity(X, Table, Key, ExchangeGbSet)
160 end, none, Table).
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_credential_validation).
17
18 -include("rabbit.hrl").
19
20 %% used for backwards compatibility
21 -define(DEFAULT_BACKEND, rabbit_credential_validator_accept_everything).
22
23 %%
24 %% API
25 %%
26
27 -export([validate/2, backend/0]).
28
29 -spec validate(rabbit_types:username(), rabbit_types:password()) -> 'ok' | {'error', string()}.
30
31 %% Validates a username/password pair by delegating to the effective
32 %% `rabbit_credential_validator`. Used by `rabbit_auth_backend_internal`.
33 %% Note that some validators may choose to only validate passwords.
34 %%
35 %% Possible return values:
36 %%
37 %% * ok: provided credentials passed validation.
38 %% * {error, Error, Args}: provided password password failed validation.
39
40 validate(Username, Password) ->
41 Backend = backend(),
42 Backend:validate(Username, Password).
43
44 -spec backend() -> atom().
45
46 backend() ->
47 case application:get_env(rabbit, credential_validator) of
48 undefined ->
49 ?DEFAULT_BACKEND;
50 {ok, Proplist} ->
51 proplists:get_value(validation_backend, Proplist, ?DEFAULT_BACKEND)
52 end.
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_credential_validator).
17
18 -include("rabbit.hrl").
19
20 %% Validates a password. Used by `rabbit_auth_backend_internal`.
21 %%
22 %% Possible return values:
23 %%
24 %% * ok: provided password passed validation.
25 %% * {error, Error, Args}: provided password password failed validation.
26
27 -callback validate(rabbit_types:username(), rabbit_types:password()) -> 'ok' | {'error', string()}.
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_credential_validator_accept_everything).
17
18 -include("rabbit.hrl").
19
20 -behaviour(rabbit_credential_validator).
21
22 %%
23 %% API
24 %%
25
26 -export([validate/2]).
27
28 -spec validate(rabbit_types:username(), rabbit_types:password()) -> 'ok' | {'error', string()}.
29
30 validate(_Username, _Password) ->
31 ok.
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_credential_validator_min_password_length).
17
18 -include("rabbit.hrl").
19
20 -behaviour(rabbit_credential_validator).
21
22 %% accommodates default (localhost-only) user credentials,
23 %% guest/guest
24 -define(DEFAULT_MIN_LENGTH, 5).
25
26 %%
27 %% API
28 %%
29
30 -export([validate/2]).
31 %% for tests
32 -export([validate/3]).
33
34 -spec validate(rabbit_types:username(), rabbit_types:password()) -> 'ok' | {'error', string()}.
35
36 validate(Username, Password) ->
37 MinLength = case application:get_env(rabbit, credential_validator) of
38 undefined ->
39 ?DEFAULT_MIN_LENGTH;
40 {ok, Proplist} ->
41 case proplists:get_value(min_length, Proplist) of
42 undefined -> ?DEFAULT_MIN_LENGTH;
43 Value -> rabbit_data_coercion:to_integer(Value)
44 end
45 end,
46 validate(Username, Password, MinLength).
47
48
49 -spec validate(rabbit_types:username(), rabbit_types:password(), integer()) -> 'ok' | {'error', string(), [any()]}.
50
51 validate(_Username, Password, MinLength) ->
52 case size(Password) >= MinLength of
53 true -> ok;
54 false -> {error, rabbit_misc:format("minimum required password length is ~B", [MinLength])}
55 end.
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15
16
17 %% A `rabbit_credential_validator` implementation that matches
18 %% password against a pre-configured regular expression.
19 -module(rabbit_credential_validator_password_regexp).
20
21 -include("rabbit.hrl").
22
23 -behaviour(rabbit_credential_validator).
24
25 %%
26 %% API
27 %%
28
29 -export([validate/2]).
30 %% for tests
31 -export([validate/3]).
32
33 -spec validate(rabbit_types:username(), rabbit_types:password()) -> 'ok' | {'error', string()}.
34
35 validate(Username, Password) ->
36 {ok, Proplist} = application:get_env(rabbit, credential_validator),
37 Regexp = case proplists:get_value(regexp, Proplist) of
38 undefined -> {error, "rabbit.credential_validator.regexp config key is undefined"};
39 Value -> rabbit_data_coercion:to_list(Value)
40 end,
41 validate(Username, Password, Regexp).
42
43
44 -spec validate(rabbit_types:username(), rabbit_types:password(), string()) -> 'ok' | {'error', string(), [any()]}.
45
46 validate(_Username, Password, Pattern) ->
47 case re:run(rabbit_data_coercion:to_list(Password), Pattern) of
48 {match, _} -> ok;
49 nomatch -> {error, "provided password does not match the validator regular expression"}
50 end.
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_dead_letter).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_diagnostics).
6363 maybe_stuck_stacktrace({prim_inet, recv0, _}) -> false;
6464 maybe_stuck_stacktrace({rabbit_heartbeat, heartbeater, _}) -> false;
6565 maybe_stuck_stacktrace({rabbit_net, recv, _}) -> false;
66 maybe_stuck_stacktrace({mochiweb_http, request, _}) -> false;
6766 maybe_stuck_stacktrace({group, _, _}) -> false;
6867 maybe_stuck_stacktrace({shell, _, _}) -> false;
6968 maybe_stuck_stacktrace({io, _, _}) -> false;
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_direct).
108108 connect1(User, VHost, Protocol, Pid, Infos) ->
109109 try rabbit_access_control:check_vhost_access(User, VHost, authz_socket_info_direct(Infos)) of
110110 ok -> ok = pg_local:join(rabbit_direct, Pid),
111 rabbit_core_metrics:connection_created(Pid, Infos),
111112 rabbit_event:notify(connection_created, Infos),
112113 {ok, {User, rabbit_reader:server_properties(Protocol)}}
113114 catch
126127
127128 disconnect(Pid, Infos) ->
128129 pg_local:leave(rabbit_direct, Pid),
130 rabbit_core_metrics:connection_closed(Pid),
129131 rabbit_event:notify(connection_closed, Infos).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_disk_monitor).
6464 alarmed,
6565 %% is monitoring enabled? false on unsupported
6666 %% platforms
67 enabled
67 enabled,
68 %% number of retries to enable monitoring if it fails
69 %% on start-up
70 retries,
71 %% Interval between retries
72 interval
6873 }).
6974
7075 %%----------------------------------------------------------------------------
7176
72 -type disk_free_limit() :: (integer() | string() | {'mem_relative', float()}).
77 -type disk_free_limit() :: (integer() | string() | {'mem_relative', float() | integer()}).
7378 -spec start_link(disk_free_limit()) -> rabbit_types:ok_pid_or_error().
7479 -spec get_disk_free_limit() -> integer().
7580 -spec set_disk_free_limit(disk_free_limit()) -> 'ok'.
113118
114119 init([Limit]) ->
115120 Dir = dir(),
121 {ok, Retries} = application:get_env(rabbit, disk_monitor_failure_retries),
122 {ok, Interval} = application:get_env(rabbit, disk_monitor_failure_retry_interval),
116123 State = #state{dir = Dir,
117124 min_interval = ?DEFAULT_MIN_DISK_CHECK_INTERVAL,
118125 max_interval = ?DEFAULT_MAX_DISK_CHECK_INTERVAL,
119126 alarmed = false,
120 enabled = true},
121 case {catch get_disk_free(Dir),
122 vm_memory_monitor:get_total_memory()} of
123 {N1, N2} when is_integer(N1), is_integer(N2) ->
124 {ok, start_timer(set_disk_limits(State, Limit))};
125 Err ->
126 rabbit_log:info("Disabling disk free space monitoring "
127 "on unsupported platform:~n~p~n", [Err]),
128 {ok, State#state{enabled = false}}
129 end.
127 enabled = true,
128 limit = Limit,
129 retries = Retries,
130 interval = Interval},
131 {ok, enable(State)}.
130132
131133 handle_call(get_disk_free_limit, _From, State = #state{limit = Limit}) ->
132134 {reply, Limit, State};
160162 handle_cast(_Request, State) ->
161163 {noreply, State}.
162164
165 handle_info(try_enable, #state{retries = Retries} = State) ->
166 {noreply, enable(State#state{retries = Retries - 1})};
163167 handle_info(update, State) ->
164168 {noreply, start_timer(internal_update(State))};
165169
232236 list_to_integer(lists:reverse(Free)).
233237
234238 interpret_limit({mem_relative, Relative})
235 when is_float(Relative) ->
239 when is_number(Relative) ->
236240 round(Relative * vm_memory_monitor:get_total_memory());
237241 interpret_limit(Absolute) ->
238242 case rabbit_resource_monitor_misc:parse_information_unit(Absolute) of
245249
246250 emit_update_info(StateStr, CurrentFree, Limit) ->
247251 rabbit_log:info(
248 "Disk free space ~s. Free bytes:~p Limit:~p~n",
252 "Free disk space is ~s. Free bytes: ~p. Limit: ~p~n",
249253 [StateStr, CurrentFree, Limit]).
250254
251255 start_timer(State) ->
260264 max_interval = MaxInterval}) ->
261265 IdealInterval = 2 * (Actual - Limit) / ?FAST_RATE,
262266 trunc(erlang:max(MinInterval, erlang:min(MaxInterval, IdealInterval))).
267
268 enable(#state{retries = 0} = State) ->
269 State;
270 enable(#state{dir = Dir, interval = Interval, limit = Limit, retries = Retries}
271 = State) ->
272 case {catch get_disk_free(Dir),
273 vm_memory_monitor:get_total_memory()} of
274 {N1, N2} when is_integer(N1), is_integer(N2) ->
275 rabbit_log:info("Enabling free disk space monitoring~n", []),
276 start_timer(set_disk_limits(State, Limit));
277 Err ->
278 rabbit_log:info("Free disk space monitor encountered an error "
279 "(e.g. failed to parse output from OS tools): ~p, retries left: ~s~n",
280 [Err, Retries]),
281 timer:send_after(Interval, self(), try_enable),
282 State#state{enabled = false}
283 end.
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_epmd_monitor).
9191 "epmd does not know us, re-registering ~s at port ~b~n",
9292 [Me, Port]),
9393 rabbit_nodes:ensure_epmd(),
94 erl_epmd:register_node(Me, Port);
94 Mod:register_node(Me, Port);
9595 _ -> ok
9696 end.
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_error_logger).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_error_logger_file_h).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_exchange).
2222 lookup/1, lookup_or_die/1, list/0, list/1, lookup_scratch/2,
2323 update_scratch/3, update_decorators/1, immutable/1,
2424 info_keys/0, info/1, info/2, info_all/1, info_all/2, info_all/4,
25 route/2, delete/2, validate_binding/2]).
25 route/2, delete/2, validate_binding/2, list_names/0]).
2626 %% these must be run inside a mnesia tx
2727 -export([maybe_auto_delete/2, serial/1, peek_serial/1, update/2]).
2828
6060 (name()) -> rabbit_types:exchange() |
6161 rabbit_types:channel_exit().
6262 -spec list() -> [rabbit_types:exchange()].
63 -spec list_names() -> [rabbit_exchange:name()].
6364 -spec list(rabbit_types:vhost()) -> [rabbit_types:exchange()].
6465 -spec lookup_scratch(name(), atom()) ->
6566 rabbit_types:ok(term()) |
257258
258259 list() -> mnesia:dirty_match_object(rabbit_exchange, #exchange{_ = '_'}).
259260
261 list_names() -> mnesia:dirty_all_keys(rabbit_exchange).
262
260263 %% Not dirty_match_object since that would not be transactional when used in a
261264 %% tx context
262265 list(VHostPath) ->
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_exchange_parameters).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_exchange_type_direct).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_exchange_type_fanout).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_exchange_type_headers).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_exchange_type_invalid).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_exchange_type_topic).
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_fhc_helpers).
17
18 -export([clear_read_cache/0]).
19
20 -include("rabbit.hrl"). % For #amqqueue record definition.
21
22 clear_read_cache() ->
23 case application:get_env(rabbit, fhc_read_buffering) of
24 {ok, true} ->
25 file_handle_cache:clear_read_cache(),
26 clear_vhost_read_cache(rabbit_vhost:list());
27 _ -> %% undefined or {ok, false}
28 ok
29 end.
30
31 clear_vhost_read_cache([]) ->
32 ok;
33 clear_vhost_read_cache([VHost | Rest]) ->
34 clear_queue_read_cache(rabbit_amqqueue:list(VHost)),
35 clear_vhost_read_cache(Rest).
36
37 clear_queue_read_cache([]) ->
38 ok;
39 clear_queue_read_cache([#amqqueue{pid = MPid, slave_pids = SPids} | Rest]) ->
40 %% Limit the action to the current node.
41 Pids = [P || P <- [MPid | SPids], node(P) =:= node()],
42 %% This function is executed in the context of the backing queue
43 %% process because the read buffer is stored in the process
44 %% dictionary.
45 Fun = fun(_, State) ->
46 _ = file_handle_cache:clear_process_read_cache(),
47 State
48 end,
49 [rabbit_amqqueue:run_backing_queue(Pid, rabbit_variable_queue, Fun)
50 || Pid <- Pids],
51 clear_queue_read_cache(Rest).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% TODO auto-generate
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_guid).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% The purpose of the limiter is to stem the flow of messages from
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_log).
2626 -type category() :: atom().
2727 -type level() :: 'debug' | 'info' | 'warning' | 'error'.
2828
29 -spec log(category(), level(), string()) -> 'ok'.
30 -spec log(category(), level(), string(), [any()]) -> 'ok'.
31
32 -spec debug(string()) -> 'ok'.
33 -spec debug(string(), [any()]) -> 'ok'.
34 -spec info(string()) -> 'ok'.
35 -spec info(string(), [any()]) -> 'ok'.
36 -spec warning(string()) -> 'ok'.
37 -spec warning(string(), [any()]) -> 'ok'.
38 -spec error(string()) -> 'ok'.
39 -spec error(string(), [any()]) -> 'ok'.
40
41 -spec with_local_io(fun (() -> A)) -> A.
42
4329 %%----------------------------------------------------------------------------
4430
31 -spec log(category(), level(), string()) -> 'ok'.
4532 log(Category, Level, Fmt) -> log(Category, Level, Fmt, []).
4633
34 -spec log(category(), level(), string(), [any()]) -> 'ok'.
4735 log(Category, Level, Fmt, Args) when is_list(Args) ->
4836 case level(Level) =< catlevel(Category) of
4937 false -> ok;
5644 with_local_io(fun () -> F(Fmt, Args) end)
5745 end.
5846
47 -spec debug(string()) -> 'ok'.
5948 debug(Fmt) -> log(default, debug, Fmt).
49 -spec debug(string(), [any()]) -> 'ok'.
6050 debug(Fmt, Args) -> log(default, debug, Fmt, Args).
51 -spec info(string()) -> 'ok'.
6152 info(Fmt) -> log(default, info, Fmt).
53 -spec info(string(), [any()]) -> 'ok'.
6254 info(Fmt, Args) -> log(default, info, Fmt, Args).
55 -spec warning(string()) -> 'ok'.
6356 warning(Fmt) -> log(default, warning, Fmt).
57 -spec warning(string(), [any()]) -> 'ok'.
6458 warning(Fmt, Args) -> log(default, warning, Fmt, Args).
59 -spec error(string()) -> 'ok'.
6560 error(Fmt) -> log(default, error, Fmt).
61 -spec error(string(), [any()]) -> 'ok'.
6662 error(Fmt, Args) -> log(default, error, Fmt, Args).
6763
6864 catlevel(Category) ->
8682 %% Execute Fun using the IO system of the local node (i.e. the node on
8783 %% which the code is executing). Since this is invoked for every log
8884 %% message, we try to avoid unnecessarily churning group_leader/1.
85 -spec with_local_io(fun (() -> A)) -> A.
8986 with_local_io(Fun) ->
9087 GL = group_leader(),
9188 Node = node(),
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616
8888 conserve_resources(_Pid, _Source, _Conserve) ->
8989 ok.
9090
91 memory_use(bytes) ->
92 MemoryLimit = vm_memory_monitor:get_memory_limit(),
93 {erlang:memory(total), case MemoryLimit > 0.0 of
94 true -> MemoryLimit;
95 false -> infinity
96 end};
97 memory_use(ratio) ->
98 MemoryLimit = vm_memory_monitor:get_memory_limit(),
99 case MemoryLimit > 0.0 of
100 true -> erlang:memory(total) / MemoryLimit;
101 false -> infinity
102 end.
91 memory_use(Type) ->
92 vm_memory_monitor:get_memory_use(Type).
10393
10494 %%----------------------------------------------------------------------------
10595 %% Gen_server callbacks
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_metrics).
17
18 -behaviour(gen_server).
19
20 -export([start_link/0]).
21
22 -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
23 code_change/3]).
24
25 -define(SERVER, ?MODULE).
26
27 -spec start_link() -> rabbit_types:ok_pid_or_error().
28
29 %%----------------------------------------------------------------------------
30 %% Starts the raw metrics storage and owns the ETS tables.
31 %%----------------------------------------------------------------------------
32 start_link() ->
33 gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
34
35 init([]) ->
36 rabbit_core_metrics:init(),
37 {ok, none}.
38
39 handle_call(_Request, _From, State) ->
40 {noreply, State}.
41
42 handle_cast(_Request, State) ->
43 {noreply, State}.
44
45 handle_info(_Msg, State) ->
46 {noreply, State}.
47
48 terminate(_Reason, _State) ->
49 ok.
50
51 code_change(_OldVsn, State, _Extra) ->
52 {ok, State}.
3636 %%
3737 %% Master Syncer Slave(s)
3838 %% sync_mirrors -> || ||
39 %% (from channel) || -- (spawns) --> || ||
39 %% || -- (spawns) --> || ||
4040 %% || --------- sync_start (over GM) -------> ||
4141 %% || || <--- sync_ready ---- ||
4242 %% || || (or) ||
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mnesia).
103103 false ->
104104 NodeType = node_type(),
105105 init_db_and_upgrade(cluster_nodes(all), NodeType,
106 NodeType =:= ram)
106 NodeType =:= ram, _Retry = true)
107107 end,
108108 %% We intuitively expect the global name server to be synced when
109109 %% Mnesia is up. In fact that's not guaranteed to be the case -
137137 e(invalid_cluster_nodes_conf)
138138 end,
139139 case TryNodes of
140 [] -> init_db_and_upgrade([node()], disc, false);
140 [] -> init_db_and_upgrade([node()], disc, false, _Retry = true);
141141 _ -> auto_cluster(TryNodes, NodeType)
142142 end.
143143
146146 {ok, Node} ->
147147 rabbit_log:info("Node '~p' selected for auto-clustering~n", [Node]),
148148 {ok, {_, DiscNodes, _}} = discover_cluster0(Node),
149 init_db_and_upgrade(DiscNodes, NodeType, true),
149 init_db_and_upgrade(DiscNodes, NodeType, true, _Retry = true),
150150 rabbit_node_monitor:notify_joined_cluster();
151151 none ->
152152 rabbit_log:warning(
153153 "Could not find any node for auto-clustering from: ~p~n"
154154 "Starting blank node...~n", [TryNodes]),
155 init_db_and_upgrade([node()], disc, false)
155 init_db_and_upgrade([node()], disc, false, _Retry = true)
156156 end.
157157
158158 %% Make the node join a cluster. The node will be reset automatically
192192 rabbit_log:info("Clustering with ~p as ~p node~n",
193193 [ClusterNodes, NodeType]),
194194 ok = init_db_with_mnesia(ClusterNodes, NodeType,
195 true, true),
195 true, true, _Retry = true),
196196 rabbit_node_monitor:notify_joined_cluster(),
197197 ok;
198198 {error, Reason} ->
231231 %% need to check for consistency because we are resetting.
232232 %% Force=true here so that reset still works when clustered with a
233233 %% node which is down.
234 init_db_with_mnesia(AllNodes, node_type(), false, false),
234 init_db_with_mnesia(AllNodes, node_type(), false, false, _Retry = false),
235235 case is_only_clustered_disc_node() of
236236 true -> e(resetting_only_disc_node);
237237 false -> ok
280280 rabbit_node_monitor:write_cluster_status(Status),
281281 rabbit_log:info("Updating cluster nodes from ~p~n",
282282 [DiscoveryNode]),
283 init_db_with_mnesia(AllNodes, node_type(), true, true);
283 init_db_with_mnesia(AllNodes, node_type(), true, true, _Retry = false);
284284 false ->
285285 e(inconsistent_cluster)
286286 end,
324324 %% is by force loading the table, and making sure that
325325 %% they are loaded.
326326 rabbit_table:force_load(),
327 rabbit_table:wait_for_replicated(),
327 rabbit_table:wait_for_replicated(_Retry = false),
328328 forget_cluster_node(Node, false),
329329 force_load_next_boot()
330330 after
469469 {[_ | _], _, _} ->
470470 %% Subsequent node in cluster, catch up
471471 maybe_force_load(),
472 ok = rabbit_table:wait_for_replicated(),
472 ok = rabbit_table:wait_for_replicated(_Retry = true),
473473 ok = rabbit_table:create_local_copy(NodeType)
474474 end,
475475 ensure_schema_integrity(),
479479 init_db_unchecked(ClusterNodes, NodeType) ->
480480 init_db(ClusterNodes, NodeType, false).
481481
482 init_db_and_upgrade(ClusterNodes, NodeType, CheckOtherNodes) ->
482 init_db_and_upgrade(ClusterNodes, NodeType, CheckOtherNodes, Retry) ->
483483 ok = init_db(ClusterNodes, NodeType, CheckOtherNodes),
484484 ok = case rabbit_upgrade:maybe_upgrade_local() of
485485 ok -> ok;
494494 disc -> ok
495495 end,
496496 %% ...and all nodes will need to wait for tables
497 rabbit_table:wait_for_replicated(),
497 rabbit_table:wait_for_replicated(Retry),
498498 ok.
499499
500500 init_db_with_mnesia(ClusterNodes, NodeType,
501 CheckOtherNodes, CheckConsistency) ->
501 CheckOtherNodes, CheckConsistency, Retry) ->
502502 start_mnesia(CheckConsistency),
503503 try
504 init_db_and_upgrade(ClusterNodes, NodeType, CheckOtherNodes)
504 init_db_and_upgrade(ClusterNodes, NodeType, CheckOtherNodes, Retry)
505505 after
506506 stop_mnesia()
507507 end.
538538 end.
539539
540540 ensure_schema_integrity() ->
541 case rabbit_table:check_schema_integrity() of
541 case rabbit_table:check_schema_integrity(_Retry = true) of
542542 ok ->
543543 ok;
544544 {error, Reason} ->
669669 rpc:call(Node, rabbit_mnesia, cluster_status_from_mnesia, []).
670670
671671 schema_ok_or_move() ->
672 case rabbit_table:check_schema_integrity() of
672 case rabbit_table:check_schema_integrity(_Retry = false) of
673673 ok ->
674674 ok;
675675 {error, Reason} ->
839839 case IsMnesiaRunning of
840840 true -> Fun();
841841 false ->
842 {ok, MnesiaDir} = application:get_env(mnesia, dir),
842 SavedMnesiaDir = dir(),
843843 application:unset_env(mnesia, dir),
844844 mnesia:start(),
845845 Result = Fun(),
846846 application:stop(mnesia),
847 application:set_env(mnesia, dir, MnesiaDir),
847 application:set_env(mnesia, dir, SavedMnesiaDir),
848848 Result
849849 end.
850850
857857 %% that a `reset' would leave it in. We cannot simply check if the
858858 %% mnesia tables aren't there because restarted RAM nodes won't have
859859 %% tables while still being non-virgin. What we do instead is to
860 %% check if the mnesia directory is non existant or empty, with the
860 %% check if the mnesia directory is non existent or empty, with the
861861 %% exception of the cluster status files, which will be there thanks to
862862 %% `rabbit_node_monitor:prepare_cluster_status_file/0'.
863863 is_virgin_node() ->
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mnesia_rename).
186186
187187 start_mnesia() -> rabbit_misc:ensure_ok(mnesia:start(), cannot_start_mnesia),
188188 rabbit_table:force_load(),
189 rabbit_table:wait_for_replicated().
189 rabbit_table:wait_for_replicated(_Retry = false).
190190 stop_mnesia() -> stopped = mnesia:stop().
191191
192192 convert_backup(NodeMap, FromBackup, ToBackup) ->
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_msg_file).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_msg_store).
264264 %% updated.
265265 %%
266266 %% On non-clean startup, we scan the files we discover, dealing with
267 %% the possibilites of a crash having occured during a compaction
267 %% the possibilites of a crash having occurred during a compaction
268268 %% (this consists of tidyup - the compaction is deliberately designed
269269 %% such that data is duplicated on disk rather than risking it being
270270 %% lost), and rebuild the file summary and index ETS table.
309309 %% From this reasoning, we do have a bound on the number of times the
310310 %% message is rewritten. From when it is inserted, there can be no
311311 %% files inserted between it and the head of the queue, and the worst
312 %% case is that everytime it is rewritten, it moves one position lower
312 %% case is that every time it is rewritten, it moves one position lower
313313 %% in the file (for it to stay at the same position requires that
314314 %% there are no holes beneath it, which means truncate would be used
315315 %% and so it would not be rewritten at all). Thus this seems to
351351 %% because in the event of the same message being sent to several
352352 %% different queues, there is the possibility of one queue writing and
353353 %% removing the message before other queues write it at all. Thus
354 %% accomodating 0-reference counts allows us to avoid unnecessary
354 %% accommodating 0-reference counts allows us to avoid unnecessary
355355 %% writes here. Of course, there are complications: the file to which
356356 %% the message has already been written could be locked pending
357357 %% deletion or GC, which means we have to rewrite the message as the
984984 flying_ets = FlyingEts,
985985 clients = Clients,
986986 dir = Dir }) ->
987 rabbit_log:info("Stopping message store for directory '~s'", [Dir]),
987988 %% stop the gc first, otherwise it could be working and we pull
988989 %% out the ets tables from under it.
989990 ok = rabbit_msg_store_gc:stop(GCPid),
10001001 IndexModule:terminate(IndexState),
10011002 ok = store_recovery_terms([{client_refs, dict:fetch_keys(Clients)},
10021003 {index_module, IndexModule}], Dir),
1004 rabbit_log:info("Message store for directory '~s' is stopped", [Dir]),
10031005 State3 #msstate { index_state = undefined,
10041006 current_file_handle = undefined }.
10051007
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_msg_store_ets_index).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_msg_store_gc).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_node_monitor).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_parameter_validation).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_password).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% Legacy hashing implementation, only used as a last resort when
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_password_hashing_sha256).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_password_hashing_sha512).
+0
-194
deps/rabbit/src/rabbit_pbe.erl less more
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_pbe).
17
18 -export([supported_ciphers/0, supported_hashes/0, default_cipher/0, default_hash/0, default_iterations/0]).
19 -export([encrypt_term/5, decrypt_term/5]).
20 -export([encrypt/5, decrypt/5]).
21
22 %% Supported ciphers and hashes
23
24 supported_ciphers() ->
25 proplists:get_value(ciphers, crypto:supports())
26 -- [aes_ctr, aes_ecb, des_ecb, blowfish_ecb, rc4, aes_gcm].
27
28 supported_hashes() ->
29 proplists:get_value(hashs, crypto:supports())
30 -- [md4, ripemd160].
31
32 %% Default encryption parameters (keep those in sync with rabbit.app.src)
33 default_cipher() ->
34 aes_cbc256.
35
36 default_hash() ->
37 sha512.
38
39 default_iterations() ->
40 1000.
41
42 %% Encryption/decryption of arbitrary Erlang terms.
43
44 encrypt_term(Cipher, Hash, Iterations, PassPhrase, Term) ->
45 encrypt(Cipher, Hash, Iterations, PassPhrase, term_to_binary(Term)).
46
47 decrypt_term(Cipher, Hash, Iterations, PassPhrase, Base64Binary) ->
48 binary_to_term(decrypt(Cipher, Hash, Iterations, PassPhrase, Base64Binary)).
49
50 %% The cipher for encryption is from the list of supported ciphers.
51 %% The hash for generating the key from the passphrase is from the list
52 %% of supported hashes. See crypto:supports/0 to obtain both lists.
53 %% The key is generated by applying the hash N times with N >= 1.
54 %%
55 %% The encrypt/5 function returns a base64 binary and the decrypt/5
56 %% function accepts that same base64 binary.
57
58 -spec encrypt(crypto:block_cipher(), crypto:hash_algorithms(),
59 pos_integer(), iodata(), binary()) -> binary().
60 encrypt(Cipher, Hash, Iterations, PassPhrase, ClearText) ->
61 Salt = crypto:strong_rand_bytes(16),
62 Ivec = crypto:strong_rand_bytes(iv_length(Cipher)),
63 Key = make_key(Cipher, Hash, Iterations, PassPhrase, Salt),
64 Binary = crypto:block_encrypt(Cipher, Key, Ivec, pad(Cipher, ClearText)),
65 base64:encode(<< Salt/binary, Ivec/binary, Binary/binary >>).
66
67 -spec decrypt(crypto:block_cipher(), crypto:hash_algorithms(),
68 pos_integer(), iodata(), binary()) -> binary().
69 decrypt(Cipher, Hash, Iterations, PassPhrase, Base64Binary) ->
70 IvLength = iv_length(Cipher),
71 << Salt:16/binary, Ivec:IvLength/binary, Binary/bits >> = base64:decode(Base64Binary),
72 Key = make_key(Cipher, Hash, Iterations, PassPhrase, Salt),
73 unpad(crypto:block_decrypt(Cipher, Key, Ivec, Binary)).
74
75 %% Generate a key from a passphrase.
76
77 make_key(Cipher, Hash, Iterations, PassPhrase, Salt) ->
78 Key = pbdkdf2(PassPhrase, Salt, Iterations, key_length(Cipher),
79 fun crypto:hmac/4, Hash, hash_length(Hash)),
80 if
81 Cipher =:= des3_cbc; Cipher =:= des3_cbf; Cipher =:= des3_cfb; Cipher =:= des_ede3 ->
82 << A:8/binary, B:8/binary, C:8/binary >> = Key,
83 [A, B, C];
84 true ->
85 Key
86 end.
87
88 %% Functions to pad/unpad input to a multiplier of block size.
89
90 pad(Cipher, Data) ->
91 BlockSize = block_size(Cipher),
92 N = BlockSize - (byte_size(Data) rem BlockSize),
93 Pad = list_to_binary(lists:duplicate(N, N)),
94 <<Data/binary, Pad/binary>>.
95
96 unpad(Data) ->
97 N = binary:last(Data),
98 binary:part(Data, 0, byte_size(Data) - N).
99
100 %% These functions are necessary because the current Erlang crypto interface
101 %% is lacking interfaces to the following OpenSSL functions:
102 %%
103 %% * int EVP_MD_size(const EVP_MD *md);
104 %% * int EVP_CIPHER_iv_length(const EVP_CIPHER *e);
105 %% * int EVP_CIPHER_key_length(const EVP_CIPHER *e);
106 %% * int EVP_CIPHER_block_size(const EVP_CIPHER *e);
107
108 hash_length(md4) -> 16;
109 hash_length(md5) -> 16;
110 hash_length(sha) -> 20;
111 hash_length(sha224) -> 28;
112 hash_length(sha256) -> 32;
113 hash_length(sha384) -> 48;
114 hash_length(sha512) -> 64.
115
116 iv_length(des_cbc) -> 8;
117 iv_length(des_cfb) -> 8;
118 iv_length(des3_cbc) -> 8;
119 iv_length(des3_cbf) -> 8;
120 iv_length(des3_cfb) -> 8;
121 iv_length(des_ede3) -> 8;
122 iv_length(blowfish_cbc) -> 8;
123 iv_length(blowfish_cfb64) -> 8;
124 iv_length(blowfish_ofb64) -> 8;
125 iv_length(rc2_cbc) -> 8;
126 iv_length(aes_cbc) -> 16;
127 iv_length(aes_cbc128) -> 16;
128 iv_length(aes_cfb8) -> 16;
129 iv_length(aes_cfb128) -> 16;
130 iv_length(aes_cbc256) -> 16;
131 iv_length(aes_ige256) -> 32.
132
133 key_length(des_cbc) -> 8;
134 key_length(des_cfb) -> 8;
135 key_length(des3_cbc) -> 24;
136 key_length(des3_cbf) -> 24;
137 key_length(des3_cfb) -> 24;
138 key_length(des_ede3) -> 24;
139 key_length(blowfish_cbc) -> 16;
140 key_length(blowfish_cfb64) -> 16;
141 key_length(blowfish_ofb64) -> 16;
142 key_length(rc2_cbc) -> 16;
143 key_length(aes_cbc) -> 16;
144 key_length(aes_cbc128) -> 16;
145 key_length(aes_cfb8) -> 16;
146 key_length(aes_cfb128) -> 16;
147 key_length(aes_cbc256) -> 32;
148 key_length(aes_ige256) -> 16.
149
150 block_size(aes_cbc256) -> 32;
151 block_size(aes_cbc128) -> 32;
152 block_size(aes_ige256) -> 32;
153 block_size(aes_cbc) -> 32;
154 block_size(_) -> 8.
155
156 %% The following was taken from OTP's lib/public_key/src/pubkey_pbe.erl
157 %%
158 %% This is an undocumented interface to password-based encryption algorithms.
159 %% These functions have been copied here to stay compatible with R16B03.
160
161 %%--------------------------------------------------------------------
162 -spec pbdkdf2(string(), iodata(), integer(), integer(), fun(), atom(), integer())
163 -> binary().
164 %%
165 %% Description: Implements password based decryption key derive function 2.
166 %% Exported mainly for testing purposes.
167 %%--------------------------------------------------------------------
168 pbdkdf2(Password, Salt, Count, DerivedKeyLen, Prf, PrfHash, PrfOutputLen)->
169 NumBlocks = ceiling(DerivedKeyLen / PrfOutputLen),
170 NumLastBlockOctets = DerivedKeyLen - (NumBlocks - 1) * PrfOutputLen ,
171 blocks(NumBlocks, NumLastBlockOctets, 1, Password, Salt,
172 Count, Prf, PrfHash, PrfOutputLen, <<>>).
173
174 blocks(1, N, Index, Password, Salt, Count, Prf, PrfHash, PrfLen, Acc) ->
175 <<XorSum:N/binary, _/binary>> = xor_sum(Password, Salt, Count, Index, Prf, PrfHash, PrfLen),
176 <<Acc/binary, XorSum/binary>>;
177 blocks(NumBlocks, N, Index, Password, Salt, Count, Prf, PrfHash, PrfLen, Acc) ->
178 XorSum = xor_sum(Password, Salt, Count, Index, Prf, PrfHash, PrfLen),
179 blocks(NumBlocks -1, N, Index +1, Password, Salt, Count, Prf, PrfHash,
180 PrfLen, <<Acc/binary, XorSum/binary>>).
181
182 xor_sum(Password, Salt, Count, Index, Prf, PrfHash, PrfLen) ->
183 Result = Prf(PrfHash, Password, [Salt,<<Index:32/unsigned-big-integer>>], PrfLen),
184 do_xor_sum(Prf, PrfHash, PrfLen, Result, Password, Count-1, Result).
185
186 do_xor_sum(_, _, _, _, _, 0, Acc) ->
187 Acc;
188 do_xor_sum(Prf, PrfHash, PrfLen, Prev, Password, Count, Acc) ->
189 Result = Prf(PrfHash, Password, Prev, PrfLen),
190 do_xor_sum(Prf, PrfHash, PrfLen, Result, Password, Count-1, crypto:exor(Acc, Result)).
191
192 ceiling(Float) ->
193 erlang:round(Float + 0.5).
1414 %%
1515
1616 -module(rabbit_plugins).
17 -include("rabbit.hrl").
17 -include_lib("rabbit_common/include/rabbit.hrl").
1818
1919 -export([setup/0, active/0, read_enabled/1, list/1, list/2, dependencies/3]).
2020 -export([ensure/1]).
6060 {error, {enabled_plugins_mismatch, FileJustChanged, OurFile}}
6161 end.
6262
63 -spec plugins_expand_dir() -> file:filename().
64 plugins_expand_dir() ->
65 case application:get_env(rabbit, plugins_expand_dir) of
66 {ok, ExpandDir} ->
67 ExpandDir;
68 _ ->
69 filename:join([rabbit_mnesia:dir(), "plugins_expand_dir"])
70 end.
71
72 -spec plugins_dist_dir() -> file:filename().
73 plugins_dist_dir() ->
74 case application:get_env(rabbit, plugins_dir) of
75 {ok, PluginsDistDir} ->
76 PluginsDistDir;
77 _ ->
78 filename:join([rabbit_mnesia:dir(), "plugins_dir_stub"])
79 end.
80
81 -spec enabled_plugins() -> [atom()].
82 enabled_plugins() ->
83 case application:get_env(rabbit, enabled_plugins_file) of
84 {ok, EnabledFile} ->
85 read_enabled(EnabledFile);
86 _ ->
87 []
88 end.
89
6390 %% @doc Prepares the file system and installs all enabled plugins.
6491 setup() ->
65 {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir),
66
92 ExpandDir = plugins_expand_dir(),
6793 %% Eliminate the contents of the destination directory
6894 case delete_recursively(ExpandDir) of
6995 ok -> ok;
7197 [ExpandDir, E1]}})
7298 end,
7399
74 {ok, EnabledFile} = application:get_env(rabbit, enabled_plugins_file),
75 Enabled = read_enabled(EnabledFile),
100 Enabled = enabled_plugins(),
76101 prepare_plugins(Enabled).
77102
78103 %% @doc Lists the plugins which are currently running.
79104 active() ->
80 {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir),
81 InstalledPlugins = plugin_names(list(ExpandDir)),
105 InstalledPlugins = plugin_names(list(plugins_expand_dir())),
82106 [App || {App, _, _} <- rabbit_misc:which_applications(),
83107 lists:member(App, InstalledPlugins)].
84108
85109 %% @doc Get the list of plugins which are ready to be enabled.
86 list(PluginsDir) ->
87 list(PluginsDir, false).
88
89 list(PluginsDir, IncludeRequiredDeps) ->
90 EZs = [{ez, EZ} || EZ <- filelib:wildcard("*.ez", PluginsDir)],
91 FreeApps = [{app, App} ||
92 App <- filelib:wildcard("*/ebin/*.app", PluginsDir)],
93 %% We load the "rabbit" application to be sure we can get the
94 %% "applications" key. This is required for rabbitmq-plugins for
95 %% instance.
96 application:load(rabbit),
97 {ok, RabbitDeps} = application:get_key(rabbit, applications),
98 {AvailablePlugins, Problems} =
99 lists:foldl(fun ({error, EZ, Reason}, {Plugins1, Problems1}) ->
100 {Plugins1, [{EZ, Reason} | Problems1]};
101 (Plugin = #plugin{name = Name}, {Plugins1, Problems1}) ->
102 %% Applications RabbitMQ depends on (eg.
103 %% "rabbit_common") can't be considered
104 %% plugins, otherwise rabbitmq-plugins would
105 %% list them and the user may believe he can
106 %% disable them.
107 case IncludeRequiredDeps orelse
108 not lists:member(Name, RabbitDeps) of
109 true -> {[Plugin|Plugins1], Problems1};
110 false -> {Plugins1, Problems1}
111 end
112 end, {[], []},
113 [plugin_info(PluginsDir, Plug) || Plug <- EZs ++ FreeApps]),
114 case Problems of
115 [] -> ok;
116 _ -> rabbit_log:warning(
117 "Problem reading some plugins: ~p~n", [Problems])
118 end,
119 Plugins = lists:filter(fun(P) -> not plugin_provided_by_otp(P) end,
120 AvailablePlugins),
121 ensure_dependencies(Plugins).
110 list(PluginsPath) ->
111 list(PluginsPath, false).
112
113 list(PluginsPath, IncludeRequiredDeps) ->
114 {AllPlugins, LoadingProblems} = discover_plugins(split_path(PluginsPath)),
115 {UniquePlugins, DuplicateProblems} = remove_duplicate_plugins(AllPlugins),
116 Plugins1 = maybe_keep_required_deps(IncludeRequiredDeps, UniquePlugins),
117 Plugins2 = remove_otp_overrideable_plugins(Plugins1),
118 maybe_report_plugin_loading_problems(LoadingProblems ++ DuplicateProblems),
119 ensure_dependencies(Plugins2).
122120
123121 %% @doc Read the list of enabled plugins from the supplied term file.
124122 read_enabled(PluginsFile) ->
188186 %%----------------------------------------------------------------------------
189187
190188 prepare_plugins(Enabled) ->
191 {ok, PluginsDistDir} = application:get_env(rabbit, plugins_dir),
192 {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir),
193
194 AllPlugins = list(PluginsDistDir),
189 ExpandDir = plugins_expand_dir(),
190
191 AllPlugins = list(plugins_dist_dir()),
195192 Wanted = dependencies(false, Enabled, AllPlugins),
196193 WantedPlugins = lookup_plugins(Wanted, AllPlugins),
197194
208205 Wanted.
209206
210207 clean_plugins(Plugins) ->
211 {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir),
208 ExpandDir = plugins_expand_dir(),
212209 [clean_plugin(Plugin, ExpandDir) || Plugin <- Plugins].
213210
214211 clean_plugin(Plugin, ExpandDir) ->
258255 ExpandDir) ->
259256 rabbit_file:recursive_copy(Location, filename:join([ExpandDir, Name])).
260257
261 plugin_info(Base, {ez, EZ0}) ->
262 EZ = filename:join([Base, EZ0]),
258 plugin_info({ez, EZ}) ->
263259 case read_app_file(EZ) of
264260 {application, Name, Props} -> mkplugin(Name, Props, ez, EZ);
265261 {error, Reason} -> {error, EZ, Reason}
266262 end;
267 plugin_info(Base, {app, App0}) ->
268 App = filename:join([Base, App0]),
263 plugin_info({app, App}) ->
269264 case rabbit_file:read_term_file(App) of
270265 {ok, [{application, Name, Props}]} ->
271266 mkplugin(Name, Props, dir,
316311
317312 lookup_plugins(Names, AllPlugins) ->
318313 [P || P = #plugin{name = Name} <- AllPlugins, lists:member(Name, Names)].
314
315 %% Split PATH-like value into its components.
316 split_path(PathString) ->
317 Delimiters = case os:type() of
318 {unix, _} -> ":";
319 {win32, _} -> ";"
320 end,
321 string:tokens(PathString, Delimiters).
322
323 %% Search for files using glob in a given dir. Returns full filenames of those files.
324 full_path_wildcard(Glob, Dir) ->
325 [filename:join([Dir, File]) || File <- filelib:wildcard(Glob, Dir)].
326
327 %% Returns list off all .ez files in a given set of directories
328 list_ezs([]) ->
329 [];
330 list_ezs([Dir|Rest]) ->
331 [{ez, EZ} || EZ <- full_path_wildcard("*.ez", Dir)] ++ list_ezs(Rest).
332
333 %% Returns list of all files that look like OTP applications in a
334 %% given set of directories.
335 list_free_apps([]) ->
336 [];
337 list_free_apps([Dir|Rest]) ->
338 [{app, App} || App <- full_path_wildcard("*/ebin/*.app", Dir)]
339 ++ list_free_apps(Rest).
340
341 compare_by_name_and_version(#plugin{name = Name, version = VersionA},
342 #plugin{name = Name, version = VersionB}) ->
343 ec_semver:lte(VersionA, VersionB);
344 compare_by_name_and_version(#plugin{name = NameA},
345 #plugin{name = NameB}) ->
346 NameA =< NameB.
347
348 -spec discover_plugins([Directory]) -> {[#plugin{}], [Problem]} when
349 Directory :: file:name(),
350 Problem :: {file:name(), term()}.
351 discover_plugins(PluginsDirs) ->
352 EZs = list_ezs(PluginsDirs),
353 FreeApps = list_free_apps(PluginsDirs),
354 read_plugins_info(EZs ++ FreeApps, {[], []}).
355
356 read_plugins_info([], Acc) ->
357 Acc;
358 read_plugins_info([Path|Paths], {Plugins, Problems}) ->
359 case plugin_info(Path) of
360 #plugin{} = Plugin ->
361 read_plugins_info(Paths, {[Plugin|Plugins], Problems});
362 {error, Location, Reason} ->
363 read_plugins_info(Paths, {Plugins, [{Location, Reason}|Problems]})
364 end.
365
366 remove_duplicate_plugins(Plugins) ->
367 %% Reverse order ensures that if there are several versions of the
368 %% same plugin, the most recent one comes first.
369 Sorted = lists:reverse(
370 lists:sort(fun compare_by_name_and_version/2, Plugins)),
371 remove_duplicate_plugins(Sorted, {[], []}).
372
373 remove_duplicate_plugins([], Acc) ->
374 Acc;
375 remove_duplicate_plugins([Best = #plugin{name = Name}, Offender = #plugin{name = Name} | Rest],
376 {Plugins0, Problems0}) ->
377 Problems1 = [{Offender#plugin.location, duplicate_plugin}|Problems0],
378 remove_duplicate_plugins([Best|Rest], {Plugins0, Problems1});
379 remove_duplicate_plugins([Plugin|Rest], {Plugins0, Problems0}) ->
380 Plugins1 = [Plugin|Plugins0],
381 remove_duplicate_plugins(Rest, {Plugins1, Problems0}).
382
383 maybe_keep_required_deps(true, Plugins) ->
384 Plugins;
385 maybe_keep_required_deps(false, Plugins) ->
386 %% We load the "rabbit" application to be sure we can get the
387 %% "applications" key. This is required for rabbitmq-plugins for
388 %% instance.
389 application:load(rabbit),
390 {ok, RabbitDeps} = application:get_key(rabbit, applications),
391 lists:filter(fun(#plugin{name = Name}) ->
392 not lists:member(Name, RabbitDeps)
393 end,
394 Plugins).
395
396 remove_otp_overrideable_plugins(Plugins) ->
397 lists:filter(fun(P) -> not plugin_provided_by_otp(P) end,
398 Plugins).
399
400 maybe_report_plugin_loading_problems([]) ->
401 ok;
402 maybe_report_plugin_loading_problems(Problems) ->
403 rabbit_log:warning("Problem reading some plugins: ~p~n", [Problems]).
168168
169169 EnabledImplicitly = Implicit -- Enabled,
170170 {StatusMsg, Running} =
171 case rabbit_misc:rpc_call(Node, rabbit_plugins, active, []) of
172 {badrpc, _} -> {"[failed to contact ~s - status not shown]", []};
173 Active -> {"* = running on ~s", Active}
171 case remote_running_plugins(Node) of
172 {ok, Active} -> {"* = running on ~s", Active};
173 error -> {"[failed to contact ~s - status not shown]", []}
174174 end,
175175 {ok, RE} = re:compile(Pattern),
176176 Plugins = [ Plugin ||
195195 Format, MaxWidth) || P <- Plugins1],
196196 ok.
197197
198 format_plugin(#plugin{name = Name, version = Version,
198 format_plugin(#plugin{name = Name, version = OnDiskVersion,
199199 description = Description, dependencies = Deps},
200200 Enabled, EnabledImplicitly, Running, Format,
201201 MaxWidth) ->
205205 {false, true} -> "e";
206206 _ -> " "
207207 end,
208 RunningGlyph = case lists:member(Name, Running) of
208 RunningGlyph = case lists:keymember(Name, 1, Running) of
209209 true -> "*";
210210 false -> " "
211211 end,
213213 Opt = fun (_F, A, A) -> ok;
214214 ( F, A, _) -> io:format(F, [A])
215215 end,
216 Version = format_running_plugin_version(Name, OnDiskVersion, Running),
216217 case Format of
217218 minimal -> io:format("~s~n", [Name]);
218219 normal -> io:format("~s ~-" ++ integer_to_list(MaxWidth) ++ "w ",
304305
305306 plur([_]) -> "";
306307 plur(_) -> "s".
308
309 -spec remote_running_plugins(node()) -> [{atom(), Vsn :: string()}].
310 remote_running_plugins(Node) ->
311 case rabbit_misc:rpc_call(Node, rabbit_plugins, active, []) of
312 {badrpc, _} -> error;
313 Active -> maybe_augment_with_versions(Node, Active)
314 end.
315
316 -spec maybe_augment_with_versions(node(), [atom()]) -> [{atom(), Vsn :: string()}].
317 maybe_augment_with_versions(Node, Plugins) ->
318 case rabbit_misc:rpc_call(Node, rabbit_misc, which_applications, []) of
319 {badrpc, _} ->
320 error;
321 All ->
322 {ok, [{App, Vsn} || {App, _, Vsn} <- All,
323 lists:member(App, Plugins)]}
324 end.
325
326 -spec format_running_plugin_version(atom(), string(), [{atom(), Vsn :: string()}]) -> string().
327 format_running_plugin_version(Name, OnDiskVersion, RunningPlugins) ->
328 case lists:keyfind(Name, 1, RunningPlugins) of
329 false ->
330 OnDiskVersion;
331 {_, OnDiskVersion} ->
332 OnDiskVersion;
333 {_, RunningVersion} ->
334 io_lib:format("~s (pending upgrade to ~s)", [RunningVersion, OnDiskVersion])
335 end.
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_policies).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_policy).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_prelaunch).
656656 cse(lazy, _) -> lazy;
657657 %% numerical stats
658658 cse(A, B) when is_number(A) -> A + B;
659 cse({delta, _, _, _}, _) -> {delta, todo, todo, todo};
659 cse({delta, _, _, _, _}, _) -> {delta, todo, todo, todo, todo};
660660 cse(A, B) -> exit({A, B}).
661661
662662 %% When asked about 'head_message_timestamp' fro this priority queue, we
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_queue_consumers).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_queue_index).
122122 -define(SEGMENT_EXTENSION, ".idx").
123123
124124 %% TODO: The segment size would be configurable, but deriving all the
125 %% other values is quite hairy and quite possibly noticably less
125 %% other values is quite hairy and quite possibly noticeably less
126126 %% efficient, depending on how clever the compiler is when it comes to
127127 %% binary generation/matching with constant vs variable lengths.
128128
175175
176176 %%----------------------------------------------------------------------------
177177
178 -record(qistate, {dir, segments, journal_handle, dirty_count,
179 max_journal_entries, on_sync, on_sync_msg,
180 unconfirmed, unconfirmed_msg,
181 pre_publish_cache, delivered_cache}).
182
183 -record(segment, {num, path, journal_entries,
184 entries_to_segment, unacked}).
178 -record(qistate, {
179 %% queue directory where segment and journal files are stored
180 dir,
181 %% map of #segment records
182 segments,
183 %% journal file handle obtained from/used by file_handle_cache
184 journal_handle,
185 %% how many not yet flushed entries are there
186 dirty_count,
187 %% this many not yet flushed journal entries will force a flush
188 max_journal_entries,
189 %% callback function invoked when a message is "handled"
190 %% by the index and potentially can be confirmed to the publisher
191 on_sync,
192 on_sync_msg,
193 %% set of IDs of unconfirmed [to publishers] messages
194 unconfirmed,
195 unconfirmed_msg,
196 %% optimisation
197 pre_publish_cache,
198 %% optimisation
199 delivered_cache}).
200
201 -record(segment, {
202 %% segment ID (an integer)
203 num,
204 %% segment file path (see also ?SEGMENT_EXTENSION)
205 path,
206 %% index operation log entries in this segment
207 journal_entries,
208 entries_to_segment,
209 %% counter of unacknowledged messages
210 unacked
211 }).
185212
186213 -include("rabbit.hrl").
187214
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_queue_location_client_local).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_queue_location_min_masters).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_queue_location_random).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_queue_location_validator).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_queue_master_location_misc).
6262 end.
6363
6464 get_location_mod_by_args(#amqqueue{arguments=Args}) ->
65 case proplists:lookup(<<"x-queue-master-locator">> , Args) of
66 {<<"x-queue-master-locator">> , Strategy} ->
65 case rabbit_misc:table_lookup(Args, <<"x-queue-master-locator">>) of
66 {_Type, Strategy} ->
6767 case rabbit_queue_location_validator:validate_strategy(Strategy) of
6868 Reply = {ok, _CB} -> Reply;
6969 Error -> Error
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% We use a gen_server simply so that during the terminate/2 call
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_registry).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_restartable_sup).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_router).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_runtime_parameters).
2626 %%
2727 %% The most obvious use case for runtime parameters is policies but
2828 %% there are others:
29 %%
29 %%
3030 %% * Plugin-specific parameters that only make sense at runtime,
3131 %% e.g. Federation and Shovel link settings
3232 %% * Exchange and queue decorators
3939 %% Parameters are stored in Mnesia and can be global. Their changes
4040 %% are broadcasted over rabbit_event.
4141 %%
42 %% Global parameters keys are atoms and values are JSON documents.
43 %%
4244 %% See also:
4345 %%
4446 %% * rabbit_policies
5254 list_component/1, list/2, list_formatted/1, list_formatted/3,
5355 lookup/3, value/3, value/4, info_keys/0, clear_component/1]).
5456
55 -export([set_global/2, value_global/1, value_global/2]).
57 -export([parse_set_global/2, set_global/2, value_global/1, value_global/2,
58 list_global/0, list_global_formatted/0, list_global_formatted/2,
59 lookup_global/1, global_info_keys/0, clear_global/1]).
5660
5761 %%----------------------------------------------------------------------------
5862
107111 set(VHost, Component, Name, Term, User) ->
108112 set_any(VHost, Component, Name, Term, User).
109113
110 set_global(Name, Term) ->
111 mnesia_update(Name, Term),
112 event_notify(parameter_set, none, global, [{name, Name},
113 {value, Term}]),
114 parse_set_global(Name, String) ->
115 case rabbit_misc:json_decode(String) of
116 {ok, JSON} -> set_global(Name, rabbit_misc:json_to_term(JSON));
117 error -> {error_string, "JSON decoding error"}
118 end.
119
120 set_global(Name, Term) ->
121 NameAsAtom = rabbit_data_coercion:to_atom(Name),
122 mnesia_update(NameAsAtom, Term),
123 event_notify(parameter_set, none, global, [{name, NameAsAtom},
124 {value, Term}]),
114125 ok.
115126
116127 format_error(L) ->
166177 clear(VHost, Component, Name) ->
167178 clear_any(VHost, Component, Name).
168179
180 clear_global(Key) ->
181 KeyAsAtom = rabbit_data_coercion:to_atom(Key),
182 Notify = fun() ->
183 event_notify(parameter_set, none, global, [{name, KeyAsAtom}]),
184 ok
185 end,
186 case value_global(KeyAsAtom) of
187 not_found ->
188 {error_string, "Parameter does not exist"};
189 _ ->
190 F = fun () ->
191 ok = mnesia:delete(?TABLE, KeyAsAtom, write)
192 end,
193 ok = rabbit_misc:execute_mnesia_transaction(F),
194 case mnesia:is_transaction() of
195 true -> Notify;
196 false -> Notify()
197 end
198 end.
199
169200 clear_component(Component) ->
170201 case rabbit_runtime_parameters:list_component(Component) of
171202 [] ->
233264 Comp =/= <<"policy">> orelse Component =:= <<"policy">>]
234265 end).
235266
267 list_global() ->
268 %% list only atom keys
269 mnesia:async_dirty(
270 fun () ->
271 Match = #runtime_parameters{key = '_', _ = '_'},
272 [p(P) || P <- mnesia:match_object(?TABLE, Match, read),
273 is_atom(P#runtime_parameters.key)]
274 end).
275
236276 list_formatted(VHost) ->
237277 [pset(value, format(pget(value, P)), P) || P <- list(VHost)].
238278
239279 list_formatted(VHost, Ref, AggregatorPid) ->
240280 rabbit_control_misc:emitting_map(
241 AggregatorPid, Ref,
242 fun(P) -> pset(value, format(pget(value, P)), P) end, list(VHost)).
281 AggregatorPid, Ref,
282 fun(P) -> pset(value, format(pget(value, P)), P) end, list(VHost)).
283
284 list_global_formatted() ->
285 [pset(value, format(pget(value, P)), P) || P <- list_global()].
286
287 list_global_formatted(Ref, AggregatorPid) ->
288 rabbit_control_misc:emitting_map(
289 AggregatorPid, Ref,
290 fun(P) -> pset(value, format(pget(value, P)), P) end, list_global()).
243291
244292 lookup(VHost, Component, Name) ->
245293 case lookup0({VHost, Component, Name}, rabbit_misc:const(not_found)) of
247295 Params -> p(Params)
248296 end.
249297
298 lookup_global(Name) ->
299 case lookup0(Name, rabbit_misc:const(not_found)) of
300 not_found -> not_found;
301 Params -> p(Params)
302 end.
303
250304 value(VHost, Comp, Name) -> value0({VHost, Comp, Name}).
251305 value(VHost, Comp, Name, Def) -> value0({VHost, Comp, Name}, Def).
252306
253 value_global(Key) -> value0(Key).
254 value_global(Key, Default) -> value0(Key, Default).
307 value_global(Key) ->
308 value0(Key).
309
310 value_global(Key, Default) ->
311 value0(Key, Default).
255312
256313 value0(Key) ->
257314 case lookup0(Key, rabbit_misc:const(not_found)) of
288345 [{vhost, VHost},
289346 {component, Component},
290347 {name, Name},
348 {value, Value}];
349
350 p(#runtime_parameters{key = Key, value = Value}) when is_atom(Key) ->
351 [{name, Key},
291352 {value, Value}].
292353
293354 info_keys() -> [component, name, value].
355
356 global_info_keys() -> [name, value].
294357
295358 %%---------------------------------------------------------------------------
296359
304367
305368 format(Term) ->
306369 {ok, JSON} = rabbit_misc:json_encode(rabbit_misc:term_to_json(Term)),
307 list_to_binary(JSON).
370 iolist_to_binary(JSON).
308371
309372 flatten_errors(L) ->
310373 case [{F, A} || I <- lists:flatten([L]), {error, F, A} <- [I]] of
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_sasl_report_file_h).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_ssl).
17
18 -include("rabbit.hrl").
1917
2018 -include_lib("public_key/include/public_key.hrl").
2119
2624
2725 -export_type([certificate/0]).
2826
29 -type certificate() :: binary().
30
31 -spec peer_cert_issuer(certificate()) -> string().
32 -spec peer_cert_subject(certificate()) -> string().
33 -spec peer_cert_validity(certificate()) -> string().
34 -spec peer_cert_subject_items
35 (certificate(), tuple()) -> [string()] | 'not_found'.
36 -spec peer_cert_auth_name
37 (certificate()) -> binary() | 'not_found' | 'unsafe'.
27 -type certificate() :: rabbit_cert_info:certificate().
3828
3929 %%--------------------------------------------------------------------------
4030 %% High-level functions used by reader
4232
4333 %% Return a string describing the certificate's issuer.
4434 peer_cert_issuer(Cert) ->
45 cert_info(fun(#'OTPCertificate' {
46 tbsCertificate = #'OTPTBSCertificate' {
47 issuer = Issuer }}) ->
48 format_rdn_sequence(Issuer)
49 end, Cert).
35 rabbit_cert_info:issuer(Cert).
5036
5137 %% Return a string describing the certificate's subject, as per RFC4514.
5238 peer_cert_subject(Cert) ->
53 cert_info(fun(#'OTPCertificate' {
54 tbsCertificate = #'OTPTBSCertificate' {
55 subject = Subject }}) ->
56 format_rdn_sequence(Subject)
57 end, Cert).
39 rabbit_cert_info:subject(Cert).
5840
5941 %% Return the parts of the certificate's subject.
6042 peer_cert_subject_items(Cert, Type) ->
61 cert_info(fun(#'OTPCertificate' {
62 tbsCertificate = #'OTPTBSCertificate' {
63 subject = Subject }}) ->
64 find_by_type(Type, Subject)
65 end, Cert).
43 rabbit_cert_info:subject_items(Cert, Type).
6644
6745 %% Return a string describing the certificate's validity.
6846 peer_cert_validity(Cert) ->
69 cert_info(fun(#'OTPCertificate' {
70 tbsCertificate = #'OTPTBSCertificate' {
71 validity = {'Validity', Start, End} }}) ->
72 rabbit_misc:format("~s - ~s", [format_asn1_value(Start),
73 format_asn1_value(End)])
74 end, Cert).
47 rabbit_cert_info:validity(Cert).
7548
7649 %% Extract a username from the certificate
50 -spec peer_cert_auth_name
51 (certificate()) -> binary() | 'not_found' | 'unsafe'.
52
7753 peer_cert_auth_name(Cert) ->
7854 {ok, Mode} = application:get_env(rabbit, ssl_cert_login_from),
7955 peer_cert_auth_name(Mode, Cert).
10581 "disabled, verify=~p~n", [V]),
10682 false
10783 end.
108
109 %%--------------------------------------------------------------------------
110
111 cert_info(F, Cert) ->
112 F(case public_key:pkix_decode_cert(Cert, otp) of
113 {ok, DecCert} -> DecCert; %%pre R14B
114 DecCert -> DecCert %%R14B onwards
115 end).
116
117 find_by_type(Type, {rdnSequence, RDNs}) ->
118 case [V || #'AttributeTypeAndValue'{type = T, value = V}
119 <- lists:flatten(RDNs),
120 T == Type] of
121 [] -> not_found;
122 L -> [format_asn1_value(V) || V <- L]
123 end.
124
125 %%--------------------------------------------------------------------------
126 %% Formatting functions
127 %%--------------------------------------------------------------------------
128
129 %% Format and rdnSequence as a RFC4514 subject string.
130 format_rdn_sequence({rdnSequence, Seq}) ->
131 string:join(lists:reverse([format_complex_rdn(RDN) || RDN <- Seq]), ",").
132
133 %% Format an RDN set.
134 format_complex_rdn(RDNs) ->
135 string:join([format_rdn(RDN) || RDN <- RDNs], "+").
136
137 %% Format an RDN. If the type name is unknown, use the dotted decimal
138 %% representation. See RFC4514, section 2.3.
139 format_rdn(#'AttributeTypeAndValue'{type = T, value = V}) ->
140 FV = escape_rdn_value(format_asn1_value(V)),
141 Fmts = [{?'id-at-surname' , "SN"},
142 {?'id-at-givenName' , "GIVENNAME"},
143 {?'id-at-initials' , "INITIALS"},
144 {?'id-at-generationQualifier' , "GENERATIONQUALIFIER"},
145 {?'id-at-commonName' , "CN"},
146 {?'id-at-localityName' , "L"},
147 {?'id-at-stateOrProvinceName' , "ST"},
148 {?'id-at-organizationName' , "O"},
149 {?'id-at-organizationalUnitName' , "OU"},
150 {?'id-at-title' , "TITLE"},
151 {?'id-at-countryName' , "C"},
152 {?'id-at-serialNumber' , "SERIALNUMBER"},
153 {?'id-at-pseudonym' , "PSEUDONYM"},
154 {?'id-domainComponent' , "DC"},
155 {?'id-emailAddress' , "EMAILADDRESS"},
156 {?'street-address' , "STREET"},
157 {{0,9,2342,19200300,100,1,1} , "UID"}], %% Not in public_key.hrl
158 case proplists:lookup(T, Fmts) of
159 {_, Fmt} ->
160 rabbit_misc:format(Fmt ++ "=~s", [FV]);
161 none when is_tuple(T) ->
162 TypeL = [rabbit_misc:format("~w", [X]) || X <- tuple_to_list(T)],
163 rabbit_misc:format("~s=~s", [string:join(TypeL, "."), FV]);
164 none ->
165 rabbit_misc:format("~p=~s", [T, FV])
166 end.
167
168 %% Escape a string as per RFC4514.
169 escape_rdn_value(V) ->
170 escape_rdn_value(V, start).
171
172 escape_rdn_value([], _) ->
173 [];
174 escape_rdn_value([C | S], start) when C =:= $ ; C =:= $# ->
175 [$\\, C | escape_rdn_value(S, middle)];
176 escape_rdn_value(S, start) ->
177 escape_rdn_value(S, middle);
178 escape_rdn_value([$ ], middle) ->
179 [$\\, $ ];
180 escape_rdn_value([C | S], middle) when C =:= $"; C =:= $+; C =:= $,; C =:= $;;
181 C =:= $<; C =:= $>; C =:= $\\ ->
182 [$\\, C | escape_rdn_value(S, middle)];
183 escape_rdn_value([C | S], middle) when C < 32 ; C >= 126 ->
184 %% Of ASCII characters only U+0000 needs escaping, but for display
185 %% purposes it's handy to escape all non-printable chars. All non-ASCII
186 %% characters get converted to UTF-8 sequences and then escaped. We've
187 %% already got a UTF-8 sequence here, so just escape it.
188 rabbit_misc:format("\\~2.16.0B", [C]) ++ escape_rdn_value(S, middle);
189 escape_rdn_value([C | S], middle) ->
190 [C | escape_rdn_value(S, middle)].
191
192 %% Get the string representation of an OTPCertificate field.
193 format_asn1_value({ST, S}) when ST =:= teletexString; ST =:= printableString;
194 ST =:= universalString; ST =:= utf8String;
195 ST =:= bmpString ->
196 format_directory_string(ST, S);
197 format_asn1_value({utcTime, [Y1, Y2, M1, M2, D1, D2, H1, H2,
198 Min1, Min2, S1, S2, $Z]}) ->
199 rabbit_misc:format("20~c~c-~c~c-~c~cT~c~c:~c~c:~c~cZ",
200 [Y1, Y2, M1, M2, D1, D2, H1, H2, Min1, Min2, S1, S2]);
201 %% We appear to get an untagged value back for an ia5string
202 %% (e.g. domainComponent).
203 format_asn1_value(V) when is_list(V) ->
204 V;
205 format_asn1_value(V) when is_binary(V) ->
206 %% OTP does not decode some values when combined with an unknown
207 %% type. That's probably wrong, so as a last ditch effort let's
208 %% try manually decoding. 'DirectoryString' is semi-arbitrary -
209 %% but it is the type which covers the various string types we
210 %% handle below.
211 try
212 {ST, S} = public_key:der_decode('DirectoryString', V),
213 format_directory_string(ST, S)
214 catch _:_ ->
215 rabbit_misc:format("~p", [V])
216 end;
217 format_asn1_value(V) ->
218 rabbit_misc:format("~p", [V]).
219
220 %% DirectoryString { INTEGER : maxSize } ::= CHOICE {
221 %% teletexString TeletexString (SIZE (1..maxSize)),
222 %% printableString PrintableString (SIZE (1..maxSize)),
223 %% bmpString BMPString (SIZE (1..maxSize)),
224 %% universalString UniversalString (SIZE (1..maxSize)),
225 %% uTF8String UTF8String (SIZE (1..maxSize)) }
226 %%
227 %% Precise definitions of printable / teletexString are hard to come
228 %% by. This is what I reconstructed:
229 %%
230 %% printableString:
231 %% "intended to represent the limited character sets available to
232 %% mainframe input terminals"
233 %% A-Z a-z 0-9 ' ( ) + , - . / : = ? [space]
234 %% http://msdn.microsoft.com/en-us/library/bb540814(v=vs.85).aspx
235 %%
236 %% teletexString:
237 %% "a sizable volume of software in the world treats TeletexString
238 %% (T61String) as a simple 8-bit string with mostly Windows Latin 1
239 %% (superset of iso-8859-1) encoding"
240 %% http://www.mail-archive.com/asn1@asn1.org/msg00460.html
241 %%
242 %% (However according to that link X.680 actually defines
243 %% TeletexString in some much more involved and crazy way. I suggest
244 %% we treat it as ISO-8859-1 since Erlang does not support Windows
245 %% Latin 1).
246 %%
247 %% bmpString:
248 %% UCS-2 according to RFC 3641. Hence cannot represent Unicode
249 %% characters above 65535 (outside the "Basic Multilingual Plane").
250 %%
251 %% universalString:
252 %% UCS-4 according to RFC 3641.
253 %%
254 %% utf8String:
255 %% UTF-8 according to RFC 3641.
256 %%
257 %% Within Rabbit we assume UTF-8 encoding. Since printableString is a
258 %% subset of ASCII it is also a subset of UTF-8. The others need
259 %% converting. Fortunately since the Erlang SSL library does the
260 %% decoding for us (albeit into a weird format, see below), we just
261 %% need to handle encoding into UTF-8. Note also that utf8Strings come
262 %% back as binary.
263 %%
264 %% Note for testing: the default Ubuntu configuration for openssl will
265 %% only create printableString or teletexString types no matter what
266 %% you do. Edit string_mask in the [req] section of
267 %% /etc/ssl/openssl.cnf to change this (see comments there). You
268 %% probably also need to set utf8 = yes to get it to accept UTF-8 on
269 %% the command line. Also note I could not get openssl to generate a
270 %% universalString.
271
272 format_directory_string(printableString, S) -> S;
273 format_directory_string(teletexString, S) -> utf8_list_from(S);
274 format_directory_string(bmpString, S) -> utf8_list_from(S);
275 format_directory_string(universalString, S) -> utf8_list_from(S);
276 format_directory_string(utf8String, S) -> binary_to_list(S).
277
278 utf8_list_from(S) ->
279 binary_to_list(
280 unicode:characters_to_binary(flatten_ssl_list(S), utf32, utf8)).
281
282 %% The Erlang SSL implementation invents its own representation for
283 %% non-ascii strings - looking like [97,{0,0,3,187}] (that's LATIN
284 %% SMALL LETTER A followed by GREEK SMALL LETTER LAMDA). We convert
285 %% this into a list of unicode characters, which we can tell
286 %% unicode:characters_to_binary is utf32.
287
288 flatten_ssl_list(L) -> [flatten_ssl_list_item(I) || I <- L].
289
290 flatten_ssl_list_item({A, B, C, D}) ->
291 A * (1 bsl 24) + B * (1 bsl 16) + C * (1 bsl 8) + D;
292 flatten_ssl_list_item(N) when is_number (N) ->
293 N.
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_sup).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_table).
1717
18 -export([create/0, create_local_copy/1, wait_for_replicated/0, wait/1,
18 -export([create/0, create_local_copy/1, wait_for_replicated/1, wait/1,
1919 force_load/0, is_present/0, is_empty/0, needs_default_data/0,
20 check_schema_integrity/0, clear_ram_only_tables/0, wait_timeout/0]).
20 check_schema_integrity/1, clear_ram_only_tables/0, retry_timeout/0,
21 wait_for_replicated/0]).
2122
2223 -include("rabbit.hrl").
2324
2425 %%----------------------------------------------------------------------------
26 -type retry() :: boolean().
2527
2628 -spec create() -> 'ok'.
2729 -spec create_local_copy('disc' | 'ram') -> 'ok'.
30 -spec wait_for_replicated(retry()) -> 'ok'.
2831 -spec wait_for_replicated() -> 'ok'.
2932 -spec wait([atom()]) -> 'ok'.
30 -spec wait_timeout() -> non_neg_integer() | infinity.
33 -spec retry_timeout() -> {non_neg_integer() | infinity, non_neg_integer()}.
3134 -spec force_load() -> 'ok'.
3235 -spec is_present() -> boolean().
3336 -spec is_empty() -> boolean().
3437 -spec needs_default_data() -> boolean().
35 -spec check_schema_integrity() -> rabbit_types:ok_or_error(any()).
38 -spec check_schema_integrity(retry()) -> rabbit_types:ok_or_error(any()).
3639 -spec clear_ram_only_tables() -> 'ok'.
3740
3841 %%----------------------------------------------------------------------------
6265 create_local_copies(ram),
6366 create_local_copy(schema, ram_copies).
6467
68 %% This arity only exists for backwards compatibility with certain
69 %% plugins. See https://github.com/rabbitmq/rabbitmq-clusterer/issues/19.
6570 wait_for_replicated() ->
71 wait_for_replicated(false).
72
73 wait_for_replicated(Retry) ->
6674 wait([Tab || {Tab, TabDef} <- definitions(),
67 not lists:member({local_content, true}, TabDef)]).
75 not lists:member({local_content, true}, TabDef)], Retry).
6876
6977 wait(TableNames) ->
78 wait(TableNames, _Retry = false).
79
80 wait(TableNames, Retry) ->
81 {Timeout, Retries} = retry_timeout(Retry),
82 wait(TableNames, Timeout, Retries).
83
84 wait(TableNames, Timeout, Retries) ->
7085 %% We might be in ctl here for offline ops, in which case we can't
7186 %% get_env() for the rabbit app.
72 Timeout = wait_timeout(),
73 case mnesia:wait_for_tables(TableNames, Timeout) of
74 ok ->
87 rabbit_log:info("Waiting for Mnesia tables for ~p ms, ~p retries left~n",
88 [Timeout, Retries - 1]),
89 Result = case mnesia:wait_for_tables(TableNames, Timeout) of
90 ok ->
91 ok;
92 {timeout, BadTabs} ->
93 {error, {timeout_waiting_for_tables, BadTabs}};
94 {error, Reason} ->
95 {error, {failed_waiting_for_tables, Reason}}
96 end,
97 case {Retries, Result} of
98 {_, ok} ->
7599 ok;
76 {timeout, BadTabs} ->
77 throw({error, {timeout_waiting_for_tables, BadTabs}});
78 {error, Reason} ->
79 throw({error, {failed_waiting_for_tables, Reason}})
80 end.
81
82 wait_timeout() ->
83 case application:get_env(rabbit, mnesia_table_loading_timeout) of
100 {1, {error, _} = Error} ->
101 throw(Error);
102 {_, {error, Error}} ->
103 rabbit_log:warning("Error while waiting for Mnesia tables: ~p~n", [Error]),
104 wait(TableNames, Timeout, Retries - 1);
105 _ ->
106 wait(TableNames, Timeout, Retries - 1)
107 end.
108
109 retry_timeout(_Retry = false) ->
110 {retry_timeout(), 1};
111 retry_timeout(_Retry = true) ->
112 Retries = case application:get_env(rabbit, mnesia_table_loading_retry_limit) of
113 {ok, T} -> T;
114 undefined -> 10
115 end,
116 {retry_timeout(), Retries}.
117
118 retry_timeout() ->
119 case application:get_env(rabbit, mnesia_table_loading_retry_timeout) of
84120 {ok, T} -> T;
85121 undefined -> 30000
86122 end.
97133 lists:all(fun (Tab) -> mnesia:dirty_first(Tab) == '$end_of_table' end,
98134 Names).
99135
100 check_schema_integrity() ->
136 check_schema_integrity(Retry) ->
101137 Tables = mnesia:system_info(tables),
102138 case check(fun (Tab, TabDef) ->
103139 case lists:member(Tab, Tables) of
105141 true -> check_attributes(Tab, TabDef)
106142 end
107143 end) of
108 ok -> ok = wait(names()),
144 ok -> wait(names(), Retry),
109145 check(fun check_content/2);
110146 Other -> Other
111147 end.
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_trace).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_upgrade).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_upgrade_functions).
1818 %% If you are tempted to add include("rabbit.hrl"). here, don't. Using record
1919 %% defs here leads to pain later.
2020
21 -compile([export_all]).
21 -compile([nowarn_export_all, export_all]).
2222
2323 -rabbit_upgrade({remove_user_scope, mnesia, []}).
2424 -rabbit_upgrade({hash_passwords, mnesia, []}).
2525 -rabbit_upgrade({add_ip_to_listener, mnesia, []}).
26 -rabbit_upgrade({add_opts_to_listener, mnesia, [add_ip_to_listener]}).
2627 -rabbit_upgrade({internal_exchanges, mnesia, []}).
2728 -rabbit_upgrade({user_to_internal_user, mnesia, [hash_passwords]}).
2829 -rabbit_upgrade({topic_trie, mnesia, []}).
5859 -spec remove_user_scope() -> 'ok'.
5960 -spec hash_passwords() -> 'ok'.
6061 -spec add_ip_to_listener() -> 'ok'.
62 -spec add_opts_to_listener() -> 'ok'.
6163 -spec internal_exchanges() -> 'ok'.
6264 -spec user_to_internal_user() -> 'ok'.
6365 -spec topic_trie() -> 'ok'.
122124 end,
123125 [node, protocol, host, ip_address, port]).
124126
127 add_opts_to_listener() ->
128 transform(
129 rabbit_listener,
130 fun ({listener, Node, Protocol, Host, IP, Port}) ->
131 {listener, Node, Protocol, Host, IP, Port, []}
132 end,
133 [node, protocol, host, ip_address, port, opts]).
134
125135 internal_exchanges() ->
126136 Tables = [rabbit_exchange, rabbit_durable_exchange],
127137 AddInternalFun =
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_variable_queue).
282282 unacked_bytes,
283283 persistent_count, %% w unacked
284284 persistent_bytes, %% w unacked
285 delta_transient_bytes, %%
285286
286287 target_ram_count,
287288 ram_msg_count, %% w/o unacked
329330 -record(delta,
330331 { start_seq_id, %% start_seq_id is inclusive
331332 count,
333 transient,
332334 end_seq_id %% end_seq_id is exclusive
333335 }).
334336
414416
415417 -define(BLANK_DELTA, #delta { start_seq_id = undefined,
416418 count = 0,
419 transient = 0,
417420 end_seq_id = undefined }).
418421 -define(BLANK_DELTA_PATTERN(Z), #delta { start_seq_id = Z,
419422 count = 0,
423 transient = 0,
420424 end_seq_id = Z }).
421425
422426 -define(MICROS_PER_SECOND, 1000000.0).
431435 %% rabbit_amqqueue_process need fairly fresh rates.
432436 -define(MSGS_PER_RATE_CALC, 100).
433437
434
435438 %% we define the garbage collector threshold
436 %% it needs to tune the GC calls inside `reduce_memory_use`
437 %% see: rabbitmq-server-973 and `maybe_execute_gc` function
438 -define(DEFAULT_EXPLICIT_GC_RUN_OP_THRESHOLD, 250).
439 -define(EXPLICIT_GC_RUN_OP_THRESHOLD,
439 %% it needs to tune the `reduce_memory_use` calls. Thus, the garbage collection.
440 %% see: rabbitmq-server-973 and rabbitmq-server-964
441 -define(DEFAULT_EXPLICIT_GC_RUN_OP_THRESHOLD, 1000).
442 -define(EXPLICIT_GC_RUN_OP_THRESHOLD(Mode),
440443 case get(explicit_gc_run_operation_threshold) of
441444 undefined ->
442 Val = rabbit_misc:get_env(rabbit, lazy_queue_explicit_gc_run_operation_threshold,
443 ?DEFAULT_EXPLICIT_GC_RUN_OP_THRESHOLD),
445 Val = explicit_gc_run_operation_threshold_for_mode(Mode),
444446 put(explicit_gc_run_operation_threshold, Val),
445447 Val;
446448 Val -> Val
447449 end).
450
451 explicit_gc_run_operation_threshold_for_mode(Mode) ->
452 {Key, Fallback} = case Mode of
453 lazy -> {lazy_queue_explicit_gc_run_operation_threshold,
454 ?DEFAULT_EXPLICIT_GC_RUN_OP_THRESHOLD};
455 _ -> {queue_explicit_gc_run_operation_threshold,
456 ?DEFAULT_EXPLICIT_GC_RUN_OP_THRESHOLD}
457 end,
458 rabbit_misc:get_env(rabbit, Key, Fallback).
448459
449460 %%----------------------------------------------------------------------------
450461 %% Public API
585596 publish1(Msg, MsgProps, IsDelivered, ChPid, Flow,
586597 fun maybe_write_to_disk/4,
587598 State),
588 a(reduce_memory_use(maybe_update_rates(State1))).
599 a(maybe_reduce_memory_use(maybe_update_rates(State1))).
589600
590601 batch_publish(Publishes, ChPid, Flow, State) ->
591602 {ChPid, Flow, State1} =
592603 lists:foldl(fun batch_publish1/2, {ChPid, Flow, State}, Publishes),
593604 State2 = ui(State1),
594 a(reduce_memory_use(maybe_update_rates(State2))).
605 a(maybe_reduce_memory_use(maybe_update_rates(State2))).
595606
596607 publish_delivered(Msg, MsgProps, ChPid, Flow, State) ->
597608 {SeqId, State1} =
598609 publish_delivered1(Msg, MsgProps, ChPid, Flow,
599610 fun maybe_write_to_disk/4,
600611 State),
601 {SeqId, a(reduce_memory_use(maybe_update_rates(State1)))}.
612 {SeqId, a(maybe_reduce_memory_use(maybe_update_rates(State1)))}.
602613
603614 batch_publish_delivered(Publishes, ChPid, Flow, State) ->
604615 {ChPid, Flow, SeqIds, State1} =
605616 lists:foldl(fun batch_publish_delivered1/2,
606617 {ChPid, Flow, [], State}, Publishes),
607618 State2 = ui(State1),
608 {lists:reverse(SeqIds), a(reduce_memory_use(maybe_update_rates(State2)))}.
619 {lists:reverse(SeqIds), a(maybe_reduce_memory_use(maybe_update_rates(State2)))}.
609620
610621 discard(_MsgId, _ChPid, _Flow, State) -> State.
611622
709720 {Delta1, MsgIds2, State3} = delta_merge(SeqIds1, Delta, MsgIds1,
710721 State2),
711722 MsgCount = length(MsgIds2),
712 {MsgIds2, a(reduce_memory_use(
723 {MsgIds2, a(maybe_reduce_memory_use(
713724 maybe_update_rates(ui(
714725 State3 #vqstate { delta = Delta1,
715726 q3 = Q3a,
727738 {Delta1, MsgIds1, State2} = delta_merge(SeqIds, Delta, MsgIds,
728739 State1),
729740 MsgCount = length(MsgIds1),
730 {MsgIds1, a(reduce_memory_use(
741 {MsgIds1, a(maybe_reduce_memory_use(
731742 maybe_update_rates(ui(
732743 State2 #vqstate { delta = Delta1,
733744 q3 = Q3a,
777788 (TargetRamCount =/= infinity andalso
778789 TargetRamCount1 >= TargetRamCount) of
779790 true -> State1;
780 false -> reduce_memory_use(State1)
791 false -> maybe_reduce_memory_use(State1)
781792 end).
782793
783794 maybe_update_rates(State = #vqstate{ in_counter = InCount,
859870 handle_pre_hibernate(State = #vqstate { index_state = IndexState }) ->
860871 State #vqstate { index_state = rabbit_queue_index:flush(IndexState) }.
861872
862 resume(State) -> a(reduce_memory_use(State)).
873 resume(State) -> a(maybe_reduce_memory_use(State)).
863874
864875 msg_rates(#vqstate { rates = #rates { in = AvgIngressRate,
865876 out = AvgEgressRate } }) ->
874885 info(messages_ready_ram, State) + info(messages_unacknowledged_ram, State);
875886 info(messages_persistent, #vqstate{persistent_count = PersistentCount}) ->
876887 PersistentCount;
888 info(messages_paged_out, #vqstate{delta = #delta{transient = Count}}) ->
889 Count;
877890 info(message_bytes, #vqstate{bytes = Bytes,
878891 unacked_bytes = UBytes}) ->
879892 Bytes + UBytes;
885898 RamBytes;
886899 info(message_bytes_persistent, #vqstate{persistent_bytes = PersistentBytes}) ->
887900 PersistentBytes;
901 info(message_bytes_paged_out, #vqstate{delta_transient_bytes = PagedOutBytes}) ->
902 PagedOutBytes;
888903 info(head_message_timestamp, #vqstate{
889904 q3 = Q3,
890905 q4 = Q4,
12411256 rabbit_queue_index:deliver([SeqId], IndexState).
12421257
12431258 betas_from_index_entries(List, TransientThreshold, DelsAndAcksFun, State) ->
1244 {Filtered, Delivers, Acks, RamReadyCount, RamBytes} =
1259 {Filtered, Delivers, Acks, RamReadyCount, RamBytes, TransientCount, TransientBytes} =
12451260 lists:foldr(
12461261 fun ({_MsgOrId, SeqId, _MsgProps, IsPersistent, IsDelivered} = M,
1247 {Filtered1, Delivers1, Acks1, RRC, RB} = Acc) ->
1262 {Filtered1, Delivers1, Acks1, RRC, RB, TC, TB} = Acc) ->
12481263 case SeqId < TransientThreshold andalso not IsPersistent of
12491264 true -> {Filtered1,
12501265 cons_if(not IsDelivered, SeqId, Delivers1),
1251 [SeqId | Acks1], RRC, RB};
1266 [SeqId | Acks1], RRC, RB, TC, TB};
12521267 false -> MsgStatus = m(beta_msg_status(M)),
12531268 HaveMsg = msg_in_ram(MsgStatus),
12541269 Size = msg_size(MsgStatus),
12561271 false -> {?QUEUE:in_r(MsgStatus, Filtered1),
12571272 Delivers1, Acks1,
12581273 RRC + one_if(HaveMsg),
1259 RB + one_if(HaveMsg) * Size};
1274 RB + one_if(HaveMsg) * Size,
1275 TC + one_if(not IsPersistent),
1276 TB + one_if(not IsPersistent) * Size};
12601277 true -> Acc %% [0]
12611278 end
12621279 end
1263 end, {?QUEUE:new(), [], [], 0, 0}, List),
1264 {Filtered, RamReadyCount, RamBytes, DelsAndAcksFun(Delivers, Acks, State)}.
1280 end, {?QUEUE:new(), [], [], 0, 0, 0, 0}, List),
1281 {Filtered, RamReadyCount, RamBytes, DelsAndAcksFun(Delivers, Acks, State),
1282 TransientCount, TransientBytes}.
12651283 %% [0] We don't increase RamBytes here, even though it pertains to
12661284 %% unacked messages too, since if HaveMsg then the message must have
12671285 %% been stored in the QI, thus the message must have been in
12741292 gb_trees:is_defined(SeqId, DPA) orelse
12751293 gb_trees:is_defined(SeqId, QPA)).
12761294
1277 expand_delta(SeqId, ?BLANK_DELTA_PATTERN(X)) ->
1278 d(#delta { start_seq_id = SeqId, count = 1, end_seq_id = SeqId + 1 });
1295 expand_delta(SeqId, ?BLANK_DELTA_PATTERN(X), IsPersistent) ->
1296 d(#delta { start_seq_id = SeqId, count = 1, end_seq_id = SeqId + 1,
1297 transient = one_if(not IsPersistent)});
12791298 expand_delta(SeqId, #delta { start_seq_id = StartSeqId,
1280 count = Count } = Delta)
1299 count = Count,
1300 transient = Transient } = Delta,
1301 IsPersistent )
12811302 when SeqId < StartSeqId ->
1282 d(Delta #delta { start_seq_id = SeqId, count = Count + 1 });
1303 d(Delta #delta { start_seq_id = SeqId, count = Count + 1,
1304 transient = Transient + one_if(not IsPersistent)});
12831305 expand_delta(SeqId, #delta { count = Count,
1284 end_seq_id = EndSeqId } = Delta)
1306 end_seq_id = EndSeqId,
1307 transient = Transient } = Delta,
1308 IsPersistent)
12851309 when SeqId >= EndSeqId ->
1286 d(Delta #delta { count = Count + 1, end_seq_id = SeqId + 1 });
1287 expand_delta(_SeqId, #delta { count = Count } = Delta) ->
1288 d(Delta #delta { count = Count + 1 }).
1310 d(Delta #delta { count = Count + 1, end_seq_id = SeqId + 1,
1311 transient = Transient + one_if(not IsPersistent)});
1312 expand_delta(_SeqId, #delta { count = Count,
1313 transient = Transient } = Delta,
1314 IsPersistent ) ->
1315 d(Delta #delta { count = Count + 1,
1316 transient = Transient + one_if(not IsPersistent) }).
12891317
12901318 %%----------------------------------------------------------------------------
12911319 %% Internal major helpers for Public API
13071335 true -> ?BLANK_DELTA;
13081336 false -> d(#delta { start_seq_id = LowSeqId,
13091337 count = DeltaCount1,
1338 transient = 0,
13101339 end_seq_id = NextSeqId })
13111340 end,
13121341 Now = time_compat:monotonic_time(),
13351364 persistent_count = DeltaCount1,
13361365 bytes = DeltaBytes1,
13371366 persistent_bytes = DeltaBytes1,
1367 delta_transient_bytes = 0,
13381368
13391369 target_ram_count = infinity,
13401370 ram_msg_count = 0,
13741404 false -> {Msg, State1 = #vqstate { q4 = Q4a }} =
13751405 read_msg(MsgStatus, State),
13761406 MsgStatus1 = MsgStatus#msg_status{msg = Msg},
1377 stats(ready0, {MsgStatus, MsgStatus1},
1407 stats(ready0, {MsgStatus, MsgStatus1}, 0,
13781408 State1 #vqstate { q4 = ?QUEUE:in_r(MsgStatus1, Q4a) })
13791409 end;
13801410 in_r(MsgStatus,
13811411 State = #vqstate { mode = default, q4 = Q4 }) ->
13821412 State #vqstate { q4 = ?QUEUE:in_r(MsgStatus, Q4) };
13831413 %% lazy queues
1384 in_r(MsgStatus = #msg_status { seq_id = SeqId },
1414 in_r(MsgStatus = #msg_status { seq_id = SeqId, is_persistent = IsPersistent },
13851415 State = #vqstate { mode = lazy, q3 = Q3, delta = Delta}) ->
13861416 case ?QUEUE:is_empty(Q3) of
13871417 true ->
13881418 {_MsgStatus1, State1} =
13891419 maybe_write_to_disk(true, true, MsgStatus, State),
1390 State2 = stats(ready0, {MsgStatus, none}, State1),
1391 Delta1 = expand_delta(SeqId, Delta),
1392 State2 #vqstate{ delta = Delta1 };
1420 State2 = stats(ready0, {MsgStatus, none}, 1, State1),
1421 Delta1 = expand_delta(SeqId, Delta, IsPersistent),
1422 State2 #vqstate{ delta = Delta1};
13931423 false ->
13941424 State #vqstate { q3 = ?QUEUE:in_r(MsgStatus, Q3) }
13951425 end.
14251455 {Msg, State #vqstate {msg_store_clients = MSCState1,
14261456 disk_read_count = Count + 1}}.
14271457
1428 stats(Signs, Statuses, State) ->
1429 stats0(expand_signs(Signs), expand_statuses(Statuses), State).
1458 stats(Signs, Statuses, DeltaPaged, State) ->
1459 stats0(expand_signs(Signs), expand_statuses(Statuses), DeltaPaged, State).
14301460
14311461 expand_signs(ready0) -> {0, 0, true};
14321462 expand_signs(lazy_pub) -> {1, 0, true};
14411471 %% contains "Ready" or "Unacked" iff that is what it counts. If
14421472 %% neither is present it counts both.
14431473 stats0({DeltaReady, DeltaUnacked, ReadyMsgPaged},
1444 {InRamBefore, InRamAfter, MsgStatus},
1474 {InRamBefore, InRamAfter, MsgStatus}, DeltaPaged,
14451475 State = #vqstate{len = ReadyCount,
14461476 bytes = ReadyBytes,
14471477 ram_msg_count = RamReadyCount,
14481478 persistent_count = PersistentCount,
14491479 unacked_bytes = UnackedBytes,
14501480 ram_bytes = RamBytes,
1481 delta_transient_bytes = DeltaBytes,
14511482 persistent_bytes = PersistentBytes}) ->
14521483 S = msg_size(MsgStatus),
14531484 DeltaTotal = DeltaReady + DeltaUnacked,
14701501 bytes = ReadyBytes + DeltaReady * S,
14711502 unacked_bytes = UnackedBytes + DeltaUnacked * S,
14721503 ram_bytes = RamBytes + DeltaRam * S,
1473 persistent_bytes = PersistentBytes + DeltaPersistent * S}.
1504 persistent_bytes = PersistentBytes + DeltaPersistent * S,
1505 delta_transient_bytes = DeltaBytes + DeltaPaged * one_if(not MsgStatus#msg_status.is_persistent) * S}.
14741506
14751507 msg_size(#msg_status{msg_props = #message_properties{size = Size}}) -> Size.
14761508
14921524 MsgStatus #msg_status {
14931525 is_delivered = true }, State),
14941526
1495 State2 = stats({-1, 1}, {MsgStatus, MsgStatus}, State1),
1527 State2 = stats({-1, 1}, {MsgStatus, MsgStatus}, 0, State1),
14961528
14971529 {SeqId, maybe_update_rates(
14981530 State2 #vqstate {out_counter = OutCount + 1,
15281560 false -> IndexState1
15291561 end,
15301562
1531 State1 = stats({-1, 0}, {MsgStatus, none}, State),
1563 State1 = stats({-1, 0}, {MsgStatus, none}, 0, State),
15321564
15331565 {undefined, maybe_update_rates(
15341566 State1 #vqstate {out_counter = OutCount + 1,
16121644 is_delivered = true }, State1),
16131645 {cons_if(IndexOnDisk andalso not IsDelivered, SeqId, Delivers),
16141646 Fun(Msg, SeqId, FetchAcc),
1615 stats({-1, 1}, {MsgStatus, MsgStatus}, State2)}.
1647 stats({-1, 1}, {MsgStatus, MsgStatus}, 0, State2)}.
16161648
16171649 collect_by_predicate(Pred, QAcc, State) ->
16181650 case queue_out(State) of
17141746 end,
17151747 cons_if(IndexOnDisk andalso not IsDelivered, SeqId, Delivers),
17161748 cons_if(IndexOnDisk, SeqId, Acks),
1717 stats({-1, 0}, {MsgStatus, none}, State)}.
1749 stats({-1, 0}, {MsgStatus, none}, 0, State)}.
17181750
17191751 process_delivers_and_acks_fun(deliver_and_ack) ->
17201752 fun (Delivers, Acks, State = #vqstate { index_state = IndexState }) ->
17511783 end,
17521784 InCount1 = InCount + 1,
17531785 UC1 = gb_sets_maybe_insert(NeedsConfirming, MsgId, UC),
1754 stats({1, 0}, {none, MsgStatus1},
1786 stats({1, 0}, {none, MsgStatus1}, 0,
17551787 State2#vqstate{ next_seq_id = SeqId + 1,
17561788 in_counter = InCount1,
17571789 unconfirmed = UC1 });
17641796 in_counter = InCount,
17651797 durable = IsDurable,
17661798 unconfirmed = UC,
1767 delta = Delta }) ->
1799 delta = Delta}) ->
17681800 IsPersistent1 = IsDurable andalso IsPersistent,
17691801 MsgStatus = msg_status(IsPersistent1, IsDelivered, SeqId, Msg, MsgProps, IndexMaxSize),
17701802 {MsgStatus1, State1} = PersistFun(true, true, MsgStatus, State),
1771 Delta1 = expand_delta(SeqId, Delta),
1803 Delta1 = expand_delta(SeqId, Delta, IsPersistent),
17721804 UC1 = gb_sets_maybe_insert(NeedsConfirming, MsgId, UC),
1773 stats(lazy_pub, {lazy, m(MsgStatus1)},
1805 stats(lazy_pub, {lazy, m(MsgStatus1)}, 1,
17741806 State1#vqstate{ delta = Delta1,
17751807 next_seq_id = SeqId + 1,
17761808 in_counter = InCount + 1,
1777 unconfirmed = UC1 }).
1809 unconfirmed = UC1}).
17781810
17791811 batch_publish1({Msg, MsgProps, IsDelivered}, {ChPid, Flow, State}) ->
17801812 {ChPid, Flow, publish1(Msg, MsgProps, IsDelivered, ChPid, Flow,
17971829 {MsgStatus1, State1} = PersistFun(false, false, MsgStatus, State),
17981830 State2 = record_pending_ack(m(MsgStatus1), State1),
17991831 UC1 = gb_sets_maybe_insert(NeedsConfirming, MsgId, UC),
1800 State3 = stats({0, 1}, {none, MsgStatus1},
1832 State3 = stats({0, 1}, {none, MsgStatus1}, 0,
18011833 State2 #vqstate { next_seq_id = SeqId + 1,
18021834 out_counter = OutCount + 1,
18031835 in_counter = InCount + 1,
18201852 {MsgStatus1, State1} = PersistFun(true, true, MsgStatus, State),
18211853 State2 = record_pending_ack(m(MsgStatus1), State1),
18221854 UC1 = gb_sets_maybe_insert(NeedsConfirming, MsgId, UC),
1823 State3 = stats({0, 1}, {none, MsgStatus1},
1855 State3 = stats({0, 1}, {none, MsgStatus1}, 0,
18241856 State2 #vqstate { next_seq_id = SeqId + 1,
18251857 out_counter = OutCount + 1,
18261858 in_counter = InCount + 1,
20082040 {none, _} ->
20092041 {none, State};
20102042 {MsgStatus, State1} ->
2011 {MsgStatus, stats({0, -1}, {MsgStatus, none}, State1)}
2043 {MsgStatus, stats({0, -1}, {MsgStatus, none}, 0, State1)}
20122044 end;
20132045 remove_pending_ack(false, SeqId, State = #vqstate{ram_pending_ack = RPA,
20142046 disk_pending_ack = DPA,
21532185 publish_alpha(#msg_status { msg = undefined } = MsgStatus, State) ->
21542186 {Msg, State1} = read_msg(MsgStatus, State),
21552187 MsgStatus1 = MsgStatus#msg_status { msg = Msg },
2156 {MsgStatus1, stats({1, -1}, {MsgStatus, MsgStatus1}, State1)};
2188 {MsgStatus1, stats({1, -1}, {MsgStatus, MsgStatus1}, 0, State1)};
21572189 publish_alpha(MsgStatus, State) ->
2158 {MsgStatus, stats({1, -1}, {MsgStatus, MsgStatus}, State)}.
2190 {MsgStatus, stats({1, -1}, {MsgStatus, MsgStatus}, 0, State)}.
21592191
21602192 publish_beta(MsgStatus, State) ->
21612193 {MsgStatus1, State1} = maybe_prepare_write_to_disk(true, false, MsgStatus, State),
21622194 MsgStatus2 = m(trim_msg_status(MsgStatus1)),
2163 {MsgStatus2, stats({1, -1}, {MsgStatus, MsgStatus2}, State1)}.
2195 {MsgStatus2, stats({1, -1}, {MsgStatus, MsgStatus2}, 0, State1)}.
21642196
21652197 %% Rebuild queue, inserting sequence ids to maintain ordering
21662198 queue_merge(SeqIds, Q, MsgIds, Limit, PubFun, State) ->
21992231 case msg_from_pending_ack(SeqId, State0) of
22002232 {none, _} ->
22012233 Acc;
2202 {#msg_status { msg_id = MsgId } = MsgStatus, State1} ->
2234 {#msg_status { msg_id = MsgId,
2235 is_persistent = IsPersistent } = MsgStatus, State1} ->
22032236 {_MsgStatus, State2} =
22042237 maybe_prepare_write_to_disk(true, true, MsgStatus, State1),
2205 {expand_delta(SeqId, Delta0), [MsgId | MsgIds0],
2206 stats({1, -1}, {MsgStatus, none}, State2)}
2238 {expand_delta(SeqId, Delta0, IsPersistent), [MsgId | MsgIds0],
2239 stats({1, -1}, {MsgStatus, none}, 1, State2)}
22072240 end
22082241 end, {Delta, MsgIds, State}, SeqIds).
22092242
23092342 %% Phase changes
23102343 %%----------------------------------------------------------------------------
23112344
2312 maybe_execute_gc(State = #vqstate {memory_reduction_run_count = MRedRunCount}) ->
2313 case MRedRunCount >= ?EXPLICIT_GC_RUN_OP_THRESHOLD of
2314 true -> garbage_collect(),
2315 State#vqstate{memory_reduction_run_count = 0};
2316 false -> State#vqstate{memory_reduction_run_count = MRedRunCount + 1}
2317
2345 maybe_reduce_memory_use(State = #vqstate {memory_reduction_run_count = MRedRunCount,
2346 mode = Mode}) ->
2347 case MRedRunCount >= ?EXPLICIT_GC_RUN_OP_THRESHOLD(Mode) of
2348 true -> State1 = reduce_memory_use(State),
2349 State1#vqstate{memory_reduction_run_count = 0};
2350 false -> State#vqstate{memory_reduction_run_count = MRedRunCount + 1}
23182351 end.
23192352
23202353 reduce_memory_use(State = #vqstate { target_ram_count = infinity }) ->
23292362 out = AvgEgress,
23302363 ack_in = AvgAckIngress,
23312364 ack_out = AvgAckEgress } }) ->
2332
23332365 State1 = #vqstate { q2 = Q2, q3 = Q3 } =
23342366 case chunk_size(RamMsgCount + gb_trees:size(RPA), TargetRamCount) of
23352367 0 -> State;
23892421 S2 ->
23902422 push_betas_to_deltas(S2, State1)
23912423 end,
2392 maybe_execute_gc(State3).
2424 garbage_collect(),
2425 State3.
23932426
23942427 limit_ram_acks(0, State) ->
23952428 {0, ui(State)};
24052438 MsgStatus2 = m(trim_msg_status(MsgStatus1)),
24062439 DPA1 = gb_trees:insert(SeqId, MsgStatus2, DPA),
24072440 limit_ram_acks(Quota - 1,
2408 stats({0, 0}, {MsgStatus, MsgStatus2},
2441 stats({0, 0}, {MsgStatus, MsgStatus2}, 0,
24092442 State1 #vqstate { ram_pending_ack = RPA1,
24102443 disk_pending_ack = DPA1 }))
24112444 end.
24942527 ram_msg_count = RamMsgCount,
24952528 ram_bytes = RamBytes,
24962529 disk_read_count = DiskReadCount,
2530 delta_transient_bytes = DeltaTransientBytes,
24972531 transient_threshold = TransientThreshold }) ->
24982532 #delta { start_seq_id = DeltaSeqId,
24992533 count = DeltaCount,
2534 transient = Transient,
25002535 end_seq_id = DeltaSeqIdEnd } = Delta,
25012536 DeltaSeqId1 =
25022537 lists:min([rabbit_queue_index:next_segment_boundary(DeltaSeqId),
25032538 DeltaSeqIdEnd]),
25042539 {List, IndexState1} = rabbit_queue_index:read(DeltaSeqId, DeltaSeqId1,
25052540 IndexState),
2506 {Q3a, RamCountsInc, RamBytesInc, State1} =
2541 {Q3a, RamCountsInc, RamBytesInc, State1, TransientCount, TransientBytes} =
25072542 betas_from_index_entries(List, TransientThreshold,
25082543 DelsAndAcksFun,
25092544 State #vqstate { index_state = IndexState1 }),
25262561 %% can now join q2 onto q3
25272562 State2 #vqstate { q2 = ?QUEUE:new(),
25282563 delta = ?BLANK_DELTA,
2529 q3 = ?QUEUE:join(Q3b, Q2) };
2564 q3 = ?QUEUE:join(Q3b, Q2),
2565 delta_transient_bytes = 0};
25302566 N when N > 0 ->
25312567 Delta1 = d(#delta { start_seq_id = DeltaSeqId1,
25322568 count = N,
2569 transient = Transient - TransientCount,
25332570 end_seq_id = DeltaSeqIdEnd }),
25342571 State2 #vqstate { delta = Delta1,
2535 q3 = Q3b }
2572 q3 = Q3b,
2573 delta_transient_bytes = DeltaTransientBytes - TransientBytes }
25362574 end
25372575 end.
25382576
25412579 push_alphas_to_betas(
25422580 fun ?QUEUE:out/1,
25432581 fun (MsgStatus, Q1a,
2544 State0 = #vqstate { q3 = Q3, delta = #delta { count = 0 } }) ->
2582 State0 = #vqstate { q3 = Q3, delta = #delta { count = 0,
2583 transient = 0 } }) ->
25452584 State0 #vqstate { q1 = Q1a, q3 = ?QUEUE:in(MsgStatus, Q3) };
25462585 (MsgStatus, Q1a, State0 = #vqstate { q2 = Q2 }) ->
25472586 State0 #vqstate { q1 = Q1a, q2 = ?QUEUE:in(MsgStatus, Q2) }
25772616 State),
25782617 MsgStatus2 = m(trim_msg_status(MsgStatus1)),
25792618 State2 = stats(
2580 ready0, {MsgStatus, MsgStatus2}, State1),
2619 ready0, {MsgStatus, MsgStatus2}, 0, State1),
25812620 State3 = Consumer(MsgStatus2, Qa, State2),
25822621 push_alphas_to_betas(Generator, Consumer, Quota - 1,
25832622 Qa, State3)
26402679 when SeqId < Limit ->
26412680 {Q, {Quota, Delta, ui(State)}};
26422681 {{value, MsgStatus = #msg_status { seq_id = SeqId }}, Qa} ->
2643 {#msg_status { index_on_disk = true }, State1} =
2682 {#msg_status { index_on_disk = true,
2683 is_persistent = IsPersistent }, State1} =
26442684 maybe_batch_write_index_to_disk(true, MsgStatus, State),
2645 State2 = stats(ready0, {MsgStatus, none}, State1),
2646 Delta1 = expand_delta(SeqId, Delta),
2685 State2 = stats(ready0, {MsgStatus, none}, 1, State1),
2686 Delta1 = expand_delta(SeqId, Delta, IsPersistent),
26472687 push_betas_to_deltas1(Generator, Limit, Qa,
26482688 {Quota - 1, Delta1, State2})
26492689 end.
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_version).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_vhost).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_vm).
1717
1818 -export([memory/0, binary/0, ets_tables_memory/1]).
1919
20 -define(MAGIC_PLUGINS, ["mochiweb", "webmachine", "cowboy", "sockjs",
21 "rfc4627_jsonrpc"]).
20 -define(MAGIC_PLUGINS, ["cowboy", "sockjs", "rfc4627_jsonrpc"]).
2221
2322 %%----------------------------------------------------------------------------
2423
4140 [aggregate(Names, Sums, memory, fun (X) -> X end)
4241 || Names <- distinguished_interesting_sups()],
4342
44 Mnesia = mnesia_memory(),
45 MsgIndexETS = ets_memory([msg_store_persistent, msg_store_transient]),
46 MgmtDbETS = ets_memory([rabbit_mgmt_event_collector]),
43 Mnesia = mnesia_memory(),
44 MsgIndexETS = ets_memory([msg_store_persistent, msg_store_transient]),
45 MetricsETS = ets_memory([rabbit_metrics]),
46 MetricsProc = try
47 [{_, M}] = process_info(whereis(rabbit_metrics), [memory]),
48 M
49 catch
50 error:badarg ->
51 0
52 end,
53 MgmtDbETS = ets_memory([rabbit_mgmt_storage]),
4754
4855 [{total, Total},
4956 {processes, Processes},
5663
5764 OtherProc = Processes
5865 - ConnsReader - ConnsWriter - ConnsChannel - ConnsOther
59 - Qs - QsSlave - MsgIndexProc - Plugins - MgmtDbProc,
66 - Qs - QsSlave - MsgIndexProc - Plugins - MgmtDbProc - MetricsProc,
6067
6168 [{total, Total},
6269 {connection_readers, ConnsReader},
6875 {plugins, Plugins},
6976 {other_proc, lists:max([0, OtherProc])}, %% [1]
7077 {mnesia, Mnesia},
78 {metrics, MetricsETS + MetricsProc},
7179 {mgmt_db, MgmtDbETS + MgmtDbProc},
7280 {msg_index, MsgIndexETS + MsgIndexProc},
7381 {other_ets, ETS - Mnesia - MsgIndexETS - MgmtDbETS},
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% Invoke callbacks on startup and termination.
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(tcp_listener).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(tcp_listener_sup).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(truncate).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% In practice Erlang shouldn't be allowed to grow to more than a half
3434 -export([get_total_memory/0, get_vm_limit/0,
3535 get_check_interval/0, set_check_interval/1,
3636 get_vm_memory_high_watermark/0, set_vm_memory_high_watermark/1,
37 get_memory_limit/0]).
37 get_memory_limit/0, get_memory_use/1]).
3838
3939 %% for tests
4040 -export([parse_line_linux/1]).
7272 -spec get_vm_memory_high_watermark() -> vm_memory_high_watermark().
7373 -spec set_vm_memory_high_watermark(vm_memory_high_watermark()) -> 'ok'.
7474 -spec get_memory_limit() -> non_neg_integer().
75 -spec get_memory_use(bytes) -> {non_neg_integer(), float() | infinity};
76 (ratio) -> float() | infinity.
7577
7678 %%----------------------------------------------------------------------------
7779 %% Public API
104106
105107 get_memory_limit() ->
106108 gen_server:call(?MODULE, get_memory_limit, infinity).
109
110 get_memory_use(bytes) ->
111 MemoryLimit = get_memory_limit(),
112 {erlang:memory(total), case MemoryLimit > 0.0 of
113 true -> MemoryLimit;
114 false -> infinity
115 end};
116 get_memory_use(ratio) ->
117 MemoryLimit = get_memory_limit(),
118 case MemoryLimit > 0.0 of
119 true -> erlang:memory(total) / MemoryLimit;
120 false -> infinity
121 end.
107122
108123 %%----------------------------------------------------------------------------
109124 %% gen_server callbacks
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(worker_pool).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(worker_pool_sup).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(worker_pool_worker).
2626 run/1]).
2727
2828 -export([set_maximum_since_use/2]).
29 -export([set_timeout/2, set_timeout/3, clear_timeout/1]).
2930
3031 -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
3132 terminate/2, code_change/3, prioritise_cast/3]).
135136 handle_info({'DOWN', _MRef, process, _Pid, _Reason}, State) ->
136137 {noreply, State, hibernate};
137138
139 handle_info({timeout, Key, Fun}, State) ->
140 clear_timeout(Key),
141 Fun(),
142 {noreply, State, hibernate};
143
138144 handle_info(Msg, State) ->
139145 {stop, {unexpected_info, Msg}, State}.
140146
143149
144150 terminate(_Reason, State) ->
145151 State.
152
153 -spec set_timeout(integer(), fun(() -> any())) -> reference().
154 set_timeout(Time, Fun) ->
155 Key = make_ref(),
156 set_timeout(Key, Time, Fun).
157
158 -spec set_timeout(Key, integer(), fun(() -> any())) -> Key when Key :: any().
159 set_timeout(Key, Time, Fun) ->
160 Timeouts = get_timeouts(),
161 set_timeout(Key, Time, Fun, Timeouts).
162
163 -spec clear_timeout(any()) -> ok.
164 clear_timeout(Key) ->
165 NewTimeouts = cancel_timeout(Key, get_timeouts()),
166 put(timeouts, NewTimeouts),
167 ok.
168
169 get_timeouts() ->
170 case get(timeouts) of
171 undefined -> dict:new();
172 Dict -> Dict
173 end.
174
175 set_timeout(Key, Time, Fun, Timeouts) ->
176 cancel_timeout(Key, Timeouts),
177 {ok, TRef} = timer:send_after(Time, {timeout, Key, Fun}),
178 NewTimeouts = dict:store(Key, TRef, Timeouts),
179 put(timeouts, NewTimeouts),
180 {ok, Key}.
181
182 cancel_timeout(Key, Timeouts) ->
183 case dict:find(Key, Timeouts) of
184 {ok, TRef} ->
185 timer:cancel(TRef),
186 receive {timeout, Key, _} -> ok
187 after 0 -> ok
188 end,
189 dict:erase(Key, Timeouts);
190 error ->
191 Timeouts
192 end.
00 PROJECT = rabbit_common
1 PROJECT_DESCRIPTION = Modules shared by rabbitmq-server and rabbitmq-erlang-client
12
2 BUILD_DEPS = rabbitmq_codegen
3 TEST_DEPS = mochiweb proper
3 define PROJECT_APP_EXTRA_KEYS
4 %% Hex.pm package informations.
5 {maintainers, [
6 "RabbitMQ Team <info@rabbitmq.com>",
7 "Jean-Sebastien Pedron <jean-sebastien@rabbitmq.com>"
8 ]},
9 {licenses, ["MPL 1.1"]},
10 {links, [
11 {"Website", "http://www.rabbitmq.com/"},
12 {"GitHub", "https://github.com/rabbitmq/rabbitmq-common"}
13 ]},
14 {build_tools, ["make", "rebar3"]},
15 {files, [
16 $(RABBITMQ_HEXPM_DEFAULT_FILES),
17 "mk"
18 ]}
19 endef
420
5 .DEFAULT_GOAL = all
6
7 EXTRA_SOURCES += include/rabbit_framing.hrl \
8 src/rabbit_framing_amqp_0_8.erl \
9 src/rabbit_framing_amqp_0_9_1.erl
10
11 .DEFAULT_GOAL = all
12 $(PROJECT).d:: $(EXTRA_SOURCES)
21 LOCAL_DEPS = compiler syntax_tools xmerl
1322
1423 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
1524 # reviewed and merged.
1726 ERLANG_MK_REPO = https://github.com/rabbitmq/erlang.mk.git
1827 ERLANG_MK_COMMIT = rabbitmq-tmp
1928
29 # Variables and recipes in development.*.mk are meant to be used from
30 # any Git clone. They are excluded from the files published to Hex.pm.
31 # Generated files are published to Hex.pm however so people using this
32 # source won't have to depend on Python and rabbitmq-codegen.
33 #
34 # That's why those Makefiles are included with `-include`: we ignore any
35 # inclusion errors.
36
37 -include development.pre.mk
38
39 DEP_EARLY_PLUGINS = $(PROJECT)/mk/rabbitmq-early-test.mk
40 DEP_PLUGINS = $(PROJECT)/mk/rabbitmq-build.mk \
41 $(PROJECT)/mk/rabbitmq-hexpm.mk \
42 $(PROJECT)/mk/rabbitmq-dist.mk \
43 $(PROJECT)/mk/rabbitmq-test.mk \
44 $(PROJECT)/mk/rabbitmq-tools.mk
45
2046 include mk/rabbitmq-components.mk
2147 include erlang.mk
22 include mk/rabbitmq-build.mk
23 include mk/rabbitmq-dist.mk
24 include mk/rabbitmq-tools.mk
2548
2649 # --------------------------------------------------------------------
2750 # Compilation.
3356 RMQ_ERLC_OPTS += -Ddefine_tls_atom_version
3457 endif
3558
36 # --------------------------------------------------------------------
37 # Framing sources generation.
38 # --------------------------------------------------------------------
59 # For src/*_compat.erl modules, we don't want to set -Werror because for
60 # instance, warnings about removed functions (e.g. ssl:connection_info/1
61 # in Erlang 20) can't be turned off.
62 #
63 # Erlang.mk doesn't provide a way to define per-file ERLC_OPTS because
64 # it compiles all files in a single run of erlc(1). So the solution is
65 # to declare those compat modules in $(ERLC_EXCLUDE) so that Erlang.mk
66 # skips them. Then we compile them ourselves below and update the .app
67 # file to include those compat modules.
68 #
69 # Because they are in $(ERLC_EXCLUDE), the line printed by
70 # $(erlc_verbose) is empty ("ERLC" alone instead of "ERLC <files>").
3971
40 PYTHON ?= python
41 CODEGEN = $(CURDIR)/codegen.py
42 CODEGEN_DIR ?= $(DEPS_DIR)/rabbitmq_codegen
43 CODEGEN_AMQP = $(CODEGEN_DIR)/amqp_codegen.py
72 COMPAT_FILES = $(wildcard src/*_compat.erl)
73 COMPAT_MODS = $(patsubst src/%.erl,%,$(COMPAT_FILES))
74 ERLC_EXCLUDE += $(COMPAT_MODS)
4475
45 AMQP_SPEC_JSON_FILES_0_8 = $(CODEGEN_DIR)/amqp-rabbitmq-0.8.json
46 AMQP_SPEC_JSON_FILES_0_9_1 = $(CODEGEN_DIR)/amqp-rabbitmq-0.9.1.json \
47 $(CODEGEN_DIR)/credit_extension.json
76 ebin/$(PROJECT).app:: ebin/ $(COMPAT_FILES)
77 $(erlc_verbose) erlc -v $(filter-out -Werror,$(ERLC_OPTS)) -o ebin/ \
78 -pa ebin/ -I include/ $(COMPAT_FILES)
79 $(eval COMPAT_MODULES := $(patsubst %,'%',$(COMPAT_MODS)))
80 $(verbose) awk "\
81 /{modules,/ { \
82 line=\$$0; \
83 sub(/$(lastword $(MODULES))]}/, \"$(lastword $(MODULES)),$(call comma_list,$(COMPAT_MODULES))]}\", line); \
84 print line; \
85 next; \
86 } { print; }" < "$@" > "$@.compat"
87 $(verbose) mv "$@.compat" "$@"
4888
49 include/rabbit_framing.hrl:: $(CODEGEN) $(CODEGEN_AMQP) \
50 $(AMQP_SPEC_JSON_FILES_0_9_1) $(AMQP_SPEC_JSON_FILES_0_8)
51 $(gen_verbose) env PYTHONPATH=$(CODEGEN_DIR) \
52 $(PYTHON) $(CODEGEN) --ignore-conflicts header \
53 $(AMQP_SPEC_JSON_FILES_0_9_1) $(AMQP_SPEC_JSON_FILES_0_8) $@
54
55 src/rabbit_framing_amqp_0_9_1.erl:: $(CODEGEN) $(CODEGEN_AMQP) \
56 $(AMQP_SPEC_JSON_FILES_0_9_1)
57 $(gen_verbose) env PYTHONPATH=$(CODEGEN_DIR) \
58 $(PYTHON) $(CODEGEN) body $(AMQP_SPEC_JSON_FILES_0_9_1) $@
59
60 src/rabbit_framing_amqp_0_8.erl:: $(CODEGEN) $(CODEGEN_AMQP) \
61 $(AMQP_SPEC_JSON_FILES_0_8)
62 $(gen_verbose) env PYTHONPATH=$(CODEGEN_DIR) \
63 $(PYTHON) $(CODEGEN) body $(AMQP_SPEC_JSON_FILES_0_8) $@
64
65 clean:: clean-extra-sources
66
67 clean-extra-sources:
68 $(gen_verbose) rm -f $(EXTRA_SOURCES)
89 -include development.post.mk
0 # --------------------------------------------------------------------
1 # Framing sources generation.
2 # --------------------------------------------------------------------
3
4 PYTHON ?= python
5 CODEGEN = $(CURDIR)/codegen.py
6 CODEGEN_DIR ?= $(DEPS_DIR)/rabbitmq_codegen
7 CODEGEN_AMQP = $(CODEGEN_DIR)/amqp_codegen.py
8
9 AMQP_SPEC_JSON_FILES_0_8 = $(CODEGEN_DIR)/amqp-rabbitmq-0.8.json
10 AMQP_SPEC_JSON_FILES_0_9_1 = $(CODEGEN_DIR)/amqp-rabbitmq-0.9.1.json \
11 $(CODEGEN_DIR)/credit_extension.json
12
13 include/rabbit_framing.hrl:: $(CODEGEN) $(CODEGEN_AMQP) \
14 $(AMQP_SPEC_JSON_FILES_0_9_1) $(AMQP_SPEC_JSON_FILES_0_8)
15 $(gen_verbose) env PYTHONPATH=$(CODEGEN_DIR) \
16 $(PYTHON) $(CODEGEN) --ignore-conflicts header \
17 $(AMQP_SPEC_JSON_FILES_0_9_1) $(AMQP_SPEC_JSON_FILES_0_8) $@
18
19 src/rabbit_framing_amqp_0_9_1.erl:: $(CODEGEN) $(CODEGEN_AMQP) \
20 $(AMQP_SPEC_JSON_FILES_0_9_1)
21 $(gen_verbose) env PYTHONPATH=$(CODEGEN_DIR) \
22 $(PYTHON) $(CODEGEN) body $(AMQP_SPEC_JSON_FILES_0_9_1) $@
23
24 src/rabbit_framing_amqp_0_8.erl:: $(CODEGEN) $(CODEGEN_AMQP) \
25 $(AMQP_SPEC_JSON_FILES_0_8)
26 $(gen_verbose) env PYTHONPATH=$(CODEGEN_DIR) \
27 $(PYTHON) $(CODEGEN) body $(AMQP_SPEC_JSON_FILES_0_8) $@
28
29 clean:: clean-extra-sources
30
31 clean-extra-sources:
32 $(gen_verbose) rm -f $(EXTRA_SOURCES)
0 # Variables and recipes in development.*.mk are meant to be used from
1 # any Git clone. They are excluded from the files published to Hex.pm.
2 # Generated files are published to Hex.pm however so people using this
3 # source won't have to depend on Python and rabbitmq-codegen.
4
5 BUILD_DEPS = rabbitmq_codegen
6 TEST_DEPS = proper
7
8 EXTRA_SOURCES += include/rabbit_framing.hrl \
9 src/rabbit_framing_amqp_0_8.erl \
10 src/rabbit_framing_amqp_0_9_1.erl
11
12 .DEFAULT_GOAL = all
13 $(PROJECT).d:: $(EXTRA_SOURCES)
0 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
11 #
22 # Permission to use, copy, modify, and/or distribute this software for any
33 # purpose with or without fee is hereby granted, provided that the above
1111 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1212 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1313
14 .PHONY: all app apps deps search rel docs install-docs check tests clean distclean help erlang-mk
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
1515
1616 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17
18 ERLANG_MK_VERSION = 2.0.0-pre.2-144-g647ffd1
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
1931
2032 # Core configuration.
2133
2436
2537 PROJECT_VERSION ?= rolling
2638 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
2740
2841 # Verbosity.
2942
8497 rel::
8598 $(verbose) :
8699
100 relup:: deps app
101
87102 check:: tests
88103
89104 clean:: clean-crashdump
101116 help::
102117 $(verbose) printf "%s\n" \
103118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
104 "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
105120 "" \
106121 "Usage: [V=1] $(MAKE) [target]..." \
107122 "" \
149164 core_native_path = $1
150165 endif
151166
152 ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
153 define core_http_get
154 wget --no-check-certificate -O $(1) $(2)|| rm $(1)
155 endef
156 else
157 define core_http_get.erl
158 ssl:start(),
159 inets:start(),
160 case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
161 {ok, {{_, 200, _}, _, Body}} ->
162 case file:write_file("$(1)", Body) of
163 ok -> ok;
164 {error, R1} -> halt(R1)
165 end;
166 {error, R2} ->
167 halt(R2)
168 end,
169 halt(0).
170 endef
171
172 define core_http_get
173 $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
174 endef
175 endif
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
176168
177169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
178170
192184 ERLANG_MK_BUILD_CONFIG ?= build.config
193185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
194186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
195188 erlang-mk:
196189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
197190 ifdef ERLANG_MK_COMMIT
198191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
199192 endif
200193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
201 $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
202195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
203196 rm -rf $(ERLANG_MK_BUILD_DIR)
204197
285278 pkg_apns_repo = https://github.com/inaka/apns4erl
286279 pkg_apns_commit = master
287280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
288289 PACKAGES += azdht
289290 pkg_azdht_name = azdht
290291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
405406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
406407 pkg_bootstrap_commit = master
407408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
408417 PACKAGES += boss_db
409418 pkg_boss_db_name = boss_db
410419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
413422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
414423 pkg_boss_db_commit = master
415424
416 PACKAGES += boss
417 pkg_boss_name = boss
418 pkg_boss_description = Erlang web MVC, now featuring Comet
419 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
420 pkg_boss_fetch = git
421 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
422 pkg_boss_commit = master
423
424425 PACKAGES += brod
425426 pkg_brod_name = brod
426427 pkg_brod_description = Kafka client in Erlang
565566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
566567 pkg_cloudi_service_api_requests_commit = master
567568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
568585 PACKAGES += cloudi_service_db_cassandra_cql
569586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
570587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
573590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
574591 pkg_cloudi_service_db_cassandra_cql_commit = master
575592
576 PACKAGES += cloudi_service_db_cassandra
577 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
579 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
580 pkg_cloudi_service_db_cassandra_fetch = git
581 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
582 pkg_cloudi_service_db_cassandra_commit = master
583
584593 PACKAGES += cloudi_service_db_couchdb
585594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
586595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
637646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
638647 pkg_cloudi_service_db_tokyotyrant_commit = master
639648
640 PACKAGES += cloudi_service_db
641 pkg_cloudi_service_db_name = cloudi_service_db
642 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
643 pkg_cloudi_service_db_homepage = http://cloudi.org/
644 pkg_cloudi_service_db_fetch = git
645 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
646 pkg_cloudi_service_db_commit = master
647
648649 PACKAGES += cloudi_service_filesystem
649650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
650651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
10371038 pkg_edown_repo = https://github.com/uwiger/edown
10381039 pkg_edown_commit = master
10391040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
10401049 PACKAGES += eep_app
10411050 pkg_eep_app_name = eep_app
10421051 pkg_eep_app_description = Embedded Event Processing
10451054 pkg_eep_app_repo = https://github.com/darach/eep-erl
10461055 pkg_eep_app_commit = master
10471056
1048 PACKAGES += eep
1049 pkg_eep_name = eep
1050 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1051 pkg_eep_homepage = https://github.com/virtan/eep
1052 pkg_eep_fetch = git
1053 pkg_eep_repo = https://github.com/virtan/eep
1054 pkg_eep_commit = master
1055
10561057 PACKAGES += efene
10571058 pkg_efene_name = efene
10581059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
12371238 pkg_eqm_repo = https://github.com/loucash/eqm
12381239 pkg_eqm_commit = master
12391240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
12401249 PACKAGES += eredis_pool
12411250 pkg_eredis_pool_name = eredis_pool
12421251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
12451254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
12461255 pkg_eredis_pool_commit = master
12471256
1248 PACKAGES += eredis
1249 pkg_eredis_name = eredis
1250 pkg_eredis_description = Erlang Redis client
1251 pkg_eredis_homepage = https://github.com/wooga/eredis
1252 pkg_eredis_fetch = git
1253 pkg_eredis_repo = https://github.com/wooga/eredis
1254 pkg_eredis_commit = master
1255
12561257 PACKAGES += erl_streams
12571258 pkg_erl_streams_name = erl_streams
12581259 pkg_erl_streams_description = Streams in Erlang
15411542 pkg_etap_repo = https://github.com/ngerakines/etap
15421543 pkg_etap_commit = master
15431544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
15441553 PACKAGES += etest_http
15451554 pkg_etest_http_name = etest_http
15461555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
15491558 pkg_etest_http_repo = https://github.com/wooga/etest_http
15501559 pkg_etest_http_commit = master
15511560
1552 PACKAGES += etest
1553 pkg_etest_name = etest
1554 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1555 pkg_etest_homepage = https://github.com/wooga/etest
1556 pkg_etest_fetch = git
1557 pkg_etest_repo = https://github.com/wooga/etest
1558 pkg_etest_commit = master
1559
15601561 PACKAGES += etoml
15611562 pkg_etoml_name = etoml
15621563 pkg_etoml_description = TOML language erlang parser
15651566 pkg_etoml_repo = https://github.com/kalta/etoml
15661567 pkg_etoml_commit = master
15671568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
15681577 PACKAGES += eunit_formatters
15691578 pkg_eunit_formatters_name = eunit_formatters
15701579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
15731582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
15741583 pkg_eunit_formatters_commit = master
15751584
1576 PACKAGES += eunit
1577 pkg_eunit_name = eunit
1578 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1579 pkg_eunit_homepage = https://github.com/richcarl/eunit
1580 pkg_eunit_fetch = git
1581 pkg_eunit_repo = https://github.com/richcarl/eunit
1582 pkg_eunit_commit = master
1583
15841585 PACKAGES += euthanasia
15851586 pkg_euthanasia_name = euthanasia
15861587 pkg_euthanasia_description = Merciful killer for your Erlang processes
15981599 pkg_evum_commit = master
15991600
16001601 PACKAGES += exec
1601 pkg_exec_name = exec
1602 pkg_exec_name = erlexec
16021603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
16031604 pkg_exec_homepage = http://saleyn.github.com/erlexec
16041605 pkg_exec_fetch = git
17171718 pkg_fn_repo = https://github.com/reiddraper/fn
17181719 pkg_fn_commit = master
17191720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
17201729 PACKAGES += folsom_cowboy
17211730 pkg_folsom_cowboy_name = folsom_cowboy
17221731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
17251734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
17261735 pkg_folsom_cowboy_commit = master
17271736
1728 PACKAGES += folsom
1729 pkg_folsom_name = folsom
1730 pkg_folsom_description = Expose Erlang Events and Metrics
1731 pkg_folsom_homepage = https://github.com/boundary/folsom
1732 pkg_folsom_fetch = git
1733 pkg_folsom_repo = https://github.com/boundary/folsom
1734 pkg_folsom_commit = master
1735
17361737 PACKAGES += folsomite
17371738 pkg_folsomite_name = folsomite
17381739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
20932094 pkg_jesse_repo = https://github.com/for-GET/jesse
20942095 pkg_jesse_commit = master
20952096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
20962105 PACKAGES += jiffy_v
20972106 pkg_jiffy_v_name = jiffy_v
20982107 pkg_jiffy_v_description = JSON validation utility
21012110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
21022111 pkg_jiffy_v_commit = master
21032112
2104 PACKAGES += jiffy
2105 pkg_jiffy_name = jiffy
2106 pkg_jiffy_description = JSON NIFs for Erlang.
2107 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2108 pkg_jiffy_fetch = git
2109 pkg_jiffy_repo = https://github.com/davisp/jiffy
2110 pkg_jiffy_commit = master
2111
21122113 PACKAGES += jobs
21132114 pkg_jobs_name = jobs
21142115 pkg_jobs_description = a Job scheduler for load regulation
21252126 pkg_joxa_repo = https://github.com/joxa/joxa
21262127 pkg_joxa_commit = master
21272128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
21282137 PACKAGES += json_rec
21292138 pkg_json_rec_name = json_rec
21302139 pkg_json_rec_description = JSON to erlang record
21332142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
21342143 pkg_json_rec_commit = master
21352144
2136 PACKAGES += json
2137 pkg_json_name = json
2138 pkg_json_description = a high level json library for erlang (17.0+)
2139 pkg_json_homepage = https://github.com/talentdeficit/json
2140 pkg_json_fetch = git
2141 pkg_json_repo = https://github.com/talentdeficit/json
2142 pkg_json_commit = master
2143
21442145 PACKAGES += jsone
21452146 pkg_jsone_name = jsone
21462147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
21812182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
21822183 pkg_jsx_commit = master
21832184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
21842193 PACKAGES += kafka_protocol
21852194 pkg_kafka_protocol_name = kafka_protocol
21862195 pkg_kafka_protocol_description = Kafka protocol Erlang library
21892198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
21902199 pkg_kafka_protocol_commit = master
21912200
2192 PACKAGES += kafka
2193 pkg_kafka_name = kafka
2194 pkg_kafka_description = Kafka consumer and producer in Erlang
2195 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2196 pkg_kafka_fetch = git
2197 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2198 pkg_kafka_commit = master
2199
22002201 PACKAGES += kai
22012202 pkg_kai_name = kai
22022203 pkg_kai_description = DHT storage by Takeshi Inoue
22932294 pkg_kvs_repo = https://github.com/synrc/kvs
22942295 pkg_kvs_commit = master
22952296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
22962305 PACKAGES += lager_amqp_backend
22972306 pkg_lager_amqp_backend_name = lager_amqp_backend
22982307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
23092318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
23102319 pkg_lager_syslog_commit = master
23112320
2312 PACKAGES += lager
2313 pkg_lager_name = lager
2314 pkg_lager_description = A logging framework for Erlang/OTP.
2315 pkg_lager_homepage = https://github.com/basho/lager
2316 pkg_lager_fetch = git
2317 pkg_lager_repo = https://github.com/basho/lager
2318 pkg_lager_commit = master
2319
23202321 PACKAGES += lambdapad
23212322 pkg_lambdapad_name = lambdapad
23222323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
25732574 pkg_mixer_repo = https://github.com/chef/mixer
25742575 pkg_mixer_commit = master
25752576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
25762585 PACKAGES += mochiweb_xpath
25772586 pkg_mochiweb_xpath_name = mochiweb_xpath
25782587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
25812590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
25822591 pkg_mochiweb_xpath_commit = master
25832592
2584 PACKAGES += mochiweb
2585 pkg_mochiweb_name = mochiweb
2586 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2587 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2588 pkg_mochiweb_fetch = git
2589 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2590 pkg_mochiweb_commit = master
2591
25922593 PACKAGES += mockgyver
25932594 pkg_mockgyver_name = mockgyver
25942595 pkg_mockgyver_description = A mocking library for Erlang
30613062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
30623063 pkg_quickrand_commit = master
30633064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
30643073 PACKAGES += rabbit_exchange_type_riak
30653074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
30663075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
30693078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
30703079 pkg_rabbit_exchange_type_riak_commit = master
30713080
3072 PACKAGES += rabbit
3073 pkg_rabbit_name = rabbit
3074 pkg_rabbit_description = RabbitMQ Server
3075 pkg_rabbit_homepage = https://www.rabbitmq.com/
3076 pkg_rabbit_fetch = git
3077 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3078 pkg_rabbit_commit = master
3079
30803081 PACKAGES += rack
30813082 pkg_rack_name = rack
30823083 pkg_rack_description = Rack handler for erlang
34933494 pkg_smother_repo = https://github.com/ramsay-t/Smother
34943495 pkg_smother_commit = master
34953496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
34963505 PACKAGES += social
34973506 pkg_social_name = social
34983507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
35413550 pkg_stable_repo = https://github.com/dvv/stable
35423551 pkg_stable_commit = master
35433552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
35443561 PACKAGES += statebox_riak
35453562 pkg_statebox_riak_name = statebox_riak
35463563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
35493566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
35503567 pkg_statebox_riak_commit = master
35513568
3552 PACKAGES += statebox
3553 pkg_statebox_name = statebox
3554 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3555 pkg_statebox_homepage = https://github.com/mochi/statebox
3556 pkg_statebox_fetch = git
3557 pkg_statebox_repo = https://github.com/mochi/statebox
3558 pkg_statebox_commit = master
3559
35603569 PACKAGES += statman
35613570 pkg_statman_name = statman
35623571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
37253734 pkg_tirerl_repo = https://github.com/inaka/tirerl
37263735 pkg_tirerl_commit = master
37273736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
37283745 PACKAGES += traffic_tools
37293746 pkg_traffic_tools_name = traffic_tools
37303747 pkg_traffic_tools_description = Simple traffic limiting library
40614078 pkg_zucchini_repo = https://github.com/devinus/zucchini
40624079 pkg_zucchini_commit = master
40634080
4064 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
40654082 # This file is part of erlang.mk and subject to the terms of the ISC License.
40664083
40674084 .PHONY: search
40884105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
40894106 endif
40904107
4091 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
40924109 # This file is part of erlang.mk and subject to the terms of the ISC License.
40934110
4094 .PHONY: distclean-deps
4111 .PHONY: distclean-deps clean-tmp-deps.log
40954112
40964113 # Configuration.
40974114
41104127
41114128 REBAR_DEPS_DIR = $(DEPS_DIR)
41124129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
41134150
41144151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
41154152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
41164153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
41174154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
41184155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
41194157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
41204158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
41214159
41384176
41394177 # Core targets.
41404178
4141 ifdef IS_APP
4142 apps::
4143 else
4144 apps:: $(ALL_APPS_DIRS)
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
41454180 ifeq ($(IS_APP)$(IS_DEP),)
41464181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
41474182 endif
41494184 # Create ebin directory for all apps to make sure Erlang recognizes them
41504185 # as proper OTP applications when using -include_lib. This is a temporary
41514186 # fix, a proper fix would be to compile apps/* in the right order.
4152 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4153 mkdir -p $$dep/ebin || exit $$?; \
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
41544190 done
4155 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
41564196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
41574197 :; \
41584198 else \
41594199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4160 $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
4200 $(MAKE) -C $$dep IS_APP=1; \
41614201 fi \
41624202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
41634207 endif
41644208
41654209 ifneq ($(SKIP_DEPS),)
41664210 deps::
41674211 else
4168 deps:: $(ALL_DEPS_DIRS) apps
4169 ifeq ($(IS_APP)$(IS_DEP),)
4170 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4171 endif
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
41724213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4173 $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
41744215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
41754216 :; \
41764217 else \
41774218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
41784219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4179 $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
4220 $(MAKE) -C $$dep IS_DEP=1; \
41804221 else \
4181 echo "Error: No Makefile to build dependency $$dep."; \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
41824223 exit 2; \
41834224 fi \
41844225 fi \
41924233 # in practice only Makefile is needed so far.
41934234 define dep_autopatch
41944235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
41954237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
41964238 $(call dep_autopatch_erlang_mk,$(1)); \
41974239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4198 if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
41994243 $(call dep_autopatch2,$(1)); \
4200 elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
42014245 $(call dep_autopatch2,$(1)); \
4202 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
42034247 $(call dep_autopatch2,$(1)); \
4204 else \
4205 $(call erlang,$(call dep_autopatch_app.erl,$(1))); \
42064248 fi \
42074249 else \
42084250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
42144256 endef
42154257
42164258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
42174262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
42184263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
42194264 fi; \
42204265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4221 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
42224267 $(call dep_autopatch_fetch_rebar); \
42234268 $(call dep_autopatch_rebar,$(1)); \
42244269 else \
42304275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
42314276 endef
42324277
4233 # Overwrite erlang.mk with the current file by default.
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
42344280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
42354281 define dep_autopatch_erlang_mk
4236 echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \
4237 > $(DEPS_DIR)/$1/erlang.mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
42384287 endef
42394288 else
42404289 define dep_autopatch_erlang_mk
42534302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
42544303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
42554304 cd $(ERLANG_MK_TMP)/rebar; \
4256 git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
42574306 $(MAKE); \
42584307 cd -; \
42594308 fi
42704319 define dep_autopatch_rebar.erl
42714320 application:load(rebar),
42724321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
42734323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
42744324 {ok, Conf0} -> Conf0;
42754325 _ -> []
44234473 [] -> ok;
44244474 _ ->
44254475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4426 PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
44274477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4428 PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L \\"~s\\" -lerl_interface -lei\n",
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
44294479 [code:lib_dir(erl_interface, lib)])),
44304480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
44314481 FilterEnv = fun(Env) ->
44644514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44654515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44664516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4467 [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
44684518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
44694519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
44704520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
44764526 end,
44774527 [PortSpec(S) || S <- PortSpecs]
44784528 end,
4479 Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"),
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
44804530 RunPlugin = fun(Plugin, Step) ->
44814531 case erlang:function_exported(Plugin, Step, 2) of
44824532 false -> ok;
45244574 halt()
45254575 endef
45264576
4527 define dep_autopatch_app.erl
4528 UpdateModules = fun(App) ->
4529 case filelib:is_regular(App) of
4530 false -> ok;
4531 true ->
4532 {ok, [{application, '$(1)', L0}]} = file:consult(App),
4533 Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true,
4534 fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
4535 L = lists:keystore(modules, 1, L0, {modules, Mods}),
4536 ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}]))
4537 end
4538 end,
4539 UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"),
4540 halt()
4541 endef
4542
45434577 define dep_autopatch_appsrc_script.erl
45444578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
45454579 AppSrcScript = AppSrc ++ ".script",
45874621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
45884622 endef
45894623
4590 define dep_fetch_hex.erl
4591 ssl:start(),
4592 inets:start(),
4593 {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
4594 {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
4595 [], [{body_format, binary}]),
4596 {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
4597 {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
4598 ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
4599 halt()
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
46004626 endef
46014627
46024628 # Hex only has a package version. No need to look in the Erlang.mk packages.
46034629 define dep_fetch_hex
4604 $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
46054634 endef
46064635
46074636 define dep_fetch_fail
46314660 $(eval DEP_NAME := $(call dep_name,$1))
46324661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
46334662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4634 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
46354664 exit 17; \
46364665 fi
46374666 $(verbose) mkdir -p $(DEPS_DIR)
46734702 clean:: clean-apps
46744703
46754704 clean-apps:
4676 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4677 $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
46784707 done
46794708
46804709 distclean:: distclean-apps
46814710
46824711 distclean-apps:
4683 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4684 $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
46854714 done
46864715 endif
46874716
47014730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
47024731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
47034732
4704 # External plugins.
4705
4706 DEP_PLUGINS ?=
4707
4708 define core_dep_plugin
4709 -include $(DEPS_DIR)/$(1)
4710
4711 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4712 endef
4713
4714 $(foreach p,$(DEP_PLUGINS),\
4715 $(eval $(if $(findstring /,$p),\
4716 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4717 $(call core_dep_plugin,$p/plugins.mk,$p))))
4718
4719 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4720 # This file is part of erlang.mk and subject to the terms of the ISC License.
4721
4722 # Configuration.
4723
4724 DTL_FULL_PATH ?=
4725 DTL_PATH ?= templates/
4726 DTL_SUFFIX ?= _dtl
4727 DTL_OPTS ?=
4728
4729 # Verbosity.
4730
4731 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
4732 dtl_verbose = $(dtl_verbose_$(V))
4733
4734 # Core targets.
4735
4736 DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl))
4737
4738 ifneq ($(DTL_FILES),)
4739
4740 ifdef DTL_FULL_PATH
4741 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
4742 else
4743 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
4744 endif
4745
4746 ifneq ($(words $(DTL_FILES)),0)
4747 # Rebuild templates when the Makefile changes.
4748 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
4749 @mkdir -p $(ERLANG_MK_TMP)
4750 @if test -f $@; then \
4751 touch $(DTL_FILES); \
4752 fi
4753 @touch $@
4754
4755 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
4756 endif
4757
4758 define erlydtl_compile.erl
4759 [begin
4760 Module0 = case "$(strip $(DTL_FULL_PATH))" of
4761 "" ->
4762 filename:basename(F, ".dtl");
4763 _ ->
4764 "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
4765 re:replace(F2, "/", "_", [{return, list}, global])
4766 end,
4767 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
4768 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
4769 ok -> ok;
4770 {ok, _} -> ok
4771 end
4772 end || F <- string:tokens("$(1)", " ")],
4773 halt().
4774 endef
4775
4776 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
4777 $(if $(strip $?),\
4778 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
4779
4780 endif
4781
4782 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
47834734 # This file is part of erlang.mk and subject to the terms of the ISC License.
47844735
47854736 # Verbosity.
47984749
47994750 define compile_proto.erl
48004751 [begin
4801 Dir = filename:dirname(filename:dirname(F)),
48024752 protobuffs_compile:generate_source(F,
4803 [{output_include_dir, Dir ++ "/include"},
4804 {output_src_dir, Dir ++ "/ebin"}])
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
48054755 end || F <- string:tokens("$(1)", " ")],
48064756 halt().
48074757 endef
48114761 $(if $(strip $?),$(call compile_proto,$?))
48124762 endif
48134763
4814 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
48154765 # This file is part of erlang.mk and subject to the terms of the ISC License.
48164766
48174767 .PHONY: clean-app
48254775 ERLC_EXCLUDE ?=
48264776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
48274777
4778 ERLC_ASN1_OPTS ?=
4779
48284780 ERLC_MIB_OPTS ?=
48294781 COMPILE_MIB_FIRST ?=
48304782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
48744826
48754827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
48764828 define app_file
4877 {application, $(PROJECT), [
4829 {application, '$(PROJECT)', [
48784830 {description, "$(PROJECT_DESCRIPTION)"},
48794831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48804832 {id$(comma)$(space)"$(1)"}$(comma))
48814833 {modules, [$(call comma_list,$(2))]},
48824834 {registered, []},
4883 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48844837 ]}.
48854838 endef
48864839 else
48874840 define app_file
4888 {application, $(PROJECT), [
4841 {application, '$(PROJECT)', [
48894842 {description, "$(PROJECT_DESCRIPTION)"},
48904843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48914844 {id$(comma)$(space)"$(1)"}$(comma))
48924845 {modules, [$(call comma_list,$(2))]},
48934846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
48944847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4895 {mod, {$(PROJECT_MOD), []}}
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48964850 ]}.
48974851 endef
48984852 endif
49024856
49034857 # Source files.
49044858
4905 ERL_FILES = $(sort $(call core_find,src/,*.erl))
4906 CORE_FILES = $(sort $(call core_find,src/,*.core))
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
49074863
49084864 # ASN.1 files.
49094865
49134869
49144870 define compile_asn1
49154871 $(verbose) mkdir -p include/
4916 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
49174873 $(verbose) mv asn1/*.erl src/
49184874 $(verbose) mv asn1/*.hrl include/
49194875 $(verbose) mv asn1/*.asn1db include/
49364892
49374893 # Leex and Yecc files.
49384894
4939 XRL_FILES = $(sort $(call core_find,src/,*.xrl))
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
49404896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
49414897 ERL_FILES += $(XRL_ERL_FILES)
49424898
4943 YRL_FILES = $(sort $(call core_find,src/,*.yrl))
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
49444900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
49454901 ERL_FILES += $(YRL_ERL_FILES)
49464902
49474903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4948 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
49494905
49504906 # Erlang and Core Erlang files.
49514907
49954951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49964952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49974953 (F, Mod, import, {Imp, _}) ->
4998 case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
49994960 false -> ok;
50004961 true -> Add(Mod, Imp)
50014962 end;
50194980 end || F <- ErlFiles],
50204981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
50214982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
50224991 ok = file:write_file("$(1)", [
50234992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
5024 "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n"
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
50254994 ]),
50264995 halt()
50274996 endef
50345003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
50355004 # Rebuild everything when the Makefile changes.
50365005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5037 @mkdir -p $(ERLANG_MK_TMP)
5038 @if test -f $@; then \
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
50395008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
50405009 touch -c $(PROJECT).d; \
50415010 fi
5042 @touch $@
5011 $(verbose) touch $@
50435012
50445013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
50455014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
50465015 endif
50475016
5048 -include $(PROJECT).d
5017 include $(wildcard $(PROJECT).d)
50495018
50505019 ebin/$(PROJECT).app:: ebin/
50515020
50645033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
50655034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
50665035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5067 $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
50685037 > ebin/$(PROJECT).app
50695038 else
50705039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
50885057
50895058 endif
50905059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
50915061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
50925062 # This file is part of erlang.mk and subject to the terms of the ISC License.
50935063
51055075 doc-deps:
51065076 else
51075077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5108 $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5109 endif
5110
5111 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51125082 # This file is part of erlang.mk and subject to the terms of the ISC License.
51135083
51145084 .PHONY: rel-deps
51255095 rel-deps:
51265096 else
51275097 rel-deps: $(ALL_REL_DEPS_DIRS)
5128 $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5129 endif
5130
5131 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51325102 # This file is part of erlang.mk and subject to the terms of the ISC License.
51335103
51345104 .PHONY: test-deps test-dir test-build clean-test-dir
51505120 test-deps:
51515121 else
51525122 test-deps: $(ALL_TEST_DEPS_DIRS)
5153 $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
51545124 endif
51555125
51565126 ifneq ($(wildcard $(TEST_DIR)),)
51835153 endif
51845154 endif
51855155
5186 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51875157 # This file is part of erlang.mk and subject to the terms of the ISC License.
51885158
51895159 .PHONY: rebar.config
52195189 rebar.config:
52205190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
52215191
5222 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
52235193 # This file is part of erlang.mk and subject to the terms of the ISC License.
52245194
5225 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
5226
5227 MAN_INSTALL_PATH ?= /usr/local/share/man
5228 MAN_SECTIONS ?= 3 7
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
52295200
52305201 docs:: asciidoc
52315202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
52325207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
52335210
52345211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
52355212 asciidoc-guide:
52365213 else
5237 asciidoc-guide: distclean-asciidoc doc-deps
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
52385215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
52395216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5240 endif
5241
5242 ifeq ($(wildcard doc/src/manual/*.asciidoc),)
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
52435227 asciidoc-manual:
52445228 else
5245 asciidoc-manual: distclean-asciidoc doc-deps
5246 for f in doc/src/manual/*.asciidoc ; do \
5247 a2x -v -f manpage $$f ; \
5248 done
5249 for s in $(MAN_SECTIONS); do \
5250 mkdir -p doc/man$$s/ ; \
5251 mv doc/src/manual/*.$$s doc/man$$s/ ; \
5252 gzip doc/man$$s/*.$$s ; \
5253 done
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
52545262
52555263 install-docs:: install-asciidoc
52565264
52575265 install-asciidoc: asciidoc-manual
5258 for s in $(MAN_SECTIONS); do \
5259 mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
5260 install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
5261 done
5262 endif
5263
5264 distclean:: distclean-asciidoc
5265
5266 distclean-asciidoc:
5267 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
5268
5269 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
52705276 # This file is part of erlang.mk and subject to the terms of the ISC License.
52715277
52725278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
53235329 define bs_Makefile
53245330 PROJECT = $p
53255331 PROJECT_DESCRIPTION = New project
5326 PROJECT_VERSION = 0.0.1
5332 PROJECT_VERSION = 0.1.0
53275333
53285334 # Whitespace to be used when creating files from templates.
53295335 SP = $(SP)
53335339 define bs_Makefile
53345340 PROJECT = $p
53355341 PROJECT_DESCRIPTION = New project
5336 PROJECT_VERSION = 0.0.1
5342 PROJECT_VERSION = 0.1.0
53375343
53385344 endef
53395345 endif
53415347 define bs_apps_Makefile
53425348 PROJECT = $p
53435349 PROJECT_DESCRIPTION = New project
5344 PROJECT_VERSION = 0.0.1
5350 PROJECT_VERSION = 0.1.0
53455351
53465352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
53475353 endef
53615367 endef
53625368
53635369 define bs_relx_config
5364 {release, {$p_release, "1"}, [$p]}.
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
53655371 {extended_start_script, true}.
53665372 {sys_config, "rel/sys.config"}.
53675373 {vm_args, "rel/vm.args"}.
57125718 ifndef t
57135719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57145720 endif
5715 ifndef tpl_$(t)
5716 $(error Unknown template)
5717 endif
57185721 ifndef n
57195722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57205723 endif
57275730 list-templates:
57285731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
57295732
5730 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
57315734 # This file is part of erlang.mk and subject to the terms of the ISC License.
57325735
57335736 .PHONY: clean-c_src distclean-c_src-env
59625965 $(call render_template,bs_erl_nif,src/$n.erl)
59635966 endif
59645967
5965 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
59665969 # This file is part of erlang.mk and subject to the terms of the ISC License.
59675970
5968 .PHONY: ci ci-setup distclean-kerl
5969
5970 KERL ?= $(CURDIR)/kerl
5971 export KERL
5972
5973 KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
5974
5975 OTP_GIT ?= https://github.com/erlang/otp
5976
5977 CI_INSTALL_DIR ?= $(HOME)/erlang
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
59785973 CI_OTP ?=
5979
5980 ifeq ($(strip $(CI_OTP)),)
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
59815986 ci::
59825987 else
5983 ci:: $(addprefix ci-,$(CI_OTP))
5984
5985 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
59866007
59876008 ci-setup::
6009
6010 ci-extra::
59886011
59896012 ci_verbose_0 = @echo " CI " $(1);
59906013 ci_verbose = $(ci_verbose_$(V))
59916014
59926015 define ci_target
5993 ci-$(1): $(CI_INSTALL_DIR)/$(1)
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
59946018 $(ci_verbose) \
5995 PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
5996 CI_OTP_RELEASE="$(1)" \
5997 CT_OPTS="-label $(1)" \
5998 $(MAKE) clean ci-setup tests
5999 endef
6000
6001 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
60026030
60036031 define ci_otp_target
60046032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
60056033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6006 $(KERL) build git $(OTP_GIT) $(1) $(1)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
60076035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
60086036 endif
60096037 endef
60106038
60116039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
60126040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
60136052 $(KERL):
6014 $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
60156056 $(verbose) chmod +x $(KERL)
60166057
60176058 help::
60286069 $(gen_verbose) rm -rf $(KERL)
60296070 endif
60306071
6031 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
60326073 # This file is part of erlang.mk and subject to the terms of the ISC License.
60336074
60346075 .PHONY: ct apps-ct distclean-ct
60366077 # Configuration.
60376078
60386079 CT_OPTS ?=
6080
60396081 ifneq ($(wildcard $(TEST_DIR)),)
6040 CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6041 else
6042 CT_SUITES ?=
6043 endif
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
60446088
60456089 # Core targets.
60466090
60636107 -noinput \
60646108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
60656109 -dir $(TEST_DIR) \
6066 -logdir $(CURDIR)/logs
6110 -logdir $(CT_LOGS_DIR)
60676111
60686112 ifeq ($(CT_SUITES),)
60696113 ct: $(if $(IS_APP),,apps-ct)
60706114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
60716117 ct: test-build $(if $(IS_APP),,apps-ct)
6072 $(verbose) mkdir -p $(CURDIR)/logs/
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
60736119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
60746121 endif
60756122
60766123 ifneq ($(ALL_APPS_DIRS),)
60976144
60986145 define ct_suite_target
60996146 ct-$(1): test-build
6100 $(verbose) mkdir -p $(CURDIR)/logs/
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
61016148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
61026149 endef
61036150
61046151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
61056152
61066153 distclean-ct:
6107 $(gen_verbose) rm -rf $(CURDIR)/logs/
6108
6109 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61106157 # This file is part of erlang.mk and subject to the terms of the ISC License.
61116158
61126159 .PHONY: plt distclean-plt dialyze
61506197 endef
61516198
61526199 $(DIALYZER_PLT): deps app
6153 $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
61546204
61556205 plt: $(DIALYZER_PLT)
61566206
61646214 endif
61656215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
61666216
6167 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61686218 # This file is part of erlang.mk and subject to the terms of the ISC License.
61696219
61706220 .PHONY: distclean-edoc edoc
61726222 # Configuration.
61736223
61746224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
61756235
61766236 # Core targets.
61776237
6178 ifneq ($(wildcard doc/overview.edoc),)
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
61796239 docs:: edoc
61806240 endif
61816241
61846244 # Plugin-specific targets.
61856245
61866246 edoc: distclean-edoc doc-deps
6187 $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
61886248
61896249 distclean-edoc:
61906250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
61916251
6192 # Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61936253 # This file is part of erlang.mk and subject to the terms of the ISC License.
61946254
6195 .PHONY: distclean-escript escript
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
61966320
61976321 # Configuration.
61986322
61996323 ESCRIPT_NAME ?= $(PROJECT)
62006324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
62016325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
62026327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6203
6204 ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
6205 ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
6206 ESCRIPT_EMU_ARGS ?= -pa . \
6207 -sasl errlog_type error \
6208 -escript main $(ESCRIPT_NAME)
6209 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6210 ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
62116332
62126333 # Core targets.
62136334
62206341
62216342 # Plugin-specific targets.
62226343
6223 # Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
6224 # Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
6225 # Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
6226 # Software may only be used for the great good and the true happiness of all
6227 # sentient beings.
6228
6229 define ESCRIPT_RAW
6230 'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
6231 'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
6232 ' [F || F <- A, not filelib:is_dir(F) ] end,'\
6233 'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
6234 'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
6235 'Ez = fun(Escript) ->'\
6236 ' Static = Files([$(ESCRIPT_STATIC)]),'\
6237 ' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
6238 ' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
6239 ' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
6240 ' {archive, Archive, [memory]},'\
6241 ' {shebang, "$(ESCRIPT_SHEBANG)"},'\
6242 ' {comment, "$(ESCRIPT_COMMENT)"},'\
6243 ' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
6244 ' ]),'\
6245 ' file:change_mode(Escript, 8#755)'\
6246 'end,'\
6247 'Ez("$(ESCRIPT_FILE)"),'\
6248 'halt().'
6249 endef
6250
6251 ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
6252
6253 escript:: distclean-escript deps app
6254 $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
62556360
62566361 distclean-escript:
6257 $(gen_verbose) rm -f $(ESCRIPT_NAME)
6258
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
62596365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6260 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
62616366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
62626367
62636368 .PHONY: eunit apps-eunit
63216426
63226427 ifneq ($(ALL_APPS_DIRS),)
63236428 apps-eunit:
6324 $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done
6325 endif
6326 endif
6327
6328 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
63296436 # This file is part of erlang.mk and subject to the terms of the ISC License.
63306437
6331 .PHONY: relx-rel distclean-relx-rel distclean-relx run
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
63326439
63336440 # Configuration.
63346441
6335 RELX ?= $(CURDIR)/relx
6442 RELX ?= $(ERLANG_MK_TMP)/relx
63366443 RELX_CONFIG ?= $(CURDIR)/relx.config
63376444
63386445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
63396446 RELX_OPTS ?=
63406447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
63416454
63426455 ifeq ($(firstword $(RELX_OPTS)),-o)
63436456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
63506463 ifeq ($(IS_DEP),)
63516464 ifneq ($(wildcard $(RELX_CONFIG)),)
63526465 rel:: relx-rel
6353 endif
6354 endif
6355
6356 distclean:: distclean-relx-rel distclean-relx
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
63576472
63586473 # Plugin-specific targets.
63596474
63626477 $(verbose) chmod +x $(RELX)
63636478
63646479 relx-rel: $(RELX) rel-deps app
6365 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
63666484
63676485 distclean-relx-rel:
63686486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6369
6370 distclean-relx:
6371 $(gen_verbose) rm -rf $(RELX)
63726487
63736488 # Run target.
63746489
63776492 else
63786493
63796494 define get_relx_release.erl
6380 {ok, Config} = file:consult("$(RELX_CONFIG)"),
6381 {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
6382 io:format("~s", [Name]),
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
63836504 halt(0).
63846505 endef
63856506
6386 RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
63876514
63886515 run: all
6389 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
63906517
63916518 help::
63926519 $(verbose) printf "%s\n" "" \
63956522
63966523 endif
63976524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
63986526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6399 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
64006527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
64016528
64026529 .PHONY: shell
64216548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
64226549
64236550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6424 $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
64256552
64266553 shell: build-shell-deps
64276554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
64286555
6429 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
64306571 # This file is part of erlang.mk and subject to the terms of the ISC License.
64316572
64326573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
64376578 tests:: triq
64386579
64396580 define triq_check.erl
6440 code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
64416582 try
64426583 case $(1) of
64436584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
64696610 endif
64706611 endif
64716612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
64726614 # Copyright (c) 2015, Erlang Solutions Ltd.
64736615 # This file is part of erlang.mk and subject to the terms of the ISC License.
64746616
64856627 XREFR ?= $(CURDIR)/xrefr
64866628 export XREFR
64876629
6488 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
64896631
64906632 # Core targets.
64916633
64926634 help::
6493 $(verbose) printf "%s\n" "" \
6494 "Xref targets:" \
6495 " xref Run Xrefr using $XREF_CONFIG as config file if defined"
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
64966638
64976639 distclean:: distclean-xref
64986640
65086650 distclean-xref:
65096651 $(gen_verbose) rm -rf $(XREFR)
65106652
6511 # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
65126655 # This file is part of erlang.mk and subject to the terms of the ISC License.
65136656
65146657 COVER_REPORT_DIR = cover
65176660
65186661 ifdef COVER
65196662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
65206664 # All modules in 'ebin'
65216665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
65226666
65316675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
65326676 endif
65336677 endif
6678 endif
65346679
65356680 # Core targets
65366681
65526697 "Cover targets:" \
65536698 " cover-report Generate a HTML coverage report from previously collected" \
65546699 " cover data." \
6555 " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
65566701 "" \
65576702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
65586703 "target tests additionally generates a HTML coverage report from the combined" \
65656710
65666711 .PHONY: coverdata-clean
65676712 coverdata-clean:
6568 $(gen_verbose) rm -f *.coverdata ct.cover.spec
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
65696714
65706715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
65716721 all.coverdata: $(COVERDATA)
6572 $(gen_verbose) $(ERL) -eval ' \
6573 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
6574 cover:export("$@"), halt(0).'
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
65756723
65766724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
65776725 # empty if you want the coverdata files but not the HTML report.
65896737 # Modules which include eunit.hrl always contain one line without coverage
65906738 # because eunit defines test/0 which is never called. We compensate for this.
65916739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6592 grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
65936741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
65946742
65956743 define cover_report.erl
66246772 endef
66256773
66266774 cover-report:
6627 $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
66286776 $(gen_verbose) $(call erlang,$(cover_report.erl))
66296777
66306778 endif
66316779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
66326839
66336840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
66346841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
66976904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
66986905 endif
66996906 ifndef IS_APP
6700 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
67016908 $(MAKE) -C $$dep $@ \
67026909 IS_APP=1 \
6703 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6704 || exit $$?; \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67056911 done
67066912 endif
6707 $(verbose) for dep in $^ ; do \
6913 $(verbose) set -e; for dep in $^ ; do \
67086914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
67096915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6710 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
67116917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
67126918 $(MAKE) -C $$dep fetch-deps \
67136919 IS_DEP=1 \
6714 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6715 || exit $$?; \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67166921 fi \
67176922 fi \
67186923 done
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is Pivotal Software, Inc.
13 %% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -include("old_builtin_types.hrl").
9797 -record(trie_edge, {exchange_name, node_id, word}).
9898 -record(trie_binding, {exchange_name, node_id, destination, arguments}).
9999
100 -record(listener, {node, protocol, host, ip_address, port}).
100 -record(listener, {node, protocol, host, ip_address, port, opts = []}).
101101
102102 -record(runtime_parameters, {key, value}).
103103
131131
132132 %%----------------------------------------------------------------------------
133133
134 -define(COPYRIGHT_MESSAGE, "Copyright (C) 2007-2016 Pivotal Software, Inc.").
134 -define(COPYRIGHT_MESSAGE, "Copyright (C) 2007-2017 Pivotal Software, Inc.").
135135 -define(INFORMATION_MESSAGE, "Licensed under the MPL. See http://www.rabbitmq.com/").
136136 -define(OTP_MINIMUM, "R16B03").
137137 -define(ERTS_MINIMUM, "5.10.4").
146146 -define(EMPTY_FRAME_SIZE, 8).
147147
148148 -define(MAX_WAIT, 16#ffffffff).
149 -define(SUPERVISOR_WAIT, infinity).
150 -define(WORKER_WAIT, 30000).
149 -define(SUPERVISOR_WAIT,
150 rabbit_misc:get_env(rabbit, supervisor_shutdown_timeout, infinity)).
151 -define(WORKER_WAIT,
152 rabbit_misc:get_env(rabbit, worker_shutdown_timeout, 30000)).
151153
152154 -define(HIBERNATE_AFTER_MIN, 1000).
153155 -define(DESIRED_HIBERNATE, 10000).
154 -define(CREDIT_DISC_BOUND, {2000, 500}).
156 -define(CREDIT_DISC_BOUND, {4000, 800}).
155157 %% When we discover that we should write some indices to disk for some
156158 %% betas, the IO_BATCH_SIZE sets the number of betas that we must be
157159 %% due to write indices for before we do any work at all.
158 -define(IO_BATCH_SIZE, 2048). %% next power-of-2 after ?CREDIT_DISC_BOUND
160 -define(IO_BATCH_SIZE, 4096). %% next power-of-2 after ?CREDIT_DISC_BOUND
159161
160162 -define(INVALID_HEADERS_KEY, <<"x-invalid-headers">>).
161163 -define(ROUTING_HEADERS, [<<"CC">>, <<"BCC">>]).
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 %% These tables contain the raw metrics as stored by RabbitMQ core
17 -define(CORE_TABLES, [{connection_created, set},
18 {connection_metrics, set},
19 {connection_coarse_metrics, set},
20 {channel_created, set},
21 {channel_metrics, set},
22 {channel_queue_exchange_metrics, set},
23 {channel_queue_metrics, set},
24 {channel_exchange_metrics, set},
25 {channel_process_metrics, set},
26 {consumer_created, set},
27 {queue_metrics, set},
28 {queue_coarse_metrics, set},
29 {node_persister_metrics, set},
30 {node_coarse_metrics, set},
31 {node_metrics, set},
32 {node_node_metrics, set}]).
33
34 -define(CORE_EXTRA_TABLES, [{gen_server2_metrics, set}]).
35
36 %% connection_created :: {connection_id, proplist}
37 %% connection_metrics :: {connection_id, proplist}
38 %% connection_coarse_metrics :: {connection_id, recv_oct, send_oct, reductions}
39 %% channel_created :: {channel_id, proplist}
40 %% channel_metrics :: {channel_id, proplist}
41 %% channel_queue_exchange_metrics :: {{channel_id, {queue_id, exchange_id}}, publish}
42 %% channel_queue_metrics :: {{channel_id, queue_id}, proplist}
43 %% channel_exchange_metrics :: {{channel_id, exchange_id}, proplist}
44 %% channel_process_metrics :: {channel_id, reductions}
45 %% consumer_created :: {{queue_id, channel_id, consumer_tag}, exclusive_consume,
46 %% ack_required, prefetch_count, args}
47 %% queue_metrics :: {queue_id, proplist}
48 %% queue_coarse_metrics :: {queue_id, messages_ready, messages_unacknowledge,
49 %% messages, reductions}
50 %% node_persister_metrics :: {node_id, proplist}
51 %% node_coarse_metrics :: {node_id, proplist}
52 %% node_metrics :: {node_id, proplist}
53 %% node_node_metrics :: {{node_id, node_id}, proplist}
54 %% gen_server2_metrics :: {pid, buffer_length}
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is Pivotal Software, Inc.
13 %% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -include("rabbit.hrl").
11 # Compiler flags.
22 # --------------------------------------------------------------------
33
4 # FIXME: We copy Erlang.mk default flags here: rabbitmq-build.mk is
5 # loaded as a plugin, so before those variables are defined. And because
6 # Erlang.mk uses '?=', the flags we set here override the default set.
7 #
8 # See: https://github.com/ninenines/erlang.mk/issues/502
9
10 WARNING_OPTS += +debug_info \
11 +warn_export_vars \
12 +warn_shadow_vars \
13 +warn_obsolete_guard
14 ERLC_OPTS += -Werror $(WARNING_OPTS)
15 TEST_ERLC_OPTS += $(WARNING_OPTS)
4 TEST_ERLC_OPTS += +nowarn_export_all
165
176 define compare_version
187 $(shell awk 'BEGIN {
4130 # Push our compilation options to both the normal and test ERLC_OPTS.
4231 ERLC_OPTS += $(RMQ_ERLC_OPTS)
4332 TEST_ERLC_OPTS += $(RMQ_ERLC_OPTS)
44
45 # --------------------------------------------------------------------
46 # Common test flags.
47 # --------------------------------------------------------------------
48
49 # Disable most messages on Travis and Concourse.
50 #
51 # Concourse doesn't set any environment variables to help us automate
52 # things. In rabbitmq-ci, we run tests under the `concourse` user so,
53 # look at that...
54 CT_QUIET_FLAGS = -verbosity 50 \
55 -erl_args \
56 -kernel error_logger silent
57 ifdef TRAVIS
58 CT_OPTS += $(CT_QUIET_FLAGS)
59 endif
60 ifdef CONCOURSE
61 CT_OPTS += $(CT_QUIET_FLAGS)
62 endif
63
64 # Enable JUnit-like report on Jenkins. Jenkins parses those reports so
65 # the results can be browsed from its UI. Furthermore, it displays a
66 # graph showing evolution of the results over time.
67 ifdef JENKINS_HOME
68 CT_OPTS += -ct_hooks cth_surefire
69 endif
0 ifeq ($(.DEFAULT_GOAL),)
1 # Define default goal to `all` because this file defines some targets
2 # before the inclusion of erlang.mk leading to the wrong target becoming
3 # the default.
4 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
26 endif
27
28 # --------------------------------------------------------------------
29 # RabbitMQ components.
30 # --------------------------------------------------------------------
31
32 dep_amqp_client = hex $(PROJECT_VERSION)
33 dep_rabbit_common = hex $(PROJECT_VERSION)
34
35 # Third-party dependencies version pinning.
36 #
37 # We do that in this file, which is copied in all projects, to ensure
38 # all projects use the same versions. It avoids conflicts and makes it
39 # possible to work with rabbitmq-public-umbrella.
40
41 dep_cowboy = hex 1.0.4
42 dep_ranch = hex 1.3.0
22 # before the inclusion of erlang.mk leading to the wrong target becoming
33 # the default.
44 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
526 endif
627
728 # --------------------------------------------------------------------
2041 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
2142 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
2243 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
2345 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
2446 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
2547 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
2648 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
2749 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
2851 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
2952 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
3054 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
3155 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
3256 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
3559 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
3660 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
3761 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
3863 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
3964 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
4065 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
6590 dep_rabbitmq_web_mqtt = git_rmq rabbitmq-web-mqtt $(current_rmq_ref) $(base_rmq_ref) master
6691 dep_rabbitmq_web_mqtt_examples = git_rmq rabbitmq-web-mqtt-examples $(current_rmq_ref) $(base_rmq_ref) master
6792 dep_rabbitmq_website = git_rmq rabbitmq-website $(current_rmq_ref) $(base_rmq_ref) live master
68 dep_sockjs = git_rmq sockjs-erlang $(current_rmq_ref) $(base_rmq_ref) master
6993 dep_toke = git_rmq toke $(current_rmq_ref) $(base_rmq_ref) master
7094
7195 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
7296
73 # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
74 # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
75 # needs to add "ranch" as a BUILD_DEPS. The list of projects needing
76 # this workaround are:
77 # o rabbitmq-web-stomp
78 dep_ranch = git https://github.com/ninenines/ranch 1.2.1
97 # Third-party dependencies version pinning.
98 #
99 # We do that in this file, which is copied in all projects, to ensure
100 # all projects use the same versions. It avoids conflicts and makes it
101 # possible to work with rabbitmq-public-umbrella.
102
103 dep_cowboy_commit = 1.0.4
104 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
105 # Last commit of PropEr supporting Erlang R16B03.
106 dep_proper_commit = 735d972758d8bd85b12483626fe1b66450d6a6fe
107 dep_ranch_commit = 1.3.1
108 # Last commit of sockjs support Erlang R16B03 and 17.x.
109 dep_sockjs = git https://github.com/rabbitmq/sockjs-erlang.git 5af2b588c812c318b19bc105b577a759c71c3e0a
110 dep_webmachine_commit = 1.10.8p2
79111
80112 RABBITMQ_COMPONENTS = amqp_client \
81113 rabbit \
82114 rabbit_common \
83115 rabbitmq_amqp1_0 \
84116 rabbitmq_auth_backend_amqp \
117 rabbitmq_auth_backend_cache \
85118 rabbitmq_auth_backend_http \
86119 rabbitmq_auth_backend_ldap \
87120 rabbitmq_auth_mechanism_ssl \
88121 rabbitmq_boot_steps_visualiser \
89122 rabbitmq_clusterer \
123 rabbitmq_cli \
90124 rabbitmq_codegen \
91125 rabbitmq_consistent_hash_exchange \
126 rabbitmq_ct_client_helpers \
92127 rabbitmq_ct_helpers \
93128 rabbitmq_delayed_message_exchange \
94129 rabbitmq_dotnet_client \
97132 rabbitmq_federation_management \
98133 rabbitmq_java_client \
99134 rabbitmq_jms_client \
135 rabbitmq_jms_cts \
100136 rabbitmq_jms_topic_exchange \
101137 rabbitmq_lvc \
102138 rabbitmq_management \
0 ifeq ($(filter rabbitmq-early-test.mk,$(notdir $(MAKEFILE_LIST))),)
1 include $(dir $(lastword $(MAKEFILE_LIST)))rabbitmq-early-test.mk
2 endif
0 # --------------------------------------------------------------------
1 # Common Test flags.
2 # --------------------------------------------------------------------
3
4 # Enable the following common_test hooks on Travis and Concourse:
5 #
6 # cth_fail_fast
7 # This hook will make sure the first failure puts an end to the
8 # testsuites; ie. all remaining tests are skipped.
9 #
10 # cth_styledout
11 # This hook will change the output of common_test to something more
12 # concise and colored.
13 #
14 # On Jenkins, in addition to those common_test hooks, enable JUnit-like
15 # report. Jenkins parses those reports so the results can be browsed
16 # from its UI. Furthermore, it displays a graph showing evolution of the
17 # results over time.
18
19 CT_HOOKS ?= cth_styledout
20 TEST_DEPS += cth_styledout
21
22 RMQ_CI_CT_HOOKS = cth_fail_fast
23 ifdef TRAVIS
24 CT_HOOKS += $(RMQ_CI_CT_HOOKS)
25 TEST_DEPS += $(RMQ_CI_CT_HOOKS)
26 endif
27 ifdef CONCOURSE
28 CT_HOOKS += $(RMQ_CI_CT_HOOKS)
29 TEST_DEPS += $(RMQ_CI_CT_HOOKS)
30 endif
31 ifdef JENKINS_HOME
32 CT_HOOKS += cth_surefire $(RMQ_CI_CT_HOOKS)
33 TEST_DEPS += $(RMQ_CI_CT_HOOKS)
34 endif
35
36 dep_cth_fail_fast = git https://github.com/rabbitmq/cth_fail_fast.git master
37 dep_cth_styledout = git https://github.com/rabbitmq/cth_styledout.git master
38
39 CT_HOOKS_PARAM_VALUE = $(patsubst %,and %,$(CT_HOOKS))
40 CT_OPTS += -ct_hooks $(wordlist 2,$(words $(CT_HOOKS_PARAM_VALUE)),$(CT_HOOKS_PARAM_VALUE))
41
42 # Disable most messages on Travis because it might exceed the limit
43 # set by Travis.
44 #
45 # CAUTION: All arguments after -erl_args are passed to the emulator and
46 # common_test doesn't interpret them! Therefore, all common_test flags
47 # *MUST* appear before.
48
49 CT_QUIET_FLAGS = -verbosity 50 \
50 -erl_args \
51 -kernel error_logger silent
52
53 ifdef TRAVIS
54 CT_OPTS += $(CT_QUIET_FLAGS)
55 endif
56
57 # On CI, set $RABBITMQ_CT_SKIP_AS_ERROR so that any skipped
58 # testsuite/testgroup/testcase is considered an error.
59
60 ifdef TRAVIS
61 export RABBITMQ_CT_SKIP_AS_ERROR = true
62 endif
63 ifdef CONCOURSE
64 export RABBITMQ_CT_SKIP_AS_ERROR = true
65 endif
66 ifdef JENKINS_HOME
67 export RABBITMQ_CT_SKIP_AS_ERROR = true
68 endif
0 # --------------------------------------------------------------------
1 # Hex.pm.
2 # --------------------------------------------------------------------
3
4 .PHONY: hex-publish hex-publish-docs
5
6 HEXPM_URL = https://github.com/rabbitmq/hexpm-cli/releases/download/v0.1.0/hexpm
7 HEXPM_CLI = $(ERLANG_MK_TMP)/hexpm
8
9 $(HEXPM_CLI):
10 $(gen_verbose) $(call core_http_get,$@,$(HEXPM_URL))
11 $(verbose) chmod +x $@
12
13 rebar.config: dep_rabbit_common = hex $(PROJECT_VERSION)
14 rebar.config: dep_amqp_client = hex $(PROJECT_VERSION)
15
16 define RABBITMQ_HEXPM_DEFAULT_FILES
17 "erlang.mk",
18 "git-revisions.txt",
19 "include",
20 "LICENSE*",
21 "Makefile",
22 "rabbitmq-components.mk",
23 "README",
24 "README.md",
25 "src"
26 endef
27
28 hex-publish: $(HEXPM_CLI) app rebar.config
29 $(gen_verbose) echo "$(PROJECT_DESCRIPTION) $(PROJECT_VERSION)" \
30 > git-revisions.txt
31 ifneq ($(PROJECT),rabbit_common)
32 $(verbose) mv rabbitmq-components.mk rabbitmq-components.mk.not-hexpm
33 $(verbose) cp \
34 $(DEPS_DIR)/rabbit_common/mk/rabbitmq-components.hexpm.mk \
35 rabbitmq-components.mk
36 $(verbose) touch -r rabbitmq-components.mk.not-hexpm \
37 rabbitmq-components.mk
38 endif
39 $(verbose) trap '\
40 rm -f git-revisions.txt rebar.lock; \
41 if test -f rabbitmq-components.mk.not-hexpm; then \
42 mv rabbitmq-components.mk.not-hexpm rabbitmq-components.mk; \
43 fi' EXIT INT; \
44 $(HEXPM_CLI) publish
45
46 hex-publish-docs: $(HEXPM_CLI) app docs
47 $(gen_verbose) trap 'rm -f rebar.lock' EXIT INT; \
48 $(HEXPM_CLI) docs
00 ifeq ($(filter rabbitmq-build.mk,$(notdir $(MAKEFILE_LIST))),)
11 include $(dir $(lastword $(MAKEFILE_LIST)))rabbitmq-build.mk
2 endif
3
4 ifeq ($(filter rabbitmq-hexpm.mk,$(notdir $(MAKEFILE_LIST))),)
5 include $(dir $(lastword $(MAKEFILE_LIST)))rabbitmq-hexpm.mk
26 endif
37
48 ifeq ($(filter rabbitmq-dist.mk,$(notdir $(MAKEFILE_LIST))),)
913 include $(dir $(lastword $(MAKEFILE_LIST)))rabbitmq-run.mk
1014 endif
1115
16 ifeq ($(filter rabbitmq-test.mk,$(notdir $(MAKEFILE_LIST))),)
17 include $(dir $(lastword $(MAKEFILE_LIST)))rabbitmq-test.mk
18 endif
19
1220 ifeq ($(filter rabbitmq-tools.mk,$(notdir $(MAKEFILE_LIST))),)
1321 include $(dir $(lastword $(MAKEFILE_LIST)))rabbitmq-tools.mk
1422 endif
164164 {verify, verify_peer},
165165 {fail_if_no_peer_cert, false},
166166 {honor_cipher_order, true}]}
167 ]}
167 ]},
168 {rabbitmq_management, [
169 {listener, [
170 {port, 15671},
171 {ssl, true},
172 {ssl_opts, [
173 {cacertfile, "$(TEST_TLS_CERTS_DIR_in_config)/testca/cacert.pem"},
174 {certfile, "$(TEST_TLS_CERTS_DIR_in_config)/server/cert.pem"},
175 {keyfile, "$(TEST_TLS_CERTS_DIR_in_config)/server/key.pem"},
176 {verify, verify_peer},
177 {fail_if_no_peer_cert, false},
178 {honor_cipher_order, true}]}
179 ]}
180 ]}
168181 ].
169182 endef
170183
0 .PHONY: ct-slow ct-fast
1
2 ct-slow ct-fast:
3 $(MAKE) ct CT_SUITES='$(CT_SUITES)'
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515 -module(app_utils).
1616
5757
5858 stop_applications(Apps, ErrorHandler) ->
5959 manage_applications(fun lists:foldr/3,
60 fun application:stop/1,
60 fun(App) ->
61 rabbit_log:info("Stopping application '~s'", [App]),
62 application:stop(App)
63 end,
6164 fun application:start/1,
6265 not_started,
6366 ErrorHandler,
1010 %% The Original Code is RabbitMQ Federation.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515 -module(code_version).
1616
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(credit_flow).
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(delegate).
17
18 %% delegate is an alternative way of doing remote calls. Compared to
19 %% the rpc module, it reduces inter-node communication. For example,
20 %% if a message is routed to 1,000 queues on node A and needs to be
21 %% propagated to nodes B and C, it would be nice to avoid doing 2,000
22 %% remote casts to queue processes.
23 %%
24 %% An important issue here is preserving order - we need to make sure
25 %% that messages from a certain channel to a certain queue take a
26 %% consistent route, to prevent them being reordered. In fact all
27 %% AMQP-ish things (such as queue declaration results and basic.get)
28 %% must take the same route as well, to ensure that clients see causal
29 %% ordering correctly. Therefore we have a rather generic mechanism
30 %% here rather than just a message-reflector. That's also why we pick
31 %% the delegate process to use based on a hash of the source pid.
32 %%
33 %% When a function is invoked using delegate:invoke/2, delegate:call/2
34 %% or delegate:cast/2 on a group of pids, the pids are first split
35 %% into local and remote ones. Remote processes are then grouped by
36 %% node. The function is then invoked locally and on every node (using
37 %% gen_server2:multi/4) as many times as there are processes on that
38 %% node, sequentially.
39 %%
40 %% Errors returned when executing functions on remote nodes are re-raised
41 %% in the caller.
42 %%
43 %% RabbitMQ starts a pool of delegate processes on boot. The size of
44 %% the pool is configurable, the aim is to make sure we don't have too
45 %% few delegates and thus limit performance on many-CPU machines.
46
47 -behaviour(gen_server2).
48
49 -export([start_link/1, start_link/2,invoke_no_result/2, invoke_no_result/3,
50 invoke/2, invoke/3, monitor/2, monitor/3, demonitor/1,
51 call/2, cast/2, call/3, cast/3]).
52
53 -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
54 terminate/2, code_change/3]).
55
56 -record(state, {node, monitors, name}).
57
58 %%----------------------------------------------------------------------------
59
60 -export_type([monitor_ref/0]).
61
62 -type monitor_ref() :: reference() | {atom(), pid()}.
63 -type fun_or_mfa(A) :: fun ((pid()) -> A) | {atom(), atom(), [any()]}.
64
65 -spec start_link
66 (non_neg_integer()) -> {'ok', pid()} | ignore | {'error', any()}.
67 -spec invoke
68 ( pid(), fun_or_mfa(A)) -> A;
69 ([pid()], fun_or_mfa(A)) -> {[{pid(), A}], [{pid(), term()}]}.
70 -spec invoke_no_result(pid() | [pid()], fun_or_mfa(any())) -> 'ok'.
71 -spec monitor('process', pid()) -> monitor_ref().
72 -spec demonitor(monitor_ref()) -> 'true'.
73
74 -spec call
75 ( pid(), any()) -> any();
76 ([pid()], any()) -> {[{pid(), any()}], [{pid(), term()}]}.
77 -spec cast(pid() | [pid()], any()) -> 'ok'.
78
79 %%----------------------------------------------------------------------------
80
81 -define(HIBERNATE_AFTER_MIN, 1000).
82 -define(DESIRED_HIBERNATE, 10000).
83 -define(DEFAULT_NAME, "delegate_").
84
85 %%----------------------------------------------------------------------------
86
87 start_link(Num) ->
88 start_link(?DEFAULT_NAME, Num).
89
90 start_link(Name, Num) ->
91 Name1 = delegate_name(Name, Num),
92 gen_server2:start_link({local, Name1}, ?MODULE, [Name1], []).
93
94 invoke(Pid, FunOrMFA) ->
95 invoke(Pid, ?DEFAULT_NAME, FunOrMFA).
96
97 invoke(Pid, _Name, FunOrMFA) when is_pid(Pid) andalso node(Pid) =:= node() ->
98 apply1(FunOrMFA, Pid);
99 invoke(Pid, Name, FunOrMFA) when is_pid(Pid) ->
100 case invoke([Pid], Name, FunOrMFA) of
101 {[{Pid, Result}], []} ->
102 Result;
103 {[], [{Pid, {Class, Reason, StackTrace}}]} ->
104 erlang:raise(Class, Reason, StackTrace)
105 end;
106
107 invoke([], _Name, _FunOrMFA) -> %% optimisation
108 {[], []};
109 invoke([Pid], _Name, FunOrMFA) when node(Pid) =:= node() -> %% optimisation
110 case safe_invoke(Pid, FunOrMFA) of
111 {ok, _, Result} -> {[{Pid, Result}], []};
112 {error, _, Error} -> {[], [{Pid, Error}]}
113 end;
114 invoke(Pids, Name, FunOrMFA) when is_list(Pids) ->
115 {LocalPids, Grouped} = group_pids_by_node(Pids),
116 %% The use of multi_call is only safe because the timeout is
117 %% infinity, and thus there is no process spawned in order to do
118 %% the sending. Thus calls can't overtake preceding calls/casts.
119 {Replies, BadNodes} =
120 case orddict:fetch_keys(Grouped) of
121 [] -> {[], []};
122 RemoteNodes -> gen_server2:multi_call(
123 RemoteNodes, delegate(self(), Name, RemoteNodes),
124 {invoke, FunOrMFA, Grouped}, infinity)
125 end,
126 BadPids = [{Pid, {exit, {nodedown, BadNode}, []}} ||
127 BadNode <- BadNodes,
128 Pid <- orddict:fetch(BadNode, Grouped)],
129 ResultsNoNode = lists:append([safe_invoke(LocalPids, FunOrMFA) |
130 [Results || {_Node, Results} <- Replies]]),
131 lists:foldl(
132 fun ({ok, Pid, Result}, {Good, Bad}) -> {[{Pid, Result} | Good], Bad};
133 ({error, Pid, Error}, {Good, Bad}) -> {Good, [{Pid, Error} | Bad]}
134 end, {[], BadPids}, ResultsNoNode).
135
136 invoke_no_result(Pid, FunOrMFA) ->
137 invoke_no_result(Pid, ?DEFAULT_NAME, FunOrMFA).
138
139 invoke_no_result(Pid, _Name, FunOrMFA) when is_pid(Pid) andalso node(Pid) =:= node() ->
140 _ = safe_invoke(Pid, FunOrMFA), %% we don't care about any error
141 ok;
142 invoke_no_result(Pid, Name, FunOrMFA) when is_pid(Pid) ->
143 invoke_no_result([Pid], Name, FunOrMFA);
144
145 invoke_no_result([], _Name, _FunOrMFA) -> %% optimisation
146 ok;
147 invoke_no_result([Pid], _Name, FunOrMFA) when node(Pid) =:= node() -> %% optimisation
148 _ = safe_invoke(Pid, FunOrMFA), %% must not die
149 ok;
150 invoke_no_result(Pids, Name, FunOrMFA) when is_list(Pids) ->
151 {LocalPids, Grouped} = group_pids_by_node(Pids),
152 case orddict:fetch_keys(Grouped) of
153 [] -> ok;
154 RemoteNodes -> gen_server2:abcast(
155 RemoteNodes, delegate(self(), Name, RemoteNodes),
156 {invoke, FunOrMFA, Grouped})
157 end,
158 _ = safe_invoke(LocalPids, FunOrMFA), %% must not die
159 ok.
160
161 monitor(process, Pid) ->
162 ?MODULE:monitor(process, Pid, ?DEFAULT_NAME).
163
164 monitor(process, Pid, _Prefix) when node(Pid) =:= node() ->
165 erlang:monitor(process, Pid);
166 monitor(process, Pid, Prefix) ->
167 Name = delegate(Pid, Prefix, [node(Pid)]),
168 gen_server2:cast(Name, {monitor, self(), Pid}),
169 {Name, Pid}.
170
171 demonitor(Ref) when is_reference(Ref) ->
172 erlang:demonitor(Ref);
173 demonitor({Name, Pid}) ->
174 gen_server2:cast(Name, {demonitor, self(), Pid}).
175
176 call(PidOrPids, Name, Msg) ->
177 invoke(PidOrPids, Name, {gen_server2, call, [Msg, infinity]}).
178
179 call(PidOrPids, Msg) ->
180 call(PidOrPids, ?DEFAULT_NAME, Msg).
181
182 cast(PidOrPids, Msg) ->
183 cast(PidOrPids, ?DEFAULT_NAME, Msg).
184
185 cast(PidOrPids, Name, Msg) ->
186 invoke_no_result(PidOrPids, Name, {gen_server2, cast, [Msg]}).
187
188 %%----------------------------------------------------------------------------
189
190 group_pids_by_node(Pids) ->
191 LocalNode = node(),
192 lists:foldl(
193 fun (Pid, {Local, Remote}) when node(Pid) =:= LocalNode ->
194 {[Pid | Local], Remote};
195 (Pid, {Local, Remote}) ->
196 {Local,
197 orddict:update(
198 node(Pid), fun (List) -> [Pid | List] end, [Pid], Remote)}
199 end, {[], orddict:new()}, Pids).
200
201 delegate_name(Name, Hash) ->
202 list_to_atom(Name ++ integer_to_list(Hash)).
203
204 delegate(Pid, Prefix, RemoteNodes) ->
205 case get(delegate) of
206 undefined -> Name = delegate_name(Prefix,
207 erlang:phash2(Pid,
208 delegate_sup:count(RemoteNodes, Prefix))),
209 put(delegate, Name),
210 Name;
211 Name -> Name
212 end.
213
214 safe_invoke(Pids, FunOrMFA) when is_list(Pids) ->
215 [safe_invoke(Pid, FunOrMFA) || Pid <- Pids];
216 safe_invoke(Pid, FunOrMFA) when is_pid(Pid) ->
217 try
218 {ok, Pid, apply1(FunOrMFA, Pid)}
219 catch Class:Reason ->
220 {error, Pid, {Class, Reason, erlang:get_stacktrace()}}
221 end.
222
223 apply1({M, F, A}, Arg) -> apply(M, F, [Arg | A]);
224 apply1(Fun, Arg) -> Fun(Arg).
225
226 %%----------------------------------------------------------------------------
227
228 init([Name]) ->
229 {ok, #state{node = node(), monitors = dict:new(), name = Name}, hibernate,
230 {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}.
231
232 handle_call({invoke, FunOrMFA, Grouped}, _From, State = #state{node = Node}) ->
233 {reply, safe_invoke(orddict:fetch(Node, Grouped), FunOrMFA), State,
234 hibernate}.
235
236 handle_cast({monitor, MonitoringPid, Pid},
237 State = #state{monitors = Monitors}) ->
238 Monitors1 = case dict:find(Pid, Monitors) of
239 {ok, {Ref, Pids}} ->
240 Pids1 = gb_sets:add_element(MonitoringPid, Pids),
241 dict:store(Pid, {Ref, Pids1}, Monitors);
242 error ->
243 Ref = erlang:monitor(process, Pid),
244 Pids = gb_sets:singleton(MonitoringPid),
245 dict:store(Pid, {Ref, Pids}, Monitors)
246 end,
247 {noreply, State#state{monitors = Monitors1}, hibernate};
248
249 handle_cast({demonitor, MonitoringPid, Pid},
250 State = #state{monitors = Monitors}) ->
251 Monitors1 = case dict:find(Pid, Monitors) of
252 {ok, {Ref, Pids}} ->
253 Pids1 = gb_sets:del_element(MonitoringPid, Pids),
254 case gb_sets:is_empty(Pids1) of
255 true -> erlang:demonitor(Ref),
256 dict:erase(Pid, Monitors);
257 false -> dict:store(Pid, {Ref, Pids1}, Monitors)
258 end;
259 error ->
260 Monitors
261 end,
262 {noreply, State#state{monitors = Monitors1}, hibernate};
263
264 handle_cast({invoke, FunOrMFA, Grouped}, State = #state{node = Node}) ->
265 _ = safe_invoke(orddict:fetch(Node, Grouped), FunOrMFA),
266 {noreply, State, hibernate}.
267
268 handle_info({'DOWN', Ref, process, Pid, Info},
269 State = #state{monitors = Monitors, name = Name}) ->
270 {noreply,
271 case dict:find(Pid, Monitors) of
272 {ok, {Ref, Pids}} ->
273 Msg = {'DOWN', {Name, Pid}, process, Pid, Info},
274 gb_sets:fold(fun (MonitoringPid, _) -> MonitoringPid ! Msg end,
275 none, Pids),
276 State#state{monitors = dict:erase(Pid, Monitors)};
277 error ->
278 State
279 end, hibernate};
280
281 handle_info(_Info, State) ->
282 {noreply, State, hibernate}.
283
284 terminate(_Reason, _State) ->
285 ok.
286
287 code_change(_OldVsn, State, _Extra) ->
288 {ok, State}.
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(delegate_sup).
17
18 -behaviour(supervisor).
19
20 -export([start_link/1, start_link/2, count/1, count/2, sup_name/1]).
21
22 -export([init/1]).
23
24 -define(SERVER, "delegate_").
25
26 %%----------------------------------------------------------------------------
27
28 -spec start_link(integer()) -> rabbit_types:ok_pid_or_error().
29 -spec start_link(integer(), string()) -> rabbit_types:ok_pid_or_error().
30 -spec count([node()]) -> integer().
31
32 %%----------------------------------------------------------------------------
33
34 sup_name(Prefix) ->
35 list_to_atom(Prefix ++ "sup").
36
37 start_link(Count, Prefix) ->
38 supervisor:start_link({local, sup_name(Prefix)}, ?MODULE, [Count, Prefix]).
39 start_link(Count) ->
40 start_link(Count, ?SERVER).
41
42 count(Nodes) ->
43 count(Nodes, ?SERVER).
44
45 count([], _) ->
46 1;
47 count([Node | Nodes], Prefix) ->
48 try
49 length(supervisor:which_children({sup_name(Prefix), Node}))
50 catch exit:{{R, _}, _} when R =:= nodedown; R =:= shutdown ->
51 count(Nodes, Prefix);
52 exit:{R, _} when R =:= noproc; R =:= normal; R =:= shutdown;
53 R =:= nodedown ->
54 count(Nodes, Prefix)
55 end.
56
57 %%----------------------------------------------------------------------------
58
59 init([Count, Name]) ->
60 {ok, {{one_for_one, 10, 10},
61 [{Num, {delegate, start_link, [Name, Num]},
62 transient, 16#ffffffff, worker, [delegate]} ||
63 Num <- lists:seq(0, Count - 1)]}}.
00 %%% vi:ts=4 sw=4 et
11
22 %%% Imported from https://github.com/erlware/erlware_commons.git
3 %%% Commit 603441a0363d5433de2139759991c640846c3a62
4 %%% We export normalize/1 here
3 %%% Commit 09168347525916e291c8aa6e3073e260e5f4a116
4 %%% - We export normalize/1.
5 %%% - We add a few more testcases around string/binary comparison.
56
67 %%%-------------------------------------------------------------------
78 %%% @copyright (C) 2011, Erlware LLC
370371 "1.0.0.1+build.2")),
371372 ?assertMatch(true, not eql("FFF", "BBB")),
372373 ?assertMatch(true, not eql("1", "1BBBB")).
373
374374
375375 gt_test() ->
376376 ?assertMatch(true, gt("1.0.0-alpha.1",
00 %%% Imported from https://github.com/erlware/erlware_commons.git
1 %%% Commit 603441a0363d5433de2139759991c640846c3a62
1 %%% Commit 09168347525916e291c8aa6e3073e260e5f4a116
22
33 -module(ec_semver_parser).
44 -export([parse/1,file/1]).
1414 -define(p_zero_or_more,true).
1515
1616
17 -compile(export_all).
17
1818 -spec file(file:name()) -> any().
1919 file(Filename) -> case file:read_file(Filename) of {ok,Bin} -> parse(Bin); Err -> Err end.
2020
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(file_handle_cache).
17
18 %% A File Handle Cache
19 %%
20 %% This extends a subset of the functionality of the Erlang file
21 %% module. In the below, we use "file handle" to specifically refer to
22 %% file handles, and "file descriptor" to refer to descriptors which
23 %% are not file handles, e.g. sockets.
24 %%
25 %% Some constraints
26 %% 1) This supports one writer, multiple readers per file. Nothing
27 %% else.
28 %% 2) Do not open the same file from different processes. Bad things
29 %% may happen, especially for writes.
30 %% 3) Writes are all appends. You cannot write to the middle of a
31 %% file, although you can truncate and then append if you want.
32 %% 4) There are read and write buffers. Feel free to use the read_ahead
33 %% mode, but beware of the interaction between that buffer and the write
34 %% buffer.
35 %%
36 %% Some benefits
37 %% 1) You do not have to remember to call sync before close
38 %% 2) Buffering is much more flexible than with the plain file module,
39 %% and you can control when the buffer gets flushed out. This means
40 %% that you can rely on reads-after-writes working, without having to
41 %% call the expensive sync.
42 %% 3) Unnecessary calls to position and sync get optimised out.
43 %% 4) You can find out what your 'real' offset is, and what your
44 %% 'virtual' offset is (i.e. where the hdl really is, and where it
45 %% would be after the write buffer is written out).
46 %%
47 %% There is also a server component which serves to limit the number
48 %% of open file descriptors. This is a hard limit: the server
49 %% component will ensure that clients do not have more file
50 %% descriptors open than it's configured to allow.
51 %%
52 %% On open, the client requests permission from the server to open the
53 %% required number of file handles. The server may ask the client to
54 %% close other file handles that it has open, or it may queue the
55 %% request and ask other clients to close file handles they have open
56 %% in order to satisfy the request. Requests are always satisfied in
57 %% the order they arrive, even if a latter request (for a small number
58 %% of file handles) can be satisfied before an earlier request (for a
59 %% larger number of file handles). On close, the client sends a
60 %% message to the server. These messages allow the server to keep
61 %% track of the number of open handles. The client also keeps a
62 %% gb_tree which is updated on every use of a file handle, mapping the
63 %% time at which the file handle was last used (timestamp) to the
64 %% handle. Thus the smallest key in this tree maps to the file handle
65 %% that has not been used for the longest amount of time. This
66 %% smallest key is included in the messages to the server. As such,
67 %% the server keeps track of when the least recently used file handle
68 %% was used *at the point of the most recent open or close* by each
69 %% client.
70 %%
71 %% Note that this data can go very out of date, by the client using
72 %% the least recently used handle.
73 %%
74 %% When the limit is exceeded (i.e. the number of open file handles is
75 %% at the limit and there are pending 'open' requests), the server
76 %% calculates the average age of the last reported least recently used
77 %% file handle of all the clients. It then tells all the clients to
78 %% close any handles not used for longer than this average, by
79 %% invoking the callback the client registered. The client should
80 %% receive this message and pass it into
81 %% set_maximum_since_use/1. However, it is highly possible this age
82 %% will be greater than the ages of all the handles the client knows
83 %% of because the client has used its file handles in the mean
84 %% time. Thus at this point the client reports to the server the
85 %% current timestamp at which its least recently used file handle was
86 %% last used. The server will check two seconds later that either it
87 %% is back under the limit, in which case all is well again, or if
88 %% not, it will calculate a new average age. Its data will be much
89 %% more recent now, and so it is very likely that when this is
90 %% communicated to the clients, the clients will close file handles.
91 %% (In extreme cases, where it's very likely that all clients have
92 %% used their open handles since they last sent in an update, which
93 %% would mean that the average will never cause any file handles to
94 %% be closed, the server can send out an average age of 0, resulting
95 %% in all available clients closing all their file handles.)
96 %%
97 %% Care is taken to ensure that (a) processes which are blocked
98 %% waiting for file descriptors to become available are not sent
99 %% requests to close file handles; and (b) given it is known how many
100 %% file handles a process has open, when the average age is forced to
101 %% 0, close messages are only sent to enough processes to release the
102 %% correct number of file handles and the list of processes is
103 %% randomly shuffled. This ensures we don't cause processes to
104 %% needlessly close file handles, and ensures that we don't always
105 %% make such requests of the same processes.
106 %%
107 %% The advantage of this scheme is that there is only communication
108 %% from the client to the server on open, close, and when in the
109 %% process of trying to reduce file handle usage. There is no
110 %% communication from the client to the server on normal file handle
111 %% operations. This scheme forms a feed-back loop - the server does
112 %% not care which file handles are closed, just that some are, and it
113 %% checks this repeatedly when over the limit.
114 %%
115 %% Handles which are closed as a result of the server are put into a
116 %% "soft-closed" state in which the handle is closed (data flushed out
117 %% and sync'd first) but the state is maintained. The handle will be
118 %% fully reopened again as soon as needed, thus users of this library
119 %% do not need to worry about their handles being closed by the server
120 %% - reopening them when necessary is handled transparently.
121 %%
122 %% The server also supports obtain, release and transfer. obtain/{0,1}
123 %% blocks until a file descriptor is available, at which point the
124 %% requesting process is considered to 'own' more descriptor(s).
125 %% release/{0,1} is the inverse operation and releases previously obtained
126 %% descriptor(s). transfer/{1,2} transfers ownership of file descriptor(s)
127 %% between processes. It is non-blocking. Obtain has a
128 %% lower limit, set by the ?OBTAIN_LIMIT/1 macro. File handles can use
129 %% the entire limit, but will be evicted by obtain calls up to the
130 %% point at which no more obtain calls can be satisfied by the obtains
131 %% limit. Thus there will always be some capacity available for file
132 %% handles. Processes that use obtain are never asked to return them,
133 %% and they are not managed in any way by the server. It is simply a
134 %% mechanism to ensure that processes that need file descriptors such
135 %% as sockets can do so in such a way that the overall number of open
136 %% file descriptors is managed.
137 %%
138 %% The callers of register_callback/3, obtain, and the argument of
139 %% transfer are monitored, reducing the count of handles in use
140 %% appropriately when the processes terminate.
141
142 -behaviour(gen_server2).
143
144 -export([register_callback/3]).
145 -export([open/3, close/1, read/2, append/2, needs_sync/1, sync/1, position/2,
146 truncate/1, current_virtual_offset/1, current_raw_offset/1, flush/1,
147 copy/3, set_maximum_since_use/1, delete/1, clear/1,
148 open_with_absolute_path/3]).
149 -export([obtain/0, obtain/1, release/0, release/1, transfer/1, transfer/2,
150 set_limit/1, get_limit/0, info_keys/0, with_handle/1, with_handle/2,
151 info/0, info/1, clear_read_cache/0, clear_process_read_cache/0]).
152 -export([ulimit/0]).
153
154 -export([start_link/0, start_link/2, init/1, handle_call/3, handle_cast/2,
155 handle_info/2, terminate/2, code_change/3, prioritise_cast/3]).
156
157 -define(SERVER, ?MODULE).
158 -define(RESERVED_FOR_OTHERS, 100).
159
160 -define(FILE_HANDLES_LIMIT_OTHER, 1024).
161 -define(FILE_HANDLES_CHECK_INTERVAL, 2000).
162
163 -define(OBTAIN_LIMIT(LIMIT), trunc((LIMIT * 0.9) - 2)).
164 -define(CLIENT_ETS_TABLE, file_handle_cache_client).
165 -define(ELDERS_ETS_TABLE, file_handle_cache_elders).
166
167 %%----------------------------------------------------------------------------
168
169 -record(file,
170 { reader_count,
171 has_writer
172 }).
173
174 -record(handle,
175 { hdl,
176 ref,
177 offset,
178 is_dirty,
179 write_buffer_size,
180 write_buffer_size_limit,
181 write_buffer,
182 read_buffer,
183 read_buffer_pos,
184 read_buffer_rem, %% Num of bytes from pos to end
185 read_buffer_size, %% Next size of read buffer to use
186 read_buffer_size_limit, %% Max size of read buffer to use
187 read_buffer_usage, %% Bytes we have read from it, for tuning
188 at_eof,
189 path,
190 mode,
191 options,
192 is_write,
193 is_read,
194 last_used_at
195 }).
196
197 -record(fhc_state,
198 { elders,
199 limit,
200 open_count,
201 open_pending,
202 obtain_limit, %%socket
203 obtain_count_socket,
204 obtain_count_file,
205 obtain_pending_socket,
206 obtain_pending_file,
207 clients,
208 timer_ref,
209 alarm_set,
210 alarm_clear
211 }).
212
213 -record(cstate,
214 { pid,
215 callback,
216 opened,
217 obtained_socket,
218 obtained_file,
219 blocked,
220 pending_closes
221 }).
222
223 -record(pending,
224 { kind,
225 pid,
226 requested,
227 from
228 }).
229
230 %%----------------------------------------------------------------------------
231 %% Specs
232 %%----------------------------------------------------------------------------
233
234 -type ref() :: any().
235 -type ok_or_error() :: 'ok' | {'error', any()}.
236 -type val_or_error(T) :: {'ok', T} | {'error', any()}.
237 -type position() :: ('bof' | 'eof' | non_neg_integer() |
238 {('bof' |'eof'), non_neg_integer()} |
239 {'cur', integer()}).
240 -type offset() :: non_neg_integer().
241
242 -spec register_callback(atom(), atom(), [any()]) -> 'ok'.
243 -spec open
244 (file:filename(), [any()],
245 [{'write_buffer', (non_neg_integer() | 'infinity' | 'unbuffered')} |
246 {'read_buffer', (non_neg_integer() | 'unbuffered')}]) ->
247 val_or_error(ref()).
248 -spec open_with_absolute_path
249 (file:filename(), [any()],
250 [{'write_buffer', (non_neg_integer() | 'infinity' | 'unbuffered')} |
251 {'read_buffer', (non_neg_integer() | 'unbuffered')}]) ->
252 val_or_error(ref()).
253 -spec close(ref()) -> ok_or_error().
254 -spec read
255 (ref(), non_neg_integer()) -> val_or_error([char()] | binary()) | 'eof'.
256 -spec append(ref(), iodata()) -> ok_or_error().
257 -spec sync(ref()) -> ok_or_error().
258 -spec position(ref(), position()) -> val_or_error(offset()).
259 -spec truncate(ref()) -> ok_or_error().
260 -spec current_virtual_offset(ref()) -> val_or_error(offset()).
261 -spec current_raw_offset(ref()) -> val_or_error(offset()).
262 -spec flush(ref()) -> ok_or_error().
263 -spec copy(ref(), ref(), non_neg_integer()) -> val_or_error(non_neg_integer()).
264 -spec delete(ref()) -> ok_or_error().
265 -spec clear(ref()) -> ok_or_error().
266 -spec set_maximum_since_use(non_neg_integer()) -> 'ok'.
267 -spec obtain() -> 'ok'.
268 -spec obtain(non_neg_integer()) -> 'ok'.
269 -spec release() -> 'ok'.
270 -spec release(non_neg_integer()) -> 'ok'.
271 -spec transfer(pid()) -> 'ok'.
272 -spec transfer(pid(), non_neg_integer()) -> 'ok'.
273 -spec with_handle(fun(() -> A)) -> A.
274 -spec with_handle(non_neg_integer(), fun(() -> A)) -> A.
275 -spec set_limit(non_neg_integer()) -> 'ok'.
276 -spec get_limit() -> non_neg_integer().
277 -spec info_keys() -> rabbit_types:info_keys().
278 -spec info() -> rabbit_types:infos().
279 -spec info([atom()]) -> rabbit_types:infos().
280 -spec ulimit() -> 'unknown' | non_neg_integer().
281
282 %%----------------------------------------------------------------------------
283 -define(INFO_KEYS, [total_limit, total_used, sockets_limit, sockets_used]).
284
285 %%----------------------------------------------------------------------------
286 %% Public API
287 %%----------------------------------------------------------------------------
288
289 start_link() ->
290 start_link(fun alarm_handler:set_alarm/1, fun alarm_handler:clear_alarm/1).
291
292 start_link(AlarmSet, AlarmClear) ->
293 gen_server2:start_link({local, ?SERVER}, ?MODULE, [AlarmSet, AlarmClear],
294 [{timeout, infinity}]).
295
296 register_callback(M, F, A)
297 when is_atom(M) andalso is_atom(F) andalso is_list(A) ->
298 gen_server2:cast(?SERVER, {register_callback, self(), {M, F, A}}).
299
300 open(Path, Mode, Options) ->
301 open_with_absolute_path(filename:absname(Path), Mode, Options).
302
303 open_with_absolute_path(Path, Mode, Options) ->
304 File1 = #file { reader_count = RCount, has_writer = HasWriter } =
305 case get({Path, fhc_file}) of
306 File = #file {} -> File;
307 undefined -> #file { reader_count = 0,
308 has_writer = false }
309 end,
310 Mode1 = append_to_write(Mode),
311 IsWriter = is_writer(Mode1),
312 case IsWriter andalso HasWriter of
313 true -> {error, writer_exists};
314 false -> {ok, Ref} = new_closed_handle(Path, Mode1, Options),
315 case get_or_reopen_timed([{Ref, new}]) of
316 {ok, [_Handle1]} ->
317 RCount1 = case is_reader(Mode1) of
318 true -> RCount + 1;
319 false -> RCount
320 end,
321 HasWriter1 = HasWriter orelse IsWriter,
322 put({Path, fhc_file},
323 File1 #file { reader_count = RCount1,
324 has_writer = HasWriter1 }),
325 {ok, Ref};
326 Error ->
327 erase({Ref, fhc_handle}),
328 Error
329 end
330 end.
331
332 close(Ref) ->
333 case erase({Ref, fhc_handle}) of
334 undefined -> ok;
335 Handle -> case hard_close(Handle) of
336 ok -> ok;
337 {Error, Handle1} -> put_handle(Ref, Handle1),
338 Error
339 end
340 end.
341
342 read(Ref, Count) ->
343 with_flushed_handles(
344 [Ref], keep,
345 fun ([#handle { is_read = false }]) ->
346 {error, not_open_for_reading};
347 ([#handle{read_buffer_size_limit = 0,
348 hdl = Hdl, offset = Offset} = Handle]) ->
349 %% The read buffer is disabled. This is just an
350 %% optimization: the clauses below can handle this case.
351 case prim_file_read(Hdl, Count) of
352 {ok, Data} -> {{ok, Data},
353 [Handle#handle{offset = Offset+size(Data)}]};
354 eof -> {eof, [Handle #handle { at_eof = true }]};
355 Error -> {Error, Handle}
356 end;
357 ([Handle = #handle{read_buffer = Buf,
358 read_buffer_pos = BufPos,
359 read_buffer_rem = BufRem,
360 read_buffer_usage = BufUsg,
361 offset = Offset}])
362 when BufRem >= Count ->
363 <<_:BufPos/binary, Res:Count/binary, _/binary>> = Buf,
364 {{ok, Res}, [Handle#handle{offset = Offset + Count,
365 read_buffer_pos = BufPos + Count,
366 read_buffer_rem = BufRem - Count,
367 read_buffer_usage = BufUsg + Count }]};
368 ([Handle0]) ->
369 maybe_reduce_read_cache([Ref]),
370 Handle = #handle{read_buffer = Buf,
371 read_buffer_pos = BufPos,
372 read_buffer_rem = BufRem,
373 read_buffer_size = BufSz,
374 hdl = Hdl,
375 offset = Offset}
376 = tune_read_buffer_limit(Handle0, Count),
377 WantedCount = Count - BufRem,
378 case prim_file_read(Hdl, max(BufSz, WantedCount)) of
379 {ok, Data} ->
380 <<_:BufPos/binary, BufTl/binary>> = Buf,
381 ReadCount = size(Data),
382 case ReadCount < WantedCount of
383 true ->
384 OffSet1 = Offset + BufRem + ReadCount,
385 {{ok, <<BufTl/binary, Data/binary>>},
386 [reset_read_buffer(
387 Handle#handle{offset = OffSet1})]};
388 false ->
389 <<Hd:WantedCount/binary, _/binary>> = Data,
390 OffSet1 = Offset + BufRem + WantedCount,
391 BufRem1 = ReadCount - WantedCount,
392 {{ok, <<BufTl/binary, Hd/binary>>},
393 [Handle#handle{offset = OffSet1,
394 read_buffer = Data,
395 read_buffer_pos = WantedCount,
396 read_buffer_rem = BufRem1,
397 read_buffer_usage = WantedCount}]}
398 end;
399 eof ->
400 {eof, [Handle #handle { at_eof = true }]};
401 Error ->
402 {Error, [reset_read_buffer(Handle)]}
403 end
404 end).
405
406 append(Ref, Data) ->
407 with_handles(
408 [Ref],
409 fun ([#handle { is_write = false }]) ->
410 {error, not_open_for_writing};
411 ([Handle]) ->
412 case maybe_seek(eof, Handle) of
413 {{ok, _Offset}, #handle { hdl = Hdl, offset = Offset,
414 write_buffer_size_limit = 0,
415 at_eof = true } = Handle1} ->
416 Offset1 = Offset + iolist_size(Data),
417 {prim_file_write(Hdl, Data),
418 [Handle1 #handle { is_dirty = true, offset = Offset1 }]};
419 {{ok, _Offset}, #handle { write_buffer = WriteBuffer,
420 write_buffer_size = Size,
421 write_buffer_size_limit = Limit,
422 at_eof = true } = Handle1} ->
423 WriteBuffer1 = [Data | WriteBuffer],
424 Size1 = Size + iolist_size(Data),
425 Handle2 = Handle1 #handle { write_buffer = WriteBuffer1,
426 write_buffer_size = Size1 },
427 case Limit =/= infinity andalso Size1 > Limit of
428 true -> {Result, Handle3} = write_buffer(Handle2),
429 {Result, [Handle3]};
430 false -> {ok, [Handle2]}
431 end;
432 {{error, _} = Error, Handle1} ->
433 {Error, [Handle1]}
434 end
435 end).
436
437 sync(Ref) ->
438 with_flushed_handles(
439 [Ref], keep,
440 fun ([#handle { is_dirty = false, write_buffer = [] }]) ->
441 ok;
442 ([Handle = #handle { hdl = Hdl,
443 is_dirty = true, write_buffer = [] }]) ->
444 case prim_file_sync(Hdl) of
445 ok -> {ok, [Handle #handle { is_dirty = false }]};
446 Error -> {Error, [Handle]}
447 end
448 end).
449
450 needs_sync(Ref) ->
451 %% This must *not* use with_handles/2; see bug 25052
452 case get({Ref, fhc_handle}) of
453 #handle { is_dirty = false, write_buffer = [] } -> false;
454 #handle {} -> true
455 end.
456
457 position(Ref, NewOffset) ->
458 with_flushed_handles(
459 [Ref], keep,
460 fun ([Handle]) -> {Result, Handle1} = maybe_seek(NewOffset, Handle),
461 {Result, [Handle1]}
462 end).
463
464 truncate(Ref) ->
465 with_flushed_handles(
466 [Ref],
467 fun ([Handle1 = #handle { hdl = Hdl }]) ->
468 case prim_file:truncate(Hdl) of
469 ok -> {ok, [Handle1 #handle { at_eof = true }]};
470 Error -> {Error, [Handle1]}
471 end
472 end).
473
474 current_virtual_offset(Ref) ->
475 with_handles([Ref], fun ([#handle { at_eof = true, is_write = true,
476 offset = Offset,
477 write_buffer_size = Size }]) ->
478 {ok, Offset + Size};
479 ([#handle { offset = Offset }]) ->
480 {ok, Offset}
481 end).
482
483 current_raw_offset(Ref) ->
484 with_handles([Ref], fun ([Handle]) -> {ok, Handle #handle.offset} end).
485
486 flush(Ref) ->
487 with_flushed_handles([Ref], fun ([Handle]) -> {ok, [Handle]} end).
488
489 copy(Src, Dest, Count) ->
490 with_flushed_handles(
491 [Src, Dest],
492 fun ([SHandle = #handle { is_read = true, hdl = SHdl, offset = SOffset },
493 DHandle = #handle { is_write = true, hdl = DHdl, offset = DOffset }]
494 ) ->
495 case prim_file:copy(SHdl, DHdl, Count) of
496 {ok, Count1} = Result1 ->
497 {Result1,
498 [SHandle #handle { offset = SOffset + Count1 },
499 DHandle #handle { offset = DOffset + Count1,
500 is_dirty = true }]};
501 Error ->
502 {Error, [SHandle, DHandle]}
503 end;
504 (_Handles) ->
505 {error, incorrect_handle_modes}
506 end).
507
508 delete(Ref) ->
509 case erase({Ref, fhc_handle}) of
510 undefined ->
511 ok;
512 Handle = #handle { path = Path } ->
513 case hard_close(Handle #handle { is_dirty = false,
514 write_buffer = [] }) of
515 ok -> prim_file:delete(Path);
516 {Error, Handle1} -> put_handle(Ref, Handle1),
517 Error
518 end
519 end.
520
521 clear(Ref) ->
522 with_handles(
523 [Ref],
524 fun ([#handle { at_eof = true, write_buffer_size = 0, offset = 0 }]) ->
525 ok;
526 ([Handle]) ->
527 case maybe_seek(bof, Handle#handle{write_buffer = [],
528 write_buffer_size = 0}) of
529 {{ok, 0}, Handle1 = #handle { hdl = Hdl }} ->
530 case prim_file:truncate(Hdl) of
531 ok -> {ok, [Handle1 #handle { at_eof = true }]};
532 Error -> {Error, [Handle1]}
533 end;
534 {{error, _} = Error, Handle1} ->
535 {Error, [Handle1]}
536 end
537 end).
538
539 set_maximum_since_use(MaximumAge) ->
540 Now = time_compat:monotonic_time(),
541 case lists:foldl(
542 fun ({{Ref, fhc_handle},
543 Handle = #handle { hdl = Hdl, last_used_at = Then }}, Rep) ->
544 case Hdl =/= closed andalso
545 time_compat:convert_time_unit(Now - Then,
546 native,
547 micro_seconds)
548 >= MaximumAge of
549 true -> soft_close(Ref, Handle) orelse Rep;
550 false -> Rep
551 end;
552 (_KeyValuePair, Rep) ->
553 Rep
554 end, false, get()) of
555 false -> age_tree_change(), ok;
556 true -> ok
557 end.
558
559 obtain() -> obtain(1).
560 release() -> release(1).
561 transfer(Pid) -> transfer(Pid, 1).
562
563 obtain(Count) -> obtain(Count, socket).
564 release(Count) -> release(Count, socket).
565
566 with_handle(Fun) ->
567 with_handle(1, Fun).
568
569 with_handle(N, Fun) ->
570 ok = obtain(N, file),
571 try Fun()
572 after ok = release(N, file)
573 end.
574
575 obtain(Count, Type) when Count > 0 ->
576 %% If the FHC isn't running, obtains succeed immediately.
577 case whereis(?SERVER) of
578 undefined -> ok;
579 _ -> gen_server2:call(
580 ?SERVER, {obtain, Count, Type, self()}, infinity)
581 end.
582
583 release(Count, Type) when Count > 0 ->
584 gen_server2:cast(?SERVER, {release, Count, Type, self()}).
585
586 transfer(Pid, Count) when Count > 0 ->
587 gen_server2:cast(?SERVER, {transfer, Count, self(), Pid}).
588
589 set_limit(Limit) ->
590 gen_server2:call(?SERVER, {set_limit, Limit}, infinity).
591
592 get_limit() ->
593 gen_server2:call(?SERVER, get_limit, infinity).
594
595 info_keys() -> ?INFO_KEYS.
596
597 info() -> info(?INFO_KEYS).
598 info(Items) -> gen_server2:call(?SERVER, {info, Items}, infinity).
599
600 clear_read_cache() ->
601 gen_server2:cast(?SERVER, clear_read_cache).
602
603 clear_process_read_cache() ->
604 [
605 begin
606 Handle1 = reset_read_buffer(Handle),
607 put({Ref, fhc_handle}, Handle1)
608 end ||
609 {{Ref, fhc_handle}, Handle} <- get(),
610 size(Handle#handle.read_buffer) > 0
611 ].
612
613 %%----------------------------------------------------------------------------
614 %% Internal functions
615 %%----------------------------------------------------------------------------
616
617 prim_file_read(Hdl, Size) ->
618 file_handle_cache_stats:update(
619 io_read, Size, fun() -> prim_file:read(Hdl, Size) end).
620
621 prim_file_write(Hdl, Bytes) ->
622 file_handle_cache_stats:update(
623 io_write, iolist_size(Bytes), fun() -> prim_file:write(Hdl, Bytes) end).
624
625 prim_file_sync(Hdl) ->
626 file_handle_cache_stats:update(io_sync, fun() -> prim_file:sync(Hdl) end).
627
628 prim_file_position(Hdl, NewOffset) ->
629 file_handle_cache_stats:update(
630 io_seek, fun() -> prim_file:position(Hdl, NewOffset) end).
631
632 is_reader(Mode) -> lists:member(read, Mode).
633
634 is_writer(Mode) -> lists:member(write, Mode).
635
636 append_to_write(Mode) ->
637 case lists:member(append, Mode) of
638 true -> [write | Mode -- [append, write]];
639 false -> Mode
640 end.
641
642 with_handles(Refs, Fun) ->
643 with_handles(Refs, reset, Fun).
644
645 with_handles(Refs, ReadBuffer, Fun) ->
646 case get_or_reopen_timed([{Ref, reopen} || Ref <- Refs]) of
647 {ok, Handles0} ->
648 Handles = case ReadBuffer of
649 reset -> [reset_read_buffer(H) || H <- Handles0];
650 keep -> Handles0
651 end,
652 case Fun(Handles) of
653 {Result, Handles1} when is_list(Handles1) ->
654 _ = lists:zipwith(fun put_handle/2, Refs, Handles1),
655 Result;
656 Result ->
657 Result
658 end;
659 Error ->
660 Error
661 end.
662
663 with_flushed_handles(Refs, Fun) ->
664 with_flushed_handles(Refs, reset, Fun).
665
666 with_flushed_handles(Refs, ReadBuffer, Fun) ->
667 with_handles(
668 Refs, ReadBuffer,
669 fun (Handles) ->
670 case lists:foldl(
671 fun (Handle, {ok, HandlesAcc}) ->
672 {Res, Handle1} = write_buffer(Handle),
673 {Res, [Handle1 | HandlesAcc]};
674 (Handle, {Error, HandlesAcc}) ->
675 {Error, [Handle | HandlesAcc]}
676 end, {ok, []}, Handles) of
677 {ok, Handles1} ->
678 Fun(lists:reverse(Handles1));
679 {Error, Handles1} ->
680 {Error, lists:reverse(Handles1)}
681 end
682 end).
683
684 get_or_reopen_timed(RefNewOrReopens) ->
685 file_handle_cache_stats:update(
686 io_file_handle_open_attempt, fun() -> get_or_reopen(RefNewOrReopens) end).
687
688 get_or_reopen(RefNewOrReopens) ->
689 case partition_handles(RefNewOrReopens) of
690 {OpenHdls, []} ->
691 {ok, [Handle || {_Ref, Handle} <- OpenHdls]};
692 {OpenHdls, ClosedHdls} ->
693 Oldest = oldest(get_age_tree(),
694 fun () -> time_compat:monotonic_time() end),
695 case gen_server2:call(?SERVER, {open, self(), length(ClosedHdls),
696 Oldest}, infinity) of
697 ok ->
698 case reopen(ClosedHdls) of
699 {ok, RefHdls} -> sort_handles(RefNewOrReopens,
700 OpenHdls, RefHdls, []);
701 Error -> Error
702 end;
703 close ->
704 [soft_close(Ref, Handle) ||
705 {{Ref, fhc_handle}, Handle = #handle { hdl = Hdl }} <-
706 get(),
707 Hdl =/= closed],
708 get_or_reopen(RefNewOrReopens)
709 end
710 end.
711
712 reopen(ClosedHdls) -> reopen(ClosedHdls, get_age_tree(), []).
713
714 reopen([], Tree, RefHdls) ->
715 put_age_tree(Tree),
716 {ok, lists:reverse(RefHdls)};
717 reopen([{Ref, NewOrReopen, Handle = #handle { hdl = closed,
718 path = Path,
719 mode = Mode0,
720 offset = Offset,
721 last_used_at = undefined }} |
722 RefNewOrReopenHdls] = ToOpen, Tree, RefHdls) ->
723 Mode = case NewOrReopen of
724 new -> Mode0;
725 reopen -> file_handle_cache_stats:update(io_reopen),
726 [read | Mode0]
727 end,
728 case prim_file:open(Path, Mode) of
729 {ok, Hdl} ->
730 Now = time_compat:monotonic_time(),
731 {{ok, _Offset}, Handle1} =
732 maybe_seek(Offset, reset_read_buffer(
733 Handle#handle{hdl = Hdl,
734 offset = 0,
735 last_used_at = Now})),
736 put({Ref, fhc_handle}, Handle1),
737 reopen(RefNewOrReopenHdls, gb_trees:insert({Now, Ref}, true, Tree),
738 [{Ref, Handle1} | RefHdls]);
739 Error ->
740 %% NB: none of the handles in ToOpen are in the age tree
741 Oldest = oldest(Tree, fun () -> undefined end),
742 [gen_server2:cast(?SERVER, {close, self(), Oldest}) || _ <- ToOpen],
743 put_age_tree(Tree),
744 Error
745 end.
746
747 partition_handles(RefNewOrReopens) ->
748 lists:foldr(
749 fun ({Ref, NewOrReopen}, {Open, Closed}) ->
750 case get({Ref, fhc_handle}) of
751 #handle { hdl = closed } = Handle ->
752 {Open, [{Ref, NewOrReopen, Handle} | Closed]};
753 #handle {} = Handle ->
754 {[{Ref, Handle} | Open], Closed}
755 end
756 end, {[], []}, RefNewOrReopens).
757
758 sort_handles([], [], [], Acc) ->
759 {ok, lists:reverse(Acc)};
760 sort_handles([{Ref, _} | RefHdls], [{Ref, Handle} | RefHdlsA], RefHdlsB, Acc) ->
761 sort_handles(RefHdls, RefHdlsA, RefHdlsB, [Handle | Acc]);
762 sort_handles([{Ref, _} | RefHdls], RefHdlsA, [{Ref, Handle} | RefHdlsB], Acc) ->
763 sort_handles(RefHdls, RefHdlsA, RefHdlsB, [Handle | Acc]).
764
765 put_handle(Ref, Handle = #handle { last_used_at = Then }) ->
766 Now = time_compat:monotonic_time(),
767 age_tree_update(Then, Now, Ref),
768 put({Ref, fhc_handle}, Handle #handle { last_used_at = Now }).
769
770 with_age_tree(Fun) -> put_age_tree(Fun(get_age_tree())).
771
772 get_age_tree() ->
773 case get(fhc_age_tree) of
774 undefined -> gb_trees:empty();
775 AgeTree -> AgeTree
776 end.
777
778 put_age_tree(Tree) -> put(fhc_age_tree, Tree).
779
780 age_tree_update(Then, Now, Ref) ->
781 with_age_tree(
782 fun (Tree) ->
783 gb_trees:insert({Now, Ref}, true,
784 gb_trees:delete_any({Then, Ref}, Tree))
785 end).
786
787 age_tree_delete(Then, Ref) ->
788 with_age_tree(
789 fun (Tree) ->
790 Tree1 = gb_trees:delete_any({Then, Ref}, Tree),
791 Oldest = oldest(Tree1, fun () -> undefined end),
792 gen_server2:cast(?SERVER, {close, self(), Oldest}),
793 Tree1
794 end).
795
796 age_tree_change() ->
797 with_age_tree(
798 fun (Tree) ->
799 case gb_trees:is_empty(Tree) of
800 true -> Tree;
801 false -> {{Oldest, _Ref}, _} = gb_trees:smallest(Tree),
802 gen_server2:cast(?SERVER, {update, self(), Oldest}),
803 Tree
804 end
805 end).
806
807 oldest(Tree, DefaultFun) ->
808 case gb_trees:is_empty(Tree) of
809 true -> DefaultFun();
810 false -> {{Oldest, _Ref}, _} = gb_trees:smallest(Tree),
811 Oldest
812 end.
813
814 new_closed_handle(Path, Mode, Options) ->
815 WriteBufferSize =
816 case application:get_env(rabbit, fhc_write_buffering) of
817 {ok, false} -> 0;
818 {ok, true} ->
819 case proplists:get_value(write_buffer, Options, unbuffered) of
820 unbuffered -> 0;
821 infinity -> infinity;
822 N when is_integer(N) -> N
823 end
824 end,
825 ReadBufferSize =
826 case application:get_env(rabbit, fhc_read_buffering) of
827 {ok, false} -> 0;
828 {ok, true} ->
829 case proplists:get_value(read_buffer, Options, unbuffered) of
830 unbuffered -> 0;
831 N2 when is_integer(N2) -> N2
832 end
833 end,
834 Ref = make_ref(),
835 put({Ref, fhc_handle}, #handle { hdl = closed,
836 ref = Ref,
837 offset = 0,
838 is_dirty = false,
839 write_buffer_size = 0,
840 write_buffer_size_limit = WriteBufferSize,
841 write_buffer = [],
842 read_buffer = <<>>,
843 read_buffer_pos = 0,
844 read_buffer_rem = 0,
845 read_buffer_size = ReadBufferSize,
846 read_buffer_size_limit = ReadBufferSize,
847 read_buffer_usage = 0,
848 at_eof = false,
849 path = Path,
850 mode = Mode,
851 options = Options,
852 is_write = is_writer(Mode),
853 is_read = is_reader(Mode),
854 last_used_at = undefined }),
855 {ok, Ref}.
856
857 soft_close(Ref, Handle) ->
858 {Res, Handle1} = soft_close(Handle),
859 case Res of
860 ok -> put({Ref, fhc_handle}, Handle1),
861 true;
862 _ -> put_handle(Ref, Handle1),
863 false
864 end.
865
866 soft_close(Handle = #handle { hdl = closed }) ->
867 {ok, Handle};
868 soft_close(Handle) ->
869 case write_buffer(Handle) of
870 {ok, #handle { hdl = Hdl,
871 ref = Ref,
872 is_dirty = IsDirty,
873 last_used_at = Then } = Handle1 } ->
874 ok = case IsDirty of
875 true -> prim_file_sync(Hdl);
876 false -> ok
877 end,
878 ok = prim_file:close(Hdl),
879 age_tree_delete(Then, Ref),
880 {ok, Handle1 #handle { hdl = closed,
881 is_dirty = false,
882 last_used_at = undefined }};
883 {_Error, _Handle} = Result ->
884 Result
885 end.
886
887 hard_close(Handle) ->
888 case soft_close(Handle) of
889 {ok, #handle { path = Path,
890 is_read = IsReader, is_write = IsWriter }} ->
891 #file { reader_count = RCount, has_writer = HasWriter } = File =
892 get({Path, fhc_file}),
893 RCount1 = case IsReader of
894 true -> RCount - 1;
895 false -> RCount
896 end,
897 HasWriter1 = HasWriter andalso not IsWriter,
898 case RCount1 =:= 0 andalso not HasWriter1 of
899 true -> erase({Path, fhc_file});
900 false -> put({Path, fhc_file},
901 File #file { reader_count = RCount1,
902 has_writer = HasWriter1 })
903 end,
904 ok;
905 {_Error, _Handle} = Result ->
906 Result
907 end.
908
909 maybe_seek(New, Handle = #handle{hdl = Hdl,
910 offset = Old,
911 read_buffer_pos = BufPos,
912 read_buffer_rem = BufRem,
913 at_eof = AtEoF}) ->
914 {AtEoF1, NeedsSeek} = needs_seek(AtEoF, Old, New),
915 case NeedsSeek of
916 true when is_number(New) andalso
917 ((New >= Old andalso New =< BufRem + Old)
918 orelse (New < Old andalso Old - New =< BufPos)) ->
919 Diff = New - Old,
920 {{ok, New}, Handle#handle{offset = New,
921 at_eof = AtEoF1,
922 read_buffer_pos = BufPos + Diff,
923 read_buffer_rem = BufRem - Diff}};
924 true ->
925 case prim_file_position(Hdl, New) of
926 {ok, Offset1} = Result ->
927 {Result, reset_read_buffer(Handle#handle{offset = Offset1,
928 at_eof = AtEoF1})};
929 {error, _} = Error ->
930 {Error, Handle}
931 end;
932 false ->
933 {{ok, Old}, Handle}
934 end.
935
936 needs_seek( AtEoF, _CurOffset, cur ) -> {AtEoF, false};
937 needs_seek( AtEoF, _CurOffset, {cur, 0}) -> {AtEoF, false};
938 needs_seek( true, _CurOffset, eof ) -> {true , false};
939 needs_seek( true, _CurOffset, {eof, 0}) -> {true , false};
940 needs_seek( false, _CurOffset, eof ) -> {true , true };
941 needs_seek( false, _CurOffset, {eof, 0}) -> {true , true };
942 needs_seek( AtEoF, 0, bof ) -> {AtEoF, false};
943 needs_seek( AtEoF, 0, {bof, 0}) -> {AtEoF, false};
944 needs_seek( AtEoF, CurOffset, CurOffset) -> {AtEoF, false};
945 needs_seek( true, CurOffset, {bof, DesiredOffset})
946 when DesiredOffset >= CurOffset ->
947 {true, true};
948 needs_seek( true, _CurOffset, {cur, DesiredOffset})
949 when DesiredOffset > 0 ->
950 {true, true};
951 needs_seek( true, CurOffset, DesiredOffset) %% same as {bof, DO}
952 when is_integer(DesiredOffset) andalso DesiredOffset >= CurOffset ->
953 {true, true};
954 %% because we can't really track size, we could well end up at EoF and not know
955 needs_seek(_AtEoF, _CurOffset, _DesiredOffset) ->
956 {false, true}.
957
958 write_buffer(Handle = #handle { write_buffer = [] }) ->
959 {ok, Handle};
960 write_buffer(Handle = #handle { hdl = Hdl, offset = Offset,
961 write_buffer = WriteBuffer,
962 write_buffer_size = DataSize,
963 at_eof = true }) ->
964 case prim_file_write(Hdl, lists:reverse(WriteBuffer)) of
965 ok ->
966 Offset1 = Offset + DataSize,
967 {ok, Handle #handle { offset = Offset1, is_dirty = true,
968 write_buffer = [], write_buffer_size = 0 }};
969 {error, _} = Error ->
970 {Error, Handle}
971 end.
972
973 reset_read_buffer(Handle) ->
974 Handle#handle{read_buffer = <<>>,
975 read_buffer_pos = 0,
976 read_buffer_rem = 0}.
977
978 %% We come into this function whenever there's been a miss while
979 %% reading from the buffer - but note that when we first start with a
980 %% new handle the usage will be 0. Therefore in that case don't take
981 %% it as meaning the buffer was useless, we just haven't done anything
982 %% yet!
983 tune_read_buffer_limit(Handle = #handle{read_buffer_usage = 0}, _Count) ->
984 Handle;
985 %% In this head we have been using the buffer but now tried to read
986 %% outside it. So how did we do? If we used less than the size of the
987 %% buffer, make the new buffer the size of what we used before, but
988 %% add one byte (so that next time we can distinguish between getting
989 %% the buffer size exactly right and actually wanting more). If we
990 %% read 100% of what we had, then double it for next time, up to the
991 %% limit that was set when we were created.
992 tune_read_buffer_limit(Handle = #handle{read_buffer = Buf,
993 read_buffer_usage = Usg,
994 read_buffer_size = Sz,
995 read_buffer_size_limit = Lim}, Count) ->
996 %% If the buffer is <<>> then we are in the first read after a
997 %% reset, the read_buffer_usage is the total usage from before the
998 %% reset. But otherwise we are in a read which read off the end of
999 %% the buffer, so really the size of this read should be included
1000 %% in the usage.
1001 TotalUsg = case Buf of
1002 <<>> -> Usg;
1003 _ -> Usg + Count
1004 end,
1005 Handle#handle{read_buffer_usage = 0,
1006 read_buffer_size = erlang:min(case TotalUsg < Sz of
1007 true -> Usg + 1;
1008 false -> Usg * 2
1009 end, Lim)}.
1010
1011 maybe_reduce_read_cache(SparedRefs) ->
1012 case vm_memory_monitor:get_memory_use(bytes) of
1013 {_, infinity} -> ok;
1014 {MemUse, MemLimit} when MemUse < MemLimit -> ok;
1015 {MemUse, MemLimit} -> reduce_read_cache(
1016 (MemUse - MemLimit) * 2,
1017 SparedRefs)
1018 end.
1019
1020 reduce_read_cache(MemToFree, SparedRefs) ->
1021 Handles = lists:sort(
1022 fun({_, H1}, {_, H2}) -> H1 < H2 end,
1023 [{R, H} || {{R, fhc_handle}, H} <- get(),
1024 not lists:member(R, SparedRefs)
1025 andalso size(H#handle.read_buffer) > 0]),
1026 FreedMem = lists:foldl(
1027 fun
1028 (_, Freed) when Freed >= MemToFree ->
1029 Freed;
1030 ({Ref, #handle{read_buffer = Buf} = Handle}, Freed) ->
1031 Handle1 = reset_read_buffer(Handle),
1032 put({Ref, fhc_handle}, Handle1),
1033 Freed + size(Buf)
1034 end, 0, Handles),
1035 if
1036 FreedMem < MemToFree andalso SparedRefs =/= [] ->
1037 reduce_read_cache(MemToFree - FreedMem, []);
1038 true ->
1039 ok
1040 end.
1041
1042 infos(Items, State) -> [{Item, i(Item, State)} || Item <- Items].
1043
1044 i(total_limit, #fhc_state{limit = Limit}) -> Limit;
1045 i(total_used, State) -> used(State);
1046 i(sockets_limit, #fhc_state{obtain_limit = Limit}) -> Limit;
1047 i(sockets_used, #fhc_state{obtain_count_socket = Count}) -> Count;
1048 i(Item, _) -> throw({bad_argument, Item}).
1049
1050 used(#fhc_state{open_count = C1,
1051 obtain_count_socket = C2,
1052 obtain_count_file = C3}) -> C1 + C2 + C3.
1053
1054 %%----------------------------------------------------------------------------
1055 %% gen_server2 callbacks
1056 %%----------------------------------------------------------------------------
1057
1058 init([AlarmSet, AlarmClear]) ->
1059 _ = file_handle_cache_stats:init(),
1060 Limit = case application:get_env(file_handles_high_watermark) of
1061 {ok, Watermark} when (is_integer(Watermark) andalso
1062 Watermark > 0) ->
1063 Watermark;
1064 _ ->
1065 case ulimit() of
1066 unknown -> ?FILE_HANDLES_LIMIT_OTHER;
1067 Lim -> lists:max([2, Lim - ?RESERVED_FOR_OTHERS])
1068 end
1069 end,
1070 ObtainLimit = obtain_limit(Limit),
1071 error_logger:info_msg("Limiting to approx ~p file handles (~p sockets)~n",
1072 [Limit, ObtainLimit]),
1073 Clients = ets:new(?CLIENT_ETS_TABLE, [set, private, {keypos, #cstate.pid}]),
1074 Elders = ets:new(?ELDERS_ETS_TABLE, [set, private]),
1075 {ok, #fhc_state { elders = Elders,
1076 limit = Limit,
1077 open_count = 0,
1078 open_pending = pending_new(),
1079 obtain_limit = ObtainLimit,
1080 obtain_count_file = 0,
1081 obtain_pending_file = pending_new(),
1082 obtain_count_socket = 0,
1083 obtain_pending_socket = pending_new(),
1084 clients = Clients,
1085 timer_ref = undefined,
1086 alarm_set = AlarmSet,
1087 alarm_clear = AlarmClear }}.
1088
1089 prioritise_cast(Msg, _Len, _State) ->
1090 case Msg of
1091 {release, _, _, _} -> 5;
1092 _ -> 0
1093 end.
1094
1095 handle_call({open, Pid, Requested, EldestUnusedSince}, From,
1096 State = #fhc_state { open_count = Count,
1097 open_pending = Pending,
1098 elders = Elders,
1099 clients = Clients })
1100 when EldestUnusedSince =/= undefined ->
1101 true = ets:insert(Elders, {Pid, EldestUnusedSince}),
1102 Item = #pending { kind = open,
1103 pid = Pid,
1104 requested = Requested,
1105 from = From },
1106 ok = track_client(Pid, Clients),
1107 case needs_reduce(State #fhc_state { open_count = Count + Requested }) of
1108 true -> case ets:lookup(Clients, Pid) of
1109 [#cstate { opened = 0 }] ->
1110 true = ets:update_element(
1111 Clients, Pid, {#cstate.blocked, true}),
1112 {noreply,
1113 reduce(State #fhc_state {
1114 open_pending = pending_in(Item, Pending) })};
1115 [#cstate { opened = Opened }] ->
1116 true = ets:update_element(
1117 Clients, Pid,
1118 {#cstate.pending_closes, Opened}),
1119 {reply, close, State}
1120 end;
1121 false -> {noreply, run_pending_item(Item, State)}
1122 end;
1123
1124 handle_call({obtain, N, Type, Pid}, From,
1125 State = #fhc_state { clients = Clients }) ->
1126 Count = obtain_state(Type, count, State),
1127 Pending = obtain_state(Type, pending, State),
1128 ok = track_client(Pid, Clients),
1129 Item = #pending { kind = {obtain, Type}, pid = Pid,
1130 requested = N, from = From },
1131 Enqueue = fun () ->
1132 true = ets:update_element(Clients, Pid,
1133 {#cstate.blocked, true}),
1134 set_obtain_state(Type, pending,
1135 pending_in(Item, Pending), State)
1136 end,
1137 {noreply,
1138 case obtain_limit_reached(Type, State) of
1139 true -> Enqueue();
1140 false -> case needs_reduce(
1141 set_obtain_state(Type, count, Count + 1, State)) of
1142 true -> reduce(Enqueue());
1143 false -> adjust_alarm(
1144 State, run_pending_item(Item, State))
1145 end
1146 end};
1147
1148 handle_call({set_limit, Limit}, _From, State) ->
1149 {reply, ok, adjust_alarm(
1150 State, maybe_reduce(
1151 process_pending(
1152 State #fhc_state {
1153 limit = Limit,
1154 obtain_limit = obtain_limit(Limit) })))};
1155
1156 handle_call(get_limit, _From, State = #fhc_state { limit = Limit }) ->
1157 {reply, Limit, State};
1158
1159 handle_call({info, Items}, _From, State) ->
1160 {reply, infos(Items, State), State}.
1161
1162 handle_cast({register_callback, Pid, MFA},
1163 State = #fhc_state { clients = Clients }) ->
1164 ok = track_client(Pid, Clients),
1165 true = ets:update_element(Clients, Pid, {#cstate.callback, MFA}),
1166 {noreply, State};
1167
1168 handle_cast({update, Pid, EldestUnusedSince},
1169 State = #fhc_state { elders = Elders })
1170 when EldestUnusedSince =/= undefined ->
1171 true = ets:insert(Elders, {Pid, EldestUnusedSince}),
1172 %% don't call maybe_reduce from here otherwise we can create a
1173 %% storm of messages
1174 {noreply, State};
1175
1176 handle_cast({release, N, Type, Pid}, State) ->
1177 State1 = process_pending(update_counts({obtain, Type}, Pid, -N, State)),
1178 {noreply, adjust_alarm(State, State1)};
1179
1180 handle_cast({close, Pid, EldestUnusedSince},
1181 State = #fhc_state { elders = Elders, clients = Clients }) ->
1182 true = case EldestUnusedSince of
1183 undefined -> ets:delete(Elders, Pid);
1184 _ -> ets:insert(Elders, {Pid, EldestUnusedSince})
1185 end,
1186 ets:update_counter(Clients, Pid, {#cstate.pending_closes, -1, 0, 0}),
1187 {noreply, adjust_alarm(State, process_pending(
1188 update_counts(open, Pid, -1, State)))};
1189
1190 handle_cast({transfer, N, FromPid, ToPid}, State) ->
1191 ok = track_client(ToPid, State#fhc_state.clients),
1192 {noreply, process_pending(
1193 update_counts({obtain, socket}, ToPid, +N,
1194 update_counts({obtain, socket}, FromPid, -N,
1195 State)))};
1196
1197 handle_cast(clear_read_cache, State) ->
1198 _ = clear_process_read_cache(),
1199 {noreply, State}.
1200
1201 handle_info(check_counts, State) ->
1202 {noreply, maybe_reduce(State #fhc_state { timer_ref = undefined })};
1203
1204 handle_info({'DOWN', _MRef, process, Pid, _Reason},
1205 State = #fhc_state { elders = Elders,
1206 open_count = OpenCount,
1207 open_pending = OpenPending,
1208 obtain_count_file = ObtainCountF,
1209 obtain_count_socket = ObtainCountS,
1210 obtain_pending_file = ObtainPendingF,
1211 obtain_pending_socket = ObtainPendingS,
1212 clients = Clients }) ->
1213 [#cstate { opened = Opened,
1214 obtained_file = ObtainedFile,
1215 obtained_socket = ObtainedSocket}] =
1216 ets:lookup(Clients, Pid),
1217 true = ets:delete(Clients, Pid),
1218 true = ets:delete(Elders, Pid),
1219 Fun = fun (#pending { pid = Pid1 }) -> Pid1 =/= Pid end,
1220 State1 = process_pending(
1221 State #fhc_state {
1222 open_count = OpenCount - Opened,
1223 open_pending = filter_pending(Fun, OpenPending),
1224 obtain_count_file = ObtainCountF - ObtainedFile,
1225 obtain_count_socket = ObtainCountS - ObtainedSocket,
1226 obtain_pending_file = filter_pending(Fun, ObtainPendingF),
1227 obtain_pending_socket = filter_pending(Fun, ObtainPendingS) }),
1228 {noreply, adjust_alarm(State, State1)}.
1229
1230 terminate(_Reason, State = #fhc_state { clients = Clients,
1231 elders = Elders }) ->
1232 ets:delete(Clients),
1233 ets:delete(Elders),
1234 State.
1235
1236 code_change(_OldVsn, State, _Extra) ->
1237 {ok, State}.
1238
1239 %%----------------------------------------------------------------------------
1240 %% pending queue abstraction helpers
1241 %%----------------------------------------------------------------------------
1242
1243 queue_fold(Fun, Init, Q) ->
1244 case queue:out(Q) of
1245 {empty, _Q} -> Init;
1246 {{value, V}, Q1} -> queue_fold(Fun, Fun(V, Init), Q1)
1247 end.
1248
1249 filter_pending(Fun, {Count, Queue}) ->
1250 {Delta, Queue1} =
1251 queue_fold(
1252 fun (Item = #pending { requested = Requested }, {DeltaN, QueueN}) ->
1253 case Fun(Item) of
1254 true -> {DeltaN, queue:in(Item, QueueN)};
1255 false -> {DeltaN - Requested, QueueN}
1256 end
1257 end, {0, queue:new()}, Queue),
1258 {Count + Delta, Queue1}.
1259
1260 pending_new() ->
1261 {0, queue:new()}.
1262
1263 pending_in(Item = #pending { requested = Requested }, {Count, Queue}) ->
1264 {Count + Requested, queue:in(Item, Queue)}.
1265
1266 pending_out({0, _Queue} = Pending) ->
1267 {empty, Pending};
1268 pending_out({N, Queue}) ->
1269 {{value, #pending { requested = Requested }} = Result, Queue1} =
1270 queue:out(Queue),
1271 {Result, {N - Requested, Queue1}}.
1272
1273 pending_count({Count, _Queue}) ->
1274 Count.
1275
1276 %%----------------------------------------------------------------------------
1277 %% server helpers
1278 %%----------------------------------------------------------------------------
1279
1280 obtain_limit(infinity) -> infinity;
1281 obtain_limit(Limit) -> case ?OBTAIN_LIMIT(Limit) of
1282 OLimit when OLimit < 0 -> 0;
1283 OLimit -> OLimit
1284 end.
1285
1286 obtain_limit_reached(socket, State) -> obtain_limit_reached(State);
1287 obtain_limit_reached(file, State) -> needs_reduce(State).
1288
1289 obtain_limit_reached(#fhc_state{obtain_limit = Limit,
1290 obtain_count_socket = Count}) ->
1291 Limit =/= infinity andalso Count >= Limit.
1292
1293 obtain_state(file, count, #fhc_state{obtain_count_file = N}) -> N;
1294 obtain_state(socket, count, #fhc_state{obtain_count_socket = N}) -> N;
1295 obtain_state(file, pending, #fhc_state{obtain_pending_file = N}) -> N;
1296 obtain_state(socket, pending, #fhc_state{obtain_pending_socket = N}) -> N.
1297
1298 set_obtain_state(file, count, N, S) -> S#fhc_state{obtain_count_file = N};
1299 set_obtain_state(socket, count, N, S) -> S#fhc_state{obtain_count_socket = N};
1300 set_obtain_state(file, pending, N, S) -> S#fhc_state{obtain_pending_file = N};
1301 set_obtain_state(socket, pending, N, S) -> S#fhc_state{obtain_pending_socket = N}.
1302
1303 adjust_alarm(OldState = #fhc_state { alarm_set = AlarmSet,
1304 alarm_clear = AlarmClear }, NewState) ->
1305 case {obtain_limit_reached(OldState), obtain_limit_reached(NewState)} of
1306 {false, true} -> AlarmSet({file_descriptor_limit, []});
1307 {true, false} -> AlarmClear(file_descriptor_limit);
1308 _ -> ok
1309 end,
1310 NewState.
1311
1312 process_pending(State = #fhc_state { limit = infinity }) ->
1313 State;
1314 process_pending(State) ->
1315 process_open(process_obtain(socket, process_obtain(file, State))).
1316
1317 process_open(State = #fhc_state { limit = Limit,
1318 open_pending = Pending}) ->
1319 {Pending1, State1} = process_pending(Pending, Limit - used(State), State),
1320 State1 #fhc_state { open_pending = Pending1 }.
1321
1322 process_obtain(socket, State = #fhc_state { limit = Limit,
1323 obtain_limit = ObtainLimit,
1324 open_count = OpenCount,
1325 obtain_count_socket = ObtainCount,
1326 obtain_pending_socket = Pending,
1327 obtain_count_file = ObtainCountF}) ->
1328 Quota = min(ObtainLimit - ObtainCount,
1329 Limit - (OpenCount + ObtainCount + ObtainCountF)),
1330 {Pending1, State1} = process_pending(Pending, Quota, State),
1331 State1#fhc_state{obtain_pending_socket = Pending1};
1332 process_obtain(file, State = #fhc_state { limit = Limit,
1333 open_count = OpenCount,
1334 obtain_count_socket = ObtainCountS,
1335 obtain_count_file = ObtainCountF,
1336 obtain_pending_file = Pending}) ->
1337 Quota = Limit - (OpenCount + ObtainCountS + ObtainCountF),
1338 {Pending1, State1} = process_pending(Pending, Quota, State),
1339 State1#fhc_state{obtain_pending_file = Pending1}.
1340
1341 process_pending(Pending, Quota, State) when Quota =< 0 ->
1342 {Pending, State};
1343 process_pending(Pending, Quota, State) ->
1344 case pending_out(Pending) of
1345 {empty, _Pending} ->
1346 {Pending, State};
1347 {{value, #pending { requested = Requested }}, _Pending1}
1348 when Requested > Quota ->
1349 {Pending, State};
1350 {{value, #pending { requested = Requested } = Item}, Pending1} ->
1351 process_pending(Pending1, Quota - Requested,
1352 run_pending_item(Item, State))
1353 end.
1354
1355 run_pending_item(#pending { kind = Kind,
1356 pid = Pid,
1357 requested = Requested,
1358 from = From },
1359 State = #fhc_state { clients = Clients }) ->
1360 gen_server2:reply(From, ok),
1361 true = ets:update_element(Clients, Pid, {#cstate.blocked, false}),
1362 update_counts(Kind, Pid, Requested, State).
1363
1364 update_counts(open, Pid, Delta,
1365 State = #fhc_state { open_count = OpenCount,
1366 clients = Clients }) ->
1367 ets:update_counter(Clients, Pid, {#cstate.opened, Delta}),
1368 State #fhc_state { open_count = OpenCount + Delta};
1369 update_counts({obtain, file}, Pid, Delta,
1370 State = #fhc_state {obtain_count_file = ObtainCountF,
1371 clients = Clients }) ->
1372 ets:update_counter(Clients, Pid, {#cstate.obtained_file, Delta}),
1373 State #fhc_state { obtain_count_file = ObtainCountF + Delta};
1374 update_counts({obtain, socket}, Pid, Delta,
1375 State = #fhc_state {obtain_count_socket = ObtainCountS,
1376 clients = Clients }) ->
1377 ets:update_counter(Clients, Pid, {#cstate.obtained_socket, Delta}),
1378 State #fhc_state { obtain_count_socket = ObtainCountS + Delta}.
1379
1380 maybe_reduce(State) ->
1381 case needs_reduce(State) of
1382 true -> reduce(State);
1383 false -> State
1384 end.
1385
1386 needs_reduce(#fhc_state { limit = Limit,
1387 open_count = OpenCount,
1388 open_pending = {OpenPending, _},
1389 obtain_limit = ObtainLimit,
1390 obtain_count_socket = ObtainCountS,
1391 obtain_count_file = ObtainCountF,
1392 obtain_pending_file = {ObtainPendingF, _},
1393 obtain_pending_socket = {ObtainPendingS, _} }) ->
1394 Limit =/= infinity
1395 andalso (((OpenCount + ObtainCountS + ObtainCountF) > Limit)
1396 orelse (OpenPending =/= 0)
1397 orelse (ObtainPendingF =/= 0)
1398 orelse (ObtainCountS < ObtainLimit
1399 andalso (ObtainPendingS =/= 0))).
1400
1401 reduce(State = #fhc_state { open_pending = OpenPending,
1402 obtain_pending_file = ObtainPendingFile,
1403 obtain_pending_socket = ObtainPendingSocket,
1404 elders = Elders,
1405 clients = Clients,
1406 timer_ref = TRef }) ->
1407 Now = time_compat:monotonic_time(),
1408 {CStates, Sum, ClientCount} =
1409 ets:foldl(fun ({Pid, Eldest}, {CStatesAcc, SumAcc, CountAcc} = Accs) ->
1410 [#cstate { pending_closes = PendingCloses,
1411 opened = Opened,
1412 blocked = Blocked } = CState] =
1413 ets:lookup(Clients, Pid),
1414 TimeDiff = time_compat:convert_time_unit(
1415 Now - Eldest, native, micro_seconds),
1416 case Blocked orelse PendingCloses =:= Opened of
1417 true -> Accs;
1418 false -> {[CState | CStatesAcc],
1419 SumAcc + TimeDiff,
1420 CountAcc + 1}
1421 end
1422 end, {[], 0, 0}, Elders),
1423 case CStates of
1424 [] -> ok;
1425 _ -> case (Sum / ClientCount) -
1426 (1000 * ?FILE_HANDLES_CHECK_INTERVAL) of
1427 AverageAge when AverageAge > 0 ->
1428 notify_age(CStates, AverageAge);
1429 _ ->
1430 notify_age0(Clients, CStates,
1431 pending_count(OpenPending) +
1432 pending_count(ObtainPendingFile) +
1433 pending_count(ObtainPendingSocket))
1434 end
1435 end,
1436 case TRef of
1437 undefined -> TRef1 = erlang:send_after(
1438 ?FILE_HANDLES_CHECK_INTERVAL, ?SERVER,
1439 check_counts),
1440 State #fhc_state { timer_ref = TRef1 };
1441 _ -> State
1442 end.
1443
1444 notify_age(CStates, AverageAge) ->
1445 lists:foreach(
1446 fun (#cstate { callback = undefined }) -> ok;
1447 (#cstate { callback = {M, F, A} }) -> apply(M, F, A ++ [AverageAge])
1448 end, CStates).
1449
1450 notify_age0(Clients, CStates, Required) ->
1451 case [CState || CState <- CStates, CState#cstate.callback =/= undefined] of
1452 [] -> ok;
1453 Notifications -> S = rand_compat:uniform(length(Notifications)),
1454 {L1, L2} = lists:split(S, Notifications),
1455 notify(Clients, Required, L2 ++ L1)
1456 end.
1457
1458 notify(_Clients, _Required, []) ->
1459 ok;
1460 notify(_Clients, Required, _Notifications) when Required =< 0 ->
1461 ok;
1462 notify(Clients, Required, [#cstate{ pid = Pid,
1463 callback = {M, F, A},
1464 opened = Opened } | Notifications]) ->
1465 apply(M, F, A ++ [0]),
1466 ets:update_element(Clients, Pid, {#cstate.pending_closes, Opened}),
1467 notify(Clients, Required - Opened, Notifications).
1468
1469 track_client(Pid, Clients) ->
1470 case ets:insert_new(Clients, #cstate { pid = Pid,
1471 callback = undefined,
1472 opened = 0,
1473 obtained_file = 0,
1474 obtained_socket = 0,
1475 blocked = false,
1476 pending_closes = 0 }) of
1477 true -> _MRef = erlang:monitor(process, Pid),
1478 ok;
1479 false -> ok
1480 end.
1481
1482
1483 %% To increase the number of file descriptors: on Windows set ERL_MAX_PORTS
1484 %% environment variable, on Linux set `ulimit -n`.
1485 ulimit() ->
1486 case proplists:get_value(max_fds, erlang:system_info(check_io)) of
1487 MaxFds when is_integer(MaxFds) andalso MaxFds > 1 ->
1488 case os:type() of
1489 {win32, _OsName} ->
1490 %% On Windows max_fds is twice the number of open files:
1491 %% https://github.com/yrashk/erlang/blob/e1282325ed75e52a98d5/erts/emulator/sys/win32/sys.c#L2459-2466
1492 MaxFds div 2;
1493 _Any ->
1494 %% For other operating systems trust Erlang.
1495 MaxFds
1496 end;
1497 _ ->
1498 unknown
1499 end.
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(file_handle_cache_stats).
17
18 %% stats about read / write operations that go through the fhc.
19
20 -export([init/0, update/3, update/2, update/1, get/0]).
21
22 -define(TABLE, ?MODULE).
23
24 -define(COUNT,
25 [io_reopen, mnesia_ram_tx, mnesia_disk_tx,
26 msg_store_read, msg_store_write,
27 queue_index_journal_write, queue_index_write, queue_index_read]).
28 -define(COUNT_TIME, [io_sync, io_seek, io_file_handle_open_attempt]).
29 -define(COUNT_TIME_BYTES, [io_read, io_write]).
30
31 init() ->
32 ets:new(?TABLE, [public, named_table]),
33 [ets:insert(?TABLE, {{Op, Counter}, 0}) || Op <- ?COUNT_TIME_BYTES,
34 Counter <- [count, bytes, time]],
35 [ets:insert(?TABLE, {{Op, Counter}, 0}) || Op <- ?COUNT_TIME,
36 Counter <- [count, time]],
37 [ets:insert(?TABLE, {{Op, Counter}, 0}) || Op <- ?COUNT,
38 Counter <- [count]].
39
40 update(Op, Bytes, Thunk) ->
41 {Time, Res} = timer_tc(Thunk),
42 ets:update_counter(?TABLE, {Op, count}, 1),
43 ets:update_counter(?TABLE, {Op, bytes}, Bytes),
44 ets:update_counter(?TABLE, {Op, time}, Time),
45 Res.
46
47 update(Op, Thunk) ->
48 {Time, Res} = timer_tc(Thunk),
49 ets:update_counter(?TABLE, {Op, count}, 1),
50 ets:update_counter(?TABLE, {Op, time}, Time),
51 Res.
52
53 update(Op) ->
54 ets:update_counter(?TABLE, {Op, count}, 1),
55 ok.
56
57 get() ->
58 lists:sort(ets:tab2list(?TABLE)).
59
60 timer_tc(Thunk) ->
61 T1 = time_compat:monotonic_time(),
62 Res = Thunk(),
63 T2 = time_compat:monotonic_time(),
64 Diff = time_compat:convert_time_unit(T2 - T1, native, micro_seconds),
65 {Diff, Res}.
9090 %% call/3, i.e. a pid, global name, local name, or local name on a
9191 %% particular node.
9292 %%
93
94 %% All modifications are (C) 2009-2013 GoPivotal, Inc.
93 %% 11) Internal buffer length is emitted as a core [RabbitMQ] metric.
94
95 %% All modifications are (C) 2009-2017 Pivotal Software, Inc.
9596
9697 %% ``The contents of this file are subject to the Erlang Public License,
9798 %% Version 1.1, (the "License"); you may not use this file except in
217218
218219 %% State record
219220 -record(gs2_state, {parent, name, state, mod, time,
220 timeout_state, queue, debug, prioritisers}).
221 timeout_state, queue, debug, prioritisers,
222 timer, init_stats_fun, emit_stats_fun, stop_stats_fun}).
221223
222224 %%%=========================================================================
223225 %%% Specs. These exist only to shut up dialyzer's warnings
512514 Debug = debug_options(Name, Options),
513515 Queue = priority_queue:new(),
514516 Backoff1 = extend_backoff(Backoff),
515 loop(find_prioritisers(
517 {InitStatsFun, EmitStatsFun, StopStatsFun} = stats_funs(),
518 loop(InitStatsFun(find_prioritisers(
516519 #gs2_state { parent = Parent, name = Name, state = State,
517520 mod = Mod, time = Timeout, timeout_state = Backoff1,
518 queue = Queue, debug = Debug })).
521 queue = Queue, debug = Debug,
522 init_stats_fun = InitStatsFun,
523 emit_stats_fun = EmitStatsFun,
524 stop_stats_fun = StopStatsFun }))).
519525
520526 %%%========================================================================
521527 %%% Gen-callback functions
534540 Name = name(Name0),
535541 Debug = debug_options(Name, Options),
536542 Queue = priority_queue:new(),
543 {InitStatsFun, EmitStatsFun, StopStatsFun} = stats_funs(),
537544 GS2State = find_prioritisers(
538545 #gs2_state { parent = Parent,
539546 name = Name,
540547 mod = Mod,
541548 queue = Queue,
542 debug = Debug }),
549 debug = Debug,
550 init_stats_fun = InitStatsFun,
551 emit_stats_fun = EmitStatsFun,
552 stop_stats_fun = StopStatsFun }),
543553 case catch Mod:init(Args) of
544554 {ok, State} ->
545555 proc_lib:init_ack(Starter, {ok, self()}),
546 loop(GS2State #gs2_state { state = State,
547 time = infinity,
548 timeout_state = undefined });
556 loop(InitStatsFun(GS2State#gs2_state { state = State,
557 time = infinity,
558 timeout_state = undefined }));
549559 {ok, State, Timeout} ->
550560 proc_lib:init_ack(Starter, {ok, self()}),
551 loop(GS2State #gs2_state { state = State,
552 time = Timeout,
553 timeout_state = undefined });
561 loop(InitStatsFun(
562 GS2State#gs2_state { state = State,
563 time = Timeout,
564 timeout_state = undefined }));
554565 {ok, State, Timeout, Backoff = {backoff, _, _, _}} ->
555566 Backoff1 = extend_backoff(Backoff),
556567 proc_lib:init_ack(Starter, {ok, self()}),
557 loop(GS2State #gs2_state { state = State,
558 time = Timeout,
559 timeout_state = Backoff1 });
568 loop(InitStatsFun(GS2State#gs2_state { state = State,
569 time = Timeout,
570 timeout_state = Backoff1 }));
560571 {ok, State, Timeout, Backoff = {backoff, _, _, _}, Mod1} ->
561572 Backoff1 = extend_backoff(Backoff),
562573 proc_lib:init_ack(Starter, {ok, self()}),
563 loop(find_prioritisers(
564 GS2State #gs2_state { mod = Mod1,
565 state = State,
566 time = Timeout,
567 timeout_state = Backoff1 }));
574 loop(InitStatsFun(find_prioritisers(
575 GS2State#gs2_state { mod = Mod1,
576 state = State,
577 time = Timeout,
578 timeout_state = Backoff1 })));
568579 {stop, Reason} ->
569580 %% For consistency, we must make sure that the
570581 %% registered name (if any) is unregistered before
696707 proc_lib:hibernate(?MODULE, wake_hib,
697708 [GS2State #gs2_state { timeout_state = TS }]).
698709
699 pre_hibernate(GS2State = #gs2_state { state = State,
700 mod = Mod }) ->
710 pre_hibernate(GS2State0 = #gs2_state { state = State,
711 mod = Mod }) ->
712 GS2State = maybe_stop_stats(GS2State0),
701713 case erlang:function_exported(Mod, handle_pre_hibernate, 1) of
702714 true ->
703715 case catch Mod:handle_pre_hibernate(State) of
710722 hibernate(GS2State)
711723 end.
712724
713 post_hibernate(GS2State = #gs2_state { state = State,
714 mod = Mod }) ->
725 post_hibernate(GS2State0 = #gs2_state { state = State,
726 mod = Mod,
727 init_stats_fun = InitStatsFun }) ->
728 GS2State = InitStatsFun(GS2State0),
715729 case erlang:function_exported(Mod, handle_post_hibernate, 1) of
716730 true ->
717731 case catch Mod:handle_post_hibernate(State) of
765779 in(Input, infinity, GS2State);
766780 in({system, _From, _Req} = Input, GS2State) ->
767781 in(Input, infinity, GS2State);
782 in(emit_gen_server2_stats, GS2State = #gs2_state{ emit_stats_fun = EmitStatsFun }) ->
783 EmitStatsFun(GS2State);
768784 in(Input, GS2State = #gs2_state { prioritisers = {_, _, F} }) ->
769785 in(Input, F(Input, GS2State), GS2State).
770786
11241140 terminate(Reason, Msg, #gs2_state { name = Name,
11251141 mod = Mod,
11261142 state = State,
1127 debug = Debug }) ->
1143 debug = Debug,
1144 stop_stats_fun = StopStatsFun} = GS2State) ->
1145 StopStatsFun(GS2State),
11281146 case catch Mod:terminate(Reason, State) of
11291147 {'EXIT', R} ->
11301148 error_info(R, Reason, Name, Msg, State, Debug),
13441362 end;
13451363 false -> DefaultThunk()
13461364 end.
1365
1366 stats_funs() ->
1367 case ets:info(gen_server2_metrics) of
1368 undefined ->
1369 {fun(GS2State) -> GS2State end,
1370 fun(GS2State) -> GS2State end,
1371 fun(GS2State) -> GS2State end};
1372 _ ->
1373 {fun init_stats/1, fun emit_stats/1, fun stop_stats/1}
1374 end.
1375
1376 init_stats(GS2State) ->
1377 emit_stats(rabbit_event:init_stats_timer(GS2State, #gs2_state.timer)).
1378
1379 emit_stats(GS2State = #gs2_state{queue = Queue}) ->
1380 rabbit_core_metrics:gen_server2_stats(self(), priority_queue:len(Queue)),
1381 rabbit_event:ensure_stats_timer(
1382 rabbit_event:reset_stats_timer(GS2State, #gs2_state.timer),
1383 #gs2_state.timer, emit_gen_server2_stats).
1384
1385 stop_stats(GS2State) ->
1386 maybe_stop_stats(GS2State),
1387 rabbit_core_metrics:gen_server2_deleted(self()).
1388
1389 maybe_stop_stats(#gs2_state{timer = undefined} = GS2State) ->
1390 GS2State;
1391 maybe_stop_stats(GS2State) ->
1392 rabbit_event:stop_stats_timer(GS2State, #gs2_state.timer).
0 %% @author Bob Ippolito <bob@mochimedia.com>
1 %% @copyright 2007 Mochi Media, Inc.
2
3 %% @doc Utilities for parsing and quoting.
4
5 -module(mochiweb_util).
6 -author('bob@mochimedia.com').
7 -export([join/2, quote_plus/1, urlencode/1, parse_qs/1, unquote/1]).
8 -export([path_split/1]).
9 -export([urlsplit/1, urlsplit_path/1, urlunsplit/1, urlunsplit_path/1]).
10 -export([parse_header/1]).
11 -export([shell_quote/1, cmd/1, cmd_string/1, cmd_port/2, cmd_status/1, cmd_status/2]).
12 -export([record_to_proplist/2, record_to_proplist/3]).
13 -export([safe_relative_path/1, partition/2]).
14 -export([parse_qvalues/1, pick_accepted_encodings/3]).
15 -export([make_io/1]).
16
17 -define(PERCENT, 37). % $\%
18 -define(FULLSTOP, 46). % $\.
19 -define(IS_HEX(C), ((C >= $0 andalso C =< $9) orelse
20 (C >= $a andalso C =< $f) orelse
21 (C >= $A andalso C =< $F))).
22 -define(QS_SAFE(C), ((C >= $a andalso C =< $z) orelse
23 (C >= $A andalso C =< $Z) orelse
24 (C >= $0 andalso C =< $9) orelse
25 (C =:= ?FULLSTOP orelse C =:= $- orelse C =:= $~ orelse
26 C =:= $_))).
27
28 hexdigit(C) when C < 10 -> $0 + C;
29 hexdigit(C) when C < 16 -> $A + (C - 10).
30
31 unhexdigit(C) when C >= $0, C =< $9 -> C - $0;
32 unhexdigit(C) when C >= $a, C =< $f -> C - $a + 10;
33 unhexdigit(C) when C >= $A, C =< $F -> C - $A + 10.
34
35 %% @spec partition(String, Sep) -> {String, [], []} | {Prefix, Sep, Postfix}
36 %% @doc Inspired by Python 2.5's str.partition:
37 %% partition("foo/bar", "/") = {"foo", "/", "bar"},
38 %% partition("foo", "/") = {"foo", "", ""}.
39 partition(String, Sep) ->
40 case partition(String, Sep, []) of
41 undefined ->
42 {String, "", ""};
43 Result ->
44 Result
45 end.
46
47 partition("", _Sep, _Acc) ->
48 undefined;
49 partition(S, Sep, Acc) ->
50 case partition2(S, Sep) of
51 undefined ->
52 [C | Rest] = S,
53 partition(Rest, Sep, [C | Acc]);
54 Rest ->
55 {lists:reverse(Acc), Sep, Rest}
56 end.
57
58 partition2(Rest, "") ->
59 Rest;
60 partition2([C | R1], [C | R2]) ->
61 partition2(R1, R2);
62 partition2(_S, _Sep) ->
63 undefined.
64
65
66
67 %% @spec safe_relative_path(string()) -> string() | undefined
68 %% @doc Return the reduced version of a relative path or undefined if it
69 %% is not safe. safe relative paths can be joined with an absolute path
70 %% and will result in a subdirectory of the absolute path. Safe paths
71 %% never contain a backslash character.
72 safe_relative_path("/" ++ _) ->
73 undefined;
74 safe_relative_path(P) ->
75 case string:chr(P, $\\) of
76 0 ->
77 safe_relative_path(P, []);
78 _ ->
79 undefined
80 end.
81
82 safe_relative_path("", Acc) ->
83 case Acc of
84 [] ->
85 "";
86 _ ->
87 string:join(lists:reverse(Acc), "/")
88 end;
89 safe_relative_path(P, Acc) ->
90 case partition(P, "/") of
91 {"", "/", _} ->
92 %% /foo or foo//bar
93 undefined;
94 {"..", _, _} when Acc =:= [] ->
95 undefined;
96 {"..", _, Rest} ->
97 safe_relative_path(Rest, tl(Acc));
98 {Part, "/", ""} ->
99 safe_relative_path("", ["", Part | Acc]);
100 {Part, _, Rest} ->
101 safe_relative_path(Rest, [Part | Acc])
102 end.
103
104 %% @spec shell_quote(string()) -> string()
105 %% @doc Quote a string according to UNIX shell quoting rules, returns a string
106 %% surrounded by double quotes.
107 shell_quote(L) ->
108 shell_quote(L, [$\"]).
109
110 %% @spec cmd_port([string()], Options) -> port()
111 %% @doc open_port({spawn, mochiweb_util:cmd_string(Argv)}, Options).
112 cmd_port(Argv, Options) ->
113 open_port({spawn, cmd_string(Argv)}, Options).
114
115 %% @spec cmd([string()]) -> string()
116 %% @doc os:cmd(cmd_string(Argv)).
117 cmd(Argv) ->
118 os:cmd(cmd_string(Argv)).
119
120 %% @spec cmd_string([string()]) -> string()
121 %% @doc Create a shell quoted command string from a list of arguments.
122 cmd_string(Argv) ->
123 string:join([shell_quote(X) || X <- Argv], " ").
124
125 %% @spec cmd_status([string()]) -> {ExitStatus::integer(), Stdout::binary()}
126 %% @doc Accumulate the output and exit status from the given application,
127 %% will be spawned with cmd_port/2.
128 cmd_status(Argv) ->
129 cmd_status(Argv, []).
130
131 %% @spec cmd_status([string()], [atom()]) -> {ExitStatus::integer(), Stdout::binary()}
132 %% @doc Accumulate the output and exit status from the given application,
133 %% will be spawned with cmd_port/2.
134 cmd_status(Argv, Options) ->
135 Port = cmd_port(Argv, [exit_status, stderr_to_stdout,
136 use_stdio, binary | Options]),
137 try cmd_loop(Port, [])
138 after catch port_close(Port)
139 end.
140
141 %% @spec cmd_loop(port(), list()) -> {ExitStatus::integer(), Stdout::binary()}
142 %% @doc Accumulate the output and exit status from a port.
143 cmd_loop(Port, Acc) ->
144 receive
145 {Port, {exit_status, Status}} ->
146 {Status, iolist_to_binary(lists:reverse(Acc))};
147 {Port, {data, Data}} ->
148 cmd_loop(Port, [Data | Acc])
149 end.
150
151 %% @spec join([iolist()], iolist()) -> iolist()
152 %% @doc Join a list of strings or binaries together with the given separator
153 %% string or char or binary. The output is flattened, but may be an
154 %% iolist() instead of a string() if any of the inputs are binary().
155 join([], _Separator) ->
156 [];
157 join([S], _Separator) ->
158 lists:flatten(S);
159 join(Strings, Separator) ->
160 lists:flatten(revjoin(lists:reverse(Strings), Separator, [])).
161
162 revjoin([], _Separator, Acc) ->
163 Acc;
164 revjoin([S | Rest], Separator, []) ->
165 revjoin(Rest, Separator, [S]);
166 revjoin([S | Rest], Separator, Acc) ->
167 revjoin(Rest, Separator, [S, Separator | Acc]).
168
169 %% @spec quote_plus(atom() | integer() | float() | string() | binary()) -> string()
170 %% @doc URL safe encoding of the given term.
171 quote_plus(Atom) when is_atom(Atom) ->
172 quote_plus(atom_to_list(Atom));
173 quote_plus(Int) when is_integer(Int) ->
174 quote_plus(integer_to_list(Int));
175 quote_plus(Binary) when is_binary(Binary) ->
176 quote_plus(binary_to_list(Binary));
177 quote_plus(Float) when is_float(Float) ->
178 quote_plus(mochinum:digits(Float));
179 quote_plus(String) ->
180 quote_plus(String, []).
181
182 quote_plus([], Acc) ->
183 lists:reverse(Acc);
184 quote_plus([C | Rest], Acc) when ?QS_SAFE(C) ->
185 quote_plus(Rest, [C | Acc]);
186 quote_plus([$\s | Rest], Acc) ->
187 quote_plus(Rest, [$+ | Acc]);
188 quote_plus([C | Rest], Acc) ->
189 <<Hi:4, Lo:4>> = <<C>>,
190 quote_plus(Rest, [hexdigit(Lo), hexdigit(Hi), ?PERCENT | Acc]).
191
192 %% @spec urlencode([{Key, Value}]) -> string()
193 %% @doc URL encode the property list.
194 urlencode(Props) ->
195 Pairs = lists:foldr(
196 fun ({K, V}, Acc) ->
197 [quote_plus(K) ++ "=" ++ quote_plus(V) | Acc]
198 end, [], Props),
199 string:join(Pairs, "&").
200
201 %% @spec parse_qs(string() | binary()) -> [{Key, Value}]
202 %% @doc Parse a query string or application/x-www-form-urlencoded.
203 parse_qs(Binary) when is_binary(Binary) ->
204 parse_qs(binary_to_list(Binary));
205 parse_qs(String) ->
206 parse_qs(String, []).
207
208 parse_qs([], Acc) ->
209 lists:reverse(Acc);
210 parse_qs(String, Acc) ->
211 {Key, Rest} = parse_qs_key(String),
212 {Value, Rest1} = parse_qs_value(Rest),
213 parse_qs(Rest1, [{Key, Value} | Acc]).
214
215 parse_qs_key(String) ->
216 parse_qs_key(String, []).
217
218 parse_qs_key([], Acc) ->
219 {qs_revdecode(Acc), ""};
220 parse_qs_key([$= | Rest], Acc) ->
221 {qs_revdecode(Acc), Rest};
222 parse_qs_key(Rest=[$; | _], Acc) ->
223 {qs_revdecode(Acc), Rest};
224 parse_qs_key(Rest=[$& | _], Acc) ->
225 {qs_revdecode(Acc), Rest};
226 parse_qs_key([C | Rest], Acc) ->
227 parse_qs_key(Rest, [C | Acc]).
228
229 parse_qs_value(String) ->
230 parse_qs_value(String, []).
231
232 parse_qs_value([], Acc) ->
233 {qs_revdecode(Acc), ""};
234 parse_qs_value([$; | Rest], Acc) ->
235 {qs_revdecode(Acc), Rest};
236 parse_qs_value([$& | Rest], Acc) ->
237 {qs_revdecode(Acc), Rest};
238 parse_qs_value([C | Rest], Acc) ->
239 parse_qs_value(Rest, [C | Acc]).
240
241 %% @spec unquote(string() | binary()) -> string()
242 %% @doc Unquote a URL encoded string.
243 unquote(Binary) when is_binary(Binary) ->
244 unquote(binary_to_list(Binary));
245 unquote(String) ->
246 qs_revdecode(lists:reverse(String)).
247
248 qs_revdecode(S) ->
249 qs_revdecode(S, []).
250
251 qs_revdecode([], Acc) ->
252 Acc;
253 qs_revdecode([$+ | Rest], Acc) ->
254 qs_revdecode(Rest, [$\s | Acc]);
255 qs_revdecode([Lo, Hi, ?PERCENT | Rest], Acc) when ?IS_HEX(Lo), ?IS_HEX(Hi) ->
256 qs_revdecode(Rest, [(unhexdigit(Lo) bor (unhexdigit(Hi) bsl 4)) | Acc]);
257 qs_revdecode([C | Rest], Acc) ->
258 qs_revdecode(Rest, [C | Acc]).
259
260 %% @spec urlsplit(Url) -> {Scheme, Netloc, Path, Query, Fragment}
261 %% @doc Return a 5-tuple, does not expand % escapes. Only supports HTTP style
262 %% URLs.
263 urlsplit(Url) ->
264 {Scheme, Url1} = urlsplit_scheme(Url),
265 {Netloc, Url2} = urlsplit_netloc(Url1),
266 {Path, Query, Fragment} = urlsplit_path(Url2),
267 {Scheme, Netloc, Path, Query, Fragment}.
268
269 urlsplit_scheme(Url) ->
270 case urlsplit_scheme(Url, []) of
271 no_scheme ->
272 {"", Url};
273 Res ->
274 Res
275 end.
276
277 urlsplit_scheme([C | Rest], Acc) when ((C >= $a andalso C =< $z) orelse
278 (C >= $A andalso C =< $Z) orelse
279 (C >= $0 andalso C =< $9) orelse
280 C =:= $+ orelse C =:= $- orelse
281 C =:= $.) ->
282 urlsplit_scheme(Rest, [C | Acc]);
283 urlsplit_scheme([$: | Rest], Acc=[_ | _]) ->
284 {string:to_lower(lists:reverse(Acc)), Rest};
285 urlsplit_scheme(_Rest, _Acc) ->
286 no_scheme.
287
288 urlsplit_netloc("//" ++ Rest) ->
289 urlsplit_netloc(Rest, []);
290 urlsplit_netloc(Path) ->
291 {"", Path}.
292
293 urlsplit_netloc("", Acc) ->
294 {lists:reverse(Acc), ""};
295 urlsplit_netloc(Rest=[C | _], Acc) when C =:= $/; C =:= $?; C =:= $# ->
296 {lists:reverse(Acc), Rest};
297 urlsplit_netloc([C | Rest], Acc) ->
298 urlsplit_netloc(Rest, [C | Acc]).
299
300
301 %% @spec path_split(string()) -> {Part, Rest}
302 %% @doc Split a path starting from the left, as in URL traversal.
303 %% path_split("foo/bar") = {"foo", "bar"},
304 %% path_split("/foo/bar") = {"", "foo/bar"}.
305 path_split(S) ->
306 path_split(S, []).
307
308 path_split("", Acc) ->
309 {lists:reverse(Acc), ""};
310 path_split("/" ++ Rest, Acc) ->
311 {lists:reverse(Acc), Rest};
312 path_split([C | Rest], Acc) ->
313 path_split(Rest, [C | Acc]).
314
315
316 %% @spec urlunsplit({Scheme, Netloc, Path, Query, Fragment}) -> string()
317 %% @doc Assemble a URL from the 5-tuple. Path must be absolute.
318 urlunsplit({Scheme, Netloc, Path, Query, Fragment}) ->
319 lists:flatten([case Scheme of "" -> ""; _ -> [Scheme, "://"] end,
320 Netloc,
321 urlunsplit_path({Path, Query, Fragment})]).
322
323 %% @spec urlunsplit_path({Path, Query, Fragment}) -> string()
324 %% @doc Assemble a URL path from the 3-tuple.
325 urlunsplit_path({Path, Query, Fragment}) ->
326 lists:flatten([Path,
327 case Query of "" -> ""; _ -> [$? | Query] end,
328 case Fragment of "" -> ""; _ -> [$# | Fragment] end]).
329
330 %% @spec urlsplit_path(Url) -> {Path, Query, Fragment}
331 %% @doc Return a 3-tuple, does not expand % escapes. Only supports HTTP style
332 %% paths.
333 urlsplit_path(Path) ->
334 urlsplit_path(Path, []).
335
336 urlsplit_path("", Acc) ->
337 {lists:reverse(Acc), "", ""};
338 urlsplit_path("?" ++ Rest, Acc) ->
339 {Query, Fragment} = urlsplit_query(Rest),
340 {lists:reverse(Acc), Query, Fragment};
341 urlsplit_path("#" ++ Rest, Acc) ->
342 {lists:reverse(Acc), "", Rest};
343 urlsplit_path([C | Rest], Acc) ->
344 urlsplit_path(Rest, [C | Acc]).
345
346 urlsplit_query(Query) ->
347 urlsplit_query(Query, []).
348
349 urlsplit_query("", Acc) ->
350 {lists:reverse(Acc), ""};
351 urlsplit_query("#" ++ Rest, Acc) ->
352 {lists:reverse(Acc), Rest};
353 urlsplit_query([C | Rest], Acc) ->
354 urlsplit_query(Rest, [C | Acc]).
355
356 % %% @spec guess_mime(string()) -> string()
357 % %% @doc Guess the mime type of a file by the extension of its filename.
358 % guess_mime(File) ->
359 % case filename:basename(File) of
360 % "crossdomain.xml" ->
361 % "text/x-cross-domain-policy";
362 % Name ->
363 % case mochiweb_mime:from_extension(filename:extension(Name)) of
364 % undefined ->
365 % "text/plain";
366 % Mime ->
367 % Mime
368 % end
369 % end.
370
371 %% @spec parse_header(string()) -> {Type, [{K, V}]}
372 %% @doc Parse a Content-Type like header, return the main Content-Type
373 %% and a property list of options.
374 parse_header(String) ->
375 %% TODO: This is exactly as broken as Python's cgi module.
376 %% Should parse properly like mochiweb_cookies.
377 [Type | Parts] = [string:strip(S) || S <- string:tokens(String, ";")],
378 F = fun (S, Acc) ->
379 case lists:splitwith(fun (C) -> C =/= $= end, S) of
380 {"", _} ->
381 %% Skip anything with no name
382 Acc;
383 {_, ""} ->
384 %% Skip anything with no value
385 Acc;
386 {Name, [$\= | Value]} ->
387 [{string:to_lower(string:strip(Name)),
388 unquote_header(string:strip(Value))} | Acc]
389 end
390 end,
391 {string:to_lower(Type),
392 lists:foldr(F, [], Parts)}.
393
394 unquote_header("\"" ++ Rest) ->
395 unquote_header(Rest, []);
396 unquote_header(S) ->
397 S.
398
399 unquote_header("", Acc) ->
400 lists:reverse(Acc);
401 unquote_header("\"", Acc) ->
402 lists:reverse(Acc);
403 unquote_header([$\\, C | Rest], Acc) ->
404 unquote_header(Rest, [C | Acc]);
405 unquote_header([C | Rest], Acc) ->
406 unquote_header(Rest, [C | Acc]).
407
408 %% @spec record_to_proplist(Record, Fields) -> proplist()
409 %% @doc calls record_to_proplist/3 with a default TypeKey of '__record'
410 record_to_proplist(Record, Fields) ->
411 record_to_proplist(Record, Fields, '__record').
412
413 %% @spec record_to_proplist(Record, Fields, TypeKey) -> proplist()
414 %% @doc Return a proplist of the given Record with each field in the
415 %% Fields list set as a key with the corresponding value in the Record.
416 %% TypeKey is the key that is used to store the record type
417 %% Fields should be obtained by calling record_info(fields, record_type)
418 %% where record_type is the record type of Record
419 record_to_proplist(Record, Fields, TypeKey)
420 when tuple_size(Record) - 1 =:= length(Fields) ->
421 lists:zip([TypeKey | Fields], tuple_to_list(Record)).
422
423
424 shell_quote([], Acc) ->
425 lists:reverse([$\" | Acc]);
426 shell_quote([C | Rest], Acc) when C =:= $\" orelse C =:= $\` orelse
427 C =:= $\\ orelse C =:= $\$ ->
428 shell_quote(Rest, [C, $\\ | Acc]);
429 shell_quote([C | Rest], Acc) ->
430 shell_quote(Rest, [C | Acc]).
431
432 %% @spec parse_qvalues(string()) -> [qvalue()] | invalid_qvalue_string
433 %% @type qvalue() = {media_type() | encoding() , float()}.
434 %% @type media_type() = string().
435 %% @type encoding() = string().
436 %%
437 %% @doc Parses a list (given as a string) of elements with Q values associated
438 %% to them. Elements are separated by commas and each element is separated
439 %% from its Q value by a semicolon. Q values are optional but when missing
440 %% the value of an element is considered as 1.0. A Q value is always in the
441 %% range [0.0, 1.0]. A Q value list is used for example as the value of the
442 %% HTTP "Accept" and "Accept-Encoding" headers.
443 %%
444 %% Q values are described in section 2.9 of the RFC 2616 (HTTP 1.1).
445 %%
446 %% Example:
447 %%
448 %% parse_qvalues("gzip; q=0.5, deflate, identity;q=0.0") ->
449 %% [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 0.0}]
450 %%
451 parse_qvalues(QValuesStr) ->
452 try
453 lists:map(
454 fun(Pair) ->
455 [Type | Params] = string:tokens(Pair, ";"),
456 NormParams = normalize_media_params(Params),
457 {Q, NonQParams} = extract_q(NormParams),
458 {string:join([string:strip(Type) | NonQParams], ";"), Q}
459 end,
460 string:tokens(string:to_lower(QValuesStr), ",")
461 )
462 catch
463 _Type:_Error ->
464 invalid_qvalue_string
465 end.
466
467 normalize_media_params(Params) ->
468 {ok, Re} = re:compile("\\s"),
469 normalize_media_params(Re, Params, []).
470
471 normalize_media_params(_Re, [], Acc) ->
472 lists:reverse(Acc);
473 normalize_media_params(Re, [Param | Rest], Acc) ->
474 NormParam = re:replace(Param, Re, "", [global, {return, list}]),
475 normalize_media_params(Re, Rest, [NormParam | Acc]).
476
477 extract_q(NormParams) ->
478 {ok, KVRe} = re:compile("^([^=]+)=([^=]+)$"),
479 {ok, QRe} = re:compile("^((?:0|1)(?:\\.\\d{1,3})?)$"),
480 extract_q(KVRe, QRe, NormParams, []).
481
482 extract_q(_KVRe, _QRe, [], Acc) ->
483 {1.0, lists:reverse(Acc)};
484 extract_q(KVRe, QRe, [Param | Rest], Acc) ->
485 case re:run(Param, KVRe, [{capture, [1, 2], list}]) of
486 {match, [Name, Value]} ->
487 case Name of
488 "q" ->
489 {match, [Q]} = re:run(Value, QRe, [{capture, [1], list}]),
490 QVal = case Q of
491 "0" ->
492 0.0;
493 "1" ->
494 1.0;
495 Else ->
496 list_to_float(Else)
497 end,
498 case QVal < 0.0 orelse QVal > 1.0 of
499 false ->
500 {QVal, lists:reverse(Acc) ++ Rest}
501 end;
502 _ ->
503 extract_q(KVRe, QRe, Rest, [Param | Acc])
504 end
505 end.
506
507 %% @spec pick_accepted_encodings([qvalue()], [encoding()], encoding()) ->
508 %% [encoding()]
509 %%
510 %% @doc Determines which encodings specified in the given Q values list are
511 %% valid according to a list of supported encodings and a default encoding.
512 %%
513 %% The returned list of encodings is sorted, descendingly, according to the
514 %% Q values of the given list. The last element of this list is the given
515 %% default encoding unless this encoding is explicitily or implicitily
516 %% marked with a Q value of 0.0 in the given Q values list.
517 %% Note: encodings with the same Q value are kept in the same order as
518 %% found in the input Q values list.
519 %%
520 %% This encoding picking process is described in section 14.3 of the
521 %% RFC 2616 (HTTP 1.1).
522 %%
523 %% Example:
524 %%
525 %% pick_accepted_encodings(
526 %% [{"gzip", 0.5}, {"deflate", 1.0}],
527 %% ["gzip", "identity"],
528 %% "identity"
529 %% ) ->
530 %% ["gzip", "identity"]
531 %%
532 pick_accepted_encodings(AcceptedEncs, SupportedEncs, DefaultEnc) ->
533 SortedQList = lists:reverse(
534 lists:sort(fun({_, Q1}, {_, Q2}) -> Q1 < Q2 end, AcceptedEncs)
535 ),
536 {Accepted, Refused} = lists:foldr(
537 fun({E, Q}, {A, R}) ->
538 case Q > 0.0 of
539 true ->
540 {[E | A], R};
541 false ->
542 {A, [E | R]}
543 end
544 end,
545 {[], []},
546 SortedQList
547 ),
548 Refused1 = lists:foldr(
549 fun(Enc, Acc) ->
550 case Enc of
551 "*" ->
552 lists:subtract(SupportedEncs, Accepted) ++ Acc;
553 _ ->
554 [Enc | Acc]
555 end
556 end,
557 [],
558 Refused
559 ),
560 Accepted1 = lists:foldr(
561 fun(Enc, Acc) ->
562 case Enc of
563 "*" ->
564 lists:subtract(SupportedEncs, Accepted ++ Refused1) ++ Acc;
565 _ ->
566 [Enc | Acc]
567 end
568 end,
569 [],
570 Accepted
571 ),
572 Accepted2 = case lists:member(DefaultEnc, Accepted1) of
573 true ->
574 Accepted1;
575 false ->
576 Accepted1 ++ [DefaultEnc]
577 end,
578 [E || E <- Accepted2, lists:member(E, SupportedEncs),
579 not lists:member(E, Refused1)].
580
581 make_io(Atom) when is_atom(Atom) ->
582 atom_to_list(Atom);
583 make_io(Integer) when is_integer(Integer) ->
584 integer_to_list(Integer);
585 make_io(Io) when is_list(Io); is_binary(Io) ->
586 Io.
587
588 %%
589 %% Tests
590 %%
591 -ifdef(TEST).
592 -include_lib("eunit/include/eunit.hrl").
593
594 make_io_test() ->
595 ?assertEqual(
596 <<"atom">>,
597 iolist_to_binary(make_io(atom))),
598 ?assertEqual(
599 <<"20">>,
600 iolist_to_binary(make_io(20))),
601 ?assertEqual(
602 <<"list">>,
603 iolist_to_binary(make_io("list"))),
604 ?assertEqual(
605 <<"binary">>,
606 iolist_to_binary(make_io(<<"binary">>))),
607 ok.
608
609 -record(test_record, {field1=f1, field2=f2}).
610 record_to_proplist_test() ->
611 ?assertEqual(
612 [{'__record', test_record},
613 {field1, f1},
614 {field2, f2}],
615 record_to_proplist(#test_record{}, record_info(fields, test_record))),
616 ?assertEqual(
617 [{'typekey', test_record},
618 {field1, f1},
619 {field2, f2}],
620 record_to_proplist(#test_record{},
621 record_info(fields, test_record),
622 typekey)),
623 ok.
624
625 shell_quote_test() ->
626 ?assertEqual(
627 "\"foo \\$bar\\\"\\`' baz\"",
628 shell_quote("foo $bar\"`' baz")),
629 ok.
630
631 cmd_port_test_spool(Port, Acc) ->
632 receive
633 {Port, eof} ->
634 Acc;
635 {Port, {data, {eol, Data}}} ->
636 cmd_port_test_spool(Port, ["\n", Data | Acc]);
637 {Port, Unknown} ->
638 throw({unknown, Unknown})
639 after 1000 ->
640 throw(timeout)
641 end.
642
643 cmd_port_test() ->
644 Port = cmd_port(["echo", "$bling$ `word`!"],
645 [eof, stream, {line, 4096}]),
646 Res = try lists:append(lists:reverse(cmd_port_test_spool(Port, [])))
647 after catch port_close(Port)
648 end,
649 self() ! {Port, wtf},
650 try cmd_port_test_spool(Port, [])
651 catch throw:{unknown, wtf} -> ok
652 end,
653 try cmd_port_test_spool(Port, [])
654 catch throw:timeout -> ok
655 end,
656 ?assertEqual(
657 "$bling$ `word`!\n",
658 Res).
659
660 cmd_test() ->
661 ?assertEqual(
662 "$bling$ `word`!\n",
663 cmd(["echo", "$bling$ `word`!"])),
664 ok.
665
666 cmd_string_test() ->
667 ?assertEqual(
668 "\"echo\" \"\\$bling\\$ \\`word\\`!\"",
669 cmd_string(["echo", "$bling$ `word`!"])),
670 ok.
671
672 cmd_status_test() ->
673 ?assertEqual(
674 {0, <<"$bling$ `word`!\n">>},
675 cmd_status(["echo", "$bling$ `word`!"])),
676 ok.
677
678
679 parse_header_test() ->
680 ?assertEqual(
681 {"multipart/form-data", [{"boundary", "AaB03x"}]},
682 parse_header("multipart/form-data; boundary=AaB03x")),
683 %% This tests (currently) intentionally broken behavior
684 ?assertEqual(
685 {"multipart/form-data",
686 [{"b", ""},
687 {"cgi", "is"},
688 {"broken", "true\"e"}]},
689 parse_header("multipart/form-data;b=;cgi=\"i\\s;broken=true\"e;=z;z")),
690 ok.
691
692 % guess_mime_test() ->
693 % ?assertEqual("text/plain", guess_mime("")),
694 % ?assertEqual("text/plain", guess_mime(".text")),
695 % ?assertEqual("application/zip", guess_mime(".zip")),
696 % ?assertEqual("application/zip", guess_mime("x.zip")),
697 % ?assertEqual("text/html", guess_mime("x.html")),
698 % ?assertEqual("application/xhtml+xml", guess_mime("x.xhtml")),
699 % ?assertEqual("text/x-cross-domain-policy", guess_mime("crossdomain.xml")),
700 % ?assertEqual("text/x-cross-domain-policy", guess_mime("www/crossdomain.xml")),
701 % ok.
702
703 path_split_test() ->
704 {"", "foo/bar"} = path_split("/foo/bar"),
705 {"foo", "bar"} = path_split("foo/bar"),
706 {"bar", ""} = path_split("bar"),
707 ok.
708
709 urlsplit_test() ->
710 {"", "", "/foo", "", "bar?baz"} = urlsplit("/foo#bar?baz"),
711 {"http", "host:port", "/foo", "", "bar?baz"} =
712 urlsplit("http://host:port/foo#bar?baz"),
713 {"http", "host", "", "", ""} = urlsplit("http://host"),
714 {"", "", "/wiki/Category:Fruit", "", ""} =
715 urlsplit("/wiki/Category:Fruit"),
716 ok.
717
718 urlsplit_path_test() ->
719 {"/foo/bar", "", ""} = urlsplit_path("/foo/bar"),
720 {"/foo", "baz", ""} = urlsplit_path("/foo?baz"),
721 {"/foo", "", "bar?baz"} = urlsplit_path("/foo#bar?baz"),
722 {"/foo", "", "bar?baz#wibble"} = urlsplit_path("/foo#bar?baz#wibble"),
723 {"/foo", "bar", "baz"} = urlsplit_path("/foo?bar#baz"),
724 {"/foo", "bar?baz", "baz"} = urlsplit_path("/foo?bar?baz#baz"),
725 ok.
726
727 urlunsplit_test() ->
728 "/foo#bar?baz" = urlunsplit({"", "", "/foo", "", "bar?baz"}),
729 "http://host:port/foo#bar?baz" =
730 urlunsplit({"http", "host:port", "/foo", "", "bar?baz"}),
731 ok.
732
733 urlunsplit_path_test() ->
734 "/foo/bar" = urlunsplit_path({"/foo/bar", "", ""}),
735 "/foo?baz" = urlunsplit_path({"/foo", "baz", ""}),
736 "/foo#bar?baz" = urlunsplit_path({"/foo", "", "bar?baz"}),
737 "/foo#bar?baz#wibble" = urlunsplit_path({"/foo", "", "bar?baz#wibble"}),
738 "/foo?bar#baz" = urlunsplit_path({"/foo", "bar", "baz"}),
739 "/foo?bar?baz#baz" = urlunsplit_path({"/foo", "bar?baz", "baz"}),
740 ok.
741
742 join_test() ->
743 ?assertEqual("foo,bar,baz",
744 join(["foo", "bar", "baz"], $,)),
745 ?assertEqual("foo,bar,baz",
746 join(["foo", "bar", "baz"], ",")),
747 ?assertEqual("foo bar",
748 join([["foo", " bar"]], ",")),
749 ?assertEqual("foo bar,baz",
750 join([["foo", " bar"], "baz"], ",")),
751 ?assertEqual("foo",
752 join(["foo"], ",")),
753 ?assertEqual("foobarbaz",
754 join(["foo", "bar", "baz"], "")),
755 ?assertEqual("foo" ++ [<<>>] ++ "bar" ++ [<<>>] ++ "baz",
756 join(["foo", "bar", "baz"], <<>>)),
757 ?assertEqual("foobar" ++ [<<"baz">>],
758 join(["foo", "bar", <<"baz">>], "")),
759 ?assertEqual("",
760 join([], "any")),
761 ok.
762
763 quote_plus_test() ->
764 "foo" = quote_plus(foo),
765 "1" = quote_plus(1),
766 "1.1" = quote_plus(1.1),
767 "foo" = quote_plus("foo"),
768 "foo+bar" = quote_plus("foo bar"),
769 "foo%0A" = quote_plus("foo\n"),
770 "foo%0A" = quote_plus("foo\n"),
771 "foo%3B%26%3D" = quote_plus("foo;&="),
772 "foo%3B%26%3D" = quote_plus(<<"foo;&=">>),
773 ok.
774
775 unquote_test() ->
776 ?assertEqual("foo bar",
777 unquote("foo+bar")),
778 ?assertEqual("foo bar",
779 unquote("foo%20bar")),
780 ?assertEqual("foo\r\n",
781 unquote("foo%0D%0A")),
782 ?assertEqual("foo\r\n",
783 unquote(<<"foo%0D%0A">>)),
784 ok.
785
786 urlencode_test() ->
787 "foo=bar&baz=wibble+%0D%0A&z=1" = urlencode([{foo, "bar"},
788 {"baz", "wibble \r\n"},
789 {z, 1}]),
790 ok.
791
792 parse_qs_test() ->
793 ?assertEqual(
794 [{"foo", "bar"}, {"baz", "wibble \r\n"}, {"z", "1"}],
795 parse_qs("foo=bar&baz=wibble+%0D%0a&z=1")),
796 ?assertEqual(
797 [{"", "bar"}, {"baz", "wibble \r\n"}, {"z", ""}],
798 parse_qs("=bar&baz=wibble+%0D%0a&z=")),
799 ?assertEqual(
800 [{"foo", "bar"}, {"baz", "wibble \r\n"}, {"z", "1"}],
801 parse_qs(<<"foo=bar&baz=wibble+%0D%0a&z=1">>)),
802 ?assertEqual(
803 [],
804 parse_qs("")),
805 ?assertEqual(
806 [{"foo", ""}, {"bar", ""}, {"baz", ""}],
807 parse_qs("foo;bar&baz")),
808 ok.
809
810 partition_test() ->
811 {"foo", "", ""} = partition("foo", "/"),
812 {"foo", "/", "bar"} = partition("foo/bar", "/"),
813 {"foo", "/", ""} = partition("foo/", "/"),
814 {"", "/", "bar"} = partition("/bar", "/"),
815 {"f", "oo/ba", "r"} = partition("foo/bar", "oo/ba"),
816 ok.
817
818 safe_relative_path_test() ->
819 "foo" = safe_relative_path("foo"),
820 "foo/" = safe_relative_path("foo/"),
821 "foo" = safe_relative_path("foo/bar/.."),
822 "bar" = safe_relative_path("foo/../bar"),
823 "bar/" = safe_relative_path("foo/../bar/"),
824 "" = safe_relative_path("foo/.."),
825 "" = safe_relative_path("foo/../"),
826 undefined = safe_relative_path("/foo"),
827 undefined = safe_relative_path("../foo"),
828 undefined = safe_relative_path("foo/../.."),
829 undefined = safe_relative_path("foo//"),
830 undefined = safe_relative_path("foo\\bar"),
831 ok.
832
833 parse_qvalues_test() ->
834 [] = parse_qvalues(""),
835 [{"identity", 0.0}] = parse_qvalues("identity;q=0"),
836 [{"identity", 0.0}] = parse_qvalues("identity ;q=0"),
837 [{"identity", 0.0}] = parse_qvalues(" identity; q =0 "),
838 [{"identity", 0.0}] = parse_qvalues("identity ; q = 0"),
839 [{"identity", 0.0}] = parse_qvalues("identity ; q= 0.0"),
840 [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues(
841 "gzip,deflate,identity;q=0.0"
842 ),
843 [{"deflate", 1.0}, {"gzip", 1.0}, {"identity", 0.0}] = parse_qvalues(
844 "deflate,gzip,identity;q=0.0"
845 ),
846 [{"gzip", 1.0}, {"deflate", 1.0}, {"gzip", 1.0}, {"identity", 0.0}] =
847 parse_qvalues("gzip,deflate,gzip,identity;q=0"),
848 [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues(
849 "gzip, deflate , identity; q=0.0"
850 ),
851 [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues(
852 "gzip; q=1, deflate;q=1.0, identity;q=0.0"
853 ),
854 [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues(
855 "gzip; q=0.5, deflate;q=1.0, identity;q=0"
856 ),
857 [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues(
858 "gzip; q=0.5, deflate , identity;q=0.0"
859 ),
860 [{"gzip", 0.5}, {"deflate", 0.8}, {"identity", 0.0}] = parse_qvalues(
861 "gzip; q=0.5, deflate;q=0.8, identity;q=0.0"
862 ),
863 [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 1.0}] = parse_qvalues(
864 "gzip; q=0.5,deflate,identity"
865 ),
866 [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 1.0}, {"identity", 1.0}] =
867 parse_qvalues("gzip; q=0.5,deflate,identity, identity "),
868 [{"text/html;level=1", 1.0}, {"text/plain", 0.5}] =
869 parse_qvalues("text/html;level=1, text/plain;q=0.5"),
870 [{"text/html;level=1", 0.3}, {"text/plain", 1.0}] =
871 parse_qvalues("text/html;level=1;q=0.3, text/plain"),
872 [{"text/html;level=1", 0.3}, {"text/plain", 1.0}] =
873 parse_qvalues("text/html; level = 1; q = 0.3, text/plain"),
874 [{"text/html;level=1", 0.3}, {"text/plain", 1.0}] =
875 parse_qvalues("text/html;q=0.3;level=1, text/plain"),
876 invalid_qvalue_string = parse_qvalues("gzip; q=1.1, deflate"),
877 invalid_qvalue_string = parse_qvalues("gzip; q=0.5, deflate;q=2"),
878 invalid_qvalue_string = parse_qvalues("gzip, deflate;q=AB"),
879 invalid_qvalue_string = parse_qvalues("gzip; q=2.1, deflate"),
880 invalid_qvalue_string = parse_qvalues("gzip; q=0.1234, deflate"),
881 invalid_qvalue_string = parse_qvalues("text/html;level=1;q=0.3, text/html;level"),
882 ok.
883
884 pick_accepted_encodings_test() ->
885 ["identity"] = pick_accepted_encodings(
886 [],
887 ["gzip", "identity"],
888 "identity"
889 ),
890 ["gzip", "identity"] = pick_accepted_encodings(
891 [{"gzip", 1.0}],
892 ["gzip", "identity"],
893 "identity"
894 ),
895 ["identity"] = pick_accepted_encodings(
896 [{"gzip", 0.0}],
897 ["gzip", "identity"],
898 "identity"
899 ),
900 ["gzip", "identity"] = pick_accepted_encodings(
901 [{"gzip", 1.0}, {"deflate", 1.0}],
902 ["gzip", "identity"],
903 "identity"
904 ),
905 ["gzip", "identity"] = pick_accepted_encodings(
906 [{"gzip", 0.5}, {"deflate", 1.0}],
907 ["gzip", "identity"],
908 "identity"
909 ),
910 ["identity"] = pick_accepted_encodings(
911 [{"gzip", 0.0}, {"deflate", 0.0}],
912 ["gzip", "identity"],
913 "identity"
914 ),
915 ["gzip"] = pick_accepted_encodings(
916 [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}],
917 ["gzip", "identity"],
918 "identity"
919 ),
920 ["gzip", "deflate", "identity"] = pick_accepted_encodings(
921 [{"gzip", 1.0}, {"deflate", 1.0}],
922 ["gzip", "deflate", "identity"],
923 "identity"
924 ),
925 ["gzip", "deflate"] = pick_accepted_encodings(
926 [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}],
927 ["gzip", "deflate", "identity"],
928 "identity"
929 ),
930 ["deflate", "gzip", "identity"] = pick_accepted_encodings(
931 [{"gzip", 0.2}, {"deflate", 1.0}],
932 ["gzip", "deflate", "identity"],
933 "identity"
934 ),
935 ["deflate", "deflate", "gzip", "identity"] = pick_accepted_encodings(
936 [{"gzip", 0.2}, {"deflate", 1.0}, {"deflate", 1.0}],
937 ["gzip", "deflate", "identity"],
938 "identity"
939 ),
940 ["deflate", "gzip", "gzip", "identity"] = pick_accepted_encodings(
941 [{"gzip", 0.2}, {"deflate", 1.0}, {"gzip", 1.0}],
942 ["gzip", "deflate", "identity"],
943 "identity"
944 ),
945 ["gzip", "deflate", "gzip", "identity"] = pick_accepted_encodings(
946 [{"gzip", 0.2}, {"deflate", 0.9}, {"gzip", 1.0}],
947 ["gzip", "deflate", "identity"],
948 "identity"
949 ),
950 [] = pick_accepted_encodings(
951 [{"*", 0.0}],
952 ["gzip", "deflate", "identity"],
953 "identity"
954 ),
955 ["gzip", "deflate", "identity"] = pick_accepted_encodings(
956 [{"*", 1.0}],
957 ["gzip", "deflate", "identity"],
958 "identity"
959 ),
960 ["gzip", "deflate", "identity"] = pick_accepted_encodings(
961 [{"*", 0.6}],
962 ["gzip", "deflate", "identity"],
963 "identity"
964 ),
965 ["gzip"] = pick_accepted_encodings(
966 [{"gzip", 1.0}, {"*", 0.0}],
967 ["gzip", "deflate", "identity"],
968 "identity"
969 ),
970 ["gzip", "deflate"] = pick_accepted_encodings(
971 [{"gzip", 1.0}, {"deflate", 0.6}, {"*", 0.0}],
972 ["gzip", "deflate", "identity"],
973 "identity"
974 ),
975 ["deflate", "gzip"] = pick_accepted_encodings(
976 [{"gzip", 0.5}, {"deflate", 1.0}, {"*", 0.0}],
977 ["gzip", "deflate", "identity"],
978 "identity"
979 ),
980 ["gzip", "identity"] = pick_accepted_encodings(
981 [{"deflate", 0.0}, {"*", 1.0}],
982 ["gzip", "deflate", "identity"],
983 "identity"
984 ),
985 ["gzip", "identity"] = pick_accepted_encodings(
986 [{"*", 1.0}, {"deflate", 0.0}],
987 ["gzip", "deflate", "identity"],
988 "identity"
989 ),
990 ok.
991
992 -endif.
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% Priority queues have essentially the same interface as ordinary
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_amqqueue).
2424 check_exclusive_access/2, with_exclusive_access_or_die/3,
2525 stat/1, deliver/2, requeue/3, ack/3, reject/4]).
2626 -export([list/0, list/1, info_keys/0, info/1, info/2, info_all/1, info_all/2,
27 info_all/5, info_local/1]).
27 info_all/5, info_local/1, list_names/0]).
2828 -export([list_down/1]).
2929 -export([force_event_refresh/1, notify_policy_changed/1]).
3030 -export([consumers/1, consumers_all/1, consumers_all/3, consumer_info_keys/0]).
107107 A | rabbit_types:channel_exit().
108108 -spec list() -> [rabbit_types:amqqueue()].
109109 -spec list(rabbit_types:vhost()) -> [rabbit_types:amqqueue()].
110 -spec list_names() -> [rabbit_amqqueue:name()].
110111 -spec list_down(rabbit_types:vhost()) -> [rabbit_types:amqqueue()].
111112 -spec info_keys() -> rabbit_types:info_keys().
112113 -spec info(rabbit_types:amqqueue()) -> rabbit_types:infos().
171172 -spec basic_cancel
172173 (rabbit_types:amqqueue(), pid(), rabbit_types:ctag(), any()) -> 'ok'.
173174 -spec notify_decorators(rabbit_types:amqqueue()) -> 'ok'.
174 -spec notify_sent(pid(), pid()) -> 'ok'.
175 -spec notify_sent_queue_down(pid()) -> 'ok'.
176175 -spec resume(pid(), pid()) -> 'ok'.
177176 -spec internal_delete(name()) ->
178177 rabbit_types:ok_or_error('not_found') |
265264 pid = Pid}
266265 <- mnesia:table(rabbit_durable_queue),
267266 node(Pid) == Node andalso
268 %% Terminations on node down will not remove the rabbit_queue
269 %% record if it is a mirrored queue (such info is now obtained from
270 %% the policy). Thus, we must check if the local pid is alive
271 %% - if the record is present - in order to restart.
272 (mnesia:read(rabbit_queue, Name, read) =:= []
273 orelse not erlang:is_process_alive(Pid))]))
267 %% Terminations on node down will not remove the rabbit_queue
268 %% record if it is a mirrored queue (such info is now obtained from
269 %% the policy). Thus, we must check if the local pid is alive
270 %% - if the record is present - in order to restart.
271 (mnesia:read(rabbit_queue, Name, read) =:= []
272 orelse not erlang:is_process_alive(Pid))]))
274273 end).
275274
276275 recover_durable_queues(QueuesAndRecoveryTerms) ->
569568
570569 list() -> mnesia:dirty_match_object(rabbit_queue, #amqqueue{_ = '_'}).
571570
571 list_names() -> mnesia:dirty_all_keys(rabbit_queue).
572
572573 list(VHostPath) -> list(VHostPath, rabbit_queue).
573574
574575 %% Not dirty_match_object since that would not be transactional when used in a
772773 delegate:cast(QPid, notify_decorators).
773774
774775 notify_sent(QPid, ChPid) ->
775 Key = {consumer_credit_to, QPid},
776 put(Key, case get(Key) of
777 1 -> gen_server2:cast(
778 QPid, {notify_sent, ChPid,
779 ?MORE_CONSUMER_CREDIT_AFTER}),
780 ?MORE_CONSUMER_CREDIT_AFTER;
781 undefined -> erlang:monitor(process, QPid),
782 ?MORE_CONSUMER_CREDIT_AFTER - 1;
783 C -> C - 1
784 end),
785 ok.
776 rabbit_amqqueue_common:notify_sent(QPid, ChPid).
786777
787778 notify_sent_queue_down(QPid) ->
788 erase({consumer_credit_to, QPid}),
789 ok.
779 rabbit_amqqueue_common:notify_sent_queue_down(QPid).
790780
791781 resume(QPid, ChPid) -> delegate:cast(QPid, {resume, ChPid}).
792782
814804 T = rabbit_binding:process_deletions(Deletions),
815805 fun() ->
816806 ok = T(),
807 rabbit_core_metrics:queue_deleted(QueueName),
817808 ok = rabbit_event:notify(queue_deleted,
818809 [{name, QueueName}])
819810 end
861852 node_permits_offline_promotion(Node) ->
862853 case node() of
863854 Node -> not rabbit:is_running(); %% [1]
864 _ -> Running = rabbit_mnesia:cluster_nodes(running),
865 not lists:member(Node, Running) %% [2]
855 _ -> All = rabbit_mnesia:cluster_nodes(all),
856 Running = rabbit_mnesia:cluster_nodes(running),
857 lists:member(Node, All) andalso
858 not lists:member(Node, Running) %% [2]
866859 end.
867860 %% [1] In this case if we are a real running node (i.e. rabbitmqctl
868861 %% has RPCed into us) then we cannot allow promotion. If on the other
936929 qlc:e(qlc:q([{QName, delete_queue(QName)} ||
937930 #amqqueue{name = QName, pid = Pid} = Q
938931 <- mnesia:table(rabbit_queue),
939 not rabbit_amqqueue:is_mirrored(Q) andalso
940 node(Pid) == Node andalso
941 not rabbit_mnesia:is_process_alive(Pid)])),
932 not rabbit_amqqueue:is_mirrored(Q) andalso
933 node(Pid) == Node andalso
934 not rabbit_mnesia:is_process_alive(Pid)])),
942935 {Qs, Dels} = lists:unzip(QsDels),
943936 T = rabbit_binding:process_deletions(
944937 lists:foldl(fun rabbit_binding:combine_deletions/2,
947940 T(),
948941 lists:foreach(
949942 fun(QName) ->
943 rabbit_core_metrics:queue_deleted(QName),
950944 ok = rabbit_event:notify(queue_deleted,
951945 [{name, QName}])
952946 end, Qs)
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_amqqueue_common).
17
18 -export([notify_sent/2, notify_sent_queue_down/1]).
19
20 -define(MORE_CONSUMER_CREDIT_AFTER, 50).
21
22 -spec notify_sent(pid(), pid()) -> 'ok'.
23
24 notify_sent(QPid, ChPid) ->
25 Key = {consumer_credit_to, QPid},
26 put(Key, case get(Key) of
27 1 -> gen_server2:cast(
28 QPid, {notify_sent, ChPid,
29 ?MORE_CONSUMER_CREDIT_AFTER}),
30 ?MORE_CONSUMER_CREDIT_AFTER;
31 undefined -> erlang:monitor(process, QPid),
32 ?MORE_CONSUMER_CREDIT_AFTER - 1;
33 C -> C - 1
34 end),
35 ok.
36
37 -spec notify_sent_queue_down(pid()) -> 'ok'.
38
39 notify_sent_queue_down(QPid) ->
40 erase({consumer_credit_to, QPid}),
41 ok.
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_auth_backend_dummy).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_auth_backend_internal).
2525 -export([add_user/2, delete_user/1, lookup_user/1,
2626 change_password/2, clear_password/1,
2727 hash_password/2, change_password_hash/2, change_password_hash/3,
28 set_tags/2, set_permissions/5, clear_permissions/2]).
28 set_tags/2, set_permissions/5, clear_permissions/2,
29 add_user_sans_validation/2]).
2930 -export([user_info_keys/0, perms_info_keys/0,
3031 user_perms_info_keys/0, vhost_perms_info_keys/0,
3132 user_vhost_perms_info_keys/0,
4142
4243 -type regexp() :: binary().
4344
44 -spec add_user(rabbit_types:username(), rabbit_types:password()) -> 'ok'.
45 -spec add_user(rabbit_types:username(), rabbit_types:password()) -> 'ok' | {'error', string()}.
4546 -spec delete_user(rabbit_types:username()) -> 'ok'.
4647 -spec lookup_user
4748 (rabbit_types:username()) ->
164165 %%----------------------------------------------------------------------------
165166 %% Manipulation of the user database
166167
168 validate_credentials(Username, Password) ->
169 rabbit_credential_validation:validate(Username, Password).
170
171 validate_and_alternate_credentials(Username, Password, Fun) ->
172 case validate_credentials(Username, Password) of
173 ok ->
174 Fun(Username, Password);
175 {error, Err} ->
176 rabbit_log:error("Credential validation for '~s' failed!~n", [Username]),
177 {error, Err}
178 end.
179
167180 add_user(Username, Password) ->
181 validate_and_alternate_credentials(Username, Password, fun add_user_sans_validation/2).
182
183 add_user_sans_validation(Username, Password) ->
168184 rabbit_log:info("Creating user '~s'~n", [Username]),
169185 %% hash_password will pick the hashing function configured for us
170186 %% but we also need to store a hint as part of the record, so we
211227 rabbit_misc:dirty_read({rabbit_user, Username}).
212228
213229 change_password(Username, Password) ->
230 validate_and_alternate_credentials(Username, Password, fun change_password_sans_validation/2).
231
232 change_password_sans_validation(Username, Password) ->
214233 rabbit_log:info("Changing password for '~s'~n", [Username]),
215234 HashingAlgorithm = rabbit_password:hashing_mod(),
216235 R = change_password_hash(Username,
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_auth_mechanism).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_authn_backend).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_authz_backend).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_backing_queue).
2222 message_bytes, message_bytes_ready,
2323 message_bytes_unacknowledged, message_bytes_ram,
2424 message_bytes_persistent, head_message_timestamp,
25 disk_reads, disk_writes, backing_queue_status]).
25 disk_reads, disk_writes, backing_queue_status,
26 messages_paged_out, message_bytes_paged_out]).
2627
2728 %% We can't specify a per-queue ack/state with callback signatures
2829 -type ack() :: any().
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_basic).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_binary_generator).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_binary_parser).
6767 ?SIMPLE_PARSE_TABLE($d, Value:64/float, double);
6868 ?SIMPLE_PARSE_TABLE($f, Value:32/float, float);
6969
70 %% yes, both 'l' and 'L' fields are decoded to 64-bit signed values;
71 %% see https://github.com/rabbitmq/rabbitmq-server/issues/1093#issuecomment-276351183,
72 %% http://www.rabbitmq.com/amqp-0-9-1-errata.html, and section
73 %% 4.2.1 of the spec for details.
7074 ?SIMPLE_PARSE_TABLE($l, Value:64/signed, long);
75 ?SIMPLE_PARSE_TABLE($L, Value:64/signed, long);
7176
7277
7378 parse_table(<<NLen:8/unsigned, NameString:NLen/binary,
119124 ?SIMPLE_PARSE_ARRAY($f, Value:32/float, float);
120125
121126 ?SIMPLE_PARSE_ARRAY($l, Value:64/signed, long);
122
127 ?SIMPLE_PARSE_ARRAY($L, Value:64/signed, long);
123128
124129
125130 parse_array(<<$t, Value:8/unsigned, Rest/binary>>) ->
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_cert_info).
17
18 -include_lib("public_key/include/public_key.hrl").
19
20 -export([issuer/1,
21 subject/1,
22 validity/1,
23 subject_items/2]).
24
25 %%--------------------------------------------------------------------------
26
27 -export_type([certificate/0]).
28
29 -type certificate() :: binary().
30
31 %%--------------------------------------------------------------------------
32 %% High-level functions used by reader
33 %%--------------------------------------------------------------------------
34
35 %% Return a string describing the certificate's issuer.
36 -spec issuer(certificate()) -> string().
37
38 issuer(Cert) ->
39 cert_info(fun(#'OTPCertificate' {
40 tbsCertificate = #'OTPTBSCertificate' {
41 issuer = Issuer }}) ->
42 format_rdn_sequence(Issuer)
43 end, Cert).
44
45 %% Return a string describing the certificate's subject, as per RFC4514.
46 -spec subject(certificate()) -> string().
47
48 subject(Cert) ->
49 cert_info(fun(#'OTPCertificate' {
50 tbsCertificate = #'OTPTBSCertificate' {
51 subject = Subject }}) ->
52 format_rdn_sequence(Subject)
53 end, Cert).
54
55 %% Return the parts of the certificate's subject.
56 -spec subject_items
57 (certificate(), tuple()) -> [string()] | 'not_found'.
58
59 subject_items(Cert, Type) ->
60 cert_info(fun(#'OTPCertificate' {
61 tbsCertificate = #'OTPTBSCertificate' {
62 subject = Subject }}) ->
63 find_by_type(Type, Subject)
64 end, Cert).
65
66 %% Return a string describing the certificate's validity.
67 -spec validity(certificate()) -> string().
68
69 validity(Cert) ->
70 cert_info(fun(#'OTPCertificate' {
71 tbsCertificate = #'OTPTBSCertificate' {
72 validity = {'Validity', Start, End} }}) ->
73 rabbit_misc:format("~s - ~s", [format_asn1_value(Start),
74 format_asn1_value(End)])
75 end, Cert).
76
77 %%--------------------------------------------------------------------------
78
79 cert_info(F, Cert) ->
80 F(case public_key:pkix_decode_cert(Cert, otp) of
81 {ok, DecCert} -> DecCert; %%pre R14B
82 DecCert -> DecCert %%R14B onwards
83 end).
84
85 find_by_type(Type, {rdnSequence, RDNs}) ->
86 case [V || #'AttributeTypeAndValue'{type = T, value = V}
87 <- lists:flatten(RDNs),
88 T == Type] of
89 [] -> not_found;
90 L -> [format_asn1_value(V) || V <- L]
91 end.
92
93 %%--------------------------------------------------------------------------
94 %% Formatting functions
95 %%--------------------------------------------------------------------------
96
97 %% Format and rdnSequence as a RFC4514 subject string.
98 format_rdn_sequence({rdnSequence, Seq}) ->
99 string:join(lists:reverse([format_complex_rdn(RDN) || RDN <- Seq]), ",").
100
101 %% Format an RDN set.
102 format_complex_rdn(RDNs) ->
103 string:join([format_rdn(RDN) || RDN <- RDNs], "+").
104
105 %% Format an RDN. If the type name is unknown, use the dotted decimal
106 %% representation. See RFC4514, section 2.3.
107 format_rdn(#'AttributeTypeAndValue'{type = T, value = V}) ->
108 FV = escape_rdn_value(format_asn1_value(V)),
109 Fmts = [{?'id-at-surname' , "SN"},
110 {?'id-at-givenName' , "GIVENNAME"},
111 {?'id-at-initials' , "INITIALS"},
112 {?'id-at-generationQualifier' , "GENERATIONQUALIFIER"},
113 {?'id-at-commonName' , "CN"},
114 {?'id-at-localityName' , "L"},
115 {?'id-at-stateOrProvinceName' , "ST"},
116 {?'id-at-organizationName' , "O"},
117 {?'id-at-organizationalUnitName' , "OU"},
118 {?'id-at-title' , "TITLE"},
119 {?'id-at-countryName' , "C"},
120 {?'id-at-serialNumber' , "SERIALNUMBER"},
121 {?'id-at-pseudonym' , "PSEUDONYM"},
122 {?'id-domainComponent' , "DC"},
123 {?'id-emailAddress' , "EMAILADDRESS"},
124 {?'street-address' , "STREET"},
125 {{0,9,2342,19200300,100,1,1} , "UID"}], %% Not in public_key.hrl
126 case proplists:lookup(T, Fmts) of
127 {_, Fmt} ->
128 rabbit_misc:format(Fmt ++ "=~s", [FV]);
129 none when is_tuple(T) ->
130 TypeL = [rabbit_misc:format("~w", [X]) || X <- tuple_to_list(T)],
131 rabbit_misc:format("~s=~s", [string:join(TypeL, "."), FV]);
132 none ->
133 rabbit_misc:format("~p=~s", [T, FV])
134 end.
135
136 %% Escape a string as per RFC4514.
137 escape_rdn_value(V) ->
138 escape_rdn_value(V, start).
139
140 escape_rdn_value([], _) ->
141 [];
142 escape_rdn_value([C | S], start) when C =:= $ ; C =:= $# ->
143 [$\\, C | escape_rdn_value(S, middle)];
144 escape_rdn_value(S, start) ->
145 escape_rdn_value(S, middle);
146 escape_rdn_value([$ ], middle) ->
147 [$\\, $ ];
148 escape_rdn_value([C | S], middle) when C =:= $"; C =:= $+; C =:= $,; C =:= $;;
149 C =:= $<; C =:= $>; C =:= $\\ ->
150 [$\\, C | escape_rdn_value(S, middle)];
151 escape_rdn_value([C | S], middle) when C < 32 ; C >= 126 ->
152 %% Of ASCII characters only U+0000 needs escaping, but for display
153 %% purposes it's handy to escape all non-printable chars. All non-ASCII
154 %% characters get converted to UTF-8 sequences and then escaped. We've
155 %% already got a UTF-8 sequence here, so just escape it.
156 rabbit_misc:format("\\~2.16.0B", [C]) ++ escape_rdn_value(S, middle);
157 escape_rdn_value([C | S], middle) ->
158 [C | escape_rdn_value(S, middle)].
159
160 %% Get the string representation of an OTPCertificate field.
161 format_asn1_value({ST, S}) when ST =:= teletexString; ST =:= printableString;
162 ST =:= universalString; ST =:= utf8String;
163 ST =:= bmpString ->
164 format_directory_string(ST, S);
165 format_asn1_value({utcTime, [Y1, Y2, M1, M2, D1, D2, H1, H2,
166 Min1, Min2, S1, S2, $Z]}) ->
167 rabbit_misc:format("20~c~c-~c~c-~c~cT~c~c:~c~c:~c~cZ",
168 [Y1, Y2, M1, M2, D1, D2, H1, H2, Min1, Min2, S1, S2]);
169 %% We appear to get an untagged value back for an ia5string
170 %% (e.g. domainComponent).
171 format_asn1_value(V) when is_list(V) ->
172 V;
173 format_asn1_value(V) when is_binary(V) ->
174 %% OTP does not decode some values when combined with an unknown
175 %% type. That's probably wrong, so as a last ditch effort let's
176 %% try manually decoding. 'DirectoryString' is semi-arbitrary -
177 %% but it is the type which covers the various string types we
178 %% handle below.
179 try
180 {ST, S} = public_key:der_decode('DirectoryString', V),
181 format_directory_string(ST, S)
182 catch _:_ ->
183 rabbit_misc:format("~p", [V])
184 end;
185 format_asn1_value(V) ->
186 rabbit_misc:format("~p", [V]).
187
188 %% DirectoryString { INTEGER : maxSize } ::= CHOICE {
189 %% teletexString TeletexString (SIZE (1..maxSize)),
190 %% printableString PrintableString (SIZE (1..maxSize)),
191 %% bmpString BMPString (SIZE (1..maxSize)),
192 %% universalString UniversalString (SIZE (1..maxSize)),
193 %% uTF8String UTF8String (SIZE (1..maxSize)) }
194 %%
195 %% Precise definitions of printable / teletexString are hard to come
196 %% by. This is what I reconstructed:
197 %%
198 %% printableString:
199 %% "intended to represent the limited character sets available to
200 %% mainframe input terminals"
201 %% A-Z a-z 0-9 ' ( ) + , - . / : = ? [space]
202 %% http://msdn.microsoft.com/en-us/library/bb540814(v=vs.85).aspx
203 %%
204 %% teletexString:
205 %% "a sizable volume of software in the world treats TeletexString
206 %% (T61String) as a simple 8-bit string with mostly Windows Latin 1
207 %% (superset of iso-8859-1) encoding"
208 %% http://www.mail-archive.com/asn1@asn1.org/msg00460.html
209 %%
210 %% (However according to that link X.680 actually defines
211 %% TeletexString in some much more involved and crazy way. I suggest
212 %% we treat it as ISO-8859-1 since Erlang does not support Windows
213 %% Latin 1).
214 %%
215 %% bmpString:
216 %% UCS-2 according to RFC 3641. Hence cannot represent Unicode
217 %% characters above 65535 (outside the "Basic Multilingual Plane").
218 %%
219 %% universalString:
220 %% UCS-4 according to RFC 3641.
221 %%
222 %% utf8String:
223 %% UTF-8 according to RFC 3641.
224 %%
225 %% Within Rabbit we assume UTF-8 encoding. Since printableString is a
226 %% subset of ASCII it is also a subset of UTF-8. The others need
227 %% converting. Fortunately since the Erlang SSL library does the
228 %% decoding for us (albeit into a weird format, see below), we just
229 %% need to handle encoding into UTF-8. Note also that utf8Strings come
230 %% back as binary.
231 %%
232 %% Note for testing: the default Ubuntu configuration for openssl will
233 %% only create printableString or teletexString types no matter what
234 %% you do. Edit string_mask in the [req] section of
235 %% /etc/ssl/openssl.cnf to change this (see comments there). You
236 %% probably also need to set utf8 = yes to get it to accept UTF-8 on
237 %% the command line. Also note I could not get openssl to generate a
238 %% universalString.
239
240 format_directory_string(printableString, S) -> S;
241 format_directory_string(teletexString, S) -> utf8_list_from(S);
242 format_directory_string(bmpString, S) -> utf8_list_from(S);
243 format_directory_string(universalString, S) -> utf8_list_from(S);
244 format_directory_string(utf8String, S) -> binary_to_list(S).
245
246 utf8_list_from(S) ->
247 binary_to_list(
248 unicode:characters_to_binary(flatten_ssl_list(S), utf32, utf8)).
249
250 %% The Erlang SSL implementation invents its own representation for
251 %% non-ascii strings - looking like [97,{0,0,3,187}] (that's LATIN
252 %% SMALL LETTER A followed by GREEK SMALL LETTER LAMDA). We convert
253 %% this into a list of unicode characters, which we can tell
254 %% unicode:characters_to_binary is utf32.
255
256 flatten_ssl_list(L) -> [flatten_ssl_list_item(I) || I <- L].
257
258 flatten_ssl_list_item({A, B, C, D}) ->
259 A * (1 bsl 24) + B * (1 bsl 16) + C * (1 bsl 8) + D;
260 flatten_ssl_list_item(N) when is_number (N) ->
261 N.
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_channel).
149149 -define(MAX_PERMISSION_CACHE_SIZE, 12).
150150
151151 -define(STATISTICS_KEYS,
152 [pid,
152 [reductions,
153 pid,
153154 transactional,
154155 confirm,
155156 consumer_count,
160161 prefetch_count,
161162 global_prefetch_count,
162163 state,
163 reductions,
164164 garbage_collection]).
165165
166166 -define(CREATION_EVENT_KEYS,
390390 State1 = State#ch{
391391 interceptor_state = rabbit_channel_interceptor:init(State)},
392392 State2 = rabbit_event:init_stats_timer(State1, #ch.stats_timer),
393 rabbit_event:notify(channel_created, infos(?CREATION_EVENT_KEYS, State2)),
393 Infos = infos(?CREATION_EVENT_KEYS, State2),
394 rabbit_core_metrics:channel_created(self(), Infos),
395 rabbit_event:notify(channel_created, Infos),
394396 rabbit_event:if_enabled(State2, #ch.stats_timer,
395397 fun() -> emit_stats(State2) end),
396398 put(channel_operation_timeout, ?CHANNEL_OPERATION_TIMEOUT),
614616 end),
615617 {hibernate, rabbit_event:stop_stats_timer(State, #ch.stats_timer)}.
616618
617 terminate(_Reason, State) ->
619 terminate(_Reason, State = #ch{}) ->
618620 {_Res, _State1} = notify_queues(State),
619621 pg_local:leave(rabbit_channels, self()),
620622 rabbit_event:if_enabled(State, #ch.stats_timer,
621623 fun() -> emit_stats(State) end),
624 [delete_stats(Tag) || {Tag, _} <- get()],
625 rabbit_core_metrics:channel_closed(self()),
622626 rabbit_event:notify(channel_closed, [{pid, self()}]).
623627
624628 code_change(_OldVsn, State, _Extra) ->
655659 ok = rabbit_writer:send_command(WriterPid, Command).
656660
657661 format_soft_error(#amqp_error{name = N, explanation = E, method = M}) ->
658 io_lib:format("operation ~s caused a channel exception ~s: ~p", [M, N, E]);
662 io_lib:format("operation ~s caused a channel exception ~s: ~ts", [M, N, E]);
659663 format_soft_error(Reason) ->
660664 Reason.
661665
19931997 list_to_binary(rabbit_misc:format("~s (~p)", [ConnName, Channel])).
19941998
19951999 incr_stats(Incs, Measure) ->
1996 [update_measures(Type, Key, Inc, Measure) || {Type, Key, Inc} <- Incs].
1997
1998 update_measures(Type, Key, Inc, Measure) ->
1999 Measures = case get({Type, Key}) of
2000 undefined -> [];
2001 D -> D
2002 end,
2003 Cur = case orddict:find(Measure, Measures) of
2004 error -> 0;
2005 {ok, C} -> C
2006 end,
2007 put({Type, Key}, orddict:store(Measure, Cur + Inc, Measures)).
2000 [begin
2001 rabbit_core_metrics:channel_stats(Type, Measure, {self(), Key}, Inc),
2002 %% Keys in the process dictionary are used to clean up the core metrics
2003 put({Type, Key}, none)
2004 end || {Type, Key, Inc} <- Incs].
20082005
20092006 emit_stats(State) -> emit_stats(State, []).
20102007
20112008 emit_stats(State, Extra) ->
2012 Coarse = infos(?STATISTICS_KEYS, State),
2013 case rabbit_event:stats_level(State, #ch.stats_timer) of
2014 coarse -> rabbit_event:notify(channel_stats, Extra ++ Coarse);
2015 fine -> Fine = [{channel_queue_stats,
2016 [{QName, Stats} ||
2017 {{queue_stats, QName}, Stats} <- get()]},
2018 {channel_exchange_stats,
2019 [{XName, Stats} ||
2020 {{exchange_stats, XName}, Stats} <- get()]},
2021 {channel_queue_exchange_stats,
2022 [{QX, Stats} ||
2023 {{queue_exchange_stats, QX}, Stats} <- get()]}],
2024 rabbit_event:notify(channel_stats, Extra ++ Coarse ++ Fine)
2025 end.
2009 [{reductions, Red} | Coarse0] = infos(?STATISTICS_KEYS, State),
2010 rabbit_core_metrics:channel_stats(self(), Extra ++ Coarse0),
2011 rabbit_core_metrics:channel_stats(reductions, self(), Red).
20262012
20272013 erase_queue_stats(QName) ->
2014 rabbit_core_metrics:channel_queue_down({self(), QName}),
20282015 erase({queue_stats, QName}),
2029 [erase({queue_exchange_stats, QX}) ||
2030 {{queue_exchange_stats, QX = {QName0, _}}, _} <- get(),
2031 QName0 =:= QName].
2016 [begin
2017 rabbit_core_metrics:channel_queue_exchange_down({self(), QX}),
2018 erase({queue_exchange_stats, QX})
2019 end || {{queue_exchange_stats, QX = {QName0, _}}, _} <- get(),
2020 QName0 =:= QName].
20322021
20332022 get_vhost(#ch{virtual_host = VHost}) -> VHost.
20342023
20352024 get_user(#ch{user = User}) -> User.
2025
2026 delete_stats({queue_stats, QName}) ->
2027 rabbit_core_metrics:channel_queue_down({self(), QName});
2028 delete_stats({exchange_stats, XName}) ->
2029 rabbit_core_metrics:channel_exchange_down({self(), XName});
2030 delete_stats({queue_exchange_stats, QX}) ->
2031 rabbit_core_metrics:channel_queue_exchange_down({self(), QX});
2032 delete_stats(_) ->
2033 ok.
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_channel_interceptor).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_command_assembler).
+0
-14
deps/rabbit_common/src/rabbit_common.app.src less more
0 % vim:ft=erlang:
1
2 {application, rabbit_common, [
3 {description, ""},
4 {vsn, "3.6.6"},
5 {id, "git"},
6 {modules, []},
7 {registered, []},
8 {applications, [
9 kernel,
10 stdlib,
11 xmerl
12 ]}
13 ]}.
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_control_misc).
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_core_metrics).
17
18 -include("rabbit_core_metrics.hrl").
19
20 -export([init/0]).
21
22 -export([connection_created/2,
23 connection_closed/1,
24 connection_stats/2,
25 connection_stats/4]).
26
27 -export([channel_created/2,
28 channel_closed/1,
29 channel_stats/2,
30 channel_stats/3,
31 channel_stats/4,
32 channel_queue_down/1,
33 channel_queue_exchange_down/1,
34 channel_exchange_down/1]).
35
36 -export([consumer_created/7,
37 consumer_deleted/3]).
38
39 -export([queue_stats/2,
40 queue_stats/5,
41 queue_deleted/1]).
42
43 -export([node_stats/2]).
44
45 -export([node_node_stats/2]).
46
47 -export([gen_server2_stats/2,
48 gen_server2_deleted/1,
49 get_gen_server2_stats/1]).
50
51 -export([delete/2]).
52
53 %% Those functions are exported for internal use only, not for public
54 %% consumption.
55 -export([
56 ets_update_counter/4,
57 ets_update_counter_pre_18/4,
58 ets_update_counter_post_18/4
59 ]).
60
61 -export([
62 ets_update_element/3,
63 ets_update_element_pre_18/3,
64 ets_update_element_post_18/3
65 ]).
66
67 -erlang_version_support([
68 {18, [
69 {ets_update_counter, 4,
70 ets_update_counter_pre_18,
71 ets_update_counter_post_18},
72 {ets_update_element, 3,
73 ets_update_element_pre_18,
74 ets_update_element_post_18}
75 ]}
76 ]).
77
78 %%----------------------------------------------------------------------------
79 %% Types
80 %%----------------------------------------------------------------------------
81 -type(channel_stats_id() :: pid() |
82 {pid(),
83 {rabbit_amqqueue:name(), rabbit_exchange:name()}} |
84 {pid(), rabbit_amqqueue:name()} |
85 {pid(), rabbit_exchange:name()}).
86
87 -type(channel_stats_type() :: queue_exchange_stats | queue_stats |
88 exchange_stats | reductions).
89 %%----------------------------------------------------------------------------
90 %% Specs
91 %%----------------------------------------------------------------------------
92 -spec init() -> ok.
93 -spec connection_created(pid(), rabbit_types:infos()) -> ok.
94 -spec connection_closed(pid()) -> ok.
95 -spec connection_stats(pid(), rabbit_types:infos()) -> ok.
96 -spec connection_stats(pid(), integer(), integer(), integer()) -> ok.
97 -spec channel_created(pid(), rabbit_types:infos()) -> ok.
98 -spec channel_closed(pid()) -> ok.
99 -spec channel_stats(pid(), rabbit_types:infos()) -> ok.
100 -spec channel_stats(channel_stats_type(), channel_stats_id(),
101 rabbit_types:infos() | integer()) -> ok.
102 -spec channel_queue_down({pid(), rabbit_amqqueue:name()}) -> ok.
103 -spec channel_queue_exchange_down({pid(), {rabbit_amqqueue:name(),
104 rabbit_exchange:name()}}) -> ok.
105 -spec channel_exchange_down({pid(), rabbit_exchange:name()}) -> ok.
106 -spec consumer_created(pid(), binary(), boolean(), boolean(),
107 rabbit_amqqueue:name(), integer(), list()) -> ok.
108 -spec consumer_deleted(pid(), binary(), rabbit_amqqueue:name()) -> ok.
109 -spec queue_stats(rabbit_amqqueue:name(), rabbit_types:infos()) -> ok.
110 -spec queue_stats(rabbit_amqqueue:name(), integer(), integer(), integer(),
111 integer()) -> ok.
112 -spec node_stats(atom(), rabbit_types:infos()) -> ok.
113 -spec node_node_stats({node(), node()}, rabbit_types:infos()) -> ok.
114 -spec gen_server2_stats(pid(), integer()) -> ok.
115 -spec gen_server2_deleted(pid()) -> ok.
116 -spec get_gen_server2_stats(pid()) -> integer() | 'not_found'.
117 -spec delete(atom(), any()) -> ok.
118 %%----------------------------------------------------------------------------
119 %% Storage of the raw metrics in RabbitMQ core. All the processing of stats
120 %% is done by the management plugin.
121 %%----------------------------------------------------------------------------
122 %%----------------------------------------------------------------------------
123 %% API
124 %%----------------------------------------------------------------------------
125 init() ->
126 [ets:new(Table, [Type, public, named_table, {write_concurrency, true}])
127 || {Table, Type} <- ?CORE_TABLES ++ ?CORE_EXTRA_TABLES],
128 ok.
129
130 connection_created(Pid, Infos) ->
131 ets:insert(connection_created, {Pid, Infos}),
132 ok.
133
134 connection_closed(Pid) ->
135 ets:delete(connection_created, Pid),
136 ets:delete(connection_metrics, Pid),
137 %% Delete marker
138 ets_update_element(connection_coarse_metrics, Pid, {5, 1}),
139 ok.
140
141 connection_stats(Pid, Infos) ->
142 ets:insert(connection_metrics, {Pid, Infos}),
143 ok.
144
145 connection_stats(Pid, Recv_oct, Send_oct, Reductions) ->
146 %% Includes delete marker
147 ets:insert(connection_coarse_metrics, {Pid, Recv_oct, Send_oct, Reductions, 0}),
148 ok.
149
150 channel_created(Pid, Infos) ->
151 ets:insert(channel_created, {Pid, Infos}),
152 ok.
153
154 channel_closed(Pid) ->
155 ets:delete(channel_created, Pid),
156 ets:delete(channel_metrics, Pid),
157 ets:delete(channel_process_metrics, Pid),
158 ok.
159
160 channel_stats(Pid, Infos) ->
161 ets:insert(channel_metrics, {Pid, Infos}),
162 ok.
163
164 channel_stats(reductions, Id, Value) ->
165 ets:insert(channel_process_metrics, {Id, Value}),
166 ok.
167
168 channel_stats(exchange_stats, publish, Id, Value) ->
169 %% Includes delete marker
170 ets_update_counter(channel_exchange_metrics, Id, {2, Value}, {Id, 0, 0, 0, 0}),
171 ok;
172 channel_stats(exchange_stats, confirm, Id, Value) ->
173 %% Includes delete marker
174 ets_update_counter(channel_exchange_metrics, Id, {3, Value}, {Id, 0, 0, 0, 0}),
175 ok;
176 channel_stats(exchange_stats, return_unroutable, Id, Value) ->
177 %% Includes delete marker
178 ets_update_counter(channel_exchange_metrics, Id, {4, Value}, {Id, 0, 0, 0, 0}),
179 ok;
180 channel_stats(queue_exchange_stats, publish, Id, Value) ->
181 %% Includes delete marker
182 ets_update_counter(channel_queue_exchange_metrics, Id, Value, {Id, 0, 0}),
183 ok;
184 channel_stats(queue_stats, get, Id, Value) ->
185 %% Includes delete marker
186 ets_update_counter(channel_queue_metrics, Id, {2, Value}, {Id, 0, 0, 0, 0, 0, 0, 0}),
187 ok;
188 channel_stats(queue_stats, get_no_ack, Id, Value) ->
189 %% Includes delete marker
190 ets_update_counter(channel_queue_metrics, Id, {3, Value}, {Id, 0, 0, 0, 0, 0, 0, 0}),
191 ok;
192 channel_stats(queue_stats, deliver, Id, Value) ->
193 %% Includes delete marker
194 ets_update_counter(channel_queue_metrics, Id, {4, Value}, {Id, 0, 0, 0, 0, 0, 0, 0}),
195 ok;
196 channel_stats(queue_stats, deliver_no_ack, Id, Value) ->
197 %% Includes delete marker
198 ets_update_counter(channel_queue_metrics, Id, {5, Value}, {Id, 0, 0, 0, 0, 0, 0, 0}),
199 ok;
200 channel_stats(queue_stats, redeliver, Id, Value) ->
201 %% Includes delete marker
202 ets_update_counter(channel_queue_metrics, Id, {6, Value}, {Id, 0, 0, 0, 0, 0, 0, 0}),
203 ok;
204 channel_stats(queue_stats, ack, Id, Value) ->
205 %% Includes delete marker
206 ets_update_counter(channel_queue_metrics, Id, {7, Value}, {Id, 0, 0, 0, 0, 0, 0, 0}),
207 ok.
208
209 delete(Table, Key) ->
210 ets:delete(Table, Key).
211
212 %% ets:update_counter(Tab, Key, Incr, Default) appeared in Erlang 18.x.
213 %% We need a wrapper for Erlang R16B03 and Erlang 17.x.
214
215 ets_update_counter(Tab, Key, Incr, Default) ->
216 code_version:update(?MODULE),
217 ?MODULE:ets_update_counter(Tab, Key, Incr, Default).
218
219 ets_update_counter_pre_18(Tab, Key, Incr, Default) ->
220 %% The wrapper tries to update the counter first. If it's missing
221 %% (and a `badarg` is raised), it inserts the default value and
222 %% tries to update the counter one more time.
223 try
224 ets:update_counter(Tab, Key, Incr)
225 catch
226 _:badarg ->
227 try
228 %% There is no atomicity here, so between the
229 %% call to `ets:insert_new/2` and the call to
230 %% `ets:update_counter/3`, the the counters have
231 %% a temporary value (which is not possible with
232 %% `ets:update_counter/4). Furthermore, there is a
233 %% chance for the counter to be removed between those
234 %% two calls as well.
235 ets:insert_new(Tab, Default),
236 ets:update_counter(Tab, Key, Incr)
237 catch
238 _:badarg ->
239 %% We can't tell with just `badarg` what the real
240 %% cause is. We have no way to decide if we should
241 %% try to insert/update the counter again, so let's
242 %% do nothing.
243 0
244 end
245 end.
246
247 ets_update_counter_post_18(Tab, Key, Incr, Default) ->
248 ets:update_counter(Tab, Key, Incr, Default).
249
250 %% ets:update_element(Tab, Key, ElementSpec) appeared in Erlang 18.x.
251 %% We need a wrapper for Erlang R16B03 and Erlang 17.x.
252
253 ets_update_element(Tab, Key, ElementSpec) ->
254 code_version:update(?MODULE),
255 ?MODULE:ets_update_element(Tab, Key, ElementSpec).
256
257 ets_update_element_pre_18(Tab, Key, {Pos, Value}) ->
258 case ets:lookup(Tab, Key) of
259 [] ->
260 ok;
261 [Tuple] ->
262 ets:insert(Tab, setelement(Pos, Tuple, Value))
263 end.
264
265 ets_update_element_post_18(Tab, Key, ElementSpec) ->
266 ets:update_element(Tab, Key, ElementSpec).
267
268 channel_queue_down(Id) ->
269 %% Delete marker
270 ets_update_element(channel_queue_metrics, Id, {8, 1}),
271 ok.
272
273 channel_queue_exchange_down(Id) ->
274 %% Delete marker
275 ets_update_element(channel_queue_exchange_metrics, Id, {3, 1}),
276 ok.
277
278 channel_exchange_down(Id) ->
279 %% Delete marker
280 ets_update_element(channel_exchange_metrics, Id, {5, 1}),
281 ok.
282
283 consumer_created(ChPid, ConsumerTag, ExclusiveConsume, AckRequired, QName,
284 PrefetchCount, Args) ->
285 ets:insert(consumer_created, {{QName, ChPid, ConsumerTag}, ExclusiveConsume,
286 AckRequired, PrefetchCount, Args}),
287 ok.
288
289 consumer_deleted(ChPid, ConsumerTag, QName) ->
290 ets:delete(consumer_created, {QName, ChPid, ConsumerTag}),
291 ok.
292
293 queue_stats(Name, Infos) ->
294 %% Includes delete marker
295 ets:insert(queue_metrics, {Name, Infos, 0}),
296 ok.
297
298 queue_stats(Name, MessagesReady, MessagesUnacknowledge, Messages, Reductions) ->
299 ets:insert(queue_coarse_metrics, {Name, MessagesReady, MessagesUnacknowledge,
300 Messages, Reductions}),
301 ok.
302
303 queue_deleted(Name) ->
304 ets:delete(queue_coarse_metrics, Name),
305 %% Delete markers
306 ets_update_element(queue_metrics, Name, {3, 1}),
307 CQX = ets:select(channel_queue_exchange_metrics, match_spec_cqx(Name)),
308 lists:foreach(fun(Key) ->
309 ets_update_element(channel_queue_exchange_metrics, Key, {3, 1})
310 end, CQX),
311 CQ = ets:select(channel_queue_metrics, match_spec_cq(Name)),
312 lists:foreach(fun(Key) ->
313 ets_update_element(channel_queue_metrics, Key, {8, 1})
314 end, CQ).
315
316 node_stats(persister_metrics, Infos) ->
317 ets:insert(node_persister_metrics, {node(), Infos});
318 node_stats(coarse_metrics, Infos) ->
319 ets:insert(node_coarse_metrics, {node(), Infos});
320 node_stats(node_metrics, Infos) ->
321 ets:insert(node_metrics, {node(), Infos}).
322
323 node_node_stats(Id, Infos) ->
324 ets:insert(node_node_metrics, {Id, Infos}).
325
326 match_spec_cqx(Id) ->
327 [{{{'$2', {'$1', '$3'}}, '_', '_'}, [{'==', {Id}, '$1'}], [{{'$2', {{'$1', '$3'}}}}]}].
328
329 match_spec_cq(Id) ->
330 [{{{'$2', '$1'}, '_', '_', '_', '_', '_', '_', '_'}, [{'==', {Id}, '$1'}], [{{'$2', '$1'}}]}].
331
332 gen_server2_stats(Pid, BufferLength) ->
333 ets:insert(gen_server2_metrics, {Pid, BufferLength}).
334
335 gen_server2_deleted(Pid) ->
336 ets:delete(gen_server2_metrics, Pid).
337
338 get_gen_server2_stats(Pid) ->
339 case ets:lookup(gen_server2_metrics, Pid) of
340 [{Pid, BufferLength}] ->
341 BufferLength;
342 [] ->
343 not_found
344 end.
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_data_coercion).
1717
18 -export([to_binary/1]).
18 -export([to_binary/1, to_list/1, to_atom/1, to_integer/1]).
19 -export([to_atom/2]).
1920
20 to_binary(Val) when is_list(Val) -> list_to_binary(Val);
21 to_binary(Val) -> Val.
21 -spec to_binary(Val :: binary() | list() | atom() | integer()) -> binary().
22 to_binary(Val) when is_list(Val) -> list_to_binary(Val);
23 to_binary(Val) when is_atom(Val) -> atom_to_binary(Val, utf8);
24 to_binary(Val) when is_integer(Val) -> integer_to_binary(Val);
25 to_binary(Val) -> Val.
26
27 -spec to_list(Val :: integer() | list() | binary() | atom()) -> list().
28 to_list(Val) when is_list(Val) -> Val;
29 to_list(Val) when is_atom(Val) -> atom_to_list(Val);
30 to_list(Val) when is_binary(Val) -> binary_to_list(Val);
31 to_list(Val) when is_integer(Val) -> integer_to_list(Val).
32
33 -spec to_atom(Val :: atom() | list() | binary()) -> atom().
34 to_atom(Val) when is_atom(Val) -> Val;
35 to_atom(Val) when is_list(Val) -> list_to_atom(Val);
36 to_atom(Val) when is_binary(Val) -> binary_to_atom(Val, utf8).
37
38 -spec to_atom(Val :: atom() | list() | binary(), Encoding :: atom()) -> atom().
39 to_atom(Val, _Encoding) when is_atom(Val) -> Val;
40 to_atom(Val, _Encoding) when is_list(Val) -> list_to_atom(Val);
41 to_atom(Val, Encoding) when is_binary(Val) -> binary_to_atom(Val, Encoding).
42
43 -spec to_integer(Val :: integer() | list() | binary()) -> integer().
44 to_integer(Val) when is_integer(Val) -> Val;
45 to_integer(Val) when is_list(Val) -> list_to_integer(Val);
46 to_integer(Val) when is_binary(Val) -> binary_to_integer(Val).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515 -module(rabbit_error_logger_handler).
1616
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_event).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_exchange_decorator).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_exchange_type).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515 -module(rabbit_health_check).
1616
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_heartbeat).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_misc).
1717 -include("rabbit.hrl").
1818 -include("rabbit_framing.hrl").
1919 -include("rabbit_misc.hrl").
20
21 -ifdef(TEST).
22 -export([decompose_pid/1, compose_pid/4]).
23 -endif.
2024
2125 -export([method_record_type/1, polite_pause/0, polite_pause/1]).
2226 -export([die/1, frame_error/2, amqp_error/4, quit/1,
5559 -export([const/1]).
5660 -export([ntoa/1, ntoab/1]).
5761 -export([is_process_alive/1]).
58 -export([pget/2, pget/3, pget_or_die/2, pmerge/3, pset/3, plmerge/2]).
62 -export([pget/2, pget/3, pupdate/3, pget_or_die/2, pmerge/3, pset/3, plmerge/2]).
5963 -export([format_message_queue/2]).
6064 -export([append_rpc_all_nodes/4]).
6165 -export([os_cmd/1]).
7175 -export([get_parent/0]).
7276 -export([store_proc_name/1, store_proc_name/2, get_proc_name/0]).
7377 -export([moving_average/4]).
74 -export([get_env/3]).
78 -export([get_env/3, lists_droplast/1]).
7579 -export([get_channel_operation_timeout/0]).
7680 -export([random/1]).
7781 -export([rpc_call/4, rpc_call/5, rpc_call/7]).
7882 -export([report_default_thread_pool_size/0]).
7983 -export([get_gc_info/1]).
84 -export([group_proplists_by/2]).
8085
8186 %% Horrible macro to use in guards
8287 -define(IS_BENIGN_EXIT(R),
267272 (node(), atom(), atom(), [any()], reference(), pid(), number()) -> any().
268273 -spec report_default_thread_pool_size() -> 'ok'.
269274 -spec get_gc_info(pid()) -> integer().
275 -spec group_proplists_by(fun((proplists:proplist()) -> any()),
276 list(proplists:proplist())) -> list(list(proplists:proplist())).
277
270278
271279 %%----------------------------------------------------------------------------
272280
727735 decompose_pid(Pid) when is_pid(Pid) ->
728736 %% see http://erlang.org/doc/apps/erts/erl_ext_dist.html (8.10 and
729737 %% 8.7)
730 <<131,103,100,NodeLen:16,NodeBin:NodeLen/binary,Id:32,Ser:32,Cre:8>>
731 = term_to_binary(Pid),
732 Node = binary_to_term(<<131,100,NodeLen:16,NodeBin:NodeLen/binary>>),
738 Node = node(Pid),
739 BinPid = term_to_binary(Pid),
740 ByteSize = byte_size(BinPid),
741 NodeByteSize = (ByteSize - 11),
742 <<131, 103, _NodePrefix:NodeByteSize/binary, Id:32, Ser:32, Cre:8>> = BinPid,
733743 {Node, Cre, Id, Ser}.
734744
735745 compose_pid(Node, Cre, Id, Ser) ->
794804 gb_trees_fold(fun (Key, Val, Acc) -> Fun(Key, Val), Acc end, ok, Tree).
795805
796806 module_attributes(Module) ->
797 case catch Module:module_info(attributes) of
798 {'EXIT', {undef, [{Module, module_info, _} | _]}} ->
807 try
808 Module:module_info(attributes)
809 catch
810 _:undef ->
799811 io:format("WARNING: module ~p not found, so not scanned for boot steps.~n",
800812 [Module]),
801 [];
802 {'EXIT', Reason} ->
803 exit(Reason);
804 V ->
805 V
813 []
806814 end.
807815
808816 all_module_attributes(Name) ->
863871 %% See also rabbit_mnesia:is_process_alive/1 which also requires the
864872 %% process be in the same running cluster as us (i.e. not partitioned
865873 %% or some random node).
874 is_process_alive(Pid) when node(Pid) =:= node() ->
875 erlang:is_process_alive(Pid);
866876 is_process_alive(Pid) ->
867877 Node = node(Pid),
868878 lists:member(Node, [node() | nodes()]) andalso
889899 V -> V
890900 end.
891901
892 %% property merge
902 pupdate(K, UpdateFun, P) ->
903 case lists:keyfind(K, 1, P) of
904 {K, V} ->
905 pset(K, UpdateFun(V), P);
906 _ ->
907 undefined
908 end.
909
910 %% property merge
893911 pmerge(Key, Val, List) ->
894912 case proplists:is_defined(Key, List) of
895913 true -> List;
899917 %% proplists merge
900918 plmerge(P1, P2) ->
901919 dict:to_list(dict:merge(fun(_, V, _) ->
902 V
903 end,
904 dict:from_list(P1),
920 V
921 end,
922 dict:from_list(P1),
905923 dict:from_list(P2))).
924
925 %% groups a list of proplists by a key function
926 group_proplists_by(KeyFun, ListOfPropLists) ->
927 Res = lists:foldl(fun(P, Agg) ->
928 dict:update(KeyFun(P), fun (O) -> [P|O] end, [P], Agg)
929 end, dict:new(), ListOfPropLists),
930 [ X || {_, X} <- dict:to_list(Res)].
906931
907932 pset(Key, Value, List) -> [{Key, Value} | proplists:delete(Key, List)].
908933
11251150 {ok, Val} -> Val;
11261151 undefined -> Def
11271152 end.
1153
1154 %% lists:droplast/1 is only available in Erlang 17.0+.
1155 lists_droplast([_T]) -> [];
1156 lists_droplast([H|T]) -> [H|lists_droplast(T)].
11281157
11291158 get_channel_operation_timeout() ->
11301159 %% Default channel_operation_timeout set to net_ticktime + 10s to
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_msg_store_index).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_net).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_networking).
3939 -export([tcp_listener_addresses/1, tcp_listener_spec/9,
4040 ensure_ssl/0, fix_ssl_options/1, poodle_check/1]).
4141
42 -export([tcp_listener_started/3, tcp_listener_stopped/3]).
42 -export([tcp_listener_started/4, tcp_listener_stopped/4]).
4343
4444 %% Internal
4545 -export([connections_local/0]).
4646
47 -import(rabbit_misc, [pget/2, pget/3, pset/3]).
48
4947 -include("rabbit.hrl").
5048 -include_lib("kernel/include/inet.hrl").
5149
5250 %% IANA-suggested ephemeral port range is 49152 to 65535
5351 -define(FIRST_TEST_BIND_PORT, 49152).
54
55 %% POODLE
56 -define(BAD_SSL_PROTOCOL_VERSIONS, [sslv3]).
5752
5853 %%----------------------------------------------------------------------------
5954
10095 protocol(), any(), non_neg_integer(), label()) ->
10196 supervisor:child_spec().
10297 -spec ensure_ssl() -> rabbit_types:infos().
103 -spec fix_ssl_options(rabbit_types:infos()) -> rabbit_types:infos().
10498 -spec poodle_check(atom()) -> 'ok' | 'danger'.
10599
106100 -spec boot() -> 'ok'.
107101 -spec tcp_listener_started
108 (_,
102 (_, _,
109103 string() |
110104 {byte(),byte(),byte(),byte()} |
111105 {char(),char(),char(),char(),char(),char(),char(),char()}, _) ->
112106 'ok'.
113107 -spec tcp_listener_stopped
114 (_,
108 (_, _,
115109 string() |
116110 {byte(),byte(),byte(),byte()} |
117111 {char(),char(),char(),char(),char(),char(),char(),char()},
148142 {ok, SslAppsConfig} = application:get_env(rabbit, ssl_apps),
149143 ok = app_utils:start_applications(SslAppsConfig),
150144 {ok, SslOptsConfig} = application:get_env(rabbit, ssl_options),
151 fix_ssl_options(SslOptsConfig).
145 rabbit_ssl_options:fix(SslOptsConfig).
152146
153147 poodle_check(Context) ->
154148 {ok, Vsn} = application:get_key(ssl, vsn),
176170 [rabbit_misc:otp_release(), Context]).
177171
178172 fix_ssl_options(Config) ->
179 fix_verify_fun(fix_ssl_protocol_versions(Config)).
180
181 fix_verify_fun(SslOptsConfig) ->
182 %% Starting with ssl 4.0.1 in Erlang R14B, the verify_fun function
183 %% takes 3 arguments and returns a tuple.
184 case rabbit_misc:pget(verify_fun, SslOptsConfig) of
185 {Module, Function, InitialUserState} ->
186 Fun = make_verify_fun(Module, Function, InitialUserState),
187 rabbit_misc:pset(verify_fun, Fun, SslOptsConfig);
188 {Module, Function} when is_atom(Module) ->
189 Fun = make_verify_fun(Module, Function, none),
190 rabbit_misc:pset(verify_fun, Fun, SslOptsConfig);
191 {Verifyfun, _InitialUserState} when is_function(Verifyfun, 3) ->
192 SslOptsConfig;
193 undefined ->
194 SslOptsConfig
195 end.
196
197 make_verify_fun(Module, Function, InitialUserState) ->
198 try
199 %% Preload the module: it is required to use
200 %% erlang:function_exported/3.
201 Module:module_info()
202 catch
203 _:Exception ->
204 rabbit_log:error("SSL verify_fun: module ~s missing: ~p~n",
205 [Module, Exception]),
206 throw({error, {invalid_verify_fun, missing_module}})
207 end,
208 NewForm = erlang:function_exported(Module, Function, 3),
209 OldForm = erlang:function_exported(Module, Function, 1),
210 case {NewForm, OldForm} of
211 {true, _} ->
212 %% This verify_fun is supported by Erlang R14B+ (ssl
213 %% 4.0.1 and later).
214 Fun = fun(OtpCert, Event, UserState) ->
215 Module:Function(OtpCert, Event, UserState)
216 end,
217 {Fun, InitialUserState};
218 {_, true} ->
219 %% This verify_fun is supported by Erlang R14B+ for
220 %% undocumented backward compatibility.
221 %%
222 %% InitialUserState is ignored in this case.
223 fun(Args) ->
224 Module:Function(Args)
225 end;
226 _ ->
227 rabbit_log:error("SSL verify_fun: no ~s:~s/3 exported~n",
228 [Module, Function]),
229 throw({error, {invalid_verify_fun, function_not_exported}})
230 end.
231
232 fix_ssl_protocol_versions(Config) ->
233 case application:get_env(rabbit, ssl_allow_poodle_attack) of
234 {ok, true} ->
235 Config;
236 _ ->
237 Configured = case pget(versions, Config) of
238 undefined -> pget(available, ssl:versions(), []);
239 Vs -> Vs
240 end,
241 pset(versions, Configured -- ?BAD_SSL_PROTOCOL_VERSIONS, Config)
242 end.
173 rabbit_ssl_options:fix(Config).
243174
244175 tcp_listener_addresses(Port) when is_integer(Port) ->
245176 tcp_listener_addresses_auto(Port);
266197 {rabbit_misc:tcp_name(NamePrefix, IPAddress, Port),
267198 {tcp_listener_sup, start_link,
268199 [IPAddress, Port, Transport, [Family | SocketOpts], ProtoSup, ProtoOpts,
269 {?MODULE, tcp_listener_started, [Protocol]},
270 {?MODULE, tcp_listener_stopped, [Protocol]},
200 {?MODULE, tcp_listener_started, [Protocol, SocketOpts]},
201 {?MODULE, tcp_listener_stopped, [Protocol, SocketOpts]},
271202 NumAcceptors, Label]},
272203 transient, infinity, supervisor, [tcp_listener_sup]}.
273204
307238 ok = supervisor:terminate_child(rabbit_sup, Name),
308239 ok = supervisor:delete_child(rabbit_sup, Name).
309240
310 tcp_listener_started(Protocol, IPAddress, Port) ->
241 tcp_listener_started(Protocol, Opts, IPAddress, Port) ->
311242 %% We need the ip to distinguish e.g. 0.0.0.0 and 127.0.0.1
312243 %% We need the host so we can distinguish multiple instances of the above
313244 %% in a cluster.
317248 protocol = Protocol,
318249 host = tcp_host(IPAddress),
319250 ip_address = IPAddress,
320 port = Port}).
321
322 tcp_listener_stopped(Protocol, IPAddress, Port) ->
251 port = Port,
252 opts = Opts}).
253
254 tcp_listener_stopped(Protocol, Opts, IPAddress, Port) ->
323255 ok = mnesia:dirty_delete_object(
324256 rabbit_listener,
325257 #listener{node = node(),
326258 protocol = Protocol,
327259 host = tcp_host(IPAddress),
328260 ip_address = IPAddress,
329 port = Port}).
261 port = Port,
262 opts = Opts}).
330263
331264 record_distribution_listener() ->
332265 {Name, Host} = rabbit_nodes:parts(node()),
333266 {port, Port, _Version} = erl_epmd:port_please(Name, Host),
334 tcp_listener_started(clustering, {0,0,0,0,0,0,0,0}, Port).
267 tcp_listener_started(clustering, [], {0,0,0,0,0,0,0,0}, Port).
335268
336269 active_listeners() ->
337270 rabbit_misc:dirty_read_all(rabbit_listener).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_nodes).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_password_hashing).
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_pbe).
17
18 -export([supported_ciphers/0, supported_hashes/0, default_cipher/0, default_hash/0, default_iterations/0]).
19 -export([encrypt_term/5, decrypt_term/5]).
20 -export([encrypt/5, decrypt/5]).
21
22 %% Supported ciphers and hashes
23
24 supported_ciphers() ->
25 NotSupportedByUs = [aes_ctr, aes_ecb, des_ecb, blowfish_ecb, rc4, aes_gcm],
26 SupportedByCrypto = proplists:get_value(ciphers, crypto:supports()),
27 lists:filter(fun(Cipher) ->
28 not lists:member(Cipher, NotSupportedByUs)
29 end,
30 SupportedByCrypto).
31
32 supported_hashes() ->
33 NotSupportedByUs = [md4, ripemd160],
34 SupportedByCrypto = proplists:get_value(hashs, crypto:supports()),
35 lists:filter(fun(Hash) ->
36 not lists:member(Hash, NotSupportedByUs)
37 end,
38 SupportedByCrypto).
39
40 %% Default encryption parameters (keep those in sync with rabbit.app.src)
41 default_cipher() ->
42 aes_cbc256.
43
44 default_hash() ->
45 sha512.
46
47 default_iterations() ->
48 1000.
49
50 %% Encryption/decryption of arbitrary Erlang terms.
51
52 encrypt_term(Cipher, Hash, Iterations, PassPhrase, Term) ->
53 encrypt(Cipher, Hash, Iterations, PassPhrase, term_to_binary(Term)).
54
55 decrypt_term(Cipher, Hash, Iterations, PassPhrase, Base64Binary) ->
56 binary_to_term(decrypt(Cipher, Hash, Iterations, PassPhrase, Base64Binary)).
57
58 %% The cipher for encryption is from the list of supported ciphers.
59 %% The hash for generating the key from the passphrase is from the list
60 %% of supported hashes. See crypto:supports/0 to obtain both lists.
61 %% The key is generated by applying the hash N times with N >= 1.
62 %%
63 %% The encrypt/5 function returns a base64 binary and the decrypt/5
64 %% function accepts that same base64 binary.
65
66 -spec encrypt(crypto:block_cipher(), crypto:hash_algorithms(),
67 pos_integer(), iodata(), binary()) -> binary().
68 encrypt(Cipher, Hash, Iterations, PassPhrase, ClearText) ->
69 Salt = crypto:strong_rand_bytes(16),
70 Ivec = crypto:strong_rand_bytes(iv_length(Cipher)),
71 Key = make_key(Cipher, Hash, Iterations, PassPhrase, Salt),
72 Binary = crypto:block_encrypt(Cipher, Key, Ivec, pad(Cipher, ClearText)),
73 base64:encode(<< Salt/binary, Ivec/binary, Binary/binary >>).
74
75 -spec decrypt(crypto:block_cipher(), crypto:hash_algorithms(),
76 pos_integer(), iodata(), binary()) -> binary().
77 decrypt(Cipher, Hash, Iterations, PassPhrase, Base64Binary) ->
78 IvLength = iv_length(Cipher),
79 << Salt:16/binary, Ivec:IvLength/binary, Binary/bits >> = base64:decode(Base64Binary),
80 Key = make_key(Cipher, Hash, Iterations, PassPhrase, Salt),
81 unpad(crypto:block_decrypt(Cipher, Key, Ivec, Binary)).
82
83 %% Generate a key from a passphrase.
84
85 make_key(Cipher, Hash, Iterations, PassPhrase, Salt) ->
86 Key = pbdkdf2(PassPhrase, Salt, Iterations, key_length(Cipher),
87 fun crypto:hmac/4, Hash, hash_length(Hash)),
88 if
89 Cipher =:= des3_cbc; Cipher =:= des3_cbf; Cipher =:= des3_cfb; Cipher =:= des_ede3 ->
90 << A:8/binary, B:8/binary, C:8/binary >> = Key,
91 [A, B, C];
92 true ->
93 Key
94 end.
95
96 %% Functions to pad/unpad input to a multiplier of block size.
97
98 pad(Cipher, Data) ->
99 BlockSize = block_size(Cipher),
100 N = BlockSize - (byte_size(Data) rem BlockSize),
101 Pad = list_to_binary(lists:duplicate(N, N)),
102 <<Data/binary, Pad/binary>>.
103
104 unpad(Data) ->
105 N = binary:last(Data),
106 binary:part(Data, 0, byte_size(Data) - N).
107
108 %% These functions are necessary because the current Erlang crypto interface
109 %% is lacking interfaces to the following OpenSSL functions:
110 %%
111 %% * int EVP_MD_size(const EVP_MD *md);
112 %% * int EVP_CIPHER_iv_length(const EVP_CIPHER *e);
113 %% * int EVP_CIPHER_key_length(const EVP_CIPHER *e);
114 %% * int EVP_CIPHER_block_size(const EVP_CIPHER *e);
115
116 hash_length(md4) -> 16;
117 hash_length(md5) -> 16;
118 hash_length(sha) -> 20;
119 hash_length(sha224) -> 28;
120 hash_length(sha256) -> 32;
121 hash_length(sha384) -> 48;
122 hash_length(sha512) -> 64.
123
124 iv_length(des_cbc) -> 8;
125 iv_length(des_cfb) -> 8;
126 iv_length(des3_cbc) -> 8;
127 iv_length(des3_cbf) -> 8;
128 iv_length(des3_cfb) -> 8;
129 iv_length(des_ede3) -> 8;
130 iv_length(blowfish_cbc) -> 8;
131 iv_length(blowfish_cfb64) -> 8;
132 iv_length(blowfish_ofb64) -> 8;
133 iv_length(rc2_cbc) -> 8;
134 iv_length(aes_cbc) -> 16;
135 iv_length(aes_cbc128) -> 16;
136 iv_length(aes_cfb8) -> 16;
137 iv_length(aes_cfb128) -> 16;
138 iv_length(aes_cbc256) -> 16;
139 iv_length(aes_ige256) -> 32.
140
141 key_length(des_cbc) -> 8;
142 key_length(des_cfb) -> 8;
143 key_length(des3_cbc) -> 24;
144 key_length(des3_cbf) -> 24;
145 key_length(des3_cfb) -> 24;
146 key_length(des_ede3) -> 24;
147 key_length(blowfish_cbc) -> 16;
148 key_length(blowfish_cfb64) -> 16;
149 key_length(blowfish_ofb64) -> 16;
150 key_length(rc2_cbc) -> 16;
151 key_length(aes_cbc) -> 16;
152 key_length(aes_cbc128) -> 16;
153 key_length(aes_cfb8) -> 16;
154 key_length(aes_cfb128) -> 16;
155 key_length(aes_cbc256) -> 32;
156 key_length(aes_ige256) -> 16.
157
158 block_size(aes_cbc256) -> 32;
159 block_size(aes_cbc128) -> 32;
160 block_size(aes_ige256) -> 32;
161 block_size(aes_cbc) -> 32;
162 block_size(_) -> 8.
163
164 %% The following was taken from OTP's lib/public_key/src/pubkey_pbe.erl
165 %%
166 %% This is an undocumented interface to password-based encryption algorithms.
167 %% These functions have been copied here to stay compatible with R16B03.
168
169 %%--------------------------------------------------------------------
170 -spec pbdkdf2(string(), iodata(), integer(), integer(), fun(), atom(), integer())
171 -> binary().
172 %%
173 %% Description: Implements password based decryption key derive function 2.
174 %% Exported mainly for testing purposes.
175 %%--------------------------------------------------------------------
176 pbdkdf2(Password, Salt, Count, DerivedKeyLen, Prf, PrfHash, PrfOutputLen)->
177 NumBlocks = ceiling(DerivedKeyLen / PrfOutputLen),
178 NumLastBlockOctets = DerivedKeyLen - (NumBlocks - 1) * PrfOutputLen ,
179 blocks(NumBlocks, NumLastBlockOctets, 1, Password, Salt,
180 Count, Prf, PrfHash, PrfOutputLen, <<>>).
181
182 blocks(1, N, Index, Password, Salt, Count, Prf, PrfHash, PrfLen, Acc) ->
183 <<XorSum:N/binary, _/binary>> = xor_sum(Password, Salt, Count, Index, Prf, PrfHash, PrfLen),
184 <<Acc/binary, XorSum/binary>>;
185 blocks(NumBlocks, N, Index, Password, Salt, Count, Prf, PrfHash, PrfLen, Acc) ->
186 XorSum = xor_sum(Password, Salt, Count, Index, Prf, PrfHash, PrfLen),
187 blocks(NumBlocks -1, N, Index +1, Password, Salt, Count, Prf, PrfHash,
188 PrfLen, <<Acc/binary, XorSum/binary>>).
189
190 xor_sum(Password, Salt, Count, Index, Prf, PrfHash, PrfLen) ->
191 Result = Prf(PrfHash, Password, [Salt,<<Index:32/unsigned-big-integer>>], PrfLen),
192 do_xor_sum(Prf, PrfHash, PrfLen, Result, Password, Count-1, Result).
193
194 do_xor_sum(_, _, _, _, _, 0, Acc) ->
195 Acc;
196 do_xor_sum(Prf, PrfHash, PrfLen, Prev, Password, Count, Acc) ->
197 Result = Prf(PrfHash, Password, Prev, PrfLen),
198 do_xor_sum(Prf, PrfHash, PrfLen, Result, Password, Count-1, crypto:exor(Acc, Result)).
199
200 ceiling(Float) ->
201 erlang:round(Float + 0.5).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_policy_validator).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_queue_collector).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_queue_decorator).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_queue_master_locator).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_reader).
158158 send_pend, state, channels, reductions,
159159 garbage_collection]).
160160
161 -define(SIMPLE_METRICS, [pid, recv_oct, send_oct, reductions]).
162 -define(OTHER_METRICS, [recv_cnt, send_cnt, send_pend, state, channels,
163 garbage_collection]).
164
161165 -define(CREATION_EVENT_KEYS,
162166 [pid, name, port, peer_port, host,
163167 peer_host, ssl, peer_cert_subject, peer_cert_issuer,
258262 ok.
259263
260264 server_properties(Protocol) ->
261 {ok, Product} = application:get_key(rabbit, id),
265 {ok, Product} = application:get_key(rabbit, description),
262266 {ok, Version} = application:get_key(rabbit, vsn),
263267
264268 %% Get any configuration-specified server properties
382386 last_blocked_by = none,
383387 last_blocked_at = never}},
384388 try
385 run({?MODULE, recvloop,
386 [Deb, [], 0, switch_callback(rabbit_event:init_stats_timer(
387 State, #v1.stats_timer),
388 handshake, 8)]}),
389 log(info, "closing AMQP connection ~p (~s)~n", [self(), dynamic_connection_name(Name)])
389 case run({?MODULE, recvloop,
390 [Deb, [], 0, switch_callback(rabbit_event:init_stats_timer(
391 State, #v1.stats_timer),
392 handshake, 8)]}) of
393 %% connection was closed cleanly by the client
394 #v1{connection = #connection{user = #user{username = Username},
395 vhost = VHost}} ->
396 log(info, "closing AMQP connection ~p (~s, vhost: '~s', user: '~s')~n",
397 [self(), dynamic_connection_name(Name), VHost, Username]);
398 %% just to be more defensive
399 _ ->
400 log(info, "closing AMQP connection ~p (~s)~n",
401 [self(), dynamic_connection_name(Name)])
402 end
390403 catch
391404 Ex ->
392405 log_connection_exception(dynamic_connection_name(Name), Ex)
400413 %% socket w/o delay before termination.
401414 rabbit_net:fast_close(Sock),
402415 rabbit_networking:unregister_connection(self()),
416 rabbit_core_metrics:connection_closed(self()),
403417 rabbit_event:notify(connection_closed, [{pid, self()}])
404418 end,
405419 done.
407421 log_connection_exception(Name, Ex) ->
408422 Severity = case Ex of
409423 connection_closed_with_no_data_received -> debug;
424 {connection_closed_abruptly, _} -> warning;
410425 connection_closed_abruptly -> warning;
411426 _ -> error
412427 end,
416431 %% Long line to avoid extra spaces and line breaks in log
417432 log(Severity, "closing AMQP connection ~p (~s):~nmissed heartbeats from client, timeout: ~ps~n",
418433 [self(), Name, TimeoutSec]);
434 log_connection_exception(Severity, Name, {connection_closed_abruptly,
435 #v1{connection = #connection{user = #user{username = Username},
436 vhost = VHost}}}) ->
437 log(Severity, "closing AMQP connection ~p (~s, vhost: '~s', user: '~s'):~nclient unexpectedly closed TCP connection~n",
438 [self(), Name, VHost, Username]);
439 %% when client abruptly closes connection before connection.open/authentication/authorization
440 %% succeeded, don't log username and vhost as 'none'
441 log_connection_exception(Severity, Name, {connection_closed_abruptly, _}) ->
442 log(Severity, "closing AMQP connection ~p (~s):~nclient unexpectedly closed TCP connection~n",
443 [self(), Name]);
444 %% old exception structure
419445 log_connection_exception(Severity, Name, connection_closed_abruptly) ->
420446 log(Severity, "closing AMQP connection ~p (~s):~nclient unexpectedly closed TCP connection~n",
421447 [self(), Name]);
484510 recvloop(Deb, [Data | Buf], BufLen + size(Data),
485511 State#v1{pending_recv = false});
486512 closed when State#v1.connection_state =:= closed ->
487 ok;
513 State;
488514 closed when CS =:= pre_init andalso Buf =:= [] ->
489515 stop(tcp_healthcheck, State);
490516 closed ->
500526 ?MODULE, Deb, {Buf, BufLen, State});
501527 {other, Other} ->
502528 case handle_other(Other, State) of
503 stop -> ok;
529 stop -> State;
504530 NewState -> recvloop(Deb, Buf, BufLen, NewState)
505531 end
506532 end.
513539 throw(connection_closed_with_no_data_received);
514540 stop(closed, State) ->
515541 maybe_emit_stats(State),
516 throw(connection_closed_abruptly);
542 throw({connection_closed_abruptly, State});
517543 stop(Reason, State) ->
518544 maybe_emit_stats(State),
519545 throw({inet_error, Reason}).
717743 {Channel, State1} = channel_cleanup(ChPid, State),
718744 case {Channel, termination_kind(Reason)} of
719745 {undefined, controlled} -> State1;
720 {undefined, uncontrolled} -> exit({abnormal_dependent_exit,
746 {undefined, uncontrolled} -> handle_uncontrolled_channel_close(ChPid),
747 exit({abnormal_dependent_exit,
721748 ChPid, Reason});
722749 {_, controlled} -> maybe_close(control_throttle(State1));
723 {_, uncontrolled} -> State2 = handle_exception(
750 {_, uncontrolled} -> handle_uncontrolled_channel_close(ChPid),
751 State2 = handle_exception(
724752 State1, Channel, Reason),
725753 maybe_close(control_throttle(State2))
726754 end.
761789 "error while terminating:~n~p~n",
762790 [self(), ConnName, VHost, User#user.username,
763791 CS, Channel, Reason]),
792 handle_uncontrolled_channel_close(ChPid),
764793 wait_for_channel_termination(N-1, TimerRef, State1)
765794 end;
766795 {'EXIT', Sock, _Reason} ->
11201149 State)
11211150 catch throw:{inet_error, E} when E =:= closed; E =:= enotconn ->
11221151 maybe_emit_stats(State),
1123 throw(connection_closed_abruptly);
1152 throw({connection_closed_abruptly, State});
11241153 exit:#amqp_error{method = none} = Reason ->
11251154 handle_exception(State, 0, Reason#amqp_error{method = MethodName});
11261155 Type:Reason ->
11931222 queue_collector = Collector,
11941223 heartbeater = Heartbeater};
11951224
1196 handle_method0(#'connection.open'{virtual_host = VHostPath},
1225 handle_method0(#'connection.open'{virtual_host = VHost},
11971226 State = #v1{connection_state = opening,
11981227 connection = Connection = #connection{
1199 user = User,
1228 log_name = ConnName,
1229 user = User = #user{username = Username},
12001230 protocol = Protocol},
12011231 helper_sup = SupPid,
12021232 sock = Sock,
12031233 throttle = Throttle}) ->
1204 ok = rabbit_access_control:check_vhost_access(User, VHostPath, Sock),
1205 NewConnection = Connection#connection{vhost = VHostPath},
1234 ok = rabbit_access_control:check_vhost_access(User, VHost, Sock),
1235 NewConnection = Connection#connection{vhost = VHost},
12061236 ok = send_on_channel0(Sock, #'connection.open_ok'{}, Protocol),
12071237 Conserve = rabbit_alarm:register(self(), {?MODULE, conserve_resources, []}),
12081238 Throttle1 = Throttle#throttle{alarmed_by = Conserve},
12131243 connection = NewConnection,
12141244 channel_sup_sup_pid = ChannelSupSupPid,
12151245 throttle = Throttle1}),
1216 rabbit_event:notify(connection_created,
1217 [{type, network} |
1218 infos(?CREATION_EVENT_KEYS, State1)]),
1246 Infos = [{type, network} | infos(?CREATION_EVENT_KEYS, State1)],
1247 rabbit_core_metrics:connection_created(proplists:get_value(pid, Infos),
1248 Infos),
1249 rabbit_event:notify(connection_created, Infos),
12191250 maybe_emit_stats(State1),
1251 log(info, "connection ~p (~s): user '~s' authenticated and granted access to vhost '~s'~n",
1252 [self(), ConnName, Username, VHost]),
12201253 State1;
12211254 handle_method0(#'connection.close'{}, State) when ?IS_RUNNING(State) ->
12221255 lists:foreach(fun rabbit_channel:shutdown/1, all_channels()),
14331466
14341467 socket_info(Get, Select, #v1{sock = Sock}) ->
14351468 case Get(Sock) of
1436 {ok, T} -> Select(T);
1437 {error, _} -> ''
1469 {ok, T} -> case Select(T) of
1470 N when is_number(N) -> N;
1471 _ -> 0
1472 end;
1473 {error, _} -> 0
14381474 end.
14391475
14401476 ssl_info(F, #v1{sock = Sock}) ->
14651501 fun() -> emit_stats(State) end).
14661502
14671503 emit_stats(State) ->
1468 Infos = infos(?STATISTICS_KEYS, State),
1469 rabbit_event:notify(connection_stats, Infos),
1504 [{_, Pid}, {_, Recv_oct}, {_, Send_oct}, {_, Reductions}] = I
1505 = infos(?SIMPLE_METRICS, State),
1506 Infos = infos(?OTHER_METRICS, State),
1507 rabbit_core_metrics:connection_stats(Pid, Infos),
1508 rabbit_core_metrics:connection_stats(Pid, Recv_oct, Send_oct, Reductions),
1509 rabbit_event:notify(connection_stats, Infos ++ I),
14701510 State1 = rabbit_event:reset_stats_timer(State, #v1.stats_timer),
14711511 ensure_stats_timer(State1).
14721512
15271567 _ ->
15281568 Default
15291569 end.
1570
1571 handle_uncontrolled_channel_close(ChPid) ->
1572 rabbit_core_metrics:channel_closed(ChPid),
1573 rabbit_event:notify(channel_closed, [{pid, ChPid}]).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_runtime_parameter).
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_ssl_options).
17
18 -export([fix/1]).
19
20 %% POODLE
21 -define(BAD_SSL_PROTOCOL_VERSIONS, [sslv3]).
22
23 -spec fix(rabbit_types:infos()) -> rabbit_types:infos().
24
25 fix(Config) ->
26 fix_verify_fun(fix_ssl_protocol_versions(Config)).
27
28 fix_verify_fun(SslOptsConfig) ->
29 %% Starting with ssl 4.0.1 in Erlang R14B, the verify_fun function
30 %% takes 3 arguments and returns a tuple.
31 case rabbit_misc:pget(verify_fun, SslOptsConfig) of
32 {Module, Function, InitialUserState} ->
33 Fun = make_verify_fun(Module, Function, InitialUserState),
34 rabbit_misc:pset(verify_fun, Fun, SslOptsConfig);
35 {Module, Function} when is_atom(Module) ->
36 Fun = make_verify_fun(Module, Function, none),
37 rabbit_misc:pset(verify_fun, Fun, SslOptsConfig);
38 {Verifyfun, _InitialUserState} when is_function(Verifyfun, 3) ->
39 SslOptsConfig;
40 undefined ->
41 SslOptsConfig
42 end.
43
44 make_verify_fun(Module, Function, InitialUserState) ->
45 try
46 %% Preload the module: it is required to use
47 %% erlang:function_exported/3.
48 Module:module_info()
49 catch
50 _:Exception ->
51 rabbit_log:error("SSL verify_fun: module ~s missing: ~p~n",
52 [Module, Exception]),
53 throw({error, {invalid_verify_fun, missing_module}})
54 end,
55 NewForm = erlang:function_exported(Module, Function, 3),
56 OldForm = erlang:function_exported(Module, Function, 1),
57 case {NewForm, OldForm} of
58 {true, _} ->
59 %% This verify_fun is supported by Erlang R14B+ (ssl
60 %% 4.0.1 and later).
61 Fun = fun(OtpCert, Event, UserState) ->
62 Module:Function(OtpCert, Event, UserState)
63 end,
64 {Fun, InitialUserState};
65 {_, true} ->
66 %% This verify_fun is supported by Erlang R14B+ for
67 %% undocumented backward compatibility.
68 %%
69 %% InitialUserState is ignored in this case.
70 fun(Args) ->
71 Module:Function(Args)
72 end;
73 _ ->
74 rabbit_log:error("SSL verify_fun: no ~s:~s/3 exported~n",
75 [Module, Function]),
76 throw({error, {invalid_verify_fun, function_not_exported}})
77 end.
78
79 fix_ssl_protocol_versions(Config) ->
80 case application:get_env(rabbit, ssl_allow_poodle_attack) of
81 {ok, true} ->
82 Config;
83 _ ->
84 Configured = case rabbit_misc:pget(versions, Config) of
85 undefined -> rabbit_misc:pget(available,
86 ssl:versions(),
87 []);
88 Vs -> Vs
89 end,
90 rabbit_misc:pset(versions, Configured -- ?BAD_SSL_PROTOCOL_VERSIONS, Config)
91 end.
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_types).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_writer).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(ssl_compat).
1717
18 %% We don't want warnings about the use of erlang:now/0 in
19 %% this module.
18 %% We don't want warnings about the use of ssl:connection_info/1 in this
19 %% module.
2020 -compile(nowarn_deprecated_function).
2121
2222 %% Declare versioned functions to allow dynamic code loading,
00 PROJECT = rabbitmq_amqp1_0
1 PROJECT_DESCRIPTION = AMQP 1.0 support for RabbitMQ
12
3 define PROJECT_ENV
4 [
5 {default_user, "guest"},
6 {default_vhost, <<"/">>},
7 {protocol_strict_mode, false}
8 ]
9 endef
10
11 BUILD_DEPS = rabbitmq_codegen
212 DEPS = rabbit_common rabbit amqp_client
3 TEST_DEPS = rabbitmq_ct_helpers
13 TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers
414
15 DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
516 DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk
617
718 EXTRA_SOURCES += include/rabbit_amqp1_0_framing.hrl \
2738 CODEGEN = $(CURDIR)/codegen.py
2839 CODEGEN_DIR ?= $(DEPS_DIR)/rabbitmq_codegen
2940 CODEGEN_AMQP = $(CODEGEN_DIR)/amqp_codegen.py
30 CODEGEN_SPECS = spec/messaging.xml spec/security.xml spec/transport.xml \
31 spec/transactions.xml
41 CODEGEN_SPECS = $(CODEGEN_DIR)/amqp-1.0/messaging.xml \
42 $(CODEGEN_DIR)/amqp-1.0/security.xml \
43 $(CODEGEN_DIR)/amqp-1.0/transport.xml \
44 $(CODEGEN_DIR)/amqp-1.0/transactions.xml
3245
3346 include/rabbit_amqp1_0_framing.hrl:: $(CODEGEN) $(CODEGEN_AMQP) \
3447 $(CODEGEN_SPECS)
0 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
11 #
22 # Permission to use, copy, modify, and/or distribute this software for any
33 # purpose with or without fee is hereby granted, provided that the above
1111 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1212 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1313
14 .PHONY: all app apps deps search rel docs install-docs check tests clean distclean help erlang-mk
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
1515
1616 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17
18 ERLANG_MK_VERSION = 2.0.0-pre.2-144-g647ffd1
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
1931
2032 # Core configuration.
2133
2436
2537 PROJECT_VERSION ?= rolling
2638 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
2740
2841 # Verbosity.
2942
8497 rel::
8598 $(verbose) :
8699
100 relup:: deps app
101
87102 check:: tests
88103
89104 clean:: clean-crashdump
101116 help::
102117 $(verbose) printf "%s\n" \
103118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
104 "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
105120 "" \
106121 "Usage: [V=1] $(MAKE) [target]..." \
107122 "" \
149164 core_native_path = $1
150165 endif
151166
152 ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
153 define core_http_get
154 wget --no-check-certificate -O $(1) $(2)|| rm $(1)
155 endef
156 else
157 define core_http_get.erl
158 ssl:start(),
159 inets:start(),
160 case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
161 {ok, {{_, 200, _}, _, Body}} ->
162 case file:write_file("$(1)", Body) of
163 ok -> ok;
164 {error, R1} -> halt(R1)
165 end;
166 {error, R2} ->
167 halt(R2)
168 end,
169 halt(0).
170 endef
171
172 define core_http_get
173 $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
174 endef
175 endif
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
176168
177169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
178170
192184 ERLANG_MK_BUILD_CONFIG ?= build.config
193185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
194186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
195188 erlang-mk:
196189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
197190 ifdef ERLANG_MK_COMMIT
198191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
199192 endif
200193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
201 $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
202195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
203196 rm -rf $(ERLANG_MK_BUILD_DIR)
204197
285278 pkg_apns_repo = https://github.com/inaka/apns4erl
286279 pkg_apns_commit = master
287280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
288289 PACKAGES += azdht
289290 pkg_azdht_name = azdht
290291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
405406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
406407 pkg_bootstrap_commit = master
407408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
408417 PACKAGES += boss_db
409418 pkg_boss_db_name = boss_db
410419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
413422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
414423 pkg_boss_db_commit = master
415424
416 PACKAGES += boss
417 pkg_boss_name = boss
418 pkg_boss_description = Erlang web MVC, now featuring Comet
419 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
420 pkg_boss_fetch = git
421 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
422 pkg_boss_commit = master
423
424425 PACKAGES += brod
425426 pkg_brod_name = brod
426427 pkg_brod_description = Kafka client in Erlang
565566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
566567 pkg_cloudi_service_api_requests_commit = master
567568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
568585 PACKAGES += cloudi_service_db_cassandra_cql
569586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
570587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
573590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
574591 pkg_cloudi_service_db_cassandra_cql_commit = master
575592
576 PACKAGES += cloudi_service_db_cassandra
577 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
579 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
580 pkg_cloudi_service_db_cassandra_fetch = git
581 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
582 pkg_cloudi_service_db_cassandra_commit = master
583
584593 PACKAGES += cloudi_service_db_couchdb
585594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
586595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
637646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
638647 pkg_cloudi_service_db_tokyotyrant_commit = master
639648
640 PACKAGES += cloudi_service_db
641 pkg_cloudi_service_db_name = cloudi_service_db
642 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
643 pkg_cloudi_service_db_homepage = http://cloudi.org/
644 pkg_cloudi_service_db_fetch = git
645 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
646 pkg_cloudi_service_db_commit = master
647
648649 PACKAGES += cloudi_service_filesystem
649650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
650651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
10371038 pkg_edown_repo = https://github.com/uwiger/edown
10381039 pkg_edown_commit = master
10391040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
10401049 PACKAGES += eep_app
10411050 pkg_eep_app_name = eep_app
10421051 pkg_eep_app_description = Embedded Event Processing
10451054 pkg_eep_app_repo = https://github.com/darach/eep-erl
10461055 pkg_eep_app_commit = master
10471056
1048 PACKAGES += eep
1049 pkg_eep_name = eep
1050 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1051 pkg_eep_homepage = https://github.com/virtan/eep
1052 pkg_eep_fetch = git
1053 pkg_eep_repo = https://github.com/virtan/eep
1054 pkg_eep_commit = master
1055
10561057 PACKAGES += efene
10571058 pkg_efene_name = efene
10581059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
12371238 pkg_eqm_repo = https://github.com/loucash/eqm
12381239 pkg_eqm_commit = master
12391240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
12401249 PACKAGES += eredis_pool
12411250 pkg_eredis_pool_name = eredis_pool
12421251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
12451254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
12461255 pkg_eredis_pool_commit = master
12471256
1248 PACKAGES += eredis
1249 pkg_eredis_name = eredis
1250 pkg_eredis_description = Erlang Redis client
1251 pkg_eredis_homepage = https://github.com/wooga/eredis
1252 pkg_eredis_fetch = git
1253 pkg_eredis_repo = https://github.com/wooga/eredis
1254 pkg_eredis_commit = master
1255
12561257 PACKAGES += erl_streams
12571258 pkg_erl_streams_name = erl_streams
12581259 pkg_erl_streams_description = Streams in Erlang
15411542 pkg_etap_repo = https://github.com/ngerakines/etap
15421543 pkg_etap_commit = master
15431544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
15441553 PACKAGES += etest_http
15451554 pkg_etest_http_name = etest_http
15461555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
15491558 pkg_etest_http_repo = https://github.com/wooga/etest_http
15501559 pkg_etest_http_commit = master
15511560
1552 PACKAGES += etest
1553 pkg_etest_name = etest
1554 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1555 pkg_etest_homepage = https://github.com/wooga/etest
1556 pkg_etest_fetch = git
1557 pkg_etest_repo = https://github.com/wooga/etest
1558 pkg_etest_commit = master
1559
15601561 PACKAGES += etoml
15611562 pkg_etoml_name = etoml
15621563 pkg_etoml_description = TOML language erlang parser
15651566 pkg_etoml_repo = https://github.com/kalta/etoml
15661567 pkg_etoml_commit = master
15671568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
15681577 PACKAGES += eunit_formatters
15691578 pkg_eunit_formatters_name = eunit_formatters
15701579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
15731582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
15741583 pkg_eunit_formatters_commit = master
15751584
1576 PACKAGES += eunit
1577 pkg_eunit_name = eunit
1578 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1579 pkg_eunit_homepage = https://github.com/richcarl/eunit
1580 pkg_eunit_fetch = git
1581 pkg_eunit_repo = https://github.com/richcarl/eunit
1582 pkg_eunit_commit = master
1583
15841585 PACKAGES += euthanasia
15851586 pkg_euthanasia_name = euthanasia
15861587 pkg_euthanasia_description = Merciful killer for your Erlang processes
15981599 pkg_evum_commit = master
15991600
16001601 PACKAGES += exec
1601 pkg_exec_name = exec
1602 pkg_exec_name = erlexec
16021603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
16031604 pkg_exec_homepage = http://saleyn.github.com/erlexec
16041605 pkg_exec_fetch = git
17171718 pkg_fn_repo = https://github.com/reiddraper/fn
17181719 pkg_fn_commit = master
17191720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
17201729 PACKAGES += folsom_cowboy
17211730 pkg_folsom_cowboy_name = folsom_cowboy
17221731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
17251734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
17261735 pkg_folsom_cowboy_commit = master
17271736
1728 PACKAGES += folsom
1729 pkg_folsom_name = folsom
1730 pkg_folsom_description = Expose Erlang Events and Metrics
1731 pkg_folsom_homepage = https://github.com/boundary/folsom
1732 pkg_folsom_fetch = git
1733 pkg_folsom_repo = https://github.com/boundary/folsom
1734 pkg_folsom_commit = master
1735
17361737 PACKAGES += folsomite
17371738 pkg_folsomite_name = folsomite
17381739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
20932094 pkg_jesse_repo = https://github.com/for-GET/jesse
20942095 pkg_jesse_commit = master
20952096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
20962105 PACKAGES += jiffy_v
20972106 pkg_jiffy_v_name = jiffy_v
20982107 pkg_jiffy_v_description = JSON validation utility
21012110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
21022111 pkg_jiffy_v_commit = master
21032112
2104 PACKAGES += jiffy
2105 pkg_jiffy_name = jiffy
2106 pkg_jiffy_description = JSON NIFs for Erlang.
2107 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2108 pkg_jiffy_fetch = git
2109 pkg_jiffy_repo = https://github.com/davisp/jiffy
2110 pkg_jiffy_commit = master
2111
21122113 PACKAGES += jobs
21132114 pkg_jobs_name = jobs
21142115 pkg_jobs_description = a Job scheduler for load regulation
21252126 pkg_joxa_repo = https://github.com/joxa/joxa
21262127 pkg_joxa_commit = master
21272128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
21282137 PACKAGES += json_rec
21292138 pkg_json_rec_name = json_rec
21302139 pkg_json_rec_description = JSON to erlang record
21332142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
21342143 pkg_json_rec_commit = master
21352144
2136 PACKAGES += json
2137 pkg_json_name = json
2138 pkg_json_description = a high level json library for erlang (17.0+)
2139 pkg_json_homepage = https://github.com/talentdeficit/json
2140 pkg_json_fetch = git
2141 pkg_json_repo = https://github.com/talentdeficit/json
2142 pkg_json_commit = master
2143
21442145 PACKAGES += jsone
21452146 pkg_jsone_name = jsone
21462147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
21812182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
21822183 pkg_jsx_commit = master
21832184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
21842193 PACKAGES += kafka_protocol
21852194 pkg_kafka_protocol_name = kafka_protocol
21862195 pkg_kafka_protocol_description = Kafka protocol Erlang library
21892198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
21902199 pkg_kafka_protocol_commit = master
21912200
2192 PACKAGES += kafka
2193 pkg_kafka_name = kafka
2194 pkg_kafka_description = Kafka consumer and producer in Erlang
2195 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2196 pkg_kafka_fetch = git
2197 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2198 pkg_kafka_commit = master
2199
22002201 PACKAGES += kai
22012202 pkg_kai_name = kai
22022203 pkg_kai_description = DHT storage by Takeshi Inoue
22932294 pkg_kvs_repo = https://github.com/synrc/kvs
22942295 pkg_kvs_commit = master
22952296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
22962305 PACKAGES += lager_amqp_backend
22972306 pkg_lager_amqp_backend_name = lager_amqp_backend
22982307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
23092318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
23102319 pkg_lager_syslog_commit = master
23112320
2312 PACKAGES += lager
2313 pkg_lager_name = lager
2314 pkg_lager_description = A logging framework for Erlang/OTP.
2315 pkg_lager_homepage = https://github.com/basho/lager
2316 pkg_lager_fetch = git
2317 pkg_lager_repo = https://github.com/basho/lager
2318 pkg_lager_commit = master
2319
23202321 PACKAGES += lambdapad
23212322 pkg_lambdapad_name = lambdapad
23222323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
25732574 pkg_mixer_repo = https://github.com/chef/mixer
25742575 pkg_mixer_commit = master
25752576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
25762585 PACKAGES += mochiweb_xpath
25772586 pkg_mochiweb_xpath_name = mochiweb_xpath
25782587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
25812590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
25822591 pkg_mochiweb_xpath_commit = master
25832592
2584 PACKAGES += mochiweb
2585 pkg_mochiweb_name = mochiweb
2586 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2587 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2588 pkg_mochiweb_fetch = git
2589 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2590 pkg_mochiweb_commit = master
2591
25922593 PACKAGES += mockgyver
25932594 pkg_mockgyver_name = mockgyver
25942595 pkg_mockgyver_description = A mocking library for Erlang
30613062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
30623063 pkg_quickrand_commit = master
30633064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
30643073 PACKAGES += rabbit_exchange_type_riak
30653074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
30663075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
30693078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
30703079 pkg_rabbit_exchange_type_riak_commit = master
30713080
3072 PACKAGES += rabbit
3073 pkg_rabbit_name = rabbit
3074 pkg_rabbit_description = RabbitMQ Server
3075 pkg_rabbit_homepage = https://www.rabbitmq.com/
3076 pkg_rabbit_fetch = git
3077 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3078 pkg_rabbit_commit = master
3079
30803081 PACKAGES += rack
30813082 pkg_rack_name = rack
30823083 pkg_rack_description = Rack handler for erlang
34933494 pkg_smother_repo = https://github.com/ramsay-t/Smother
34943495 pkg_smother_commit = master
34953496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
34963505 PACKAGES += social
34973506 pkg_social_name = social
34983507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
35413550 pkg_stable_repo = https://github.com/dvv/stable
35423551 pkg_stable_commit = master
35433552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
35443561 PACKAGES += statebox_riak
35453562 pkg_statebox_riak_name = statebox_riak
35463563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
35493566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
35503567 pkg_statebox_riak_commit = master
35513568
3552 PACKAGES += statebox
3553 pkg_statebox_name = statebox
3554 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3555 pkg_statebox_homepage = https://github.com/mochi/statebox
3556 pkg_statebox_fetch = git
3557 pkg_statebox_repo = https://github.com/mochi/statebox
3558 pkg_statebox_commit = master
3559
35603569 PACKAGES += statman
35613570 pkg_statman_name = statman
35623571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
37253734 pkg_tirerl_repo = https://github.com/inaka/tirerl
37263735 pkg_tirerl_commit = master
37273736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
37283745 PACKAGES += traffic_tools
37293746 pkg_traffic_tools_name = traffic_tools
37303747 pkg_traffic_tools_description = Simple traffic limiting library
40614078 pkg_zucchini_repo = https://github.com/devinus/zucchini
40624079 pkg_zucchini_commit = master
40634080
4064 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
40654082 # This file is part of erlang.mk and subject to the terms of the ISC License.
40664083
40674084 .PHONY: search
40884105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
40894106 endif
40904107
4091 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
40924109 # This file is part of erlang.mk and subject to the terms of the ISC License.
40934110
4094 .PHONY: distclean-deps
4111 .PHONY: distclean-deps clean-tmp-deps.log
40954112
40964113 # Configuration.
40974114
41104127
41114128 REBAR_DEPS_DIR = $(DEPS_DIR)
41124129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
41134150
41144151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
41154152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
41164153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
41174154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
41184155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
41194157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
41204158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
41214159
41384176
41394177 # Core targets.
41404178
4141 ifdef IS_APP
4142 apps::
4143 else
4144 apps:: $(ALL_APPS_DIRS)
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
41454180 ifeq ($(IS_APP)$(IS_DEP),)
41464181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
41474182 endif
41494184 # Create ebin directory for all apps to make sure Erlang recognizes them
41504185 # as proper OTP applications when using -include_lib. This is a temporary
41514186 # fix, a proper fix would be to compile apps/* in the right order.
4152 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4153 mkdir -p $$dep/ebin || exit $$?; \
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
41544190 done
4155 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
41564196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
41574197 :; \
41584198 else \
41594199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4160 $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
4200 $(MAKE) -C $$dep IS_APP=1; \
41614201 fi \
41624202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
41634207 endif
41644208
41654209 ifneq ($(SKIP_DEPS),)
41664210 deps::
41674211 else
4168 deps:: $(ALL_DEPS_DIRS) apps
4169 ifeq ($(IS_APP)$(IS_DEP),)
4170 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4171 endif
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
41724213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4173 $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
41744215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
41754216 :; \
41764217 else \
41774218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
41784219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4179 $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
4220 $(MAKE) -C $$dep IS_DEP=1; \
41804221 else \
4181 echo "Error: No Makefile to build dependency $$dep."; \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
41824223 exit 2; \
41834224 fi \
41844225 fi \
41924233 # in practice only Makefile is needed so far.
41934234 define dep_autopatch
41944235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
41954237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
41964238 $(call dep_autopatch_erlang_mk,$(1)); \
41974239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4198 if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
41994243 $(call dep_autopatch2,$(1)); \
4200 elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
42014245 $(call dep_autopatch2,$(1)); \
4202 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
42034247 $(call dep_autopatch2,$(1)); \
4204 else \
4205 $(call erlang,$(call dep_autopatch_app.erl,$(1))); \
42064248 fi \
42074249 else \
42084250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
42144256 endef
42154257
42164258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
42174262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
42184263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
42194264 fi; \
42204265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4221 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
42224267 $(call dep_autopatch_fetch_rebar); \
42234268 $(call dep_autopatch_rebar,$(1)); \
42244269 else \
42304275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
42314276 endef
42324277
4233 # Overwrite erlang.mk with the current file by default.
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
42344280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
42354281 define dep_autopatch_erlang_mk
4236 echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \
4237 > $(DEPS_DIR)/$1/erlang.mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
42384287 endef
42394288 else
42404289 define dep_autopatch_erlang_mk
42534302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
42544303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
42554304 cd $(ERLANG_MK_TMP)/rebar; \
4256 git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
42574306 $(MAKE); \
42584307 cd -; \
42594308 fi
42704319 define dep_autopatch_rebar.erl
42714320 application:load(rebar),
42724321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
42734323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
42744324 {ok, Conf0} -> Conf0;
42754325 _ -> []
44234473 [] -> ok;
44244474 _ ->
44254475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4426 PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
44274477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4428 PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L \\"~s\\" -lerl_interface -lei\n",
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
44294479 [code:lib_dir(erl_interface, lib)])),
44304480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
44314481 FilterEnv = fun(Env) ->
44644514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44654515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44664516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4467 [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
44684518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
44694519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
44704520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
44764526 end,
44774527 [PortSpec(S) || S <- PortSpecs]
44784528 end,
4479 Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"),
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
44804530 RunPlugin = fun(Plugin, Step) ->
44814531 case erlang:function_exported(Plugin, Step, 2) of
44824532 false -> ok;
45244574 halt()
45254575 endef
45264576
4527 define dep_autopatch_app.erl
4528 UpdateModules = fun(App) ->
4529 case filelib:is_regular(App) of
4530 false -> ok;
4531 true ->
4532 {ok, [{application, '$(1)', L0}]} = file:consult(App),
4533 Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true,
4534 fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
4535 L = lists:keystore(modules, 1, L0, {modules, Mods}),
4536 ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}]))
4537 end
4538 end,
4539 UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"),
4540 halt()
4541 endef
4542
45434577 define dep_autopatch_appsrc_script.erl
45444578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
45454579 AppSrcScript = AppSrc ++ ".script",
45874621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
45884622 endef
45894623
4590 define dep_fetch_hex.erl
4591 ssl:start(),
4592 inets:start(),
4593 {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
4594 {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
4595 [], [{body_format, binary}]),
4596 {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
4597 {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
4598 ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
4599 halt()
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
46004626 endef
46014627
46024628 # Hex only has a package version. No need to look in the Erlang.mk packages.
46034629 define dep_fetch_hex
4604 $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
46054634 endef
46064635
46074636 define dep_fetch_fail
46314660 $(eval DEP_NAME := $(call dep_name,$1))
46324661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
46334662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4634 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
46354664 exit 17; \
46364665 fi
46374666 $(verbose) mkdir -p $(DEPS_DIR)
46734702 clean:: clean-apps
46744703
46754704 clean-apps:
4676 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4677 $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
46784707 done
46794708
46804709 distclean:: distclean-apps
46814710
46824711 distclean-apps:
4683 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4684 $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
46854714 done
46864715 endif
46874716
47014730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
47024731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
47034732
4704 # External plugins.
4705
4706 DEP_PLUGINS ?=
4707
4708 define core_dep_plugin
4709 -include $(DEPS_DIR)/$(1)
4710
4711 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4712 endef
4713
4714 $(foreach p,$(DEP_PLUGINS),\
4715 $(eval $(if $(findstring /,$p),\
4716 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4717 $(call core_dep_plugin,$p/plugins.mk,$p))))
4718
4719 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4720 # This file is part of erlang.mk and subject to the terms of the ISC License.
4721
4722 # Configuration.
4723
4724 DTL_FULL_PATH ?=
4725 DTL_PATH ?= templates/
4726 DTL_SUFFIX ?= _dtl
4727 DTL_OPTS ?=
4728
4729 # Verbosity.
4730
4731 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
4732 dtl_verbose = $(dtl_verbose_$(V))
4733
4734 # Core targets.
4735
4736 DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl))
4737
4738 ifneq ($(DTL_FILES),)
4739
4740 ifdef DTL_FULL_PATH
4741 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
4742 else
4743 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
4744 endif
4745
4746 ifneq ($(words $(DTL_FILES)),0)
4747 # Rebuild templates when the Makefile changes.
4748 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
4749 @mkdir -p $(ERLANG_MK_TMP)
4750 @if test -f $@; then \
4751 touch $(DTL_FILES); \
4752 fi
4753 @touch $@
4754
4755 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
4756 endif
4757
4758 define erlydtl_compile.erl
4759 [begin
4760 Module0 = case "$(strip $(DTL_FULL_PATH))" of
4761 "" ->
4762 filename:basename(F, ".dtl");
4763 _ ->
4764 "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
4765 re:replace(F2, "/", "_", [{return, list}, global])
4766 end,
4767 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
4768 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
4769 ok -> ok;
4770 {ok, _} -> ok
4771 end
4772 end || F <- string:tokens("$(1)", " ")],
4773 halt().
4774 endef
4775
4776 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
4777 $(if $(strip $?),\
4778 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
4779
4780 endif
4781
4782 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
47834734 # This file is part of erlang.mk and subject to the terms of the ISC License.
47844735
47854736 # Verbosity.
47984749
47994750 define compile_proto.erl
48004751 [begin
4801 Dir = filename:dirname(filename:dirname(F)),
48024752 protobuffs_compile:generate_source(F,
4803 [{output_include_dir, Dir ++ "/include"},
4804 {output_src_dir, Dir ++ "/ebin"}])
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
48054755 end || F <- string:tokens("$(1)", " ")],
48064756 halt().
48074757 endef
48114761 $(if $(strip $?),$(call compile_proto,$?))
48124762 endif
48134763
4814 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
48154765 # This file is part of erlang.mk and subject to the terms of the ISC License.
48164766
48174767 .PHONY: clean-app
48254775 ERLC_EXCLUDE ?=
48264776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
48274777
4778 ERLC_ASN1_OPTS ?=
4779
48284780 ERLC_MIB_OPTS ?=
48294781 COMPILE_MIB_FIRST ?=
48304782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
48744826
48754827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
48764828 define app_file
4877 {application, $(PROJECT), [
4829 {application, '$(PROJECT)', [
48784830 {description, "$(PROJECT_DESCRIPTION)"},
48794831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48804832 {id$(comma)$(space)"$(1)"}$(comma))
48814833 {modules, [$(call comma_list,$(2))]},
48824834 {registered, []},
4883 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48844837 ]}.
48854838 endef
48864839 else
48874840 define app_file
4888 {application, $(PROJECT), [
4841 {application, '$(PROJECT)', [
48894842 {description, "$(PROJECT_DESCRIPTION)"},
48904843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48914844 {id$(comma)$(space)"$(1)"}$(comma))
48924845 {modules, [$(call comma_list,$(2))]},
48934846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
48944847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4895 {mod, {$(PROJECT_MOD), []}}
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48964850 ]}.
48974851 endef
48984852 endif
49024856
49034857 # Source files.
49044858
4905 ERL_FILES = $(sort $(call core_find,src/,*.erl))
4906 CORE_FILES = $(sort $(call core_find,src/,*.core))
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
49074863
49084864 # ASN.1 files.
49094865
49134869
49144870 define compile_asn1
49154871 $(verbose) mkdir -p include/
4916 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
49174873 $(verbose) mv asn1/*.erl src/
49184874 $(verbose) mv asn1/*.hrl include/
49194875 $(verbose) mv asn1/*.asn1db include/
49364892
49374893 # Leex and Yecc files.
49384894
4939 XRL_FILES = $(sort $(call core_find,src/,*.xrl))
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
49404896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
49414897 ERL_FILES += $(XRL_ERL_FILES)
49424898
4943 YRL_FILES = $(sort $(call core_find,src/,*.yrl))
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
49444900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
49454901 ERL_FILES += $(YRL_ERL_FILES)
49464902
49474903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4948 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
49494905
49504906 # Erlang and Core Erlang files.
49514907
49954951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49964952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49974953 (F, Mod, import, {Imp, _}) ->
4998 case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
49994960 false -> ok;
50004961 true -> Add(Mod, Imp)
50014962 end;
50194980 end || F <- ErlFiles],
50204981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
50214982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
50224991 ok = file:write_file("$(1)", [
50234992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
5024 "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n"
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
50254994 ]),
50264995 halt()
50274996 endef
50345003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
50355004 # Rebuild everything when the Makefile changes.
50365005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5037 @mkdir -p $(ERLANG_MK_TMP)
5038 @if test -f $@; then \
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
50395008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
50405009 touch -c $(PROJECT).d; \
50415010 fi
5042 @touch $@
5011 $(verbose) touch $@
50435012
50445013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
50455014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
50465015 endif
50475016
5048 -include $(PROJECT).d
5017 include $(wildcard $(PROJECT).d)
50495018
50505019 ebin/$(PROJECT).app:: ebin/
50515020
50645033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
50655034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
50665035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5067 $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
50685037 > ebin/$(PROJECT).app
50695038 else
50705039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
50885057
50895058 endif
50905059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
50915061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
50925062 # This file is part of erlang.mk and subject to the terms of the ISC License.
50935063
51055075 doc-deps:
51065076 else
51075077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5108 $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5109 endif
5110
5111 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51125082 # This file is part of erlang.mk and subject to the terms of the ISC License.
51135083
51145084 .PHONY: rel-deps
51255095 rel-deps:
51265096 else
51275097 rel-deps: $(ALL_REL_DEPS_DIRS)
5128 $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5129 endif
5130
5131 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51325102 # This file is part of erlang.mk and subject to the terms of the ISC License.
51335103
51345104 .PHONY: test-deps test-dir test-build clean-test-dir
51505120 test-deps:
51515121 else
51525122 test-deps: $(ALL_TEST_DEPS_DIRS)
5153 $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
51545124 endif
51555125
51565126 ifneq ($(wildcard $(TEST_DIR)),)
51835153 endif
51845154 endif
51855155
5186 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51875157 # This file is part of erlang.mk and subject to the terms of the ISC License.
51885158
51895159 .PHONY: rebar.config
52195189 rebar.config:
52205190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
52215191
5222 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
52235193 # This file is part of erlang.mk and subject to the terms of the ISC License.
52245194
5225 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
5226
5227 MAN_INSTALL_PATH ?= /usr/local/share/man
5228 MAN_SECTIONS ?= 3 7
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
52295200
52305201 docs:: asciidoc
52315202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
52325207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
52335210
52345211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
52355212 asciidoc-guide:
52365213 else
5237 asciidoc-guide: distclean-asciidoc doc-deps
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
52385215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
52395216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5240 endif
5241
5242 ifeq ($(wildcard doc/src/manual/*.asciidoc),)
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
52435227 asciidoc-manual:
52445228 else
5245 asciidoc-manual: distclean-asciidoc doc-deps
5246 for f in doc/src/manual/*.asciidoc ; do \
5247 a2x -v -f manpage $$f ; \
5248 done
5249 for s in $(MAN_SECTIONS); do \
5250 mkdir -p doc/man$$s/ ; \
5251 mv doc/src/manual/*.$$s doc/man$$s/ ; \
5252 gzip doc/man$$s/*.$$s ; \
5253 done
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
52545262
52555263 install-docs:: install-asciidoc
52565264
52575265 install-asciidoc: asciidoc-manual
5258 for s in $(MAN_SECTIONS); do \
5259 mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
5260 install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
5261 done
5262 endif
5263
5264 distclean:: distclean-asciidoc
5265
5266 distclean-asciidoc:
5267 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
5268
5269 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
52705276 # This file is part of erlang.mk and subject to the terms of the ISC License.
52715277
52725278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
53235329 define bs_Makefile
53245330 PROJECT = $p
53255331 PROJECT_DESCRIPTION = New project
5326 PROJECT_VERSION = 0.0.1
5332 PROJECT_VERSION = 0.1.0
53275333
53285334 # Whitespace to be used when creating files from templates.
53295335 SP = $(SP)
53335339 define bs_Makefile
53345340 PROJECT = $p
53355341 PROJECT_DESCRIPTION = New project
5336 PROJECT_VERSION = 0.0.1
5342 PROJECT_VERSION = 0.1.0
53375343
53385344 endef
53395345 endif
53415347 define bs_apps_Makefile
53425348 PROJECT = $p
53435349 PROJECT_DESCRIPTION = New project
5344 PROJECT_VERSION = 0.0.1
5350 PROJECT_VERSION = 0.1.0
53455351
53465352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
53475353 endef
53615367 endef
53625368
53635369 define bs_relx_config
5364 {release, {$p_release, "1"}, [$p]}.
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
53655371 {extended_start_script, true}.
53665372 {sys_config, "rel/sys.config"}.
53675373 {vm_args, "rel/vm.args"}.
57125718 ifndef t
57135719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57145720 endif
5715 ifndef tpl_$(t)
5716 $(error Unknown template)
5717 endif
57185721 ifndef n
57195722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57205723 endif
57275730 list-templates:
57285731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
57295732
5730 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
57315734 # This file is part of erlang.mk and subject to the terms of the ISC License.
57325735
57335736 .PHONY: clean-c_src distclean-c_src-env
59625965 $(call render_template,bs_erl_nif,src/$n.erl)
59635966 endif
59645967
5965 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
59665969 # This file is part of erlang.mk and subject to the terms of the ISC License.
59675970
5968 .PHONY: ci ci-setup distclean-kerl
5969
5970 KERL ?= $(CURDIR)/kerl
5971 export KERL
5972
5973 KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
5974
5975 OTP_GIT ?= https://github.com/erlang/otp
5976
5977 CI_INSTALL_DIR ?= $(HOME)/erlang
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
59785973 CI_OTP ?=
5979
5980 ifeq ($(strip $(CI_OTP)),)
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
59815986 ci::
59825987 else
5983 ci:: $(addprefix ci-,$(CI_OTP))
5984
5985 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
59866007
59876008 ci-setup::
6009
6010 ci-extra::
59886011
59896012 ci_verbose_0 = @echo " CI " $(1);
59906013 ci_verbose = $(ci_verbose_$(V))
59916014
59926015 define ci_target
5993 ci-$(1): $(CI_INSTALL_DIR)/$(1)
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
59946018 $(ci_verbose) \
5995 PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
5996 CI_OTP_RELEASE="$(1)" \
5997 CT_OPTS="-label $(1)" \
5998 $(MAKE) clean ci-setup tests
5999 endef
6000
6001 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
60026030
60036031 define ci_otp_target
60046032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
60056033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6006 $(KERL) build git $(OTP_GIT) $(1) $(1)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
60076035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
60086036 endif
60096037 endef
60106038
60116039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
60126040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
60136052 $(KERL):
6014 $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
60156056 $(verbose) chmod +x $(KERL)
60166057
60176058 help::
60286069 $(gen_verbose) rm -rf $(KERL)
60296070 endif
60306071
6031 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
60326073 # This file is part of erlang.mk and subject to the terms of the ISC License.
60336074
60346075 .PHONY: ct apps-ct distclean-ct
60366077 # Configuration.
60376078
60386079 CT_OPTS ?=
6080
60396081 ifneq ($(wildcard $(TEST_DIR)),)
6040 CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6041 else
6042 CT_SUITES ?=
6043 endif
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
60446088
60456089 # Core targets.
60466090
60636107 -noinput \
60646108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
60656109 -dir $(TEST_DIR) \
6066 -logdir $(CURDIR)/logs
6110 -logdir $(CT_LOGS_DIR)
60676111
60686112 ifeq ($(CT_SUITES),)
60696113 ct: $(if $(IS_APP),,apps-ct)
60706114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
60716117 ct: test-build $(if $(IS_APP),,apps-ct)
6072 $(verbose) mkdir -p $(CURDIR)/logs/
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
60736119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
60746121 endif
60756122
60766123 ifneq ($(ALL_APPS_DIRS),)
60976144
60986145 define ct_suite_target
60996146 ct-$(1): test-build
6100 $(verbose) mkdir -p $(CURDIR)/logs/
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
61016148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
61026149 endef
61036150
61046151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
61056152
61066153 distclean-ct:
6107 $(gen_verbose) rm -rf $(CURDIR)/logs/
6108
6109 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61106157 # This file is part of erlang.mk and subject to the terms of the ISC License.
61116158
61126159 .PHONY: plt distclean-plt dialyze
61506197 endef
61516198
61526199 $(DIALYZER_PLT): deps app
6153 $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
61546204
61556205 plt: $(DIALYZER_PLT)
61566206
61646214 endif
61656215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
61666216
6167 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61686218 # This file is part of erlang.mk and subject to the terms of the ISC License.
61696219
61706220 .PHONY: distclean-edoc edoc
61726222 # Configuration.
61736223
61746224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
61756235
61766236 # Core targets.
61776237
6178 ifneq ($(wildcard doc/overview.edoc),)
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
61796239 docs:: edoc
61806240 endif
61816241
61846244 # Plugin-specific targets.
61856245
61866246 edoc: distclean-edoc doc-deps
6187 $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
61886248
61896249 distclean-edoc:
61906250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
61916251
6192 # Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61936253 # This file is part of erlang.mk and subject to the terms of the ISC License.
61946254
6195 .PHONY: distclean-escript escript
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
61966320
61976321 # Configuration.
61986322
61996323 ESCRIPT_NAME ?= $(PROJECT)
62006324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
62016325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
62026327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6203
6204 ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
6205 ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
6206 ESCRIPT_EMU_ARGS ?= -pa . \
6207 -sasl errlog_type error \
6208 -escript main $(ESCRIPT_NAME)
6209 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6210 ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
62116332
62126333 # Core targets.
62136334
62206341
62216342 # Plugin-specific targets.
62226343
6223 # Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
6224 # Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
6225 # Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
6226 # Software may only be used for the great good and the true happiness of all
6227 # sentient beings.
6228
6229 define ESCRIPT_RAW
6230 'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
6231 'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
6232 ' [F || F <- A, not filelib:is_dir(F) ] end,'\
6233 'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
6234 'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
6235 'Ez = fun(Escript) ->'\
6236 ' Static = Files([$(ESCRIPT_STATIC)]),'\
6237 ' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
6238 ' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
6239 ' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
6240 ' {archive, Archive, [memory]},'\
6241 ' {shebang, "$(ESCRIPT_SHEBANG)"},'\
6242 ' {comment, "$(ESCRIPT_COMMENT)"},'\
6243 ' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
6244 ' ]),'\
6245 ' file:change_mode(Escript, 8#755)'\
6246 'end,'\
6247 'Ez("$(ESCRIPT_FILE)"),'\
6248 'halt().'
6249 endef
6250
6251 ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
6252
6253 escript:: distclean-escript deps app
6254 $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
62556360
62566361 distclean-escript:
6257 $(gen_verbose) rm -f $(ESCRIPT_NAME)
6258
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
62596365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6260 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
62616366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
62626367
62636368 .PHONY: eunit apps-eunit
63216426
63226427 ifneq ($(ALL_APPS_DIRS),)
63236428 apps-eunit:
6324 $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done
6325 endif
6326 endif
6327
6328 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
63296436 # This file is part of erlang.mk and subject to the terms of the ISC License.
63306437
6331 .PHONY: relx-rel distclean-relx-rel distclean-relx run
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
63326439
63336440 # Configuration.
63346441
6335 RELX ?= $(CURDIR)/relx
6442 RELX ?= $(ERLANG_MK_TMP)/relx
63366443 RELX_CONFIG ?= $(CURDIR)/relx.config
63376444
63386445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
63396446 RELX_OPTS ?=
63406447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
63416454
63426455 ifeq ($(firstword $(RELX_OPTS)),-o)
63436456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
63506463 ifeq ($(IS_DEP),)
63516464 ifneq ($(wildcard $(RELX_CONFIG)),)
63526465 rel:: relx-rel
6353 endif
6354 endif
6355
6356 distclean:: distclean-relx-rel distclean-relx
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
63576472
63586473 # Plugin-specific targets.
63596474
63626477 $(verbose) chmod +x $(RELX)
63636478
63646479 relx-rel: $(RELX) rel-deps app
6365 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
63666484
63676485 distclean-relx-rel:
63686486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6369
6370 distclean-relx:
6371 $(gen_verbose) rm -rf $(RELX)
63726487
63736488 # Run target.
63746489
63776492 else
63786493
63796494 define get_relx_release.erl
6380 {ok, Config} = file:consult("$(RELX_CONFIG)"),
6381 {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
6382 io:format("~s", [Name]),
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
63836504 halt(0).
63846505 endef
63856506
6386 RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
63876514
63886515 run: all
6389 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
63906517
63916518 help::
63926519 $(verbose) printf "%s\n" "" \
63956522
63966523 endif
63976524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
63986526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6399 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
64006527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
64016528
64026529 .PHONY: shell
64216548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
64226549
64236550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6424 $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
64256552
64266553 shell: build-shell-deps
64276554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
64286555
6429 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
64306571 # This file is part of erlang.mk and subject to the terms of the ISC License.
64316572
64326573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
64376578 tests:: triq
64386579
64396580 define triq_check.erl
6440 code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
64416582 try
64426583 case $(1) of
64436584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
64696610 endif
64706611 endif
64716612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
64726614 # Copyright (c) 2015, Erlang Solutions Ltd.
64736615 # This file is part of erlang.mk and subject to the terms of the ISC License.
64746616
64856627 XREFR ?= $(CURDIR)/xrefr
64866628 export XREFR
64876629
6488 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
64896631
64906632 # Core targets.
64916633
64926634 help::
6493 $(verbose) printf "%s\n" "" \
6494 "Xref targets:" \
6495 " xref Run Xrefr using $XREF_CONFIG as config file if defined"
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
64966638
64976639 distclean:: distclean-xref
64986640
65086650 distclean-xref:
65096651 $(gen_verbose) rm -rf $(XREFR)
65106652
6511 # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
65126655 # This file is part of erlang.mk and subject to the terms of the ISC License.
65136656
65146657 COVER_REPORT_DIR = cover
65176660
65186661 ifdef COVER
65196662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
65206664 # All modules in 'ebin'
65216665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
65226666
65316675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
65326676 endif
65336677 endif
6678 endif
65346679
65356680 # Core targets
65366681
65526697 "Cover targets:" \
65536698 " cover-report Generate a HTML coverage report from previously collected" \
65546699 " cover data." \
6555 " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
65566701 "" \
65576702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
65586703 "target tests additionally generates a HTML coverage report from the combined" \
65656710
65666711 .PHONY: coverdata-clean
65676712 coverdata-clean:
6568 $(gen_verbose) rm -f *.coverdata ct.cover.spec
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
65696714
65706715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
65716721 all.coverdata: $(COVERDATA)
6572 $(gen_verbose) $(ERL) -eval ' \
6573 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
6574 cover:export("$@"), halt(0).'
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
65756723
65766724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
65776725 # empty if you want the coverdata files but not the HTML report.
65896737 # Modules which include eunit.hrl always contain one line without coverage
65906738 # because eunit defines test/0 which is never called. We compensate for this.
65916739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6592 grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
65936741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
65946742
65956743 define cover_report.erl
66246772 endef
66256773
66266774 cover-report:
6627 $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
66286776 $(gen_verbose) $(call erlang,$(cover_report.erl))
66296777
66306778 endif
66316779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
66326839
66336840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
66346841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
66976904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
66986905 endif
66996906 ifndef IS_APP
6700 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
67016908 $(MAKE) -C $$dep $@ \
67026909 IS_APP=1 \
6703 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6704 || exit $$?; \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67056911 done
67066912 endif
6707 $(verbose) for dep in $^ ; do \
6913 $(verbose) set -e; for dep in $^ ; do \
67086914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
67096915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6710 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
67116917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
67126918 $(MAKE) -C $$dep fetch-deps \
67136919 IS_DEP=1 \
6714 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6715 || exit $$?; \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67166921 fi \
67176922 fi \
67186923 done
22 # before the inclusion of erlang.mk leading to the wrong target becoming
33 # the default.
44 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
526 endif
627
728 # --------------------------------------------------------------------
2041 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
2142 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
2243 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
2345 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
2446 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
2547 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
2648 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
2749 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
2851 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
2952 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
3054 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
3155 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
3256 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
3559 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
3660 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
3761 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
3863 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
3964 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
4065 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
7095
7196 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
7297
73 # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
74 # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
75 # needs to add "ranch" as a BUILD_DEPS. The list of projects needing
76 # this workaround are:
77 # o rabbitmq-web-stomp
78 dep_ranch = git https://github.com/ninenines/ranch 1.2.1
98 # Third-party dependencies version pinning.
99 #
100 # We do that in this file, which is copied in all projects, to ensure
101 # all projects use the same versions. It avoids conflicts and makes it
102 # possible to work with rabbitmq-public-umbrella.
103
104 dep_cowboy_commit = 1.0.4
105 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
106 dep_ranch_commit = 1.3.1
107 dep_webmachine_commit = 1.10.8p2
79108
80109 RABBITMQ_COMPONENTS = amqp_client \
81110 rabbit \
82111 rabbit_common \
83112 rabbitmq_amqp1_0 \
84113 rabbitmq_auth_backend_amqp \
114 rabbitmq_auth_backend_cache \
85115 rabbitmq_auth_backend_http \
86116 rabbitmq_auth_backend_ldap \
87117 rabbitmq_auth_mechanism_ssl \
88118 rabbitmq_boot_steps_visualiser \
89119 rabbitmq_clusterer \
120 rabbitmq_cli \
90121 rabbitmq_codegen \
91122 rabbitmq_consistent_hash_exchange \
123 rabbitmq_ct_client_helpers \
92124 rabbitmq_ct_helpers \
93125 rabbitmq_delayed_message_exchange \
94126 rabbitmq_dotnet_client \
97129 rabbitmq_federation_management \
98130 rabbitmq_java_client \
99131 rabbitmq_jms_client \
132 rabbitmq_jms_cts \
100133 rabbitmq_jms_topic_exchange \
101134 rabbitmq_lvc \
102135 rabbitmq_management \
+0
-168
deps/rabbitmq_amqp1_0/spec/messaging.xml less more
0 <?xml version="1.0"?>
1
2 <!--
3 Copyright Bank of America, N.A., Barclays Bank PLC, Cisco Systems, Credit
4 Suisse, Deutsche Boerse, Envoy Technologies Inc., Goldman Sachs, HCL
5 Technologies Ltd, IIT Software GmbH, iMatix Corporation, INETCO Systems Limited,
6 Informatica Corporation, JPMorgan Chase & Co., Kaazing Corporation, N.A,
7 Microsoft Corporation, my-Channels, Novell, Progress Software, Red Hat Inc.,
8 Software AG, Solace Systems Inc., StormMQ Ltd., Tervela Inc., TWIST Process
9 Innovations Ltd, GoPivotal, Inc., and WS02 Inc. 2006-2011. All rights reserved.
10
11 Redistribution and use in source and binary forms, with or without
12 modification, are permitted provided that the following conditions
13 are met:
14 1. Redistributions of source code must retain the above copyright
15 notice, this list of conditions and the following disclaimer.
16 2. Redistributions in binary form must reproduce the above copyright
17 notice, this list of conditions and the following disclaimer in the
18 documentation and/or other materials provided with the distribution.
19 3. The name of the author may not be used to endorse or promote products
20 derived from this software without specific prior written permission.
21
22 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 -->
33
34 <amqp name="messaging" xmlns="http://www.amqp.org/schema/amqp.xsd">
35 <section name="message-format">
36 <type name="header" class="composite" source="list" provides="section">
37 <descriptor name="amqp:header:list" code="0x00000000:0x00000070"/>
38 <field name="durable" type="boolean"/>
39 <field name="priority" type="ubyte"/>
40 <field name="ttl" type="milliseconds"/>
41 <field name="first-acquirer" type="boolean"/>
42 <field name="delivery-count" type="uint"/>
43 </type>
44 <type name="delivery-annotations" class="restricted" source="annotations" provides="section">
45 <descriptor name="amqp:delivery-annotations:map" code="0x00000000:0x00000071"/>
46 </type>
47 <type name="message-annotations" class="restricted" source="annotations" provides="section">
48 <descriptor name="amqp:message-annotations:map" code="0x00000000:0x00000072"/>
49 </type>
50 <type name="properties" class="composite" source="list" provides="section">
51 <descriptor name="amqp:properties:list" code="0x00000000:0x00000073"/>
52 <field name="message-id" type="*" requires="message-id"/>
53 <field name="user-id" type="binary"/>
54 <field name="to" type="*" requires="address"/>
55 <field name="subject" type="string"/>
56 <field name="reply-to" type="*" requires="address"/>
57 <field name="correlation-id" type="*" requires="message-id"/>
58 <field name="content-type" type="symbol"/>
59 <field name="content-encoding" type="symbol"/>
60 <field name="absolute-expiry-time" type="timestamp"/>
61 <field name="creation-time" type="timestamp"/>
62 <field name="group-id" type="string"/>
63 <field name="group-sequence" type="sequence-no"/>
64 <field name="reply-to-group-id" type="string"/>
65 </type>
66 <type name="application-properties" class="restricted" source="map" provides="section">
67 <descriptor name="amqp:application-properties:map" code="0x00000000:0x00000074"/>
68 </type>
69 <type name="data" class="restricted" source="binary" provides="section">
70 <descriptor name="amqp:data:binary" code="0x00000000:0x00000075"/>
71 </type>
72 <type name="amqp-sequence" class="restricted" source="list" provides="section">
73 <descriptor name="amqp:amqp-sequence:list" code="0x00000000:0x00000076"/>
74 </type>
75 <type name="amqp-value" class="restricted" source="*" provides="section">
76 <descriptor name="amqp:amqp-value:*" code="0x00000000:0x00000077"/>
77 </type>
78 <type name="footer" class="restricted" source="annotations" provides="section">
79 <descriptor name="amqp:footer:map" code="0x00000000:0x00000078"/>
80 </type>
81 <type name="annotations" class="restricted" source="map"/>
82 <type name="message-id-ulong" class="restricted" source="ulong" provides="message-id"/>
83 <type name="message-id-uuid" class="restricted" source="uuid" provides="message-id"/>
84 <type name="message-id-binary" class="restricted" source="binary" provides="message-id"/>
85 <type name="message-id-string" class="restricted" source="string" provides="message-id"/>
86 <type name="address-string" class="restricted" source="string" provides="address"/>
87 <definition name="MESSAGE-FORMAT" value="0"/>
88 </section>
89 <section name="delivery-state">
90 <type name="received" class="composite" source="list" provides="delivery-state">
91 <descriptor name="amqp:received:list" code="0x00000000:0x00000023"/>
92 <field name="section-number" type="uint" mandatory="true"/>
93 <field name="section-offset" type="ulong" mandatory="true"/>
94 </type>
95 <type name="accepted" class="composite" source="list" provides="delivery-state, outcome">
96 <descriptor name="amqp:accepted:list" code="0x00000000:0x00000024"/>
97 </type>
98 <type name="rejected" class="composite" source="list" provides="delivery-state, outcome">
99 <descriptor name="amqp:rejected:list" code="0x00000000:0x00000025"/>
100 <field name="error" type="error"/>
101 </type>
102 <type name="released" class="composite" source="list" provides="delivery-state, outcome">
103 <descriptor name="amqp:released:list" code="0x00000000:0x00000026"/>
104 </type>
105 <type name="modified" class="composite" source="list" provides="delivery-state, outcome">
106 <descriptor name="amqp:modified:list" code="0x00000000:0x00000027"/>
107 <field name="delivery-failed" type="boolean"/>
108 <field name="undeliverable-here" type="boolean"/>
109 <field name="message-annotations" type="fields"/>
110 </type>
111 </section>
112 <section name="addressing">
113 <type name="source" class="composite" source="list" provides="source">
114 <descriptor name="amqp:source:list" code="0x00000000:0x00000028"/>
115 <field name="address" type="*" requires="address"/>
116 <field name="durable" type="terminus-durability" default="none"/>
117 <field name="expiry-policy" type="terminus-expiry-policy" default="session-end"/>
118 <field name="timeout" type="seconds" default="0"/>
119 <field name="dynamic" type="boolean" default="false"/>
120 <field name="dynamic-node-properties" type="node-properties"/>
121 <field name="distribution-mode" type="symbol" requires="distribution-mode"/>
122 <field name="filter" type="filter-set"/>
123 <field name="default-outcome" type="*" requires="outcome"/>
124 <field name="outcomes" type="symbol" multiple="true"/>
125 <field name="capabilities" type="symbol" multiple="true"/>
126 </type>
127 <type name="target" class="composite" source="list" provides="target">
128 <descriptor name="amqp:target:list" code="0x00000000:0x00000029"/>
129 <field name="address" type="*" requires="address"/>
130 <field name="durable" type="terminus-durability" default="none"/>
131 <field name="expiry-policy" type="terminus-expiry-policy" default="session-end"/>
132 <field name="timeout" type="seconds" default="0"/>
133 <field name="dynamic" type="boolean" default="false"/>
134 <field name="dynamic-node-properties" type="node-properties"/>
135 <field name="capabilities" type="symbol" multiple="true"/>
136 </type>
137 <type name="terminus-durability" class="restricted" source="uint">
138 <choice name="none" value="0"/>
139 <choice name="configuration" value="1"/>
140 <choice name="unsettled-state" value="2"/>
141 </type>
142 <type name="terminus-expiry-policy" class="restricted" source="symbol">
143 <choice name="link-detach" value="link-detach"/>
144 <choice name="session-end" value="session-end"/>
145 <choice name="connection-close" value="connection-close"/>
146 <choice name="never" value="never"/>
147 </type>
148 <type name="std-dist-mode" class="restricted" source="symbol" provides="distribution-mode">
149 <choice name="move" value="move"/>
150 <choice name="copy" value="copy"/>
151 </type>
152 <type name="filter-set" class="restricted" source="map"/>
153 <type name="node-properties" class="restricted" source="fields"/>
154 <type name="delete-on-close" class="composite" source="list" provides="lifetime-policy">
155 <descriptor name="amqp:delete-on-close:list" code="0x00000000:0x0000002b"/>
156 </type>
157 <type name="delete-on-no-links" class="composite" source="list" provides="lifetime-policy">
158 <descriptor name="amqp:delete-on-no-links:list" code="0x00000000:0x0000002c"/>
159 </type>
160 <type name="delete-on-no-messages" class="composite" source="list" provides="lifetime-policy">
161 <descriptor name="amqp:delete-on-no-messages:list" code="0x00000000:0x0000002d"/>
162 </type>
163 <type name="delete-on-no-links-or-messages" class="composite" source="list" provides="lifetime-policy">
164 <descriptor name="amqp:delete-on-no-links-or-messages:list" code="0x00000000:0x0000002e"/>
165 </type>
166 </section>
167 </amqp>
+0
-76
deps/rabbitmq_amqp1_0/spec/security.xml less more
0 <?xml version="1.0"?>
1
2 <!--
3 Copyright Bank of America, N.A., Barclays Bank PLC, Cisco Systems, Credit
4 Suisse, Deutsche Boerse, Envoy Technologies Inc., Goldman Sachs, HCL
5 Technologies Ltd, IIT Software GmbH, iMatix Corporation, INETCO Systems Limited,
6 Informatica Corporation, JPMorgan Chase & Co., Kaazing Corporation, N.A,
7 Microsoft Corporation, my-Channels, Novell, Progress Software, Red Hat Inc.,
8 Software AG, Solace Systems Inc., StormMQ Ltd., Tervela Inc., TWIST Process
9 Innovations Ltd, GoPivotal, Inc., and WS02 Inc. 2006-2011. All rights reserved.
10
11 Redistribution and use in source and binary forms, with or without
12 modification, are permitted provided that the following conditions
13 are met:
14 1. Redistributions of source code must retain the above copyright
15 notice, this list of conditions and the following disclaimer.
16 2. Redistributions in binary form must reproduce the above copyright
17 notice, this list of conditions and the following disclaimer in the
18 documentation and/or other materials provided with the distribution.
19 3. The name of the author may not be used to endorse or promote products
20 derived from this software without specific prior written permission.
21
22 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 -->
33
34 <amqp name="security" xmlns="http://www.amqp.org/schema/amqp.xsd">
35 <section name="tls">
36 <definition name="TLS-MAJOR" value="1"/>
37 <definition name="TLS-MINOR" value="0"/>
38 <definition name="TLS-REVISION" value="0"/>
39 </section>
40 <section name="sasl">
41 <type name="sasl-mechanisms" class="composite" source="list" provides="sasl-frame">
42 <descriptor name="amqp:sasl-mechanisms:list" code="0x00000000:0x00000040"/>
43 <field name="sasl-server-mechanisms" type="symbol" mandatory="true" multiple="true"/>
44 </type>
45 <type name="sasl-init" class="composite" source="list" provides="sasl-frame">
46 <descriptor name="amqp:sasl-init:list" code="0x00000000:0x00000041"/>
47 <field name="mechanism" type="symbol" mandatory="true"/>
48 <field name="initial-response" type="binary"/>
49 <field name="hostname" type="string"/>
50 </type>
51 <type name="sasl-challenge" class="composite" source="list" provides="sasl-frame">
52 <descriptor name="amqp:sasl-challenge:list" code="0x00000000:0x00000042"/>
53 <field name="challenge" type="binary" mandatory="true"/>
54 </type>
55 <type name="sasl-response" class="composite" source="list" provides="sasl-frame">
56 <descriptor name="amqp:sasl-response:list" code="0x00000000:0x00000043"/>
57 <field name="response" type="binary" mandatory="true"/>
58 </type>
59 <type name="sasl-outcome" class="composite" source="list" provides="sasl-frame">
60 <descriptor name="amqp:sasl-outcome:list" code="0x00000000:0x00000044"/>
61 <field name="code" type="sasl-code" mandatory="true"/>
62 <field name="additional-data" type="binary"/>
63 </type>
64 <type name="sasl-code" class="restricted" source="ubyte">
65 <choice name="ok" value="0"/>
66 <choice name="auth" value="1"/>
67 <choice name="sys" value="2"/>
68 <choice name="sys-perm" value="3"/>
69 <choice name="sys-temp" value="4"/>
70 </type>
71 <definition name="SASL-MAJOR" value="1"/>
72 <definition name="SASL-MINOR" value="0"/>
73 <definition name="SASL-REVISION" value="0"/>
74 </section>
75 </amqp>
+0
-73
deps/rabbitmq_amqp1_0/spec/transactions.xml less more
0 <?xml version="1.0"?>
1
2 <!--
3 Copyright Bank of America, N.A., Barclays Bank PLC, Cisco Systems, Credit
4 Suisse, Deutsche Boerse, Envoy Technologies Inc., Goldman Sachs, HCL
5 Technologies Ltd, IIT Software GmbH, iMatix Corporation, INETCO Systems Limited,
6 Informatica Corporation, JPMorgan Chase & Co., Kaazing Corporation, N.A,
7 Microsoft Corporation, my-Channels, Novell, Progress Software, Red Hat Inc.,
8 Software AG, Solace Systems Inc., StormMQ Ltd., Tervela Inc., TWIST Process
9 Innovations Ltd, GoPivotal, Inc., and WS02 Inc. 2006-2011. All rights reserved.
10
11 Redistribution and use in source and binary forms, with or without
12 modification, are permitted provided that the following conditions
13 are met:
14 1. Redistributions of source code must retain the above copyright
15 notice, this list of conditions and the following disclaimer.
16 2. Redistributions in binary form must reproduce the above copyright
17 notice, this list of conditions and the following disclaimer in the
18 documentation and/or other materials provided with the distribution.
19 3. The name of the author may not be used to endorse or promote products
20 derived from this software without specific prior written permission.
21
22 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 -->
33
34 <amqp name="transactions" xmlns="http://www.amqp.org/schema/amqp.xsd">
35 <section name="coordination">
36 <type name="coordinator" class="composite" source="list" provides="target">
37 <descriptor name="amqp:coordinator:list" code="0x00000000:0x00000030"/>
38 <field name="capabilities" type="symbol" requires="txn-capability" multiple="true"/>
39 </type>
40 <type name="declare" class="composite" source="list">
41 <descriptor name="amqp:declare:list" code="0x00000000:0x00000031"/>
42 <field name="global-id" type="*" requires="global-tx-id"/>
43 </type>
44 <type name="discharge" class="composite" source="list">
45 <descriptor name="amqp:discharge:list" code="0x00000000:0x00000032"/>
46 <field name="txn-id" type="*" mandatory="true" requires="txn-id"/>
47 <field name="fail" type="boolean"/>
48 </type>
49 <type name="transaction-id" class="restricted" source="binary" provides="txn-id"/>
50 <type name="declared" class="composite" source="list" provides="delivery-state, outcome">
51 <descriptor name="amqp:declared:list" code="0x00000000:0x00000033"/>
52 <field name="txn-id" type="*" mandatory="true" requires="txn-id"/>
53 </type>
54 <type name="transactional-state" class="composite" source="list" provides="delivery-state">
55 <descriptor name="amqp:transactional-state:list" code="0x00000000:0x00000034"/>
56 <field name="txn-id" type="*" mandatory="true" requires="txn-id"/>
57 <field name="outcome" type="*" requires="outcome"/>
58 </type>
59 <type name="txn-capability" class="restricted" source="symbol" provides="txn-capability">
60 <choice name="local-transactions" value="amqp:local-transactions"/>
61 <choice name="distributed-transactions" value="amqp:distributed-transactions"/>
62 <choice name="promotable-transactions" value="amqp:promotable-transactions"/>
63 <choice name="multi-txns-per-ssn" value="amqp:multi-txns-per-ssn"/>
64 <choice name="multi-ssns-per-txn" value="amqp:multi-ssns-per-txn"/>
65 </type>
66 <type name="transaction-error" class="restricted" source="symbol" provides="error-condition">
67 <choice name="unknown-id" value="amqp:transaction:unknown-id"/>
68 <choice name="transaction-rollback" value="amqp:transaction:rollback"/>
69 <choice name="transaction-timeout" value="amqp:transaction:timeout"/>
70 </type>
71 </section>
72 </amqp>
+0
-200
deps/rabbitmq_amqp1_0/spec/transport.xml less more
0 <?xml version="1.0"?>
1
2 <!--
3 Copyright Bank of America, N.A., Barclays Bank PLC, Cisco Systems, Credit
4 Suisse, Deutsche Boerse, Envoy Technologies Inc., Goldman Sachs, HCL
5 Technologies Ltd, IIT Software GmbH, iMatix Corporation, INETCO Systems Limited,
6 Informatica Corporation, JPMorgan Chase & Co., Kaazing Corporation, N.A,
7 Microsoft Corporation, my-Channels, Novell, Progress Software, Red Hat Inc.,
8 Software AG, Solace Systems Inc., StormMQ Ltd., Tervela Inc., TWIST Process
9 Innovations Ltd, GoPivotal, Inc., and WS02 Inc. 2006-2011. All rights reserved.
10
11 Redistribution and use in source and binary forms, with or without
12 modification, are permitted provided that the following conditions
13 are met:
14 1. Redistributions of source code must retain the above copyright
15 notice, this list of conditions and the following disclaimer.
16 2. Redistributions in binary form must reproduce the above copyright
17 notice, this list of conditions and the following disclaimer in the
18 documentation and/or other materials provided with the distribution.
19 3. The name of the author may not be used to endorse or promote products
20 derived from this software without specific prior written permission.
21
22 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 -->
33
34 <amqp name="transport" xmlns="http://www.amqp.org/schema/amqp.xsd">
35 <section name="performatives">
36 <type name="open" class="composite" source="list" provides="frame">
37 <descriptor name="amqp:open:list" code="0x00000000:0x00000010"/>
38 <field name="container-id" type="string" mandatory="true"/>
39 <field name="hostname" type="string"/>
40 <field name="max-frame-size" type="uint" default="4294967295"/>
41 <field name="channel-max" type="ushort" default="65535"/>
42 <field name="idle-time-out" type="milliseconds"/>
43 <field name="outgoing-locales" type="ietf-language-tag" multiple="true"/>
44 <field name="incoming-locales" type="ietf-language-tag" multiple="true"/>
45 <field name="offered-capabilities" type="symbol" multiple="true"/>
46 <field name="desired-capabilities" type="symbol" multiple="true"/>
47 <field name="properties" type="fields"/>
48 </type>
49 <type name="begin" class="composite" source="list" provides="frame">
50 <descriptor name="amqp:begin:list" code="0x00000000:0x00000011"/>
51 <field name="remote-channel" type="ushort"/>
52 <field name="next-outgoing-id" type="transfer-number" mandatory="true"/>
53 <field name="incoming-window" type="uint" mandatory="true"/>
54 <field name="outgoing-window" type="uint" mandatory="true"/>
55 <field name="handle-max" type="handle" default="4294967295"/>
56 <field name="offered-capabilities" type="symbol" multiple="true"/>
57 <field name="desired-capabilities" type="symbol" multiple="true"/>
58 <field name="properties" type="fields"/>
59 </type>
60 <type name="attach" class="composite" source="list" provides="frame">
61 <descriptor name="amqp:attach:list" code="0x00000000:0x00000012"/>
62 <field name="name" type="string" mandatory="true"/>
63 <field name="handle" type="handle" mandatory="true"/>
64 <field name="role" type="role" mandatory="true"/>
65 <field name="snd-settle-mode" type="sender-settle-mode" default="mixed"/>
66 <field name="rcv-settle-mode" type="receiver-settle-mode" default="first"/>
67 <field name="source" type="*" requires="source"/>
68 <field name="target" type="*" requires="target"/>
69 <field name="unsettled" type="map"/>
70 <field name="incomplete-unsettled" type="boolean" default="false"/>
71 <field name="initial-delivery-count" type="sequence-no"/>
72 <field name="max-message-size" type="ulong"/>
73 <field name="offered-capabilities" type="symbol" multiple="true"/>
74 <field name="desired-capabilities" type="symbol" multiple="true"/>
75 <field name="properties" type="fields"/>
76 </type>
77 <type name="flow" class="composite" source="list" provides="frame">
78 <descriptor name="amqp:flow:list" code="0x00000000:0x00000013"/>
79 <field name="next-incoming-id" type="transfer-number"/>
80 <field name="incoming-window" type="uint" mandatory="true"/>
81 <field name="next-outgoing-id" type="transfer-number" mandatory="true"/>
82 <field name="outgoing-window" type="uint" mandatory="true"/>
83 <field name="handle" type="handle"/>
84 <field name="delivery-count" type="sequence-no"/>
85 <field name="link-credit" type="uint"/>
86 <field name="available" type="uint"/>
87 <field name="drain" type="boolean" default="false"/>
88 <field name="echo" type="boolean" default="false"/>
89 <field name="properties" type="fields"/>
90 </type>
91 <type name="transfer" class="composite" source="list" provides="frame">
92 <descriptor name="amqp:transfer:list" code="0x00000000:0x00000014"/>
93 <field name="handle" type="handle" mandatory="true"/>
94 <field name="delivery-id" type="delivery-number"/>
95 <field name="delivery-tag" type="delivery-tag"/>
96 <field name="message-format" type="message-format"/>
97 <field name="settled" type="boolean"/>
98 <field name="more" type="boolean" default="false"/>
99 <field name="rcv-settle-mode" type="receiver-settle-mode"/>
100 <field name="state" type="*" requires="delivery-state"/>
101 <field name="resume" type="boolean" default="false"/>
102 <field name="aborted" type="boolean" default="false"/>
103 <field name="batchable" type="boolean" default="false"/>
104 </type>
105 <type name="disposition" class="composite" source="list" provides="frame">
106 <descriptor name="amqp:disposition:list" code="0x00000000:0x00000015"/>
107 <field name="role" type="role" mandatory="true"/>
108 <field name="first" type="delivery-number" mandatory="true"/>
109 <field name="last" type="delivery-number"/>
110 <field name="settled" type="boolean" default="false"/>
111 <field name="state" type="*" requires="delivery-state"/>
112 <field name="batchable" type="boolean" default="false"/>
113 </type>
114 <type name="detach" class="composite" source="list" provides="frame">
115 <descriptor name="amqp:detach:list" code="0x00000000:0x00000016"/>
116 <field name="handle" type="handle" mandatory="true"/>
117 <field name="closed" type="boolean" default="false"/>
118 <field name="error" type="error"/>
119 </type>
120 <type name="end" class="composite" source="list" provides="frame">
121 <descriptor name="amqp:end:list" code="0x00000000:0x00000017"/>
122 <field name="error" type="error"/>
123 </type>
124 <type name="close" class="composite" source="list" provides="frame">
125 <descriptor name="amqp:close:list" code="0x00000000:0x00000018"/>
126 <field name="error" type="error"/>
127 </type>
128 </section>
129 <section name="definitions">
130 <type name="role" class="restricted" source="boolean">
131 <choice name="sender" value="false"/>
132 <choice name="receiver" value="true"/>
133 </type>
134 <type name="sender-settle-mode" class="restricted" source="ubyte">
135 <choice name="unsettled" value="0"/>
136 <choice name="settled" value="1"/>
137 <choice name="mixed" value="2"/>
138 </type>
139 <type name="receiver-settle-mode" class="restricted" source="ubyte">
140 <choice name="first" value="0"/>
141 <choice name="second" value="1"/>
142 </type>
143 <type name="handle" class="restricted" source="uint"/>
144 <type name="seconds" class="restricted" source="uint"/>
145 <type name="milliseconds" class="restricted" source="uint"/>
146 <type name="delivery-tag" class="restricted" source="binary"/>
147 <type name="delivery-number" class="restricted" source="sequence-no"/>
148 <type name="transfer-number" class="restricted" source="sequence-no"/>
149 <type name="sequence-no" class="restricted" source="uint"/>
150 <type name="message-format" class="restricted" source="uint"/>
151 <type name="ietf-language-tag" class="restricted" source="symbol"/>
152 <type name="fields" class="restricted" source="map"/>
153 <type name="error" class="composite" source="list">
154 <descriptor name="amqp:error:list" code="0x00000000:0x0000001d"/>
155 <field name="condition" type="symbol" mandatory="true" requires="error-condition"/>
156 <field name="description" type="string"/>
157 <field name="info" type="fields"/>
158 </type>
159 <type name="amqp-error" class="restricted" source="symbol" provides="error-condition">
160 <choice name="internal-error" value="amqp:internal-error"/>
161 <choice name="not-found" value="amqp:not-found"/>
162 <choice name="unauthorized-access" value="amqp:unauthorized-access"/>
163 <choice name="decode-error" value="amqp:decode-error"/>
164 <choice name="resource-limit-exceeded" value="amqp:resource-limit-exceeded"/>
165 <choice name="not-allowed" value="amqp:not-allowed"/>
166 <choice name="invalid-field" value="amqp:invalid-field"/>
167 <choice name="not-implemented" value="amqp:not-implemented"/>
168 <choice name="resource-locked" value="amqp:resource-locked"/>
169 <choice name="precondition-failed" value="amqp:precondition-failed"/>
170 <choice name="resource-deleted" value="amqp:resource-deleted"/>
171 <choice name="illegal-state" value="amqp:illegal-state"/>
172 <choice name="frame-size-too-small" value="amqp:frame-size-too-small"/>
173 </type>
174 <type name="connection-error" class="restricted" source="symbol" provides="error-condition">
175 <choice name="connection-forced" value="amqp:connection:forced"/>
176 <choice name="framing-error" value="amqp:connection:framing-error"/>
177 <choice name="redirect" value="amqp:connection:redirect"/>
178 </type>
179 <type name="session-error" class="restricted" source="symbol" provides="error-condition">
180 <choice name="window-violation" value="amqp:session:window-violation"/>
181 <choice name="errant-link" value="amqp:session:errant-link"/>
182 <choice name="handle-in-use" value="amqp:session:handle-in-use"/>
183 <choice name="unattached-handle" value="amqp:session:unattached-handle"/>
184 </type>
185 <type name="link-error" class="restricted" source="symbol" provides="error-condition">
186 <choice name="detach-forced" value="amqp:link:detach-forced"/>
187 <choice name="transfer-limit-exceeded" value="amqp:link:transfer-limit-exceeded"/>
188 <choice name="message-size-exceeded" value="amqp:link:message-size-exceeded"/>
189 <choice name="redirect" value="amqp:link:redirect"/>
190 <choice name="stolen" value="amqp:link:stolen"/>
191 </type>
192 <definition name="PORT" value="5672"/>
193 <definition name="SECURE-PORT" value="5671"/>
194 <definition name="MAJOR" value="1"/>
195 <definition name="MINOR" value="0"/>
196 <definition name="REVISION" value="0"/>
197 <definition name="MIN-MAX-FRAME-SIZE" value="512"/>
198 </section>
199 </amqp>
+0
-125
deps/rabbitmq_amqp1_0/spec/types.xml less more
0 <?xml version="1.0"?>
1
2 <!--
3 Copyright Bank of America, N.A., Barclays Bank PLC, Cisco Systems, Credit
4 Suisse, Deutsche Boerse, Envoy Technologies Inc., Goldman Sachs, HCL
5 Technologies Ltd, IIT Software GmbH, iMatix Corporation, INETCO Systems Limited,
6 Informatica Corporation, JPMorgan Chase & Co., Kaazing Corporation, N.A,
7 Microsoft Corporation, my-Channels, Novell, Progress Software, Red Hat Inc.,
8 Software AG, Solace Systems Inc., StormMQ Ltd., Tervela Inc., TWIST Process
9 Innovations Ltd, GoPivotal, Inc., and WS02 Inc. 2006-2011. All rights reserved.
10
11 Redistribution and use in source and binary forms, with or without
12 modification, are permitted provided that the following conditions
13 are met:
14 1. Redistributions of source code must retain the above copyright
15 notice, this list of conditions and the following disclaimer.
16 2. Redistributions in binary form must reproduce the above copyright
17 notice, this list of conditions and the following disclaimer in the
18 documentation and/or other materials provided with the distribution.
19 3. The name of the author may not be used to endorse or promote products
20 derived from this software without specific prior written permission.
21
22 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 -->
33
34 <amqp name="types" xmlns="http://www.amqp.org/schema/amqp.xsd">
35 <section name="encodings">
36 <type name="null" class="primitive">
37 <encoding code="0x40" category="fixed" width="0"/>
38 </type>
39 <type name="boolean" class="primitive">
40 <encoding code="0x56" category="fixed" width="1"/>
41 <encoding name="true" code="0x41" category="fixed" width="0"/>
42 <encoding name="false" code="0x42" category="fixed" width="0"/>
43 </type>
44 <type name="ubyte" class="primitive">
45 <encoding code="0x50" category="fixed" width="1"/>
46 </type>
47 <type name="ushort" class="primitive">
48 <encoding code="0x60" category="fixed" width="2"/>
49 </type>
50 <type name="uint" class="primitive">
51 <encoding code="0x70" category="fixed" width="4"/>
52 <encoding name="smalluint" code="0x52" category="fixed" width="1"/>
53 <encoding name="uint0" code="0x43" category="fixed" width="0"/>
54 </type>
55 <type name="ulong" class="primitive">
56 <encoding code="0x80" category="fixed" width="8"/>
57 <encoding name="smallulong" code="0x53" category="fixed" width="1"/>
58 <encoding name="ulong0" code="0x44" category="fixed" width="0"/>
59 </type>
60 <type name="byte" class="primitive">
61 <encoding code="0x51" category="fixed" width="1"/>
62 </type>
63 <type name="short" class="primitive">
64 <encoding code="0x61" category="fixed" width="2"/>
65 </type>
66 <type name="int" class="primitive">
67 <encoding code="0x71" category="fixed" width="4"/>
68 <encoding name="smallint" code="0x54" category="fixed" width="1"/>
69 </type>
70 <type name="long" class="primitive">
71 <encoding code="0x81" category="fixed" width="8"/>
72 <encoding name="smalllong" code="0x55" category="fixed" width="1"/>
73 </type>
74 <type name="float" class="primitive">
75 <encoding name="ieee-754" code="0x72" category="fixed" width="4"/>
76 </type>
77 <type name="double" class="primitive">
78 <encoding name="ieee-754" code="0x82" category="fixed" width="8"/>
79 </type>
80 <type name="decimal32" class="primitive">
81 <encoding name="ieee-754" code="0x74" category="fixed" width="4"/>
82 </type>
83 <type name="decimal64" class="primitive">
84 <encoding name="ieee-754" code="0x84" category="fixed" width="8"/>
85 </type>
86 <type name="decimal128" class="primitive">
87 <encoding name="ieee-754" code="0x94" category="fixed" width="16"/>
88 </type>
89 <type name="char" class="primitive">
90 <encoding name="utf32" code="0x73" category="fixed" width="4"/>
91 </type>
92 <type name="timestamp" class="primitive">
93 <encoding name="ms64" code="0x83" category="fixed" width="8"/>
94 </type>
95 <type name="uuid" class="primitive">
96 <encoding code="0x98" category="fixed" width="16"/>
97 </type>
98 <type name="binary" class="primitive">
99 <encoding name="vbin8" code="0xa0" category="variable" width="1"/>
100 <encoding name="vbin32" code="0xb0" category="variable" width="4"/>
101 </type>
102 <type name="string" class="primitive">
103 <encoding name="str8-utf8" code="0xa1" category="variable" width="1"/>
104 <encoding name="str32-utf8" code="0xb1" category="variable" width="4"/>
105 </type>
106 <type name="symbol" class="primitive">
107 <encoding name="sym8" code="0xa3" category="variable" width="1"/>
108 <encoding name="sym32" code="0xb3" category="variable" width="4"/>
109 </type>
110 <type name="list" class="primitive">
111 <encoding name="list0" code="0x45" category="fixed" width="0"/>
112 <encoding name="list8" code="0xc0" category="compound" width="1"/>
113 <encoding name="list32" code="0xd0" category="compound" width="4"/>
114 </type>
115 <type name="map" class="primitive">
116 <encoding name="map8" code="0xc1" category="compound" width="1"/>
117 <encoding name="map32" code="0xd1" category="compound" width="4"/>
118 </type>
119 <type name="array" class="primitive">
120 <encoding name="array8" code="0xe0" category="array" width="1"/>
121 <encoding name="array32" code="0xf0" category="array" width="4"/>
122 </type>
123 </section>
124 </amqp>
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_amqp1_0_binary_generator).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_amqp1_0_binary_parser).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_amqp1_0_channel).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_amqp1_0_framing).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_amqp1_0_incoming_link).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_amqp1_0_link_util).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_amqp1_0_message).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_amqp1_0_outgoing_link).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_amqp1_0_reader).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_amqp1_0_session).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_amqp1_0_session_process).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_amqp1_0_session_sup).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_amqp1_0_session_sup_sup).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_amqp1_0_util).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_amqp1_0_writer).
+0
-9
deps/rabbitmq_amqp1_0/src/rabbitmq_amqp1_0.app.src less more
0 {application, rabbitmq_amqp1_0,
1 [{description, "AMQP 1.0 support for RabbitMQ"},
2 {vsn, "3.6.6"},
3 {modules, []},
4 {registered, []},
5 {env, [{default_user, "guest"},
6 {default_vhost, <<"/">>},
7 {protocol_strict_mode, false}]},
8 {applications, [kernel, stdlib, rabbit_common, rabbit, amqp_client]}]}.
00 PROJECT = rabbitmq_auth_backend_ldap
1 PROJECT_DESCRIPTION = RabbitMQ LDAP Authentication Backend
2 PROJECT_MOD = rabbit_auth_backend_ldap_app
13
4 define PROJECT_ENV
5 [
6 {servers, undefined},
7 {user_dn_pattern, "$${username}"},
8 {dn_lookup_attribute, none},
9 {dn_lookup_base, none},
10 {group_lookup_base, none},
11 {dn_lookup_bind, as_user},
12 {other_bind, as_user},
13 {anon_auth, false},
14 {vhost_access_query, {constant, true}},
15 {resource_access_query, {constant, true}},
16 {tag_queries, [{administrator, {constant, false}}]},
17 {use_ssl, false},
18 {use_starttls, false},
19 {ssl_options, []},
20 {port, 389},
21 {timeout, infinity},
22 {log, false},
23 {pool_size, 64},
24 {idle_timeout, infinity}
25 ]
26 endef
27
28 LOCAL_DEPS = eldap
229 DEPS = rabbit_common rabbit
3 TEST_DEPS = ct_helper rabbitmq_ct_helpers amqp_client
30 TEST_DEPS = ct_helper rabbitmq_ct_helpers rabbitmq_ct_client_helpers amqp_client
431 dep_ct_helper = git https://github.com/extend/ct_helper.git master
532
33 DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
634 DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk
735
836 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
0 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
11 #
22 # Permission to use, copy, modify, and/or distribute this software for any
33 # purpose with or without fee is hereby granted, provided that the above
1111 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1212 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1313
14 .PHONY: all app apps deps search rel docs install-docs check tests clean distclean help erlang-mk
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
1515
1616 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17
18 ERLANG_MK_VERSION = 2.0.0-pre.2-144-g647ffd1
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
1931
2032 # Core configuration.
2133
2436
2537 PROJECT_VERSION ?= rolling
2638 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
2740
2841 # Verbosity.
2942
8497 rel::
8598 $(verbose) :
8699
100 relup:: deps app
101
87102 check:: tests
88103
89104 clean:: clean-crashdump
101116 help::
102117 $(verbose) printf "%s\n" \
103118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
104 "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
105120 "" \
106121 "Usage: [V=1] $(MAKE) [target]..." \
107122 "" \
149164 core_native_path = $1
150165 endif
151166
152 ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
153 define core_http_get
154 wget --no-check-certificate -O $(1) $(2)|| rm $(1)
155 endef
156 else
157 define core_http_get.erl
158 ssl:start(),
159 inets:start(),
160 case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
161 {ok, {{_, 200, _}, _, Body}} ->
162 case file:write_file("$(1)", Body) of
163 ok -> ok;
164 {error, R1} -> halt(R1)
165 end;
166 {error, R2} ->
167 halt(R2)
168 end,
169 halt(0).
170 endef
171
172 define core_http_get
173 $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
174 endef
175 endif
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
176168
177169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
178170
192184 ERLANG_MK_BUILD_CONFIG ?= build.config
193185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
194186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
195188 erlang-mk:
196189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
197190 ifdef ERLANG_MK_COMMIT
198191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
199192 endif
200193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
201 $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
202195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
203196 rm -rf $(ERLANG_MK_BUILD_DIR)
204197
285278 pkg_apns_repo = https://github.com/inaka/apns4erl
286279 pkg_apns_commit = master
287280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
288289 PACKAGES += azdht
289290 pkg_azdht_name = azdht
290291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
405406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
406407 pkg_bootstrap_commit = master
407408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
408417 PACKAGES += boss_db
409418 pkg_boss_db_name = boss_db
410419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
413422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
414423 pkg_boss_db_commit = master
415424
416 PACKAGES += boss
417 pkg_boss_name = boss
418 pkg_boss_description = Erlang web MVC, now featuring Comet
419 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
420 pkg_boss_fetch = git
421 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
422 pkg_boss_commit = master
423
424425 PACKAGES += brod
425426 pkg_brod_name = brod
426427 pkg_brod_description = Kafka client in Erlang
565566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
566567 pkg_cloudi_service_api_requests_commit = master
567568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
568585 PACKAGES += cloudi_service_db_cassandra_cql
569586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
570587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
573590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
574591 pkg_cloudi_service_db_cassandra_cql_commit = master
575592
576 PACKAGES += cloudi_service_db_cassandra
577 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
579 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
580 pkg_cloudi_service_db_cassandra_fetch = git
581 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
582 pkg_cloudi_service_db_cassandra_commit = master
583
584593 PACKAGES += cloudi_service_db_couchdb
585594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
586595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
637646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
638647 pkg_cloudi_service_db_tokyotyrant_commit = master
639648
640 PACKAGES += cloudi_service_db
641 pkg_cloudi_service_db_name = cloudi_service_db
642 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
643 pkg_cloudi_service_db_homepage = http://cloudi.org/
644 pkg_cloudi_service_db_fetch = git
645 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
646 pkg_cloudi_service_db_commit = master
647
648649 PACKAGES += cloudi_service_filesystem
649650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
650651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
10371038 pkg_edown_repo = https://github.com/uwiger/edown
10381039 pkg_edown_commit = master
10391040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
10401049 PACKAGES += eep_app
10411050 pkg_eep_app_name = eep_app
10421051 pkg_eep_app_description = Embedded Event Processing
10451054 pkg_eep_app_repo = https://github.com/darach/eep-erl
10461055 pkg_eep_app_commit = master
10471056
1048 PACKAGES += eep
1049 pkg_eep_name = eep
1050 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1051 pkg_eep_homepage = https://github.com/virtan/eep
1052 pkg_eep_fetch = git
1053 pkg_eep_repo = https://github.com/virtan/eep
1054 pkg_eep_commit = master
1055
10561057 PACKAGES += efene
10571058 pkg_efene_name = efene
10581059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
12371238 pkg_eqm_repo = https://github.com/loucash/eqm
12381239 pkg_eqm_commit = master
12391240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
12401249 PACKAGES += eredis_pool
12411250 pkg_eredis_pool_name = eredis_pool
12421251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
12451254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
12461255 pkg_eredis_pool_commit = master
12471256
1248 PACKAGES += eredis
1249 pkg_eredis_name = eredis
1250 pkg_eredis_description = Erlang Redis client
1251 pkg_eredis_homepage = https://github.com/wooga/eredis
1252 pkg_eredis_fetch = git
1253 pkg_eredis_repo = https://github.com/wooga/eredis
1254 pkg_eredis_commit = master
1255
12561257 PACKAGES += erl_streams
12571258 pkg_erl_streams_name = erl_streams
12581259 pkg_erl_streams_description = Streams in Erlang
15411542 pkg_etap_repo = https://github.com/ngerakines/etap
15421543 pkg_etap_commit = master
15431544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
15441553 PACKAGES += etest_http
15451554 pkg_etest_http_name = etest_http
15461555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
15491558 pkg_etest_http_repo = https://github.com/wooga/etest_http
15501559 pkg_etest_http_commit = master
15511560
1552 PACKAGES += etest
1553 pkg_etest_name = etest
1554 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1555 pkg_etest_homepage = https://github.com/wooga/etest
1556 pkg_etest_fetch = git
1557 pkg_etest_repo = https://github.com/wooga/etest
1558 pkg_etest_commit = master
1559
15601561 PACKAGES += etoml
15611562 pkg_etoml_name = etoml
15621563 pkg_etoml_description = TOML language erlang parser
15651566 pkg_etoml_repo = https://github.com/kalta/etoml
15661567 pkg_etoml_commit = master
15671568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
15681577 PACKAGES += eunit_formatters
15691578 pkg_eunit_formatters_name = eunit_formatters
15701579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
15731582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
15741583 pkg_eunit_formatters_commit = master
15751584
1576 PACKAGES += eunit
1577 pkg_eunit_name = eunit
1578 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1579 pkg_eunit_homepage = https://github.com/richcarl/eunit
1580 pkg_eunit_fetch = git
1581 pkg_eunit_repo = https://github.com/richcarl/eunit
1582 pkg_eunit_commit = master
1583
15841585 PACKAGES += euthanasia
15851586 pkg_euthanasia_name = euthanasia
15861587 pkg_euthanasia_description = Merciful killer for your Erlang processes
15981599 pkg_evum_commit = master
15991600
16001601 PACKAGES += exec
1601 pkg_exec_name = exec
1602 pkg_exec_name = erlexec
16021603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
16031604 pkg_exec_homepage = http://saleyn.github.com/erlexec
16041605 pkg_exec_fetch = git
17171718 pkg_fn_repo = https://github.com/reiddraper/fn
17181719 pkg_fn_commit = master
17191720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
17201729 PACKAGES += folsom_cowboy
17211730 pkg_folsom_cowboy_name = folsom_cowboy
17221731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
17251734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
17261735 pkg_folsom_cowboy_commit = master
17271736
1728 PACKAGES += folsom
1729 pkg_folsom_name = folsom
1730 pkg_folsom_description = Expose Erlang Events and Metrics
1731 pkg_folsom_homepage = https://github.com/boundary/folsom
1732 pkg_folsom_fetch = git
1733 pkg_folsom_repo = https://github.com/boundary/folsom
1734 pkg_folsom_commit = master
1735
17361737 PACKAGES += folsomite
17371738 pkg_folsomite_name = folsomite
17381739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
20932094 pkg_jesse_repo = https://github.com/for-GET/jesse
20942095 pkg_jesse_commit = master
20952096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
20962105 PACKAGES += jiffy_v
20972106 pkg_jiffy_v_name = jiffy_v
20982107 pkg_jiffy_v_description = JSON validation utility
21012110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
21022111 pkg_jiffy_v_commit = master
21032112
2104 PACKAGES += jiffy
2105 pkg_jiffy_name = jiffy
2106 pkg_jiffy_description = JSON NIFs for Erlang.
2107 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2108 pkg_jiffy_fetch = git
2109 pkg_jiffy_repo = https://github.com/davisp/jiffy
2110 pkg_jiffy_commit = master
2111
21122113 PACKAGES += jobs
21132114 pkg_jobs_name = jobs
21142115 pkg_jobs_description = a Job scheduler for load regulation
21252126 pkg_joxa_repo = https://github.com/joxa/joxa
21262127 pkg_joxa_commit = master
21272128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
21282137 PACKAGES += json_rec
21292138 pkg_json_rec_name = json_rec
21302139 pkg_json_rec_description = JSON to erlang record
21332142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
21342143 pkg_json_rec_commit = master
21352144
2136 PACKAGES += json
2137 pkg_json_name = json
2138 pkg_json_description = a high level json library for erlang (17.0+)
2139 pkg_json_homepage = https://github.com/talentdeficit/json
2140 pkg_json_fetch = git
2141 pkg_json_repo = https://github.com/talentdeficit/json
2142 pkg_json_commit = master
2143
21442145 PACKAGES += jsone
21452146 pkg_jsone_name = jsone
21462147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
21812182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
21822183 pkg_jsx_commit = master
21832184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
21842193 PACKAGES += kafka_protocol
21852194 pkg_kafka_protocol_name = kafka_protocol
21862195 pkg_kafka_protocol_description = Kafka protocol Erlang library
21892198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
21902199 pkg_kafka_protocol_commit = master
21912200
2192 PACKAGES += kafka
2193 pkg_kafka_name = kafka
2194 pkg_kafka_description = Kafka consumer and producer in Erlang
2195 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2196 pkg_kafka_fetch = git
2197 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2198 pkg_kafka_commit = master
2199
22002201 PACKAGES += kai
22012202 pkg_kai_name = kai
22022203 pkg_kai_description = DHT storage by Takeshi Inoue
22932294 pkg_kvs_repo = https://github.com/synrc/kvs
22942295 pkg_kvs_commit = master
22952296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
22962305 PACKAGES += lager_amqp_backend
22972306 pkg_lager_amqp_backend_name = lager_amqp_backend
22982307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
23092318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
23102319 pkg_lager_syslog_commit = master
23112320
2312 PACKAGES += lager
2313 pkg_lager_name = lager
2314 pkg_lager_description = A logging framework for Erlang/OTP.
2315 pkg_lager_homepage = https://github.com/basho/lager
2316 pkg_lager_fetch = git
2317 pkg_lager_repo = https://github.com/basho/lager
2318 pkg_lager_commit = master
2319
23202321 PACKAGES += lambdapad
23212322 pkg_lambdapad_name = lambdapad
23222323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
25732574 pkg_mixer_repo = https://github.com/chef/mixer
25742575 pkg_mixer_commit = master
25752576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
25762585 PACKAGES += mochiweb_xpath
25772586 pkg_mochiweb_xpath_name = mochiweb_xpath
25782587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
25812590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
25822591 pkg_mochiweb_xpath_commit = master
25832592
2584 PACKAGES += mochiweb
2585 pkg_mochiweb_name = mochiweb
2586 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2587 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2588 pkg_mochiweb_fetch = git
2589 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2590 pkg_mochiweb_commit = master
2591
25922593 PACKAGES += mockgyver
25932594 pkg_mockgyver_name = mockgyver
25942595 pkg_mockgyver_description = A mocking library for Erlang
30613062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
30623063 pkg_quickrand_commit = master
30633064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
30643073 PACKAGES += rabbit_exchange_type_riak
30653074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
30663075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
30693078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
30703079 pkg_rabbit_exchange_type_riak_commit = master
30713080
3072 PACKAGES += rabbit
3073 pkg_rabbit_name = rabbit
3074 pkg_rabbit_description = RabbitMQ Server
3075 pkg_rabbit_homepage = https://www.rabbitmq.com/
3076 pkg_rabbit_fetch = git
3077 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3078 pkg_rabbit_commit = master
3079
30803081 PACKAGES += rack
30813082 pkg_rack_name = rack
30823083 pkg_rack_description = Rack handler for erlang
34933494 pkg_smother_repo = https://github.com/ramsay-t/Smother
34943495 pkg_smother_commit = master
34953496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
34963505 PACKAGES += social
34973506 pkg_social_name = social
34983507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
35413550 pkg_stable_repo = https://github.com/dvv/stable
35423551 pkg_stable_commit = master
35433552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
35443561 PACKAGES += statebox_riak
35453562 pkg_statebox_riak_name = statebox_riak
35463563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
35493566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
35503567 pkg_statebox_riak_commit = master
35513568
3552 PACKAGES += statebox
3553 pkg_statebox_name = statebox
3554 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3555 pkg_statebox_homepage = https://github.com/mochi/statebox
3556 pkg_statebox_fetch = git
3557 pkg_statebox_repo = https://github.com/mochi/statebox
3558 pkg_statebox_commit = master
3559
35603569 PACKAGES += statman
35613570 pkg_statman_name = statman
35623571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
37253734 pkg_tirerl_repo = https://github.com/inaka/tirerl
37263735 pkg_tirerl_commit = master
37273736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
37283745 PACKAGES += traffic_tools
37293746 pkg_traffic_tools_name = traffic_tools
37303747 pkg_traffic_tools_description = Simple traffic limiting library
40614078 pkg_zucchini_repo = https://github.com/devinus/zucchini
40624079 pkg_zucchini_commit = master
40634080
4064 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
40654082 # This file is part of erlang.mk and subject to the terms of the ISC License.
40664083
40674084 .PHONY: search
40884105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
40894106 endif
40904107
4091 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
40924109 # This file is part of erlang.mk and subject to the terms of the ISC License.
40934110
4094 .PHONY: distclean-deps
4111 .PHONY: distclean-deps clean-tmp-deps.log
40954112
40964113 # Configuration.
40974114
41104127
41114128 REBAR_DEPS_DIR = $(DEPS_DIR)
41124129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
41134150
41144151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
41154152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
41164153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
41174154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
41184155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
41194157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
41204158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
41214159
41384176
41394177 # Core targets.
41404178
4141 ifdef IS_APP
4142 apps::
4143 else
4144 apps:: $(ALL_APPS_DIRS)
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
41454180 ifeq ($(IS_APP)$(IS_DEP),)
41464181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
41474182 endif
41494184 # Create ebin directory for all apps to make sure Erlang recognizes them
41504185 # as proper OTP applications when using -include_lib. This is a temporary
41514186 # fix, a proper fix would be to compile apps/* in the right order.
4152 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4153 mkdir -p $$dep/ebin || exit $$?; \
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
41544190 done
4155 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
41564196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
41574197 :; \
41584198 else \
41594199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4160 $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
4200 $(MAKE) -C $$dep IS_APP=1; \
41614201 fi \
41624202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
41634207 endif
41644208
41654209 ifneq ($(SKIP_DEPS),)
41664210 deps::
41674211 else
4168 deps:: $(ALL_DEPS_DIRS) apps
4169 ifeq ($(IS_APP)$(IS_DEP),)
4170 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4171 endif
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
41724213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4173 $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
41744215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
41754216 :; \
41764217 else \
41774218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
41784219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4179 $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
4220 $(MAKE) -C $$dep IS_DEP=1; \
41804221 else \
4181 echo "Error: No Makefile to build dependency $$dep."; \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
41824223 exit 2; \
41834224 fi \
41844225 fi \
41924233 # in practice only Makefile is needed so far.
41934234 define dep_autopatch
41944235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
41954237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
41964238 $(call dep_autopatch_erlang_mk,$(1)); \
41974239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4198 if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
41994243 $(call dep_autopatch2,$(1)); \
4200 elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
42014245 $(call dep_autopatch2,$(1)); \
4202 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
42034247 $(call dep_autopatch2,$(1)); \
4204 else \
4205 $(call erlang,$(call dep_autopatch_app.erl,$(1))); \
42064248 fi \
42074249 else \
42084250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
42144256 endef
42154257
42164258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
42174262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
42184263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
42194264 fi; \
42204265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4221 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
42224267 $(call dep_autopatch_fetch_rebar); \
42234268 $(call dep_autopatch_rebar,$(1)); \
42244269 else \
42304275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
42314276 endef
42324277
4233 # Overwrite erlang.mk with the current file by default.
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
42344280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
42354281 define dep_autopatch_erlang_mk
4236 echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \
4237 > $(DEPS_DIR)/$1/erlang.mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
42384287 endef
42394288 else
42404289 define dep_autopatch_erlang_mk
42534302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
42544303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
42554304 cd $(ERLANG_MK_TMP)/rebar; \
4256 git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
42574306 $(MAKE); \
42584307 cd -; \
42594308 fi
42704319 define dep_autopatch_rebar.erl
42714320 application:load(rebar),
42724321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
42734323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
42744324 {ok, Conf0} -> Conf0;
42754325 _ -> []
44234473 [] -> ok;
44244474 _ ->
44254475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4426 PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
44274477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4428 PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L \\"~s\\" -lerl_interface -lei\n",
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
44294479 [code:lib_dir(erl_interface, lib)])),
44304480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
44314481 FilterEnv = fun(Env) ->
44644514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44654515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44664516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4467 [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
44684518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
44694519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
44704520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
44764526 end,
44774527 [PortSpec(S) || S <- PortSpecs]
44784528 end,
4479 Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"),
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
44804530 RunPlugin = fun(Plugin, Step) ->
44814531 case erlang:function_exported(Plugin, Step, 2) of
44824532 false -> ok;
45244574 halt()
45254575 endef
45264576
4527 define dep_autopatch_app.erl
4528 UpdateModules = fun(App) ->
4529 case filelib:is_regular(App) of
4530 false -> ok;
4531 true ->
4532 {ok, [{application, '$(1)', L0}]} = file:consult(App),
4533 Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true,
4534 fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
4535 L = lists:keystore(modules, 1, L0, {modules, Mods}),
4536 ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}]))
4537 end
4538 end,
4539 UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"),
4540 halt()
4541 endef
4542
45434577 define dep_autopatch_appsrc_script.erl
45444578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
45454579 AppSrcScript = AppSrc ++ ".script",
45874621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
45884622 endef
45894623
4590 define dep_fetch_hex.erl
4591 ssl:start(),
4592 inets:start(),
4593 {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
4594 {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
4595 [], [{body_format, binary}]),
4596 {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
4597 {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
4598 ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
4599 halt()
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
46004626 endef
46014627
46024628 # Hex only has a package version. No need to look in the Erlang.mk packages.
46034629 define dep_fetch_hex
4604 $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
46054634 endef
46064635
46074636 define dep_fetch_fail
46314660 $(eval DEP_NAME := $(call dep_name,$1))
46324661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
46334662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4634 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
46354664 exit 17; \
46364665 fi
46374666 $(verbose) mkdir -p $(DEPS_DIR)
46734702 clean:: clean-apps
46744703
46754704 clean-apps:
4676 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4677 $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
46784707 done
46794708
46804709 distclean:: distclean-apps
46814710
46824711 distclean-apps:
4683 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4684 $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
46854714 done
46864715 endif
46874716
47014730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
47024731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
47034732
4704 # External plugins.
4705
4706 DEP_PLUGINS ?=
4707
4708 define core_dep_plugin
4709 -include $(DEPS_DIR)/$(1)
4710
4711 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4712 endef
4713
4714 $(foreach p,$(DEP_PLUGINS),\
4715 $(eval $(if $(findstring /,$p),\
4716 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4717 $(call core_dep_plugin,$p/plugins.mk,$p))))
4718
4719 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4720 # This file is part of erlang.mk and subject to the terms of the ISC License.
4721
4722 # Configuration.
4723
4724 DTL_FULL_PATH ?=
4725 DTL_PATH ?= templates/
4726 DTL_SUFFIX ?= _dtl
4727 DTL_OPTS ?=
4728
4729 # Verbosity.
4730
4731 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
4732 dtl_verbose = $(dtl_verbose_$(V))
4733
4734 # Core targets.
4735
4736 DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl))
4737
4738 ifneq ($(DTL_FILES),)
4739
4740 ifdef DTL_FULL_PATH
4741 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
4742 else
4743 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
4744 endif
4745
4746 ifneq ($(words $(DTL_FILES)),0)
4747 # Rebuild templates when the Makefile changes.
4748 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
4749 @mkdir -p $(ERLANG_MK_TMP)
4750 @if test -f $@; then \
4751 touch $(DTL_FILES); \
4752 fi
4753 @touch $@
4754
4755 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
4756 endif
4757
4758 define erlydtl_compile.erl
4759 [begin
4760 Module0 = case "$(strip $(DTL_FULL_PATH))" of
4761 "" ->
4762 filename:basename(F, ".dtl");
4763 _ ->
4764 "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
4765 re:replace(F2, "/", "_", [{return, list}, global])
4766 end,
4767 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
4768 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
4769 ok -> ok;
4770 {ok, _} -> ok
4771 end
4772 end || F <- string:tokens("$(1)", " ")],
4773 halt().
4774 endef
4775
4776 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
4777 $(if $(strip $?),\
4778 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
4779
4780 endif
4781
4782 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
47834734 # This file is part of erlang.mk and subject to the terms of the ISC License.
47844735
47854736 # Verbosity.
47984749
47994750 define compile_proto.erl
48004751 [begin
4801 Dir = filename:dirname(filename:dirname(F)),
48024752 protobuffs_compile:generate_source(F,
4803 [{output_include_dir, Dir ++ "/include"},
4804 {output_src_dir, Dir ++ "/ebin"}])
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
48054755 end || F <- string:tokens("$(1)", " ")],
48064756 halt().
48074757 endef
48114761 $(if $(strip $?),$(call compile_proto,$?))
48124762 endif
48134763
4814 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
48154765 # This file is part of erlang.mk and subject to the terms of the ISC License.
48164766
48174767 .PHONY: clean-app
48254775 ERLC_EXCLUDE ?=
48264776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
48274777
4778 ERLC_ASN1_OPTS ?=
4779
48284780 ERLC_MIB_OPTS ?=
48294781 COMPILE_MIB_FIRST ?=
48304782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
48744826
48754827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
48764828 define app_file
4877 {application, $(PROJECT), [
4829 {application, '$(PROJECT)', [
48784830 {description, "$(PROJECT_DESCRIPTION)"},
48794831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48804832 {id$(comma)$(space)"$(1)"}$(comma))
48814833 {modules, [$(call comma_list,$(2))]},
48824834 {registered, []},
4883 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48844837 ]}.
48854838 endef
48864839 else
48874840 define app_file
4888 {application, $(PROJECT), [
4841 {application, '$(PROJECT)', [
48894842 {description, "$(PROJECT_DESCRIPTION)"},
48904843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48914844 {id$(comma)$(space)"$(1)"}$(comma))
48924845 {modules, [$(call comma_list,$(2))]},
48934846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
48944847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4895 {mod, {$(PROJECT_MOD), []}}
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48964850 ]}.
48974851 endef
48984852 endif
49024856
49034857 # Source files.
49044858
4905 ERL_FILES = $(sort $(call core_find,src/,*.erl))
4906 CORE_FILES = $(sort $(call core_find,src/,*.core))
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
49074863
49084864 # ASN.1 files.
49094865
49134869
49144870 define compile_asn1
49154871 $(verbose) mkdir -p include/
4916 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
49174873 $(verbose) mv asn1/*.erl src/
49184874 $(verbose) mv asn1/*.hrl include/
49194875 $(verbose) mv asn1/*.asn1db include/
49364892
49374893 # Leex and Yecc files.
49384894
4939 XRL_FILES = $(sort $(call core_find,src/,*.xrl))
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
49404896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
49414897 ERL_FILES += $(XRL_ERL_FILES)
49424898
4943 YRL_FILES = $(sort $(call core_find,src/,*.yrl))
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
49444900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
49454901 ERL_FILES += $(YRL_ERL_FILES)
49464902
49474903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4948 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
49494905
49504906 # Erlang and Core Erlang files.
49514907
49954951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49964952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49974953 (F, Mod, import, {Imp, _}) ->
4998 case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
49994960 false -> ok;
50004961 true -> Add(Mod, Imp)
50014962 end;
50194980 end || F <- ErlFiles],
50204981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
50214982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
50224991 ok = file:write_file("$(1)", [
50234992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
5024 "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n"
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
50254994 ]),
50264995 halt()
50274996 endef
50345003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
50355004 # Rebuild everything when the Makefile changes.
50365005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5037 @mkdir -p $(ERLANG_MK_TMP)
5038 @if test -f $@; then \
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
50395008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
50405009 touch -c $(PROJECT).d; \
50415010 fi
5042 @touch $@
5011 $(verbose) touch $@
50435012
50445013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
50455014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
50465015 endif
50475016
5048 -include $(PROJECT).d
5017 include $(wildcard $(PROJECT).d)
50495018
50505019 ebin/$(PROJECT).app:: ebin/
50515020
50645033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
50655034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
50665035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5067 $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
50685037 > ebin/$(PROJECT).app
50695038 else
50705039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
50885057
50895058 endif
50905059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
50915061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
50925062 # This file is part of erlang.mk and subject to the terms of the ISC License.
50935063
51055075 doc-deps:
51065076 else
51075077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5108 $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5109 endif
5110
5111 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51125082 # This file is part of erlang.mk and subject to the terms of the ISC License.
51135083
51145084 .PHONY: rel-deps
51255095 rel-deps:
51265096 else
51275097 rel-deps: $(ALL_REL_DEPS_DIRS)
5128 $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5129 endif
5130
5131 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51325102 # This file is part of erlang.mk and subject to the terms of the ISC License.
51335103
51345104 .PHONY: test-deps test-dir test-build clean-test-dir
51505120 test-deps:
51515121 else
51525122 test-deps: $(ALL_TEST_DEPS_DIRS)
5153 $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
51545124 endif
51555125
51565126 ifneq ($(wildcard $(TEST_DIR)),)
51835153 endif
51845154 endif
51855155
5186 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51875157 # This file is part of erlang.mk and subject to the terms of the ISC License.
51885158
51895159 .PHONY: rebar.config
52195189 rebar.config:
52205190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
52215191
5222 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
52235193 # This file is part of erlang.mk and subject to the terms of the ISC License.
52245194
5225 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
5226
5227 MAN_INSTALL_PATH ?= /usr/local/share/man
5228 MAN_SECTIONS ?= 3 7
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
52295200
52305201 docs:: asciidoc
52315202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
52325207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
52335210
52345211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
52355212 asciidoc-guide:
52365213 else
5237 asciidoc-guide: distclean-asciidoc doc-deps
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
52385215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
52395216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5240 endif
5241
5242 ifeq ($(wildcard doc/src/manual/*.asciidoc),)
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
52435227 asciidoc-manual:
52445228 else
5245 asciidoc-manual: distclean-asciidoc doc-deps
5246 for f in doc/src/manual/*.asciidoc ; do \
5247 a2x -v -f manpage $$f ; \
5248 done
5249 for s in $(MAN_SECTIONS); do \
5250 mkdir -p doc/man$$s/ ; \
5251 mv doc/src/manual/*.$$s doc/man$$s/ ; \
5252 gzip doc/man$$s/*.$$s ; \
5253 done
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
52545262
52555263 install-docs:: install-asciidoc
52565264
52575265 install-asciidoc: asciidoc-manual
5258 for s in $(MAN_SECTIONS); do \
5259 mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
5260 install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
5261 done
5262 endif
5263
5264 distclean:: distclean-asciidoc
5265
5266 distclean-asciidoc:
5267 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
5268
5269 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
52705276 # This file is part of erlang.mk and subject to the terms of the ISC License.
52715277
52725278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
53235329 define bs_Makefile
53245330 PROJECT = $p
53255331 PROJECT_DESCRIPTION = New project
5326 PROJECT_VERSION = 0.0.1
5332 PROJECT_VERSION = 0.1.0
53275333
53285334 # Whitespace to be used when creating files from templates.
53295335 SP = $(SP)
53335339 define bs_Makefile
53345340 PROJECT = $p
53355341 PROJECT_DESCRIPTION = New project
5336 PROJECT_VERSION = 0.0.1
5342 PROJECT_VERSION = 0.1.0
53375343
53385344 endef
53395345 endif
53415347 define bs_apps_Makefile
53425348 PROJECT = $p
53435349 PROJECT_DESCRIPTION = New project
5344 PROJECT_VERSION = 0.0.1
5350 PROJECT_VERSION = 0.1.0
53455351
53465352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
53475353 endef
53615367 endef
53625368
53635369 define bs_relx_config
5364 {release, {$p_release, "1"}, [$p]}.
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
53655371 {extended_start_script, true}.
53665372 {sys_config, "rel/sys.config"}.
53675373 {vm_args, "rel/vm.args"}.
57125718 ifndef t
57135719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57145720 endif
5715 ifndef tpl_$(t)
5716 $(error Unknown template)
5717 endif
57185721 ifndef n
57195722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57205723 endif
57275730 list-templates:
57285731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
57295732
5730 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
57315734 # This file is part of erlang.mk and subject to the terms of the ISC License.
57325735
57335736 .PHONY: clean-c_src distclean-c_src-env
59625965 $(call render_template,bs_erl_nif,src/$n.erl)
59635966 endif
59645967
5965 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
59665969 # This file is part of erlang.mk and subject to the terms of the ISC License.
59675970
5968 .PHONY: ci ci-setup distclean-kerl
5969
5970 KERL ?= $(CURDIR)/kerl
5971 export KERL
5972
5973 KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
5974
5975 OTP_GIT ?= https://github.com/erlang/otp
5976
5977 CI_INSTALL_DIR ?= $(HOME)/erlang
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
59785973 CI_OTP ?=
5979
5980 ifeq ($(strip $(CI_OTP)),)
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
59815986 ci::
59825987 else
5983 ci:: $(addprefix ci-,$(CI_OTP))
5984
5985 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
59866007
59876008 ci-setup::
6009
6010 ci-extra::
59886011
59896012 ci_verbose_0 = @echo " CI " $(1);
59906013 ci_verbose = $(ci_verbose_$(V))
59916014
59926015 define ci_target
5993 ci-$(1): $(CI_INSTALL_DIR)/$(1)
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
59946018 $(ci_verbose) \
5995 PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
5996 CI_OTP_RELEASE="$(1)" \
5997 CT_OPTS="-label $(1)" \
5998 $(MAKE) clean ci-setup tests
5999 endef
6000
6001 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
60026030
60036031 define ci_otp_target
60046032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
60056033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6006 $(KERL) build git $(OTP_GIT) $(1) $(1)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
60076035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
60086036 endif
60096037 endef
60106038
60116039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
60126040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
60136052 $(KERL):
6014 $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
60156056 $(verbose) chmod +x $(KERL)
60166057
60176058 help::
60286069 $(gen_verbose) rm -rf $(KERL)
60296070 endif
60306071
6031 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
60326073 # This file is part of erlang.mk and subject to the terms of the ISC License.
60336074
60346075 .PHONY: ct apps-ct distclean-ct
60366077 # Configuration.
60376078
60386079 CT_OPTS ?=
6080
60396081 ifneq ($(wildcard $(TEST_DIR)),)
6040 CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6041 else
6042 CT_SUITES ?=
6043 endif
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
60446088
60456089 # Core targets.
60466090
60636107 -noinput \
60646108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
60656109 -dir $(TEST_DIR) \
6066 -logdir $(CURDIR)/logs
6110 -logdir $(CT_LOGS_DIR)
60676111
60686112 ifeq ($(CT_SUITES),)
60696113 ct: $(if $(IS_APP),,apps-ct)
60706114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
60716117 ct: test-build $(if $(IS_APP),,apps-ct)
6072 $(verbose) mkdir -p $(CURDIR)/logs/
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
60736119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
60746121 endif
60756122
60766123 ifneq ($(ALL_APPS_DIRS),)
60976144
60986145 define ct_suite_target
60996146 ct-$(1): test-build
6100 $(verbose) mkdir -p $(CURDIR)/logs/
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
61016148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
61026149 endef
61036150
61046151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
61056152
61066153 distclean-ct:
6107 $(gen_verbose) rm -rf $(CURDIR)/logs/
6108
6109 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61106157 # This file is part of erlang.mk and subject to the terms of the ISC License.
61116158
61126159 .PHONY: plt distclean-plt dialyze
61506197 endef
61516198
61526199 $(DIALYZER_PLT): deps app
6153 $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
61546204
61556205 plt: $(DIALYZER_PLT)
61566206
61646214 endif
61656215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
61666216
6167 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61686218 # This file is part of erlang.mk and subject to the terms of the ISC License.
61696219
61706220 .PHONY: distclean-edoc edoc
61726222 # Configuration.
61736223
61746224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
61756235
61766236 # Core targets.
61776237
6178 ifneq ($(wildcard doc/overview.edoc),)
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
61796239 docs:: edoc
61806240 endif
61816241
61846244 # Plugin-specific targets.
61856245
61866246 edoc: distclean-edoc doc-deps
6187 $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
61886248
61896249 distclean-edoc:
61906250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
61916251
6192 # Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61936253 # This file is part of erlang.mk and subject to the terms of the ISC License.
61946254
6195 .PHONY: distclean-escript escript
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
61966320
61976321 # Configuration.
61986322
61996323 ESCRIPT_NAME ?= $(PROJECT)
62006324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
62016325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
62026327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6203
6204 ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
6205 ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
6206 ESCRIPT_EMU_ARGS ?= -pa . \
6207 -sasl errlog_type error \
6208 -escript main $(ESCRIPT_NAME)
6209 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6210 ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
62116332
62126333 # Core targets.
62136334
62206341
62216342 # Plugin-specific targets.
62226343
6223 # Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
6224 # Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
6225 # Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
6226 # Software may only be used for the great good and the true happiness of all
6227 # sentient beings.
6228
6229 define ESCRIPT_RAW
6230 'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
6231 'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
6232 ' [F || F <- A, not filelib:is_dir(F) ] end,'\
6233 'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
6234 'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
6235 'Ez = fun(Escript) ->'\
6236 ' Static = Files([$(ESCRIPT_STATIC)]),'\
6237 ' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
6238 ' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
6239 ' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
6240 ' {archive, Archive, [memory]},'\
6241 ' {shebang, "$(ESCRIPT_SHEBANG)"},'\
6242 ' {comment, "$(ESCRIPT_COMMENT)"},'\
6243 ' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
6244 ' ]),'\
6245 ' file:change_mode(Escript, 8#755)'\
6246 'end,'\
6247 'Ez("$(ESCRIPT_FILE)"),'\
6248 'halt().'
6249 endef
6250
6251 ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
6252
6253 escript:: distclean-escript deps app
6254 $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
62556360
62566361 distclean-escript:
6257 $(gen_verbose) rm -f $(ESCRIPT_NAME)
6258
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
62596365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6260 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
62616366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
62626367
62636368 .PHONY: eunit apps-eunit
63216426
63226427 ifneq ($(ALL_APPS_DIRS),)
63236428 apps-eunit:
6324 $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done
6325 endif
6326 endif
6327
6328 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
63296436 # This file is part of erlang.mk and subject to the terms of the ISC License.
63306437
6331 .PHONY: relx-rel distclean-relx-rel distclean-relx run
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
63326439
63336440 # Configuration.
63346441
6335 RELX ?= $(CURDIR)/relx
6442 RELX ?= $(ERLANG_MK_TMP)/relx
63366443 RELX_CONFIG ?= $(CURDIR)/relx.config
63376444
63386445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
63396446 RELX_OPTS ?=
63406447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
63416454
63426455 ifeq ($(firstword $(RELX_OPTS)),-o)
63436456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
63506463 ifeq ($(IS_DEP),)
63516464 ifneq ($(wildcard $(RELX_CONFIG)),)
63526465 rel:: relx-rel
6353 endif
6354 endif
6355
6356 distclean:: distclean-relx-rel distclean-relx
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
63576472
63586473 # Plugin-specific targets.
63596474
63626477 $(verbose) chmod +x $(RELX)
63636478
63646479 relx-rel: $(RELX) rel-deps app
6365 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
63666484
63676485 distclean-relx-rel:
63686486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6369
6370 distclean-relx:
6371 $(gen_verbose) rm -rf $(RELX)
63726487
63736488 # Run target.
63746489
63776492 else
63786493
63796494 define get_relx_release.erl
6380 {ok, Config} = file:consult("$(RELX_CONFIG)"),
6381 {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
6382 io:format("~s", [Name]),
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
63836504 halt(0).
63846505 endef
63856506
6386 RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
63876514
63886515 run: all
6389 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
63906517
63916518 help::
63926519 $(verbose) printf "%s\n" "" \
63956522
63966523 endif
63976524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
63986526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6399 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
64006527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
64016528
64026529 .PHONY: shell
64216548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
64226549
64236550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6424 $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
64256552
64266553 shell: build-shell-deps
64276554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
64286555
6429 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
64306571 # This file is part of erlang.mk and subject to the terms of the ISC License.
64316572
64326573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
64376578 tests:: triq
64386579
64396580 define triq_check.erl
6440 code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
64416582 try
64426583 case $(1) of
64436584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
64696610 endif
64706611 endif
64716612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
64726614 # Copyright (c) 2015, Erlang Solutions Ltd.
64736615 # This file is part of erlang.mk and subject to the terms of the ISC License.
64746616
64856627 XREFR ?= $(CURDIR)/xrefr
64866628 export XREFR
64876629
6488 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
64896631
64906632 # Core targets.
64916633
64926634 help::
6493 $(verbose) printf "%s\n" "" \
6494 "Xref targets:" \
6495 " xref Run Xrefr using $XREF_CONFIG as config file if defined"
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
64966638
64976639 distclean:: distclean-xref
64986640
65086650 distclean-xref:
65096651 $(gen_verbose) rm -rf $(XREFR)
65106652
6511 # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
65126655 # This file is part of erlang.mk and subject to the terms of the ISC License.
65136656
65146657 COVER_REPORT_DIR = cover
65176660
65186661 ifdef COVER
65196662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
65206664 # All modules in 'ebin'
65216665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
65226666
65316675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
65326676 endif
65336677 endif
6678 endif
65346679
65356680 # Core targets
65366681
65526697 "Cover targets:" \
65536698 " cover-report Generate a HTML coverage report from previously collected" \
65546699 " cover data." \
6555 " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
65566701 "" \
65576702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
65586703 "target tests additionally generates a HTML coverage report from the combined" \
65656710
65666711 .PHONY: coverdata-clean
65676712 coverdata-clean:
6568 $(gen_verbose) rm -f *.coverdata ct.cover.spec
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
65696714
65706715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
65716721 all.coverdata: $(COVERDATA)
6572 $(gen_verbose) $(ERL) -eval ' \
6573 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
6574 cover:export("$@"), halt(0).'
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
65756723
65766724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
65776725 # empty if you want the coverdata files but not the HTML report.
65896737 # Modules which include eunit.hrl always contain one line without coverage
65906738 # because eunit defines test/0 which is never called. We compensate for this.
65916739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6592 grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
65936741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
65946742
65956743 define cover_report.erl
66246772 endef
66256773
66266774 cover-report:
6627 $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
66286776 $(gen_verbose) $(call erlang,$(cover_report.erl))
66296777
66306778 endif
66316779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
66326839
66336840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
66346841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
66976904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
66986905 endif
66996906 ifndef IS_APP
6700 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
67016908 $(MAKE) -C $$dep $@ \
67026909 IS_APP=1 \
6703 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6704 || exit $$?; \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67056911 done
67066912 endif
6707 $(verbose) for dep in $^ ; do \
6913 $(verbose) set -e; for dep in $^ ; do \
67086914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
67096915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6710 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
67116917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
67126918 $(MAKE) -C $$dep fetch-deps \
67136919 IS_DEP=1 \
6714 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6715 || exit $$?; \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67166921 fi \
67176922 fi \
67186923 done
22 # before the inclusion of erlang.mk leading to the wrong target becoming
33 # the default.
44 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
526 endif
627
728 # --------------------------------------------------------------------
2041 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
2142 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
2243 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
2345 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
2446 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
2547 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
2648 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
2749 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
2851 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
2952 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
3054 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
3155 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
3256 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
3559 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
3660 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
3761 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
3863 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
3964 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
4065 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
7095
7196 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
7297
73 # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
74 # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
75 # needs to add "ranch" as a BUILD_DEPS. The list of projects needing
76 # this workaround are:
77 # o rabbitmq-web-stomp
78 dep_ranch = git https://github.com/ninenines/ranch 1.2.1
98 # Third-party dependencies version pinning.
99 #
100 # We do that in this file, which is copied in all projects, to ensure
101 # all projects use the same versions. It avoids conflicts and makes it
102 # possible to work with rabbitmq-public-umbrella.
103
104 dep_cowboy_commit = 1.0.4
105 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
106 dep_ranch_commit = 1.3.1
107 dep_webmachine_commit = 1.10.8p2
79108
80109 RABBITMQ_COMPONENTS = amqp_client \
81110 rabbit \
82111 rabbit_common \
83112 rabbitmq_amqp1_0 \
84113 rabbitmq_auth_backend_amqp \
114 rabbitmq_auth_backend_cache \
85115 rabbitmq_auth_backend_http \
86116 rabbitmq_auth_backend_ldap \
87117 rabbitmq_auth_mechanism_ssl \
88118 rabbitmq_boot_steps_visualiser \
89119 rabbitmq_clusterer \
120 rabbitmq_cli \
90121 rabbitmq_codegen \
91122 rabbitmq_consistent_hash_exchange \
123 rabbitmq_ct_client_helpers \
92124 rabbitmq_ct_helpers \
93125 rabbitmq_delayed_message_exchange \
94126 rabbitmq_dotnet_client \
97129 rabbitmq_federation_management \
98130 rabbitmq_java_client \
99131 rabbitmq_jms_client \
132 rabbitmq_jms_cts \
100133 rabbitmq_jms_topic_exchange \
101134 rabbitmq_lvc \
102135 rabbitmq_management \
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_auth_backend_ldap).
2525
2626 -export([user_login_authentication/2, user_login_authorization/1,
2727 check_vhost_access/3, check_resource_access/3]).
28
29 -export([get_connections/0]).
30
31 %% for tests
32 -export([purge_connections/0]).
2833
2934 -define(L(F, A), log("LDAP " ++ F, A)).
3035 -define(L1(F, A), log(" LDAP " ++ F, A)).
3641 -record(impl, { user_dn, password }).
3742
3843 %%--------------------------------------------------------------------
44
45 get_connections() ->
46 worker_pool:submit(ldap_pool, fun() -> get(ldap_conns) end, reuse).
47
48 purge_connections() ->
49 [ok = worker_pool:submit(ldap_pool,
50 fun() -> purge_conn(Anon, Servers, Opts) end, reuse)
51 || {{Anon, Servers, Opts}, _} <- dict:to_list(get_connections())],
52 ok.
3953
4054 user_login_authentication(Username, []) ->
4155 %% Without password, e.g. EXTERNAL
202216 evaluate(StringQuery1, Args, User, LDAP),
203217 evaluate(StringQuery2, Args, User, LDAP));
204218
205 evaluate0({match, StringQuery, REQuery}, Args, User, LDAP) ->
219 evaluate0({match, {string, _} = StringQuery, {string, _} = REQuery}, Args, User, LDAP) ->
206220 safe_eval(fun (String1, String2) ->
207221 do_match(String1, String2)
208222 end,
209223 evaluate(StringQuery, Args, User, LDAP),
210224 evaluate(REQuery, Args, User, LDAP));
225
226 evaluate0({match, StringQuery, {string, _} = REQuery}, Args, User, LDAP) when is_list(StringQuery)->
227 safe_eval(fun (String1, String2) ->
228 do_match(String1, String2)
229 end,
230 evaluate(StringQuery, Args, User, LDAP),
231 evaluate(REQuery, Args, User, LDAP));
232
233 evaluate0({match, {string, _} = StringQuery, REQuery}, Args, User, LDAP) when is_list(REQuery) ->
234 safe_eval(fun (String1, String2) ->
235 do_match(String1, String2)
236 end,
237 evaluate(StringQuery, Args, User, LDAP),
238 evaluate(REQuery, Args, User, LDAP));
239
240 evaluate0({match, StringQuery, REQuery}, Args, User, LDAP) when is_list(StringQuery),
241 is_list(REQuery) ->
242 safe_eval(fun (String1, String2) ->
243 do_match(String1, String2)
244 end,
245 evaluate(StringQuery, Args, User, LDAP),
246 evaluate(REQuery, Args, User, LDAP));
247
248 evaluate0({match, StringQuery, REQuery}, Args, User, LDAP) ->
249 safe_eval(fun (String1, String2) ->
250 do_match_bidirectionally(String1, String2)
251 end,
252 evaluate(StringQuery, Args, User, LDAP),
253 evaluate(REQuery, Args, User, LDAP));
211254
212255 evaluate0(StringPattern, Args, User, LDAP) when is_list(StringPattern) ->
213256 evaluate0({string, StringPattern}, Args, User, LDAP);
271314 do_match(S1, S2) ->
272315 case re:run(S1, S2) of
273316 {match, _} -> log_match(S1, S2, R = true),
317 R;
318 nomatch -> log_match(S1, S2, R = false),
319 R
320 end.
321
322 do_match_bidirectionally(S1, S2) ->
323 case re:run(S1, S2) of
324 {match, _} -> log_match(S1, S2, R = true),
274325 R;
275326 nomatch ->
276327 %% Do match bidirectionally, if intial RE consists of
336387 %% to avoid rebinding if the connection is already bound as the user
337388 %% of interest, so this could still be more efficient.
338389 with_ldap({ok, Creds}, Fun, Servers) ->
339 Opts0 = [{port, env(port)}],
390 Opts0 = [{port, env(port)},
391 {idle_timeout, env(idle_timeout)},
392 {anon_auth, env(anon_auth)}],
340393 Opts1 = case env(log) of
341394 network ->
342395 Pre = " LDAP network traffic: ",
361414 infinity -> Opts1;
362415 MS -> [{timeout, MS} | Opts1]
363416 end,
417
364418 worker_pool:submit(
365419 ldap_pool,
366420 fun () ->
411465 end,
412466 Key = {IsAnon, Servers, Opts},
413467 case dict:find(Key, Conns) of
414 {ok, Conn} -> Conn;
468 {ok, Conn} ->
469 Timeout = rabbit_misc:pget(idle_timeout, Opts, infinity),
470 %% Defer the timeout by re-setting it.
471 set_connection_timeout(Key, Timeout),
472 {ok, Conn};
415473 error ->
416 case eldap_open(Servers, Opts) of
417 {ok, _} = Conn -> put(ldap_conns, dict:store(Key, Conn, Conns)), Conn;
474 {Timeout, EldapOpts} = case lists:keytake(idle_timeout, 1, Opts) of
475 false -> {infinity, Opts};
476 {value, {idle_timeout, T}, EOpts} -> {T, EOpts}
477 end,
478 case eldap_open(Servers, EldapOpts) of
479 {ok, Conn} ->
480 put(ldap_conns, dict:store(Key, Conn, Conns)),
481 set_connection_timeout(Key, Timeout),
482 {ok, Conn};
418483 Error -> Error
419484 end
420485 end.
486
487 set_connection_timeout(_, infinity) ->
488 ok;
489 set_connection_timeout(Key, Timeout) when is_integer(Timeout) ->
490 worker_pool_worker:set_timeout(Key, Timeout,
491 fun() ->
492 Conns = case get(ldap_conns) of
493 undefined -> dict:new();
494 Dict -> Dict
495 end,
496 case dict:find(Key, Conns) of
497 {ok, Conn} ->
498 eldap:close(Conn),
499 put(ldap_conns, dict:erase(Key, Conns));
500 _ -> ok
501 end
502 end).
421503
422504 %% Get attribute(s) from eldap entry
423505 get_attributes(_AttrName, []) -> {error, not_found};
445527 purge_conn(IsAnon, Servers, Opts) ->
446528 Conns = get(ldap_conns),
447529 Key = {IsAnon, Servers, Opts},
448 {_, {_, Conn}} = dict:find(Key, Conns),
530 {ok, Conn} = dict:find(Key, Conns),
449531 rabbit_log:warning("LDAP Purging an already closed LDAP server connection~n"),
450532 % We cannot close the connection with eldap:close/1 because as of OTP-13327
451533 % eldap will try to do_unbind first and will fail with a `{gen_tcp_error, closed}`.
453535 % kill its process.
454536 unlink(Conn),
455537 exit(Conn, closed),
456 put(ldap_conns, dict:erase(Key, Conns)).
538 put(ldap_conns, dict:erase(Key, Conns)),
539 ok.
457540
458541 eldap_open(Servers, Opts) ->
459542 case eldap:open(Servers, ssl_conf() ++ Opts) of
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_auth_backend_ldap_app).
2424
2525 -rabbit_boot_step({ldap_pool,
2626 [{description, "LDAP pool"},
27 {mfa, {?MODULE, create_ldap_pool, []}},
27 {mfa, {?MODULE, create_ldap_pool, []}},
2828 {requires, kernel_ready}]}).
2929
3030 create_ldap_pool() ->
4242 {ok, SSL} = application:get_env(rabbitmq_auth_backend_ldap, use_ssl),
4343 {ok, TLS} = application:get_env(rabbitmq_auth_backend_ldap, use_starttls),
4444 case SSL orelse TLS of
45 true -> rabbit_networking:ensure_ssl();
45 true ->
46 rabbit_networking:ensure_ssl(),
47 ok;
4648 false -> ok
4749 end,
4850 supervisor:start_link({local, ?MODULE}, ?MODULE, []).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_auth_backend_ldap_util).
+0
-25
deps/rabbitmq_auth_backend_ldap/src/rabbitmq_auth_backend_ldap.app.src less more
0 %% -*- erlang -*-
1 {application, rabbitmq_auth_backend_ldap,
2 [{description, "RabbitMQ LDAP Authentication Backend"},
3 {vsn, "3.6.6"},
4 {modules, []},
5 {registered, []},
6 {mod, {rabbit_auth_backend_ldap_app, []}},
7 {env, [ {servers, undefined},
8 {user_dn_pattern, "${username}"},
9 {dn_lookup_attribute, none},
10 {dn_lookup_base, none},
11 {group_lookup_base, none},
12 {dn_lookup_bind, as_user},
13 {other_bind, as_user},
14 {vhost_access_query, {constant, true}},
15 {resource_access_query, {constant, true}},
16 {tag_queries, [{administrator, {constant, false}}]},
17 {use_ssl, false},
18 {use_starttls, false},
19 {ssl_options, []},
20 {port, 3890},
21 {timeout, infinity},
22 {log, false},
23 {pool_size, 64} ] },
24 {applications, [kernel, stdlib, eldap, rabbit_common, rabbit]}]}.
00 PROJECT = rabbitmq_auth_mechanism_ssl
1 PROJECT_DESCRIPTION = RabbitMQ SSL authentication (SASL EXTERNAL)
2 PROJECT_MOD = rabbit_auth_mechanism_ssl_app
3
4 define PROJECT_ENV
5 [
6 {name_from, distinguished_name}
7 ]
8 endef
19
210 DEPS = rabbit_common rabbit
311
12 DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
413 DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk
514
615 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
0 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
11 #
22 # Permission to use, copy, modify, and/or distribute this software for any
33 # purpose with or without fee is hereby granted, provided that the above
1111 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1212 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1313
14 .PHONY: all app apps deps search rel docs install-docs check tests clean distclean help erlang-mk
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
1515
1616 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17
18 ERLANG_MK_VERSION = 2.0.0-pre.2-144-g647ffd1
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
1931
2032 # Core configuration.
2133
2436
2537 PROJECT_VERSION ?= rolling
2638 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
2740
2841 # Verbosity.
2942
8497 rel::
8598 $(verbose) :
8699
100 relup:: deps app
101
87102 check:: tests
88103
89104 clean:: clean-crashdump
101116 help::
102117 $(verbose) printf "%s\n" \
103118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
104 "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
105120 "" \
106121 "Usage: [V=1] $(MAKE) [target]..." \
107122 "" \
149164 core_native_path = $1
150165 endif
151166
152 ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
153 define core_http_get
154 wget --no-check-certificate -O $(1) $(2)|| rm $(1)
155 endef
156 else
157 define core_http_get.erl
158 ssl:start(),
159 inets:start(),
160 case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
161 {ok, {{_, 200, _}, _, Body}} ->
162 case file:write_file("$(1)", Body) of
163 ok -> ok;
164 {error, R1} -> halt(R1)
165 end;
166 {error, R2} ->
167 halt(R2)
168 end,
169 halt(0).
170 endef
171
172 define core_http_get
173 $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
174 endef
175 endif
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
176168
177169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
178170
192184 ERLANG_MK_BUILD_CONFIG ?= build.config
193185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
194186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
195188 erlang-mk:
196189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
197190 ifdef ERLANG_MK_COMMIT
198191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
199192 endif
200193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
201 $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
202195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
203196 rm -rf $(ERLANG_MK_BUILD_DIR)
204197
285278 pkg_apns_repo = https://github.com/inaka/apns4erl
286279 pkg_apns_commit = master
287280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
288289 PACKAGES += azdht
289290 pkg_azdht_name = azdht
290291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
405406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
406407 pkg_bootstrap_commit = master
407408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
408417 PACKAGES += boss_db
409418 pkg_boss_db_name = boss_db
410419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
413422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
414423 pkg_boss_db_commit = master
415424
416 PACKAGES += boss
417 pkg_boss_name = boss
418 pkg_boss_description = Erlang web MVC, now featuring Comet
419 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
420 pkg_boss_fetch = git
421 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
422 pkg_boss_commit = master
423
424425 PACKAGES += brod
425426 pkg_brod_name = brod
426427 pkg_brod_description = Kafka client in Erlang
565566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
566567 pkg_cloudi_service_api_requests_commit = master
567568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
568585 PACKAGES += cloudi_service_db_cassandra_cql
569586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
570587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
573590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
574591 pkg_cloudi_service_db_cassandra_cql_commit = master
575592
576 PACKAGES += cloudi_service_db_cassandra
577 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
579 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
580 pkg_cloudi_service_db_cassandra_fetch = git
581 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
582 pkg_cloudi_service_db_cassandra_commit = master
583
584593 PACKAGES += cloudi_service_db_couchdb
585594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
586595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
637646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
638647 pkg_cloudi_service_db_tokyotyrant_commit = master
639648
640 PACKAGES += cloudi_service_db
641 pkg_cloudi_service_db_name = cloudi_service_db
642 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
643 pkg_cloudi_service_db_homepage = http://cloudi.org/
644 pkg_cloudi_service_db_fetch = git
645 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
646 pkg_cloudi_service_db_commit = master
647
648649 PACKAGES += cloudi_service_filesystem
649650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
650651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
10371038 pkg_edown_repo = https://github.com/uwiger/edown
10381039 pkg_edown_commit = master
10391040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
10401049 PACKAGES += eep_app
10411050 pkg_eep_app_name = eep_app
10421051 pkg_eep_app_description = Embedded Event Processing
10451054 pkg_eep_app_repo = https://github.com/darach/eep-erl
10461055 pkg_eep_app_commit = master
10471056
1048 PACKAGES += eep
1049 pkg_eep_name = eep
1050 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1051 pkg_eep_homepage = https://github.com/virtan/eep
1052 pkg_eep_fetch = git
1053 pkg_eep_repo = https://github.com/virtan/eep
1054 pkg_eep_commit = master
1055
10561057 PACKAGES += efene
10571058 pkg_efene_name = efene
10581059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
12371238 pkg_eqm_repo = https://github.com/loucash/eqm
12381239 pkg_eqm_commit = master
12391240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
12401249 PACKAGES += eredis_pool
12411250 pkg_eredis_pool_name = eredis_pool
12421251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
12451254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
12461255 pkg_eredis_pool_commit = master
12471256
1248 PACKAGES += eredis
1249 pkg_eredis_name = eredis
1250 pkg_eredis_description = Erlang Redis client
1251 pkg_eredis_homepage = https://github.com/wooga/eredis
1252 pkg_eredis_fetch = git
1253 pkg_eredis_repo = https://github.com/wooga/eredis
1254 pkg_eredis_commit = master
1255
12561257 PACKAGES += erl_streams
12571258 pkg_erl_streams_name = erl_streams
12581259 pkg_erl_streams_description = Streams in Erlang
15411542 pkg_etap_repo = https://github.com/ngerakines/etap
15421543 pkg_etap_commit = master
15431544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
15441553 PACKAGES += etest_http
15451554 pkg_etest_http_name = etest_http
15461555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
15491558 pkg_etest_http_repo = https://github.com/wooga/etest_http
15501559 pkg_etest_http_commit = master
15511560
1552 PACKAGES += etest
1553 pkg_etest_name = etest
1554 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1555 pkg_etest_homepage = https://github.com/wooga/etest
1556 pkg_etest_fetch = git
1557 pkg_etest_repo = https://github.com/wooga/etest
1558 pkg_etest_commit = master
1559
15601561 PACKAGES += etoml
15611562 pkg_etoml_name = etoml
15621563 pkg_etoml_description = TOML language erlang parser
15651566 pkg_etoml_repo = https://github.com/kalta/etoml
15661567 pkg_etoml_commit = master
15671568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
15681577 PACKAGES += eunit_formatters
15691578 pkg_eunit_formatters_name = eunit_formatters
15701579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
15731582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
15741583 pkg_eunit_formatters_commit = master
15751584
1576 PACKAGES += eunit
1577 pkg_eunit_name = eunit
1578 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1579 pkg_eunit_homepage = https://github.com/richcarl/eunit
1580 pkg_eunit_fetch = git
1581 pkg_eunit_repo = https://github.com/richcarl/eunit
1582 pkg_eunit_commit = master
1583
15841585 PACKAGES += euthanasia
15851586 pkg_euthanasia_name = euthanasia
15861587 pkg_euthanasia_description = Merciful killer for your Erlang processes
15981599 pkg_evum_commit = master
15991600
16001601 PACKAGES += exec
1601 pkg_exec_name = exec
1602 pkg_exec_name = erlexec
16021603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
16031604 pkg_exec_homepage = http://saleyn.github.com/erlexec
16041605 pkg_exec_fetch = git
17171718 pkg_fn_repo = https://github.com/reiddraper/fn
17181719 pkg_fn_commit = master
17191720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
17201729 PACKAGES += folsom_cowboy
17211730 pkg_folsom_cowboy_name = folsom_cowboy
17221731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
17251734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
17261735 pkg_folsom_cowboy_commit = master
17271736
1728 PACKAGES += folsom
1729 pkg_folsom_name = folsom
1730 pkg_folsom_description = Expose Erlang Events and Metrics
1731 pkg_folsom_homepage = https://github.com/boundary/folsom
1732 pkg_folsom_fetch = git
1733 pkg_folsom_repo = https://github.com/boundary/folsom
1734 pkg_folsom_commit = master
1735
17361737 PACKAGES += folsomite
17371738 pkg_folsomite_name = folsomite
17381739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
20932094 pkg_jesse_repo = https://github.com/for-GET/jesse
20942095 pkg_jesse_commit = master
20952096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
20962105 PACKAGES += jiffy_v
20972106 pkg_jiffy_v_name = jiffy_v
20982107 pkg_jiffy_v_description = JSON validation utility
21012110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
21022111 pkg_jiffy_v_commit = master
21032112
2104 PACKAGES += jiffy
2105 pkg_jiffy_name = jiffy
2106 pkg_jiffy_description = JSON NIFs for Erlang.
2107 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2108 pkg_jiffy_fetch = git
2109 pkg_jiffy_repo = https://github.com/davisp/jiffy
2110 pkg_jiffy_commit = master
2111
21122113 PACKAGES += jobs
21132114 pkg_jobs_name = jobs
21142115 pkg_jobs_description = a Job scheduler for load regulation
21252126 pkg_joxa_repo = https://github.com/joxa/joxa
21262127 pkg_joxa_commit = master
21272128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
21282137 PACKAGES += json_rec
21292138 pkg_json_rec_name = json_rec
21302139 pkg_json_rec_description = JSON to erlang record
21332142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
21342143 pkg_json_rec_commit = master
21352144
2136 PACKAGES += json
2137 pkg_json_name = json
2138 pkg_json_description = a high level json library for erlang (17.0+)
2139 pkg_json_homepage = https://github.com/talentdeficit/json
2140 pkg_json_fetch = git
2141 pkg_json_repo = https://github.com/talentdeficit/json
2142 pkg_json_commit = master
2143
21442145 PACKAGES += jsone
21452146 pkg_jsone_name = jsone
21462147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
21812182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
21822183 pkg_jsx_commit = master
21832184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
21842193 PACKAGES += kafka_protocol
21852194 pkg_kafka_protocol_name = kafka_protocol
21862195 pkg_kafka_protocol_description = Kafka protocol Erlang library
21892198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
21902199 pkg_kafka_protocol_commit = master
21912200
2192 PACKAGES += kafka
2193 pkg_kafka_name = kafka
2194 pkg_kafka_description = Kafka consumer and producer in Erlang
2195 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2196 pkg_kafka_fetch = git
2197 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2198 pkg_kafka_commit = master
2199
22002201 PACKAGES += kai
22012202 pkg_kai_name = kai
22022203 pkg_kai_description = DHT storage by Takeshi Inoue
22932294 pkg_kvs_repo = https://github.com/synrc/kvs
22942295 pkg_kvs_commit = master
22952296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
22962305 PACKAGES += lager_amqp_backend
22972306 pkg_lager_amqp_backend_name = lager_amqp_backend
22982307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
23092318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
23102319 pkg_lager_syslog_commit = master
23112320
2312 PACKAGES += lager
2313 pkg_lager_name = lager
2314 pkg_lager_description = A logging framework for Erlang/OTP.
2315 pkg_lager_homepage = https://github.com/basho/lager
2316 pkg_lager_fetch = git
2317 pkg_lager_repo = https://github.com/basho/lager
2318 pkg_lager_commit = master
2319
23202321 PACKAGES += lambdapad
23212322 pkg_lambdapad_name = lambdapad
23222323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
25732574 pkg_mixer_repo = https://github.com/chef/mixer
25742575 pkg_mixer_commit = master
25752576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
25762585 PACKAGES += mochiweb_xpath
25772586 pkg_mochiweb_xpath_name = mochiweb_xpath
25782587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
25812590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
25822591 pkg_mochiweb_xpath_commit = master
25832592
2584 PACKAGES += mochiweb
2585 pkg_mochiweb_name = mochiweb
2586 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2587 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2588 pkg_mochiweb_fetch = git
2589 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2590 pkg_mochiweb_commit = master
2591
25922593 PACKAGES += mockgyver
25932594 pkg_mockgyver_name = mockgyver
25942595 pkg_mockgyver_description = A mocking library for Erlang
30613062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
30623063 pkg_quickrand_commit = master
30633064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
30643073 PACKAGES += rabbit_exchange_type_riak
30653074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
30663075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
30693078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
30703079 pkg_rabbit_exchange_type_riak_commit = master
30713080
3072 PACKAGES += rabbit
3073 pkg_rabbit_name = rabbit
3074 pkg_rabbit_description = RabbitMQ Server
3075 pkg_rabbit_homepage = https://www.rabbitmq.com/
3076 pkg_rabbit_fetch = git
3077 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3078 pkg_rabbit_commit = master
3079
30803081 PACKAGES += rack
30813082 pkg_rack_name = rack
30823083 pkg_rack_description = Rack handler for erlang
34933494 pkg_smother_repo = https://github.com/ramsay-t/Smother
34943495 pkg_smother_commit = master
34953496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
34963505 PACKAGES += social
34973506 pkg_social_name = social
34983507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
35413550 pkg_stable_repo = https://github.com/dvv/stable
35423551 pkg_stable_commit = master
35433552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
35443561 PACKAGES += statebox_riak
35453562 pkg_statebox_riak_name = statebox_riak
35463563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
35493566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
35503567 pkg_statebox_riak_commit = master
35513568
3552 PACKAGES += statebox
3553 pkg_statebox_name = statebox
3554 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3555 pkg_statebox_homepage = https://github.com/mochi/statebox
3556 pkg_statebox_fetch = git
3557 pkg_statebox_repo = https://github.com/mochi/statebox
3558 pkg_statebox_commit = master
3559
35603569 PACKAGES += statman
35613570 pkg_statman_name = statman
35623571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
37253734 pkg_tirerl_repo = https://github.com/inaka/tirerl
37263735 pkg_tirerl_commit = master
37273736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
37283745 PACKAGES += traffic_tools
37293746 pkg_traffic_tools_name = traffic_tools
37303747 pkg_traffic_tools_description = Simple traffic limiting library
40614078 pkg_zucchini_repo = https://github.com/devinus/zucchini
40624079 pkg_zucchini_commit = master
40634080
4064 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
40654082 # This file is part of erlang.mk and subject to the terms of the ISC License.
40664083
40674084 .PHONY: search
40884105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
40894106 endif
40904107
4091 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
40924109 # This file is part of erlang.mk and subject to the terms of the ISC License.
40934110
4094 .PHONY: distclean-deps
4111 .PHONY: distclean-deps clean-tmp-deps.log
40954112
40964113 # Configuration.
40974114
41104127
41114128 REBAR_DEPS_DIR = $(DEPS_DIR)
41124129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
41134150
41144151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
41154152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
41164153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
41174154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
41184155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
41194157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
41204158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
41214159
41384176
41394177 # Core targets.
41404178
4141 ifdef IS_APP
4142 apps::
4143 else
4144 apps:: $(ALL_APPS_DIRS)
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
41454180 ifeq ($(IS_APP)$(IS_DEP),)
41464181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
41474182 endif
41494184 # Create ebin directory for all apps to make sure Erlang recognizes them
41504185 # as proper OTP applications when using -include_lib. This is a temporary
41514186 # fix, a proper fix would be to compile apps/* in the right order.
4152 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4153 mkdir -p $$dep/ebin || exit $$?; \
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
41544190 done
4155 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
41564196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
41574197 :; \
41584198 else \
41594199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4160 $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
4200 $(MAKE) -C $$dep IS_APP=1; \
41614201 fi \
41624202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
41634207 endif
41644208
41654209 ifneq ($(SKIP_DEPS),)
41664210 deps::
41674211 else
4168 deps:: $(ALL_DEPS_DIRS) apps
4169 ifeq ($(IS_APP)$(IS_DEP),)
4170 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4171 endif
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
41724213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4173 $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
41744215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
41754216 :; \
41764217 else \
41774218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
41784219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4179 $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
4220 $(MAKE) -C $$dep IS_DEP=1; \
41804221 else \
4181 echo "Error: No Makefile to build dependency $$dep."; \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
41824223 exit 2; \
41834224 fi \
41844225 fi \
41924233 # in practice only Makefile is needed so far.
41934234 define dep_autopatch
41944235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
41954237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
41964238 $(call dep_autopatch_erlang_mk,$(1)); \
41974239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4198 if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
41994243 $(call dep_autopatch2,$(1)); \
4200 elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
42014245 $(call dep_autopatch2,$(1)); \
4202 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
42034247 $(call dep_autopatch2,$(1)); \
4204 else \
4205 $(call erlang,$(call dep_autopatch_app.erl,$(1))); \
42064248 fi \
42074249 else \
42084250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
42144256 endef
42154257
42164258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
42174262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
42184263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
42194264 fi; \
42204265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4221 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
42224267 $(call dep_autopatch_fetch_rebar); \
42234268 $(call dep_autopatch_rebar,$(1)); \
42244269 else \
42304275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
42314276 endef
42324277
4233 # Overwrite erlang.mk with the current file by default.
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
42344280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
42354281 define dep_autopatch_erlang_mk
4236 echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \
4237 > $(DEPS_DIR)/$1/erlang.mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
42384287 endef
42394288 else
42404289 define dep_autopatch_erlang_mk
42534302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
42544303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
42554304 cd $(ERLANG_MK_TMP)/rebar; \
4256 git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
42574306 $(MAKE); \
42584307 cd -; \
42594308 fi
42704319 define dep_autopatch_rebar.erl
42714320 application:load(rebar),
42724321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
42734323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
42744324 {ok, Conf0} -> Conf0;
42754325 _ -> []
44234473 [] -> ok;
44244474 _ ->
44254475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4426 PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
44274477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4428 PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L \\"~s\\" -lerl_interface -lei\n",
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
44294479 [code:lib_dir(erl_interface, lib)])),
44304480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
44314481 FilterEnv = fun(Env) ->
44644514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44654515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44664516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4467 [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
44684518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
44694519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
44704520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
44764526 end,
44774527 [PortSpec(S) || S <- PortSpecs]
44784528 end,
4479 Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"),
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
44804530 RunPlugin = fun(Plugin, Step) ->
44814531 case erlang:function_exported(Plugin, Step, 2) of
44824532 false -> ok;
45244574 halt()
45254575 endef
45264576
4527 define dep_autopatch_app.erl
4528 UpdateModules = fun(App) ->
4529 case filelib:is_regular(App) of
4530 false -> ok;
4531 true ->
4532 {ok, [{application, '$(1)', L0}]} = file:consult(App),
4533 Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true,
4534 fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
4535 L = lists:keystore(modules, 1, L0, {modules, Mods}),
4536 ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}]))
4537 end
4538 end,
4539 UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"),
4540 halt()
4541 endef
4542
45434577 define dep_autopatch_appsrc_script.erl
45444578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
45454579 AppSrcScript = AppSrc ++ ".script",
45874621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
45884622 endef
45894623
4590 define dep_fetch_hex.erl
4591 ssl:start(),
4592 inets:start(),
4593 {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
4594 {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
4595 [], [{body_format, binary}]),
4596 {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
4597 {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
4598 ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
4599 halt()
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
46004626 endef
46014627
46024628 # Hex only has a package version. No need to look in the Erlang.mk packages.
46034629 define dep_fetch_hex
4604 $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
46054634 endef
46064635
46074636 define dep_fetch_fail
46314660 $(eval DEP_NAME := $(call dep_name,$1))
46324661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
46334662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4634 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
46354664 exit 17; \
46364665 fi
46374666 $(verbose) mkdir -p $(DEPS_DIR)
46734702 clean:: clean-apps
46744703
46754704 clean-apps:
4676 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4677 $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
46784707 done
46794708
46804709 distclean:: distclean-apps
46814710
46824711 distclean-apps:
4683 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4684 $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
46854714 done
46864715 endif
46874716
47014730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
47024731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
47034732
4704 # External plugins.
4705
4706 DEP_PLUGINS ?=
4707
4708 define core_dep_plugin
4709 -include $(DEPS_DIR)/$(1)
4710
4711 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4712 endef
4713
4714 $(foreach p,$(DEP_PLUGINS),\
4715 $(eval $(if $(findstring /,$p),\
4716 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4717 $(call core_dep_plugin,$p/plugins.mk,$p))))
4718
4719 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4720 # This file is part of erlang.mk and subject to the terms of the ISC License.
4721
4722 # Configuration.
4723
4724 DTL_FULL_PATH ?=
4725 DTL_PATH ?= templates/
4726 DTL_SUFFIX ?= _dtl
4727 DTL_OPTS ?=
4728
4729 # Verbosity.
4730
4731 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
4732 dtl_verbose = $(dtl_verbose_$(V))
4733
4734 # Core targets.
4735
4736 DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl))
4737
4738 ifneq ($(DTL_FILES),)
4739
4740 ifdef DTL_FULL_PATH
4741 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
4742 else
4743 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
4744 endif
4745
4746 ifneq ($(words $(DTL_FILES)),0)
4747 # Rebuild templates when the Makefile changes.
4748 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
4749 @mkdir -p $(ERLANG_MK_TMP)
4750 @if test -f $@; then \
4751 touch $(DTL_FILES); \
4752 fi
4753 @touch $@
4754
4755 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
4756 endif
4757
4758 define erlydtl_compile.erl
4759 [begin
4760 Module0 = case "$(strip $(DTL_FULL_PATH))" of
4761 "" ->
4762 filename:basename(F, ".dtl");
4763 _ ->
4764 "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
4765 re:replace(F2, "/", "_", [{return, list}, global])
4766 end,
4767 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
4768 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
4769 ok -> ok;
4770 {ok, _} -> ok
4771 end
4772 end || F <- string:tokens("$(1)", " ")],
4773 halt().
4774 endef
4775
4776 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
4777 $(if $(strip $?),\
4778 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
4779
4780 endif
4781
4782 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
47834734 # This file is part of erlang.mk and subject to the terms of the ISC License.
47844735
47854736 # Verbosity.
47984749
47994750 define compile_proto.erl
48004751 [begin
4801 Dir = filename:dirname(filename:dirname(F)),
48024752 protobuffs_compile:generate_source(F,
4803 [{output_include_dir, Dir ++ "/include"},
4804 {output_src_dir, Dir ++ "/ebin"}])
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
48054755 end || F <- string:tokens("$(1)", " ")],
48064756 halt().
48074757 endef
48114761 $(if $(strip $?),$(call compile_proto,$?))
48124762 endif
48134763
4814 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
48154765 # This file is part of erlang.mk and subject to the terms of the ISC License.
48164766
48174767 .PHONY: clean-app
48254775 ERLC_EXCLUDE ?=
48264776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
48274777
4778 ERLC_ASN1_OPTS ?=
4779
48284780 ERLC_MIB_OPTS ?=
48294781 COMPILE_MIB_FIRST ?=
48304782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
48744826
48754827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
48764828 define app_file
4877 {application, $(PROJECT), [
4829 {application, '$(PROJECT)', [
48784830 {description, "$(PROJECT_DESCRIPTION)"},
48794831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48804832 {id$(comma)$(space)"$(1)"}$(comma))
48814833 {modules, [$(call comma_list,$(2))]},
48824834 {registered, []},
4883 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48844837 ]}.
48854838 endef
48864839 else
48874840 define app_file
4888 {application, $(PROJECT), [
4841 {application, '$(PROJECT)', [
48894842 {description, "$(PROJECT_DESCRIPTION)"},
48904843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48914844 {id$(comma)$(space)"$(1)"}$(comma))
48924845 {modules, [$(call comma_list,$(2))]},
48934846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
48944847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4895 {mod, {$(PROJECT_MOD), []}}
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48964850 ]}.
48974851 endef
48984852 endif
49024856
49034857 # Source files.
49044858
4905 ERL_FILES = $(sort $(call core_find,src/,*.erl))
4906 CORE_FILES = $(sort $(call core_find,src/,*.core))
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
49074863
49084864 # ASN.1 files.
49094865
49134869
49144870 define compile_asn1
49154871 $(verbose) mkdir -p include/
4916 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
49174873 $(verbose) mv asn1/*.erl src/
49184874 $(verbose) mv asn1/*.hrl include/
49194875 $(verbose) mv asn1/*.asn1db include/
49364892
49374893 # Leex and Yecc files.
49384894
4939 XRL_FILES = $(sort $(call core_find,src/,*.xrl))
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
49404896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
49414897 ERL_FILES += $(XRL_ERL_FILES)
49424898
4943 YRL_FILES = $(sort $(call core_find,src/,*.yrl))
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
49444900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
49454901 ERL_FILES += $(YRL_ERL_FILES)
49464902
49474903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4948 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
49494905
49504906 # Erlang and Core Erlang files.
49514907
49954951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49964952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49974953 (F, Mod, import, {Imp, _}) ->
4998 case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
49994960 false -> ok;
50004961 true -> Add(Mod, Imp)
50014962 end;
50194980 end || F <- ErlFiles],
50204981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
50214982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
50224991 ok = file:write_file("$(1)", [
50234992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
5024 "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n"
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
50254994 ]),
50264995 halt()
50274996 endef
50345003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
50355004 # Rebuild everything when the Makefile changes.
50365005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5037 @mkdir -p $(ERLANG_MK_TMP)
5038 @if test -f $@; then \
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
50395008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
50405009 touch -c $(PROJECT).d; \
50415010 fi
5042 @touch $@
5011 $(verbose) touch $@
50435012
50445013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
50455014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
50465015 endif
50475016
5048 -include $(PROJECT).d
5017 include $(wildcard $(PROJECT).d)
50495018
50505019 ebin/$(PROJECT).app:: ebin/
50515020
50645033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
50655034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
50665035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5067 $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
50685037 > ebin/$(PROJECT).app
50695038 else
50705039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
50885057
50895058 endif
50905059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
50915061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
50925062 # This file is part of erlang.mk and subject to the terms of the ISC License.
50935063
51055075 doc-deps:
51065076 else
51075077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5108 $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5109 endif
5110
5111 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51125082 # This file is part of erlang.mk and subject to the terms of the ISC License.
51135083
51145084 .PHONY: rel-deps
51255095 rel-deps:
51265096 else
51275097 rel-deps: $(ALL_REL_DEPS_DIRS)
5128 $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5129 endif
5130
5131 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51325102 # This file is part of erlang.mk and subject to the terms of the ISC License.
51335103
51345104 .PHONY: test-deps test-dir test-build clean-test-dir
51505120 test-deps:
51515121 else
51525122 test-deps: $(ALL_TEST_DEPS_DIRS)
5153 $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
51545124 endif
51555125
51565126 ifneq ($(wildcard $(TEST_DIR)),)
51835153 endif
51845154 endif
51855155
5186 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51875157 # This file is part of erlang.mk and subject to the terms of the ISC License.
51885158
51895159 .PHONY: rebar.config
52195189 rebar.config:
52205190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
52215191
5222 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
52235193 # This file is part of erlang.mk and subject to the terms of the ISC License.
52245194
5225 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
5226
5227 MAN_INSTALL_PATH ?= /usr/local/share/man
5228 MAN_SECTIONS ?= 3 7
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
52295200
52305201 docs:: asciidoc
52315202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
52325207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
52335210
52345211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
52355212 asciidoc-guide:
52365213 else
5237 asciidoc-guide: distclean-asciidoc doc-deps
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
52385215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
52395216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5240 endif
5241
5242 ifeq ($(wildcard doc/src/manual/*.asciidoc),)
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
52435227 asciidoc-manual:
52445228 else
5245 asciidoc-manual: distclean-asciidoc doc-deps
5246 for f in doc/src/manual/*.asciidoc ; do \
5247 a2x -v -f manpage $$f ; \
5248 done
5249 for s in $(MAN_SECTIONS); do \
5250 mkdir -p doc/man$$s/ ; \
5251 mv doc/src/manual/*.$$s doc/man$$s/ ; \
5252 gzip doc/man$$s/*.$$s ; \
5253 done
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
52545262
52555263 install-docs:: install-asciidoc
52565264
52575265 install-asciidoc: asciidoc-manual
5258 for s in $(MAN_SECTIONS); do \
5259 mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
5260 install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
5261 done
5262 endif
5263
5264 distclean:: distclean-asciidoc
5265
5266 distclean-asciidoc:
5267 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
5268
5269 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
52705276 # This file is part of erlang.mk and subject to the terms of the ISC License.
52715277
52725278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
53235329 define bs_Makefile
53245330 PROJECT = $p
53255331 PROJECT_DESCRIPTION = New project
5326 PROJECT_VERSION = 0.0.1
5332 PROJECT_VERSION = 0.1.0
53275333
53285334 # Whitespace to be used when creating files from templates.
53295335 SP = $(SP)
53335339 define bs_Makefile
53345340 PROJECT = $p
53355341 PROJECT_DESCRIPTION = New project
5336 PROJECT_VERSION = 0.0.1
5342 PROJECT_VERSION = 0.1.0
53375343
53385344 endef
53395345 endif
53415347 define bs_apps_Makefile
53425348 PROJECT = $p
53435349 PROJECT_DESCRIPTION = New project
5344 PROJECT_VERSION = 0.0.1
5350 PROJECT_VERSION = 0.1.0
53455351
53465352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
53475353 endef
53615367 endef
53625368
53635369 define bs_relx_config
5364 {release, {$p_release, "1"}, [$p]}.
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
53655371 {extended_start_script, true}.
53665372 {sys_config, "rel/sys.config"}.
53675373 {vm_args, "rel/vm.args"}.
57125718 ifndef t
57135719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57145720 endif
5715 ifndef tpl_$(t)
5716 $(error Unknown template)
5717 endif
57185721 ifndef n
57195722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57205723 endif
57275730 list-templates:
57285731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
57295732
5730 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
57315734 # This file is part of erlang.mk and subject to the terms of the ISC License.
57325735
57335736 .PHONY: clean-c_src distclean-c_src-env
59625965 $(call render_template,bs_erl_nif,src/$n.erl)
59635966 endif
59645967
5965 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
59665969 # This file is part of erlang.mk and subject to the terms of the ISC License.
59675970
5968 .PHONY: ci ci-setup distclean-kerl
5969
5970 KERL ?= $(CURDIR)/kerl
5971 export KERL
5972
5973 KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
5974
5975 OTP_GIT ?= https://github.com/erlang/otp
5976
5977 CI_INSTALL_DIR ?= $(HOME)/erlang
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
59785973 CI_OTP ?=
5979
5980 ifeq ($(strip $(CI_OTP)),)
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
59815986 ci::
59825987 else
5983 ci:: $(addprefix ci-,$(CI_OTP))
5984
5985 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
59866007
59876008 ci-setup::
6009
6010 ci-extra::
59886011
59896012 ci_verbose_0 = @echo " CI " $(1);
59906013 ci_verbose = $(ci_verbose_$(V))
59916014
59926015 define ci_target
5993 ci-$(1): $(CI_INSTALL_DIR)/$(1)
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
59946018 $(ci_verbose) \
5995 PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
5996 CI_OTP_RELEASE="$(1)" \
5997 CT_OPTS="-label $(1)" \
5998 $(MAKE) clean ci-setup tests
5999 endef
6000
6001 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
60026030
60036031 define ci_otp_target
60046032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
60056033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6006 $(KERL) build git $(OTP_GIT) $(1) $(1)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
60076035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
60086036 endif
60096037 endef
60106038
60116039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
60126040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
60136052 $(KERL):
6014 $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
60156056 $(verbose) chmod +x $(KERL)
60166057
60176058 help::
60286069 $(gen_verbose) rm -rf $(KERL)
60296070 endif
60306071
6031 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
60326073 # This file is part of erlang.mk and subject to the terms of the ISC License.
60336074
60346075 .PHONY: ct apps-ct distclean-ct
60366077 # Configuration.
60376078
60386079 CT_OPTS ?=
6080
60396081 ifneq ($(wildcard $(TEST_DIR)),)
6040 CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6041 else
6042 CT_SUITES ?=
6043 endif
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
60446088
60456089 # Core targets.
60466090
60636107 -noinput \
60646108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
60656109 -dir $(TEST_DIR) \
6066 -logdir $(CURDIR)/logs
6110 -logdir $(CT_LOGS_DIR)
60676111
60686112 ifeq ($(CT_SUITES),)
60696113 ct: $(if $(IS_APP),,apps-ct)
60706114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
60716117 ct: test-build $(if $(IS_APP),,apps-ct)
6072 $(verbose) mkdir -p $(CURDIR)/logs/
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
60736119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
60746121 endif
60756122
60766123 ifneq ($(ALL_APPS_DIRS),)
60976144
60986145 define ct_suite_target
60996146 ct-$(1): test-build
6100 $(verbose) mkdir -p $(CURDIR)/logs/
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
61016148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
61026149 endef
61036150
61046151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
61056152
61066153 distclean-ct:
6107 $(gen_verbose) rm -rf $(CURDIR)/logs/
6108
6109 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61106157 # This file is part of erlang.mk and subject to the terms of the ISC License.
61116158
61126159 .PHONY: plt distclean-plt dialyze
61506197 endef
61516198
61526199 $(DIALYZER_PLT): deps app
6153 $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
61546204
61556205 plt: $(DIALYZER_PLT)
61566206
61646214 endif
61656215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
61666216
6167 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61686218 # This file is part of erlang.mk and subject to the terms of the ISC License.
61696219
61706220 .PHONY: distclean-edoc edoc
61726222 # Configuration.
61736223
61746224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
61756235
61766236 # Core targets.
61776237
6178 ifneq ($(wildcard doc/overview.edoc),)
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
61796239 docs:: edoc
61806240 endif
61816241
61846244 # Plugin-specific targets.
61856245
61866246 edoc: distclean-edoc doc-deps
6187 $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
61886248
61896249 distclean-edoc:
61906250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
61916251
6192 # Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61936253 # This file is part of erlang.mk and subject to the terms of the ISC License.
61946254
6195 .PHONY: distclean-escript escript
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
61966320
61976321 # Configuration.
61986322
61996323 ESCRIPT_NAME ?= $(PROJECT)
62006324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
62016325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
62026327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6203
6204 ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
6205 ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
6206 ESCRIPT_EMU_ARGS ?= -pa . \
6207 -sasl errlog_type error \
6208 -escript main $(ESCRIPT_NAME)
6209 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6210 ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
62116332
62126333 # Core targets.
62136334
62206341
62216342 # Plugin-specific targets.
62226343
6223 # Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
6224 # Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
6225 # Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
6226 # Software may only be used for the great good and the true happiness of all
6227 # sentient beings.
6228
6229 define ESCRIPT_RAW
6230 'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
6231 'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
6232 ' [F || F <- A, not filelib:is_dir(F) ] end,'\
6233 'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
6234 'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
6235 'Ez = fun(Escript) ->'\
6236 ' Static = Files([$(ESCRIPT_STATIC)]),'\
6237 ' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
6238 ' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
6239 ' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
6240 ' {archive, Archive, [memory]},'\
6241 ' {shebang, "$(ESCRIPT_SHEBANG)"},'\
6242 ' {comment, "$(ESCRIPT_COMMENT)"},'\
6243 ' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
6244 ' ]),'\
6245 ' file:change_mode(Escript, 8#755)'\
6246 'end,'\
6247 'Ez("$(ESCRIPT_FILE)"),'\
6248 'halt().'
6249 endef
6250
6251 ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
6252
6253 escript:: distclean-escript deps app
6254 $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
62556360
62566361 distclean-escript:
6257 $(gen_verbose) rm -f $(ESCRIPT_NAME)
6258
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
62596365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6260 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
62616366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
62626367
62636368 .PHONY: eunit apps-eunit
63216426
63226427 ifneq ($(ALL_APPS_DIRS),)
63236428 apps-eunit:
6324 $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done
6325 endif
6326 endif
6327
6328 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
63296436 # This file is part of erlang.mk and subject to the terms of the ISC License.
63306437
6331 .PHONY: relx-rel distclean-relx-rel distclean-relx run
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
63326439
63336440 # Configuration.
63346441
6335 RELX ?= $(CURDIR)/relx
6442 RELX ?= $(ERLANG_MK_TMP)/relx
63366443 RELX_CONFIG ?= $(CURDIR)/relx.config
63376444
63386445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
63396446 RELX_OPTS ?=
63406447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
63416454
63426455 ifeq ($(firstword $(RELX_OPTS)),-o)
63436456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
63506463 ifeq ($(IS_DEP),)
63516464 ifneq ($(wildcard $(RELX_CONFIG)),)
63526465 rel:: relx-rel
6353 endif
6354 endif
6355
6356 distclean:: distclean-relx-rel distclean-relx
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
63576472
63586473 # Plugin-specific targets.
63596474
63626477 $(verbose) chmod +x $(RELX)
63636478
63646479 relx-rel: $(RELX) rel-deps app
6365 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
63666484
63676485 distclean-relx-rel:
63686486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6369
6370 distclean-relx:
6371 $(gen_verbose) rm -rf $(RELX)
63726487
63736488 # Run target.
63746489
63776492 else
63786493
63796494 define get_relx_release.erl
6380 {ok, Config} = file:consult("$(RELX_CONFIG)"),
6381 {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
6382 io:format("~s", [Name]),
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
63836504 halt(0).
63846505 endef
63856506
6386 RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
63876514
63886515 run: all
6389 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
63906517
63916518 help::
63926519 $(verbose) printf "%s\n" "" \
63956522
63966523 endif
63976524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
63986526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6399 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
64006527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
64016528
64026529 .PHONY: shell
64216548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
64226549
64236550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6424 $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
64256552
64266553 shell: build-shell-deps
64276554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
64286555
6429 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
64306571 # This file is part of erlang.mk and subject to the terms of the ISC License.
64316572
64326573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
64376578 tests:: triq
64386579
64396580 define triq_check.erl
6440 code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
64416582 try
64426583 case $(1) of
64436584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
64696610 endif
64706611 endif
64716612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
64726614 # Copyright (c) 2015, Erlang Solutions Ltd.
64736615 # This file is part of erlang.mk and subject to the terms of the ISC License.
64746616
64856627 XREFR ?= $(CURDIR)/xrefr
64866628 export XREFR
64876629
6488 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
64896631
64906632 # Core targets.
64916633
64926634 help::
6493 $(verbose) printf "%s\n" "" \
6494 "Xref targets:" \
6495 " xref Run Xrefr using $XREF_CONFIG as config file if defined"
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
64966638
64976639 distclean:: distclean-xref
64986640
65086650 distclean-xref:
65096651 $(gen_verbose) rm -rf $(XREFR)
65106652
6511 # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
65126655 # This file is part of erlang.mk and subject to the terms of the ISC License.
65136656
65146657 COVER_REPORT_DIR = cover
65176660
65186661 ifdef COVER
65196662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
65206664 # All modules in 'ebin'
65216665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
65226666
65316675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
65326676 endif
65336677 endif
6678 endif
65346679
65356680 # Core targets
65366681
65526697 "Cover targets:" \
65536698 " cover-report Generate a HTML coverage report from previously collected" \
65546699 " cover data." \
6555 " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
65566701 "" \
65576702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
65586703 "target tests additionally generates a HTML coverage report from the combined" \
65656710
65666711 .PHONY: coverdata-clean
65676712 coverdata-clean:
6568 $(gen_verbose) rm -f *.coverdata ct.cover.spec
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
65696714
65706715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
65716721 all.coverdata: $(COVERDATA)
6572 $(gen_verbose) $(ERL) -eval ' \
6573 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
6574 cover:export("$@"), halt(0).'
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
65756723
65766724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
65776725 # empty if you want the coverdata files but not the HTML report.
65896737 # Modules which include eunit.hrl always contain one line without coverage
65906738 # because eunit defines test/0 which is never called. We compensate for this.
65916739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6592 grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
65936741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
65946742
65956743 define cover_report.erl
66246772 endef
66256773
66266774 cover-report:
6627 $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
66286776 $(gen_verbose) $(call erlang,$(cover_report.erl))
66296777
66306778 endif
66316779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
66326839
66336840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
66346841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
66976904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
66986905 endif
66996906 ifndef IS_APP
6700 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
67016908 $(MAKE) -C $$dep $@ \
67026909 IS_APP=1 \
6703 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6704 || exit $$?; \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67056911 done
67066912 endif
6707 $(verbose) for dep in $^ ; do \
6913 $(verbose) set -e; for dep in $^ ; do \
67086914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
67096915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6710 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
67116917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
67126918 $(MAKE) -C $$dep fetch-deps \
67136919 IS_DEP=1 \
6714 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6715 || exit $$?; \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67166921 fi \
67176922 fi \
67186923 done
22 # before the inclusion of erlang.mk leading to the wrong target becoming
33 # the default.
44 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
526 endif
627
728 # --------------------------------------------------------------------
2041 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
2142 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
2243 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
2345 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
2446 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
2547 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
2648 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
2749 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
2851 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
2952 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
3054 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
3155 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
3256 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
3559 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
3660 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
3761 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
3863 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
3964 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
4065 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
7095
7196 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
7297
73 # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
74 # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
75 # needs to add "ranch" as a BUILD_DEPS. The list of projects needing
76 # this workaround are:
77 # o rabbitmq-web-stomp
78 dep_ranch = git https://github.com/ninenines/ranch 1.2.1
98 # Third-party dependencies version pinning.
99 #
100 # We do that in this file, which is copied in all projects, to ensure
101 # all projects use the same versions. It avoids conflicts and makes it
102 # possible to work with rabbitmq-public-umbrella.
103
104 dep_cowboy_commit = 1.0.4
105 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
106 dep_ranch_commit = 1.3.1
107 dep_webmachine_commit = 1.10.8p2
79108
80109 RABBITMQ_COMPONENTS = amqp_client \
81110 rabbit \
82111 rabbit_common \
83112 rabbitmq_amqp1_0 \
84113 rabbitmq_auth_backend_amqp \
114 rabbitmq_auth_backend_cache \
85115 rabbitmq_auth_backend_http \
86116 rabbitmq_auth_backend_ldap \
87117 rabbitmq_auth_mechanism_ssl \
88118 rabbitmq_boot_steps_visualiser \
89119 rabbitmq_clusterer \
120 rabbitmq_cli \
90121 rabbitmq_codegen \
91122 rabbitmq_consistent_hash_exchange \
123 rabbitmq_ct_client_helpers \
92124 rabbitmq_ct_helpers \
93125 rabbitmq_delayed_message_exchange \
94126 rabbitmq_dotnet_client \
97129 rabbitmq_federation_management \
98130 rabbitmq_java_client \
99131 rabbitmq_jms_client \
132 rabbitmq_jms_cts \
100133 rabbitmq_jms_topic_exchange \
101134 rabbitmq_lvc \
102135 rabbitmq_management \
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_auth_mechanism_ssl_app).
+0
-9
deps/rabbitmq_auth_mechanism_ssl/src/rabbitmq_auth_mechanism_ssl.app.src less more
0 %% -*- erlang -*-
1 {application, rabbitmq_auth_mechanism_ssl,
2 [{description, "RabbitMQ SSL authentication (SASL EXTERNAL)"},
3 {vsn, "3.6.6"},
4 {modules, []},
5 {registered, []},
6 {mod, {rabbit_auth_mechanism_ssl_app, []}},
7 {env, [{name_from, distinguished_name}] },
8 {applications, [kernel, stdlib, rabbit_common, rabbit]}]}.
0 <?xml version="1.0"?>
1
2 <!--
3 Copyright Bank of America, N.A., Barclays Bank PLC, Cisco Systems, Credit
4 Suisse, Deutsche Boerse, Envoy Technologies Inc., Goldman Sachs, HCL
5 Technologies Ltd, IIT Software GmbH, iMatix Corporation, INETCO Systems Limited,
6 Informatica Corporation, JPMorgan Chase & Co., Kaazing Corporation, N.A,
7 Microsoft Corporation, my-Channels, Novell, Progress Software, Red Hat Inc.,
8 Software AG, Solace Systems Inc., StormMQ Ltd., Tervela Inc., TWIST Process
9 Innovations Ltd, GoPivotal, Inc., and WS02 Inc. 2006-2011. All rights reserved.
10
11 Redistribution and use in source and binary forms, with or without
12 modification, are permitted provided that the following conditions
13 are met:
14 1. Redistributions of source code must retain the above copyright
15 notice, this list of conditions and the following disclaimer.
16 2. Redistributions in binary form must reproduce the above copyright
17 notice, this list of conditions and the following disclaimer in the
18 documentation and/or other materials provided with the distribution.
19 3. The name of the author may not be used to endorse or promote products
20 derived from this software without specific prior written permission.
21
22 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 -->
33
34 <amqp name="messaging" xmlns="http://www.amqp.org/schema/amqp.xsd">
35 <section name="message-format">
36 <type name="header" class="composite" source="list" provides="section">
37 <descriptor name="amqp:header:list" code="0x00000000:0x00000070"/>
38 <field name="durable" type="boolean"/>
39 <field name="priority" type="ubyte"/>
40 <field name="ttl" type="milliseconds"/>
41 <field name="first-acquirer" type="boolean"/>
42 <field name="delivery-count" type="uint"/>
43 </type>
44 <type name="delivery-annotations" class="restricted" source="annotations" provides="section">
45 <descriptor name="amqp:delivery-annotations:map" code="0x00000000:0x00000071"/>
46 </type>
47 <type name="message-annotations" class="restricted" source="annotations" provides="section">
48 <descriptor name="amqp:message-annotations:map" code="0x00000000:0x00000072"/>
49 </type>
50 <type name="properties" class="composite" source="list" provides="section">
51 <descriptor name="amqp:properties:list" code="0x00000000:0x00000073"/>
52 <field name="message-id" type="*" requires="message-id"/>
53 <field name="user-id" type="binary"/>
54 <field name="to" type="*" requires="address"/>
55 <field name="subject" type="string"/>
56 <field name="reply-to" type="*" requires="address"/>
57 <field name="correlation-id" type="*" requires="message-id"/>
58 <field name="content-type" type="symbol"/>
59 <field name="content-encoding" type="symbol"/>
60 <field name="absolute-expiry-time" type="timestamp"/>
61 <field name="creation-time" type="timestamp"/>
62 <field name="group-id" type="string"/>
63 <field name="group-sequence" type="sequence-no"/>
64 <field name="reply-to-group-id" type="string"/>
65 </type>
66 <type name="application-properties" class="restricted" source="map" provides="section">
67 <descriptor name="amqp:application-properties:map" code="0x00000000:0x00000074"/>
68 </type>
69 <type name="data" class="restricted" source="binary" provides="section">
70 <descriptor name="amqp:data:binary" code="0x00000000:0x00000075"/>
71 </type>
72 <type name="amqp-sequence" class="restricted" source="list" provides="section">
73 <descriptor name="amqp:amqp-sequence:list" code="0x00000000:0x00000076"/>
74 </type>
75 <type name="amqp-value" class="restricted" source="*" provides="section">
76 <descriptor name="amqp:amqp-value:*" code="0x00000000:0x00000077"/>
77 </type>
78 <type name="footer" class="restricted" source="annotations" provides="section">
79 <descriptor name="amqp:footer:map" code="0x00000000:0x00000078"/>
80 </type>
81 <type name="annotations" class="restricted" source="map"/>
82 <type name="message-id-ulong" class="restricted" source="ulong" provides="message-id"/>
83 <type name="message-id-uuid" class="restricted" source="uuid" provides="message-id"/>
84 <type name="message-id-binary" class="restricted" source="binary" provides="message-id"/>
85 <type name="message-id-string" class="restricted" source="string" provides="message-id"/>
86 <type name="address-string" class="restricted" source="string" provides="address"/>
87 <definition name="MESSAGE-FORMAT" value="0"/>
88 </section>
89 <section name="delivery-state">
90 <type name="received" class="composite" source="list" provides="delivery-state">
91 <descriptor name="amqp:received:list" code="0x00000000:0x00000023"/>
92 <field name="section-number" type="uint" mandatory="true"/>
93 <field name="section-offset" type="ulong" mandatory="true"/>
94 </type>
95 <type name="accepted" class="composite" source="list" provides="delivery-state, outcome">
96 <descriptor name="amqp:accepted:list" code="0x00000000:0x00000024"/>
97 </type>
98 <type name="rejected" class="composite" source="list" provides="delivery-state, outcome">
99 <descriptor name="amqp:rejected:list" code="0x00000000:0x00000025"/>
100 <field name="error" type="error"/>
101 </type>
102 <type name="released" class="composite" source="list" provides="delivery-state, outcome">
103 <descriptor name="amqp:released:list" code="0x00000000:0x00000026"/>
104 </type>
105 <type name="modified" class="composite" source="list" provides="delivery-state, outcome">
106 <descriptor name="amqp:modified:list" code="0x00000000:0x00000027"/>
107 <field name="delivery-failed" type="boolean"/>
108 <field name="undeliverable-here" type="boolean"/>
109 <field name="message-annotations" type="fields"/>
110 </type>
111 </section>
112 <section name="addressing">
113 <type name="source" class="composite" source="list" provides="source">
114 <descriptor name="amqp:source:list" code="0x00000000:0x00000028"/>
115 <field name="address" type="*" requires="address"/>
116 <field name="durable" type="terminus-durability" default="none"/>
117 <field name="expiry-policy" type="terminus-expiry-policy" default="session-end"/>
118 <field name="timeout" type="seconds" default="0"/>
119 <field name="dynamic" type="boolean" default="false"/>
120 <field name="dynamic-node-properties" type="node-properties"/>
121 <field name="distribution-mode" type="symbol" requires="distribution-mode"/>
122 <field name="filter" type="filter-set"/>
123 <field name="default-outcome" type="*" requires="outcome"/>
124 <field name="outcomes" type="symbol" multiple="true"/>
125 <field name="capabilities" type="symbol" multiple="true"/>
126 </type>
127 <type name="target" class="composite" source="list" provides="target">
128 <descriptor name="amqp:target:list" code="0x00000000:0x00000029"/>
129 <field name="address" type="*" requires="address"/>
130 <field name="durable" type="terminus-durability" default="none"/>
131 <field name="expiry-policy" type="terminus-expiry-policy" default="session-end"/>
132 <field name="timeout" type="seconds" default="0"/>
133 <field name="dynamic" type="boolean" default="false"/>
134 <field name="dynamic-node-properties" type="node-properties"/>
135 <field name="capabilities" type="symbol" multiple="true"/>
136 </type>
137 <type name="terminus-durability" class="restricted" source="uint">
138 <choice name="none" value="0"/>
139 <choice name="configuration" value="1"/>
140 <choice name="unsettled-state" value="2"/>
141 </type>
142 <type name="terminus-expiry-policy" class="restricted" source="symbol">
143 <choice name="link-detach" value="link-detach"/>
144 <choice name="session-end" value="session-end"/>
145 <choice name="connection-close" value="connection-close"/>
146 <choice name="never" value="never"/>
147 </type>
148 <type name="std-dist-mode" class="restricted" source="symbol" provides="distribution-mode">
149 <choice name="move" value="move"/>
150 <choice name="copy" value="copy"/>
151 </type>
152 <type name="filter-set" class="restricted" source="map"/>
153 <type name="node-properties" class="restricted" source="fields"/>
154 <type name="delete-on-close" class="composite" source="list" provides="lifetime-policy">
155 <descriptor name="amqp:delete-on-close:list" code="0x00000000:0x0000002b"/>
156 </type>
157 <type name="delete-on-no-links" class="composite" source="list" provides="lifetime-policy">
158 <descriptor name="amqp:delete-on-no-links:list" code="0x00000000:0x0000002c"/>
159 </type>
160 <type name="delete-on-no-messages" class="composite" source="list" provides="lifetime-policy">
161 <descriptor name="amqp:delete-on-no-messages:list" code="0x00000000:0x0000002d"/>
162 </type>
163 <type name="delete-on-no-links-or-messages" class="composite" source="list" provides="lifetime-policy">
164 <descriptor name="amqp:delete-on-no-links-or-messages:list" code="0x00000000:0x0000002e"/>
165 </type>
166 </section>
167 </amqp>
0 <?xml version="1.0"?>
1
2 <!--
3 Copyright Bank of America, N.A., Barclays Bank PLC, Cisco Systems, Credit
4 Suisse, Deutsche Boerse, Envoy Technologies Inc., Goldman Sachs, HCL
5 Technologies Ltd, IIT Software GmbH, iMatix Corporation, INETCO Systems Limited,
6 Informatica Corporation, JPMorgan Chase & Co., Kaazing Corporation, N.A,
7 Microsoft Corporation, my-Channels, Novell, Progress Software, Red Hat Inc.,
8 Software AG, Solace Systems Inc., StormMQ Ltd., Tervela Inc., TWIST Process
9 Innovations Ltd, GoPivotal, Inc., and WS02 Inc. 2006-2011. All rights reserved.
10
11 Redistribution and use in source and binary forms, with or without
12 modification, are permitted provided that the following conditions
13 are met:
14 1. Redistributions of source code must retain the above copyright
15 notice, this list of conditions and the following disclaimer.
16 2. Redistributions in binary form must reproduce the above copyright
17 notice, this list of conditions and the following disclaimer in the
18 documentation and/or other materials provided with the distribution.
19 3. The name of the author may not be used to endorse or promote products
20 derived from this software without specific prior written permission.
21
22 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 -->
33
34 <amqp name="security" xmlns="http://www.amqp.org/schema/amqp.xsd">
35 <section name="tls">
36 <definition name="TLS-MAJOR" value="1"/>
37 <definition name="TLS-MINOR" value="0"/>
38 <definition name="TLS-REVISION" value="0"/>
39 </section>
40 <section name="sasl">
41 <type name="sasl-mechanisms" class="composite" source="list" provides="sasl-frame">
42 <descriptor name="amqp:sasl-mechanisms:list" code="0x00000000:0x00000040"/>
43 <field name="sasl-server-mechanisms" type="symbol" mandatory="true" multiple="true"/>
44 </type>
45 <type name="sasl-init" class="composite" source="list" provides="sasl-frame">
46 <descriptor name="amqp:sasl-init:list" code="0x00000000:0x00000041"/>
47 <field name="mechanism" type="symbol" mandatory="true"/>
48 <field name="initial-response" type="binary"/>
49 <field name="hostname" type="string"/>
50 </type>
51 <type name="sasl-challenge" class="composite" source="list" provides="sasl-frame">
52 <descriptor name="amqp:sasl-challenge:list" code="0x00000000:0x00000042"/>
53 <field name="challenge" type="binary" mandatory="true"/>
54 </type>
55 <type name="sasl-response" class="composite" source="list" provides="sasl-frame">
56 <descriptor name="amqp:sasl-response:list" code="0x00000000:0x00000043"/>
57 <field name="response" type="binary" mandatory="true"/>
58 </type>
59 <type name="sasl-outcome" class="composite" source="list" provides="sasl-frame">
60 <descriptor name="amqp:sasl-outcome:list" code="0x00000000:0x00000044"/>
61 <field name="code" type="sasl-code" mandatory="true"/>
62 <field name="additional-data" type="binary"/>
63 </type>
64 <type name="sasl-code" class="restricted" source="ubyte">
65 <choice name="ok" value="0"/>
66 <choice name="auth" value="1"/>
67 <choice name="sys" value="2"/>
68 <choice name="sys-perm" value="3"/>
69 <choice name="sys-temp" value="4"/>
70 </type>
71 <definition name="SASL-MAJOR" value="1"/>
72 <definition name="SASL-MINOR" value="0"/>
73 <definition name="SASL-REVISION" value="0"/>
74 </section>
75 </amqp>
0 <?xml version="1.0"?>
1
2 <!--
3 Copyright Bank of America, N.A., Barclays Bank PLC, Cisco Systems, Credit
4 Suisse, Deutsche Boerse, Envoy Technologies Inc., Goldman Sachs, HCL
5 Technologies Ltd, IIT Software GmbH, iMatix Corporation, INETCO Systems Limited,
6 Informatica Corporation, JPMorgan Chase & Co., Kaazing Corporation, N.A,
7 Microsoft Corporation, my-Channels, Novell, Progress Software, Red Hat Inc.,
8 Software AG, Solace Systems Inc., StormMQ Ltd., Tervela Inc., TWIST Process
9 Innovations Ltd, GoPivotal, Inc., and WS02 Inc. 2006-2011. All rights reserved.
10
11 Redistribution and use in source and binary forms, with or without
12 modification, are permitted provided that the following conditions
13 are met:
14 1. Redistributions of source code must retain the above copyright
15 notice, this list of conditions and the following disclaimer.
16 2. Redistributions in binary form must reproduce the above copyright
17 notice, this list of conditions and the following disclaimer in the
18 documentation and/or other materials provided with the distribution.
19 3. The name of the author may not be used to endorse or promote products
20 derived from this software without specific prior written permission.
21
22 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 -->
33
34 <amqp name="transactions" xmlns="http://www.amqp.org/schema/amqp.xsd">
35 <section name="coordination">
36 <type name="coordinator" class="composite" source="list" provides="target">
37 <descriptor name="amqp:coordinator:list" code="0x00000000:0x00000030"/>
38 <field name="capabilities" type="symbol" requires="txn-capability" multiple="true"/>
39 </type>
40 <type name="declare" class="composite" source="list">
41 <descriptor name="amqp:declare:list" code="0x00000000:0x00000031"/>
42 <field name="global-id" type="*" requires="global-tx-id"/>
43 </type>
44 <type name="discharge" class="composite" source="list">
45 <descriptor name="amqp:discharge:list" code="0x00000000:0x00000032"/>
46 <field name="txn-id" type="*" mandatory="true" requires="txn-id"/>
47 <field name="fail" type="boolean"/>
48 </type>
49 <type name="transaction-id" class="restricted" source="binary" provides="txn-id"/>
50 <type name="declared" class="composite" source="list" provides="delivery-state, outcome">
51 <descriptor name="amqp:declared:list" code="0x00000000:0x00000033"/>
52 <field name="txn-id" type="*" mandatory="true" requires="txn-id"/>
53 </type>
54 <type name="transactional-state" class="composite" source="list" provides="delivery-state">
55 <descriptor name="amqp:transactional-state:list" code="0x00000000:0x00000034"/>
56 <field name="txn-id" type="*" mandatory="true" requires="txn-id"/>
57 <field name="outcome" type="*" requires="outcome"/>
58 </type>
59 <type name="txn-capability" class="restricted" source="symbol" provides="txn-capability">
60 <choice name="local-transactions" value="amqp:local-transactions"/>
61 <choice name="distributed-transactions" value="amqp:distributed-transactions"/>
62 <choice name="promotable-transactions" value="amqp:promotable-transactions"/>
63 <choice name="multi-txns-per-ssn" value="amqp:multi-txns-per-ssn"/>
64 <choice name="multi-ssns-per-txn" value="amqp:multi-ssns-per-txn"/>
65 </type>
66 <type name="transaction-error" class="restricted" source="symbol" provides="error-condition">
67 <choice name="unknown-id" value="amqp:transaction:unknown-id"/>
68 <choice name="transaction-rollback" value="amqp:transaction:rollback"/>
69 <choice name="transaction-timeout" value="amqp:transaction:timeout"/>
70 </type>
71 </section>
72 </amqp>
0 <?xml version="1.0"?>
1
2 <!--
3 Copyright Bank of America, N.A., Barclays Bank PLC, Cisco Systems, Credit
4 Suisse, Deutsche Boerse, Envoy Technologies Inc., Goldman Sachs, HCL
5 Technologies Ltd, IIT Software GmbH, iMatix Corporation, INETCO Systems Limited,
6 Informatica Corporation, JPMorgan Chase & Co., Kaazing Corporation, N.A,
7 Microsoft Corporation, my-Channels, Novell, Progress Software, Red Hat Inc.,
8 Software AG, Solace Systems Inc., StormMQ Ltd., Tervela Inc., TWIST Process
9 Innovations Ltd, GoPivotal, Inc., and WS02 Inc. 2006-2011. All rights reserved.
10
11 Redistribution and use in source and binary forms, with or without
12 modification, are permitted provided that the following conditions
13 are met:
14 1. Redistributions of source code must retain the above copyright
15 notice, this list of conditions and the following disclaimer.
16 2. Redistributions in binary form must reproduce the above copyright
17 notice, this list of conditions and the following disclaimer in the
18 documentation and/or other materials provided with the distribution.
19 3. The name of the author may not be used to endorse or promote products
20 derived from this software without specific prior written permission.
21
22 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 -->
33
34 <amqp name="transport" xmlns="http://www.amqp.org/schema/amqp.xsd">
35 <section name="performatives">
36 <type name="open" class="composite" source="list" provides="frame">
37 <descriptor name="amqp:open:list" code="0x00000000:0x00000010"/>
38 <field name="container-id" type="string" mandatory="true"/>
39 <field name="hostname" type="string"/>
40 <field name="max-frame-size" type="uint" default="4294967295"/>
41 <field name="channel-max" type="ushort" default="65535"/>
42 <field name="idle-time-out" type="milliseconds"/>
43 <field name="outgoing-locales" type="ietf-language-tag" multiple="true"/>
44 <field name="incoming-locales" type="ietf-language-tag" multiple="true"/>
45 <field name="offered-capabilities" type="symbol" multiple="true"/>
46 <field name="desired-capabilities" type="symbol" multiple="true"/>
47 <field name="properties" type="fields"/>
48 </type>
49 <type name="begin" class="composite" source="list" provides="frame">
50 <descriptor name="amqp:begin:list" code="0x00000000:0x00000011"/>
51 <field name="remote-channel" type="ushort"/>
52 <field name="next-outgoing-id" type="transfer-number" mandatory="true"/>
53 <field name="incoming-window" type="uint" mandatory="true"/>
54 <field name="outgoing-window" type="uint" mandatory="true"/>
55 <field name="handle-max" type="handle" default="4294967295"/>
56 <field name="offered-capabilities" type="symbol" multiple="true"/>
57 <field name="desired-capabilities" type="symbol" multiple="true"/>
58 <field name="properties" type="fields"/>
59 </type>
60 <type name="attach" class="composite" source="list" provides="frame">
61 <descriptor name="amqp:attach:list" code="0x00000000:0x00000012"/>
62 <field name="name" type="string" mandatory="true"/>
63 <field name="handle" type="handle" mandatory="true"/>
64 <field name="role" type="role" mandatory="true"/>
65 <field name="snd-settle-mode" type="sender-settle-mode" default="mixed"/>
66 <field name="rcv-settle-mode" type="receiver-settle-mode" default="first"/>
67 <field name="source" type="*" requires="source"/>
68 <field name="target" type="*" requires="target"/>
69 <field name="unsettled" type="map"/>
70 <field name="incomplete-unsettled" type="boolean" default="false"/>
71 <field name="initial-delivery-count" type="sequence-no"/>
72 <field name="max-message-size" type="ulong"/>
73 <field name="offered-capabilities" type="symbol" multiple="true"/>
74 <field name="desired-capabilities" type="symbol" multiple="true"/>
75 <field name="properties" type="fields"/>
76 </type>
77 <type name="flow" class="composite" source="list" provides="frame">
78 <descriptor name="amqp:flow:list" code="0x00000000:0x00000013"/>
79 <field name="next-incoming-id" type="transfer-number"/>
80 <field name="incoming-window" type="uint" mandatory="true"/>
81 <field name="next-outgoing-id" type="transfer-number" mandatory="true"/>
82 <field name="outgoing-window" type="uint" mandatory="true"/>
83 <field name="handle" type="handle"/>
84 <field name="delivery-count" type="sequence-no"/>
85 <field name="link-credit" type="uint"/>
86 <field name="available" type="uint"/>
87 <field name="drain" type="boolean" default="false"/>
88 <field name="echo" type="boolean" default="false"/>
89 <field name="properties" type="fields"/>
90 </type>
91 <type name="transfer" class="composite" source="list" provides="frame">
92 <descriptor name="amqp:transfer:list" code="0x00000000:0x00000014"/>
93 <field name="handle" type="handle" mandatory="true"/>
94 <field name="delivery-id" type="delivery-number"/>
95 <field name="delivery-tag" type="delivery-tag"/>
96 <field name="message-format" type="message-format"/>
97 <field name="settled" type="boolean"/>
98 <field name="more" type="boolean" default="false"/>
99 <field name="rcv-settle-mode" type="receiver-settle-mode"/>
100 <field name="state" type="*" requires="delivery-state"/>
101 <field name="resume" type="boolean" default="false"/>
102 <field name="aborted" type="boolean" default="false"/>
103 <field name="batchable" type="boolean" default="false"/>
104 </type>
105 <type name="disposition" class="composite" source="list" provides="frame">
106 <descriptor name="amqp:disposition:list" code="0x00000000:0x00000015"/>
107 <field name="role" type="role" mandatory="true"/>
108 <field name="first" type="delivery-number" mandatory="true"/>
109 <field name="last" type="delivery-number"/>
110 <field name="settled" type="boolean" default="false"/>
111 <field name="state" type="*" requires="delivery-state"/>
112 <field name="batchable" type="boolean" default="false"/>
113 </type>
114 <type name="detach" class="composite" source="list" provides="frame">
115 <descriptor name="amqp:detach:list" code="0x00000000:0x00000016"/>
116 <field name="handle" type="handle" mandatory="true"/>
117 <field name="closed" type="boolean" default="false"/>
118 <field name="error" type="error"/>
119 </type>
120 <type name="end" class="composite" source="list" provides="frame">
121 <descriptor name="amqp:end:list" code="0x00000000:0x00000017"/>
122 <field name="error" type="error"/>
123 </type>
124 <type name="close" class="composite" source="list" provides="frame">
125 <descriptor name="amqp:close:list" code="0x00000000:0x00000018"/>
126 <field name="error" type="error"/>
127 </type>
128 </section>
129 <section name="definitions">
130 <type name="role" class="restricted" source="boolean">
131 <choice name="sender" value="false"/>
132 <choice name="receiver" value="true"/>
133 </type>
134 <type name="sender-settle-mode" class="restricted" source="ubyte">
135 <choice name="unsettled" value="0"/>
136 <choice name="settled" value="1"/>
137 <choice name="mixed" value="2"/>
138 </type>
139 <type name="receiver-settle-mode" class="restricted" source="ubyte">
140 <choice name="first" value="0"/>
141 <choice name="second" value="1"/>
142 </type>
143 <type name="handle" class="restricted" source="uint"/>
144 <type name="seconds" class="restricted" source="uint"/>
145 <type name="milliseconds" class="restricted" source="uint"/>
146 <type name="delivery-tag" class="restricted" source="binary"/>
147 <type name="delivery-number" class="restricted" source="sequence-no"/>
148 <type name="transfer-number" class="restricted" source="sequence-no"/>
149 <type name="sequence-no" class="restricted" source="uint"/>
150 <type name="message-format" class="restricted" source="uint"/>
151 <type name="ietf-language-tag" class="restricted" source="symbol"/>
152 <type name="fields" class="restricted" source="map"/>
153 <type name="error" class="composite" source="list">
154 <descriptor name="amqp:error:list" code="0x00000000:0x0000001d"/>
155 <field name="condition" type="symbol" mandatory="true" requires="error-condition"/>
156 <field name="description" type="string"/>
157 <field name="info" type="fields"/>
158 </type>
159 <type name="amqp-error" class="restricted" source="symbol" provides="error-condition">
160 <choice name="internal-error" value="amqp:internal-error"/>
161 <choice name="not-found" value="amqp:not-found"/>
162 <choice name="unauthorized-access" value="amqp:unauthorized-access"/>
163 <choice name="decode-error" value="amqp:decode-error"/>
164 <choice name="resource-limit-exceeded" value="amqp:resource-limit-exceeded"/>
165 <choice name="not-allowed" value="amqp:not-allowed"/>
166 <choice name="invalid-field" value="amqp:invalid-field"/>
167 <choice name="not-implemented" value="amqp:not-implemented"/>
168 <choice name="resource-locked" value="amqp:resource-locked"/>
169 <choice name="precondition-failed" value="amqp:precondition-failed"/>
170 <choice name="resource-deleted" value="amqp:resource-deleted"/>
171 <choice name="illegal-state" value="amqp:illegal-state"/>
172 <choice name="frame-size-too-small" value="amqp:frame-size-too-small"/>
173 </type>
174 <type name="connection-error" class="restricted" source="symbol" provides="error-condition">
175 <choice name="connection-forced" value="amqp:connection:forced"/>
176 <choice name="framing-error" value="amqp:connection:framing-error"/>
177 <choice name="redirect" value="amqp:connection:redirect"/>
178 </type>
179 <type name="session-error" class="restricted" source="symbol" provides="error-condition">
180 <choice name="window-violation" value="amqp:session:window-violation"/>
181 <choice name="errant-link" value="amqp:session:errant-link"/>
182 <choice name="handle-in-use" value="amqp:session:handle-in-use"/>
183 <choice name="unattached-handle" value="amqp:session:unattached-handle"/>
184 </type>
185 <type name="link-error" class="restricted" source="symbol" provides="error-condition">
186 <choice name="detach-forced" value="amqp:link:detach-forced"/>
187 <choice name="transfer-limit-exceeded" value="amqp:link:transfer-limit-exceeded"/>
188 <choice name="message-size-exceeded" value="amqp:link:message-size-exceeded"/>
189 <choice name="redirect" value="amqp:link:redirect"/>
190 <choice name="stolen" value="amqp:link:stolen"/>
191 </type>
192 <definition name="PORT" value="5672"/>
193 <definition name="SECURE-PORT" value="5671"/>
194 <definition name="MAJOR" value="1"/>
195 <definition name="MINOR" value="0"/>
196 <definition name="REVISION" value="0"/>
197 <definition name="MIN-MAX-FRAME-SIZE" value="512"/>
198 </section>
199 </amqp>
0 <?xml version="1.0"?>
1
2 <!--
3 Copyright Bank of America, N.A., Barclays Bank PLC, Cisco Systems, Credit
4 Suisse, Deutsche Boerse, Envoy Technologies Inc., Goldman Sachs, HCL
5 Technologies Ltd, IIT Software GmbH, iMatix Corporation, INETCO Systems Limited,
6 Informatica Corporation, JPMorgan Chase & Co., Kaazing Corporation, N.A,
7 Microsoft Corporation, my-Channels, Novell, Progress Software, Red Hat Inc.,
8 Software AG, Solace Systems Inc., StormMQ Ltd., Tervela Inc., TWIST Process
9 Innovations Ltd, GoPivotal, Inc., and WS02 Inc. 2006-2011. All rights reserved.
10
11 Redistribution and use in source and binary forms, with or without
12 modification, are permitted provided that the following conditions
13 are met:
14 1. Redistributions of source code must retain the above copyright
15 notice, this list of conditions and the following disclaimer.
16 2. Redistributions in binary form must reproduce the above copyright
17 notice, this list of conditions and the following disclaimer in the
18 documentation and/or other materials provided with the distribution.
19 3. The name of the author may not be used to endorse or promote products
20 derived from this software without specific prior written permission.
21
22 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 -->
33
34 <amqp name="types" xmlns="http://www.amqp.org/schema/amqp.xsd">
35 <section name="encodings">
36 <type name="null" class="primitive">
37 <encoding code="0x40" category="fixed" width="0"/>
38 </type>
39 <type name="boolean" class="primitive">
40 <encoding code="0x56" category="fixed" width="1"/>
41 <encoding name="true" code="0x41" category="fixed" width="0"/>
42 <encoding name="false" code="0x42" category="fixed" width="0"/>
43 </type>
44 <type name="ubyte" class="primitive">
45 <encoding code="0x50" category="fixed" width="1"/>
46 </type>
47 <type name="ushort" class="primitive">
48 <encoding code="0x60" category="fixed" width="2"/>
49 </type>
50 <type name="uint" class="primitive">
51 <encoding code="0x70" category="fixed" width="4"/>
52 <encoding name="smalluint" code="0x52" category="fixed" width="1"/>
53 <encoding name="uint0" code="0x43" category="fixed" width="0"/>
54 </type>
55 <type name="ulong" class="primitive">
56 <encoding code="0x80" category="fixed" width="8"/>
57 <encoding name="smallulong" code="0x53" category="fixed" width="1"/>
58 <encoding name="ulong0" code="0x44" category="fixed" width="0"/>
59 </type>
60 <type name="byte" class="primitive">
61 <encoding code="0x51" category="fixed" width="1"/>
62 </type>
63 <type name="short" class="primitive">
64 <encoding code="0x61" category="fixed" width="2"/>
65 </type>
66 <type name="int" class="primitive">
67 <encoding code="0x71" category="fixed" width="4"/>
68 <encoding name="smallint" code="0x54" category="fixed" width="1"/>
69 </type>
70 <type name="long" class="primitive">
71 <encoding code="0x81" category="fixed" width="8"/>
72 <encoding name="smalllong" code="0x55" category="fixed" width="1"/>
73 </type>
74 <type name="float" class="primitive">
75 <encoding name="ieee-754" code="0x72" category="fixed" width="4"/>
76 </type>
77 <type name="double" class="primitive">
78 <encoding name="ieee-754" code="0x82" category="fixed" width="8"/>
79 </type>
80 <type name="decimal32" class="primitive">
81 <encoding name="ieee-754" code="0x74" category="fixed" width="4"/>
82 </type>
83 <type name="decimal64" class="primitive">
84 <encoding name="ieee-754" code="0x84" category="fixed" width="8"/>
85 </type>
86 <type name="decimal128" class="primitive">
87 <encoding name="ieee-754" code="0x94" category="fixed" width="16"/>
88 </type>
89 <type name="char" class="primitive">
90 <encoding name="utf32" code="0x73" category="fixed" width="4"/>
91 </type>
92 <type name="timestamp" class="primitive">
93 <encoding name="ms64" code="0x83" category="fixed" width="8"/>
94 </type>
95 <type name="uuid" class="primitive">
96 <encoding code="0x98" category="fixed" width="16"/>
97 </type>
98 <type name="binary" class="primitive">
99 <encoding name="vbin8" code="0xa0" category="variable" width="1"/>
100 <encoding name="vbin32" code="0xb0" category="variable" width="4"/>
101 </type>
102 <type name="string" class="primitive">
103 <encoding name="str8-utf8" code="0xa1" category="variable" width="1"/>
104 <encoding name="str32-utf8" code="0xb1" category="variable" width="4"/>
105 </type>
106 <type name="symbol" class="primitive">
107 <encoding name="sym8" code="0xa3" category="variable" width="1"/>
108 <encoding name="sym32" code="0xb3" category="variable" width="4"/>
109 </type>
110 <type name="list" class="primitive">
111 <encoding name="list0" code="0x45" category="fixed" width="0"/>
112 <encoding name="list8" code="0xc0" category="compound" width="1"/>
113 <encoding name="list32" code="0xd0" category="compound" width="4"/>
114 </type>
115 <type name="map" class="primitive">
116 <encoding name="map8" code="0xc1" category="compound" width="1"/>
117 <encoding name="map32" code="0xd1" category="compound" width="4"/>
118 </type>
119 <type name="array" class="primitive">
120 <encoding name="array8" code="0xe0" category="array" width="1"/>
121 <encoding name="array32" code="0xf0" category="array" width="4"/>
122 </type>
123 </section>
124 </amqp>
1313 ## Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved.
1414 ##
1515
16 from __future__ import nested_scopes
16 from __future__ import nested_scopes, print_function
1717 import errno
1818 import re
1919 import sys
2929 else:
3030 raise e
3131 except ImportError:
32 print >> sys.stderr , " You don't appear to have simplejson.py installed"
33 print >> sys.stderr , " (an implementation of a JSON reader and writer in Python)."
34 print >> sys.stderr , " You can install it:"
35 print >> sys.stderr , " - by running 'apt-get install python-simplejson' on Debian-based systems,"
36 print >> sys.stderr , " - by running 'yum install python-simplejson' on Fedora/Red Hat system,"
37 print >> sys.stderr , " - by running 'port install py25-simplejson' on Macports on OS X"
38 print >> sys.stderr , " (you may need to say 'make PYTHON=python2.5', as well),"
39 print >> sys.stderr , " - from sources from 'http://pypi.python.org/pypi/simplejson'"
40 print >> sys.stderr , " - simplejson is a standard json library in the Python core since 2.6"
32 print(" You don't appear to have simplejson.py installed", file = sys.stderr)
33 print(" (an implementation of a JSON reader and writer in Python).", file = sys.stderr)
34 print(" You can install it:", file = sys.stderr)
35 print(" - by running 'apt-get install python-simplejson' on Debian-based systems,", file = sys.stderr)
36 print(" - by running 'yum install python-simplejson' on Fedora/Red Hat system,", file = sys.stderr)
37 print(" - by running 'port install py25-simplejson' on Macports on OS X", file = sys.stderr)
38 print(" (you may need to say 'make PYTHON=python2.5', as well),", file = sys.stderr)
39 print(" - from sources from 'http://pypi.python.org/pypi/simplejson'", file = sys.stderr)
40 print(" - simplejson is a standard json library in the Python core since 2.6", file = sys.stderr)
4141 sys.exit(1)
4242
4343 def insert_base_types(d):
139139
140140 self.major = self.spec['major-version']
141141 self.minor = self.spec['minor-version']
142 self.revision = 'revision' in self.spec and self.spec['revision'] or 0
142 self.revision = ('revision' in self.spec) and (self.spec['revision'] or 0)
143143 self.port = self.spec['port']
144144
145145 self.domains = {}
235235 self.domain = self.element['type']
236236 else:
237237 self.domain = self.element['domain']
238
239238 if 'default-value' in self.element:
240239 self.defaultvalue = self.element['default-value']
241240 else:
249248
250249 def do_main_dict(funcDict):
251250 def usage():
252 print >> sys.stderr , "Usage:"
253 print >> sys.stderr , " %s <function> <path_to_amqp_spec.json>... <path_to_output_file>" % (sys.argv[0])
254 print >> sys.stderr , " where <function> is one of %s" % ", ".join([k for k in funcDict.keys()])
251 print("Usage:", file = sys.stderr)
252 print(" {0} <function> <path_to_amqp_spec.json>... <path_to_output_file>".format(sys.argv[0]), file = sys.stderr)
253 print(" where <function> is one of: {0}".format(", ".join([k for k in funcDict.keys()])), file = sys.stderr)
255254
256255 def mkdir_p(path):
257256 try:
00 PROJECT = rabbitmq_consistent_hash_exchange
1 PROJECT_DESCRIPTION = Consistent Hash Exchange Type
12
23 DEPS = rabbit_common rabbit
3 TEST_DEPS = rabbitmq_ct_helpers amqp_client
4 TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers amqp_client
45
6 DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
57 DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk
68
79 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
0 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
11 #
22 # Permission to use, copy, modify, and/or distribute this software for any
33 # purpose with or without fee is hereby granted, provided that the above
1111 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1212 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1313
14 .PHONY: all app apps deps search rel docs install-docs check tests clean distclean help erlang-mk
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
1515
1616 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17
18 ERLANG_MK_VERSION = 2.0.0-pre.2-144-g647ffd1
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
1931
2032 # Core configuration.
2133
2436
2537 PROJECT_VERSION ?= rolling
2638 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
2740
2841 # Verbosity.
2942
8497 rel::
8598 $(verbose) :
8699
100 relup:: deps app
101
87102 check:: tests
88103
89104 clean:: clean-crashdump
101116 help::
102117 $(verbose) printf "%s\n" \
103118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
104 "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
105120 "" \
106121 "Usage: [V=1] $(MAKE) [target]..." \
107122 "" \
149164 core_native_path = $1
150165 endif
151166
152 ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
153 define core_http_get
154 wget --no-check-certificate -O $(1) $(2)|| rm $(1)
155 endef
156 else
157 define core_http_get.erl
158 ssl:start(),
159 inets:start(),
160 case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
161 {ok, {{_, 200, _}, _, Body}} ->
162 case file:write_file("$(1)", Body) of
163 ok -> ok;
164 {error, R1} -> halt(R1)
165 end;
166 {error, R2} ->
167 halt(R2)
168 end,
169 halt(0).
170 endef
171
172 define core_http_get
173 $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
174 endef
175 endif
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
176168
177169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
178170
192184 ERLANG_MK_BUILD_CONFIG ?= build.config
193185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
194186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
195188 erlang-mk:
196189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
197190 ifdef ERLANG_MK_COMMIT
198191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
199192 endif
200193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
201 $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
202195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
203196 rm -rf $(ERLANG_MK_BUILD_DIR)
204197
285278 pkg_apns_repo = https://github.com/inaka/apns4erl
286279 pkg_apns_commit = master
287280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
288289 PACKAGES += azdht
289290 pkg_azdht_name = azdht
290291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
405406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
406407 pkg_bootstrap_commit = master
407408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
408417 PACKAGES += boss_db
409418 pkg_boss_db_name = boss_db
410419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
413422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
414423 pkg_boss_db_commit = master
415424
416 PACKAGES += boss
417 pkg_boss_name = boss
418 pkg_boss_description = Erlang web MVC, now featuring Comet
419 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
420 pkg_boss_fetch = git
421 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
422 pkg_boss_commit = master
423
424425 PACKAGES += brod
425426 pkg_brod_name = brod
426427 pkg_brod_description = Kafka client in Erlang
565566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
566567 pkg_cloudi_service_api_requests_commit = master
567568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
568585 PACKAGES += cloudi_service_db_cassandra_cql
569586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
570587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
573590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
574591 pkg_cloudi_service_db_cassandra_cql_commit = master
575592
576 PACKAGES += cloudi_service_db_cassandra
577 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
579 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
580 pkg_cloudi_service_db_cassandra_fetch = git
581 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
582 pkg_cloudi_service_db_cassandra_commit = master
583
584593 PACKAGES += cloudi_service_db_couchdb
585594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
586595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
637646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
638647 pkg_cloudi_service_db_tokyotyrant_commit = master
639648
640 PACKAGES += cloudi_service_db
641 pkg_cloudi_service_db_name = cloudi_service_db
642 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
643 pkg_cloudi_service_db_homepage = http://cloudi.org/
644 pkg_cloudi_service_db_fetch = git
645 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
646 pkg_cloudi_service_db_commit = master
647
648649 PACKAGES += cloudi_service_filesystem
649650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
650651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
10371038 pkg_edown_repo = https://github.com/uwiger/edown
10381039 pkg_edown_commit = master
10391040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
10401049 PACKAGES += eep_app
10411050 pkg_eep_app_name = eep_app
10421051 pkg_eep_app_description = Embedded Event Processing
10451054 pkg_eep_app_repo = https://github.com/darach/eep-erl
10461055 pkg_eep_app_commit = master
10471056
1048 PACKAGES += eep
1049 pkg_eep_name = eep
1050 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1051 pkg_eep_homepage = https://github.com/virtan/eep
1052 pkg_eep_fetch = git
1053 pkg_eep_repo = https://github.com/virtan/eep
1054 pkg_eep_commit = master
1055
10561057 PACKAGES += efene
10571058 pkg_efene_name = efene
10581059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
12371238 pkg_eqm_repo = https://github.com/loucash/eqm
12381239 pkg_eqm_commit = master
12391240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
12401249 PACKAGES += eredis_pool
12411250 pkg_eredis_pool_name = eredis_pool
12421251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
12451254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
12461255 pkg_eredis_pool_commit = master
12471256
1248 PACKAGES += eredis
1249 pkg_eredis_name = eredis
1250 pkg_eredis_description = Erlang Redis client
1251 pkg_eredis_homepage = https://github.com/wooga/eredis
1252 pkg_eredis_fetch = git
1253 pkg_eredis_repo = https://github.com/wooga/eredis
1254 pkg_eredis_commit = master
1255
12561257 PACKAGES += erl_streams
12571258 pkg_erl_streams_name = erl_streams
12581259 pkg_erl_streams_description = Streams in Erlang
15411542 pkg_etap_repo = https://github.com/ngerakines/etap
15421543 pkg_etap_commit = master
15431544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
15441553 PACKAGES += etest_http
15451554 pkg_etest_http_name = etest_http
15461555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
15491558 pkg_etest_http_repo = https://github.com/wooga/etest_http
15501559 pkg_etest_http_commit = master
15511560
1552 PACKAGES += etest
1553 pkg_etest_name = etest
1554 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1555 pkg_etest_homepage = https://github.com/wooga/etest
1556 pkg_etest_fetch = git
1557 pkg_etest_repo = https://github.com/wooga/etest
1558 pkg_etest_commit = master
1559
15601561 PACKAGES += etoml
15611562 pkg_etoml_name = etoml
15621563 pkg_etoml_description = TOML language erlang parser
15651566 pkg_etoml_repo = https://github.com/kalta/etoml
15661567 pkg_etoml_commit = master
15671568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
15681577 PACKAGES += eunit_formatters
15691578 pkg_eunit_formatters_name = eunit_formatters
15701579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
15731582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
15741583 pkg_eunit_formatters_commit = master
15751584
1576 PACKAGES += eunit
1577 pkg_eunit_name = eunit
1578 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1579 pkg_eunit_homepage = https://github.com/richcarl/eunit
1580 pkg_eunit_fetch = git
1581 pkg_eunit_repo = https://github.com/richcarl/eunit
1582 pkg_eunit_commit = master
1583
15841585 PACKAGES += euthanasia
15851586 pkg_euthanasia_name = euthanasia
15861587 pkg_euthanasia_description = Merciful killer for your Erlang processes
15981599 pkg_evum_commit = master
15991600
16001601 PACKAGES += exec
1601 pkg_exec_name = exec
1602 pkg_exec_name = erlexec
16021603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
16031604 pkg_exec_homepage = http://saleyn.github.com/erlexec
16041605 pkg_exec_fetch = git
17171718 pkg_fn_repo = https://github.com/reiddraper/fn
17181719 pkg_fn_commit = master
17191720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
17201729 PACKAGES += folsom_cowboy
17211730 pkg_folsom_cowboy_name = folsom_cowboy
17221731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
17251734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
17261735 pkg_folsom_cowboy_commit = master
17271736
1728 PACKAGES += folsom
1729 pkg_folsom_name = folsom
1730 pkg_folsom_description = Expose Erlang Events and Metrics
1731 pkg_folsom_homepage = https://github.com/boundary/folsom
1732 pkg_folsom_fetch = git
1733 pkg_folsom_repo = https://github.com/boundary/folsom
1734 pkg_folsom_commit = master
1735
17361737 PACKAGES += folsomite
17371738 pkg_folsomite_name = folsomite
17381739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
20932094 pkg_jesse_repo = https://github.com/for-GET/jesse
20942095 pkg_jesse_commit = master
20952096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
20962105 PACKAGES += jiffy_v
20972106 pkg_jiffy_v_name = jiffy_v
20982107 pkg_jiffy_v_description = JSON validation utility
21012110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
21022111 pkg_jiffy_v_commit = master
21032112
2104 PACKAGES += jiffy
2105 pkg_jiffy_name = jiffy
2106 pkg_jiffy_description = JSON NIFs for Erlang.
2107 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2108 pkg_jiffy_fetch = git
2109 pkg_jiffy_repo = https://github.com/davisp/jiffy
2110 pkg_jiffy_commit = master
2111
21122113 PACKAGES += jobs
21132114 pkg_jobs_name = jobs
21142115 pkg_jobs_description = a Job scheduler for load regulation
21252126 pkg_joxa_repo = https://github.com/joxa/joxa
21262127 pkg_joxa_commit = master
21272128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
21282137 PACKAGES += json_rec
21292138 pkg_json_rec_name = json_rec
21302139 pkg_json_rec_description = JSON to erlang record
21332142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
21342143 pkg_json_rec_commit = master
21352144
2136 PACKAGES += json
2137 pkg_json_name = json
2138 pkg_json_description = a high level json library for erlang (17.0+)
2139 pkg_json_homepage = https://github.com/talentdeficit/json
2140 pkg_json_fetch = git
2141 pkg_json_repo = https://github.com/talentdeficit/json
2142 pkg_json_commit = master
2143
21442145 PACKAGES += jsone
21452146 pkg_jsone_name = jsone
21462147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
21812182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
21822183 pkg_jsx_commit = master
21832184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
21842193 PACKAGES += kafka_protocol
21852194 pkg_kafka_protocol_name = kafka_protocol
21862195 pkg_kafka_protocol_description = Kafka protocol Erlang library
21892198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
21902199 pkg_kafka_protocol_commit = master
21912200
2192 PACKAGES += kafka
2193 pkg_kafka_name = kafka
2194 pkg_kafka_description = Kafka consumer and producer in Erlang
2195 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2196 pkg_kafka_fetch = git
2197 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2198 pkg_kafka_commit = master
2199
22002201 PACKAGES += kai
22012202 pkg_kai_name = kai
22022203 pkg_kai_description = DHT storage by Takeshi Inoue
22932294 pkg_kvs_repo = https://github.com/synrc/kvs
22942295 pkg_kvs_commit = master
22952296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
22962305 PACKAGES += lager_amqp_backend
22972306 pkg_lager_amqp_backend_name = lager_amqp_backend
22982307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
23092318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
23102319 pkg_lager_syslog_commit = master
23112320
2312 PACKAGES += lager
2313 pkg_lager_name = lager
2314 pkg_lager_description = A logging framework for Erlang/OTP.
2315 pkg_lager_homepage = https://github.com/basho/lager
2316 pkg_lager_fetch = git
2317 pkg_lager_repo = https://github.com/basho/lager
2318 pkg_lager_commit = master
2319
23202321 PACKAGES += lambdapad
23212322 pkg_lambdapad_name = lambdapad
23222323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
25732574 pkg_mixer_repo = https://github.com/chef/mixer
25742575 pkg_mixer_commit = master
25752576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
25762585 PACKAGES += mochiweb_xpath
25772586 pkg_mochiweb_xpath_name = mochiweb_xpath
25782587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
25812590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
25822591 pkg_mochiweb_xpath_commit = master
25832592
2584 PACKAGES += mochiweb
2585 pkg_mochiweb_name = mochiweb
2586 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2587 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2588 pkg_mochiweb_fetch = git
2589 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2590 pkg_mochiweb_commit = master
2591
25922593 PACKAGES += mockgyver
25932594 pkg_mockgyver_name = mockgyver
25942595 pkg_mockgyver_description = A mocking library for Erlang
30613062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
30623063 pkg_quickrand_commit = master
30633064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
30643073 PACKAGES += rabbit_exchange_type_riak
30653074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
30663075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
30693078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
30703079 pkg_rabbit_exchange_type_riak_commit = master
30713080
3072 PACKAGES += rabbit
3073 pkg_rabbit_name = rabbit
3074 pkg_rabbit_description = RabbitMQ Server
3075 pkg_rabbit_homepage = https://www.rabbitmq.com/
3076 pkg_rabbit_fetch = git
3077 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3078 pkg_rabbit_commit = master
3079
30803081 PACKAGES += rack
30813082 pkg_rack_name = rack
30823083 pkg_rack_description = Rack handler for erlang
34933494 pkg_smother_repo = https://github.com/ramsay-t/Smother
34943495 pkg_smother_commit = master
34953496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
34963505 PACKAGES += social
34973506 pkg_social_name = social
34983507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
35413550 pkg_stable_repo = https://github.com/dvv/stable
35423551 pkg_stable_commit = master
35433552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
35443561 PACKAGES += statebox_riak
35453562 pkg_statebox_riak_name = statebox_riak
35463563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
35493566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
35503567 pkg_statebox_riak_commit = master
35513568
3552 PACKAGES += statebox
3553 pkg_statebox_name = statebox
3554 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3555 pkg_statebox_homepage = https://github.com/mochi/statebox
3556 pkg_statebox_fetch = git
3557 pkg_statebox_repo = https://github.com/mochi/statebox
3558 pkg_statebox_commit = master
3559
35603569 PACKAGES += statman
35613570 pkg_statman_name = statman
35623571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
37253734 pkg_tirerl_repo = https://github.com/inaka/tirerl
37263735 pkg_tirerl_commit = master
37273736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
37283745 PACKAGES += traffic_tools
37293746 pkg_traffic_tools_name = traffic_tools
37303747 pkg_traffic_tools_description = Simple traffic limiting library
40614078 pkg_zucchini_repo = https://github.com/devinus/zucchini
40624079 pkg_zucchini_commit = master
40634080
4064 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
40654082 # This file is part of erlang.mk and subject to the terms of the ISC License.
40664083
40674084 .PHONY: search
40884105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
40894106 endif
40904107
4091 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
40924109 # This file is part of erlang.mk and subject to the terms of the ISC License.
40934110
4094 .PHONY: distclean-deps
4111 .PHONY: distclean-deps clean-tmp-deps.log
40954112
40964113 # Configuration.
40974114
41104127
41114128 REBAR_DEPS_DIR = $(DEPS_DIR)
41124129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
41134150
41144151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
41154152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
41164153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
41174154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
41184155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
41194157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
41204158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
41214159
41384176
41394177 # Core targets.
41404178
4141 ifdef IS_APP
4142 apps::
4143 else
4144 apps:: $(ALL_APPS_DIRS)
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
41454180 ifeq ($(IS_APP)$(IS_DEP),)
41464181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
41474182 endif
41494184 # Create ebin directory for all apps to make sure Erlang recognizes them
41504185 # as proper OTP applications when using -include_lib. This is a temporary
41514186 # fix, a proper fix would be to compile apps/* in the right order.
4152 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4153 mkdir -p $$dep/ebin || exit $$?; \
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
41544190 done
4155 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
41564196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
41574197 :; \
41584198 else \
41594199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4160 $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
4200 $(MAKE) -C $$dep IS_APP=1; \
41614201 fi \
41624202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
41634207 endif
41644208
41654209 ifneq ($(SKIP_DEPS),)
41664210 deps::
41674211 else
4168 deps:: $(ALL_DEPS_DIRS) apps
4169 ifeq ($(IS_APP)$(IS_DEP),)
4170 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4171 endif
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
41724213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4173 $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
41744215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
41754216 :; \
41764217 else \
41774218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
41784219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4179 $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
4220 $(MAKE) -C $$dep IS_DEP=1; \
41804221 else \
4181 echo "Error: No Makefile to build dependency $$dep."; \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
41824223 exit 2; \
41834224 fi \
41844225 fi \
41924233 # in practice only Makefile is needed so far.
41934234 define dep_autopatch
41944235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
41954237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
41964238 $(call dep_autopatch_erlang_mk,$(1)); \
41974239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4198 if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
41994243 $(call dep_autopatch2,$(1)); \
4200 elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
42014245 $(call dep_autopatch2,$(1)); \
4202 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
42034247 $(call dep_autopatch2,$(1)); \
4204 else \
4205 $(call erlang,$(call dep_autopatch_app.erl,$(1))); \
42064248 fi \
42074249 else \
42084250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
42144256 endef
42154257
42164258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
42174262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
42184263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
42194264 fi; \
42204265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4221 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
42224267 $(call dep_autopatch_fetch_rebar); \
42234268 $(call dep_autopatch_rebar,$(1)); \
42244269 else \
42304275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
42314276 endef
42324277
4233 # Overwrite erlang.mk with the current file by default.
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
42344280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
42354281 define dep_autopatch_erlang_mk
4236 echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \
4237 > $(DEPS_DIR)/$1/erlang.mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
42384287 endef
42394288 else
42404289 define dep_autopatch_erlang_mk
42534302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
42544303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
42554304 cd $(ERLANG_MK_TMP)/rebar; \
4256 git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
42574306 $(MAKE); \
42584307 cd -; \
42594308 fi
42704319 define dep_autopatch_rebar.erl
42714320 application:load(rebar),
42724321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
42734323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
42744324 {ok, Conf0} -> Conf0;
42754325 _ -> []
44234473 [] -> ok;
44244474 _ ->
44254475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4426 PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
44274477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4428 PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L \\"~s\\" -lerl_interface -lei\n",
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
44294479 [code:lib_dir(erl_interface, lib)])),
44304480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
44314481 FilterEnv = fun(Env) ->
44644514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44654515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44664516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4467 [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
44684518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
44694519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
44704520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
44764526 end,
44774527 [PortSpec(S) || S <- PortSpecs]
44784528 end,
4479 Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"),
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
44804530 RunPlugin = fun(Plugin, Step) ->
44814531 case erlang:function_exported(Plugin, Step, 2) of
44824532 false -> ok;
45244574 halt()
45254575 endef
45264576
4527 define dep_autopatch_app.erl
4528 UpdateModules = fun(App) ->
4529 case filelib:is_regular(App) of
4530 false -> ok;
4531 true ->
4532 {ok, [{application, '$(1)', L0}]} = file:consult(App),
4533 Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true,
4534 fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
4535 L = lists:keystore(modules, 1, L0, {modules, Mods}),
4536 ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}]))
4537 end
4538 end,
4539 UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"),
4540 halt()
4541 endef
4542
45434577 define dep_autopatch_appsrc_script.erl
45444578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
45454579 AppSrcScript = AppSrc ++ ".script",
45874621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
45884622 endef
45894623
4590 define dep_fetch_hex.erl
4591 ssl:start(),
4592 inets:start(),
4593 {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
4594 {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
4595 [], [{body_format, binary}]),
4596 {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
4597 {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
4598 ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
4599 halt()
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
46004626 endef
46014627
46024628 # Hex only has a package version. No need to look in the Erlang.mk packages.
46034629 define dep_fetch_hex
4604 $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
46054634 endef
46064635
46074636 define dep_fetch_fail
46314660 $(eval DEP_NAME := $(call dep_name,$1))
46324661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
46334662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4634 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
46354664 exit 17; \
46364665 fi
46374666 $(verbose) mkdir -p $(DEPS_DIR)
46734702 clean:: clean-apps
46744703
46754704 clean-apps:
4676 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4677 $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
46784707 done
46794708
46804709 distclean:: distclean-apps
46814710
46824711 distclean-apps:
4683 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4684 $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
46854714 done
46864715 endif
46874716
47014730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
47024731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
47034732
4704 # External plugins.
4705
4706 DEP_PLUGINS ?=
4707
4708 define core_dep_plugin
4709 -include $(DEPS_DIR)/$(1)
4710
4711 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4712 endef
4713
4714 $(foreach p,$(DEP_PLUGINS),\
4715 $(eval $(if $(findstring /,$p),\
4716 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4717 $(call core_dep_plugin,$p/plugins.mk,$p))))
4718
4719 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4720 # This file is part of erlang.mk and subject to the terms of the ISC License.
4721
4722 # Configuration.
4723
4724 DTL_FULL_PATH ?=
4725 DTL_PATH ?= templates/
4726 DTL_SUFFIX ?= _dtl
4727 DTL_OPTS ?=
4728
4729 # Verbosity.
4730
4731 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
4732 dtl_verbose = $(dtl_verbose_$(V))
4733
4734 # Core targets.
4735
4736 DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl))
4737
4738 ifneq ($(DTL_FILES),)
4739
4740 ifdef DTL_FULL_PATH
4741 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
4742 else
4743 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
4744 endif
4745
4746 ifneq ($(words $(DTL_FILES)),0)
4747 # Rebuild templates when the Makefile changes.
4748 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
4749 @mkdir -p $(ERLANG_MK_TMP)
4750 @if test -f $@; then \
4751 touch $(DTL_FILES); \
4752 fi
4753 @touch $@
4754
4755 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
4756 endif
4757
4758 define erlydtl_compile.erl
4759 [begin
4760 Module0 = case "$(strip $(DTL_FULL_PATH))" of
4761 "" ->
4762 filename:basename(F, ".dtl");
4763 _ ->
4764 "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
4765 re:replace(F2, "/", "_", [{return, list}, global])
4766 end,
4767 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
4768 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
4769 ok -> ok;
4770 {ok, _} -> ok
4771 end
4772 end || F <- string:tokens("$(1)", " ")],
4773 halt().
4774 endef
4775
4776 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
4777 $(if $(strip $?),\
4778 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
4779
4780 endif
4781
4782 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
47834734 # This file is part of erlang.mk and subject to the terms of the ISC License.
47844735
47854736 # Verbosity.
47984749
47994750 define compile_proto.erl
48004751 [begin
4801 Dir = filename:dirname(filename:dirname(F)),
48024752 protobuffs_compile:generate_source(F,
4803 [{output_include_dir, Dir ++ "/include"},
4804 {output_src_dir, Dir ++ "/ebin"}])
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
48054755 end || F <- string:tokens("$(1)", " ")],
48064756 halt().
48074757 endef
48114761 $(if $(strip $?),$(call compile_proto,$?))
48124762 endif
48134763
4814 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
48154765 # This file is part of erlang.mk and subject to the terms of the ISC License.
48164766
48174767 .PHONY: clean-app
48254775 ERLC_EXCLUDE ?=
48264776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
48274777
4778 ERLC_ASN1_OPTS ?=
4779
48284780 ERLC_MIB_OPTS ?=
48294781 COMPILE_MIB_FIRST ?=
48304782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
48744826
48754827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
48764828 define app_file
4877 {application, $(PROJECT), [
4829 {application, '$(PROJECT)', [
48784830 {description, "$(PROJECT_DESCRIPTION)"},
48794831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48804832 {id$(comma)$(space)"$(1)"}$(comma))
48814833 {modules, [$(call comma_list,$(2))]},
48824834 {registered, []},
4883 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48844837 ]}.
48854838 endef
48864839 else
48874840 define app_file
4888 {application, $(PROJECT), [
4841 {application, '$(PROJECT)', [
48894842 {description, "$(PROJECT_DESCRIPTION)"},
48904843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48914844 {id$(comma)$(space)"$(1)"}$(comma))
48924845 {modules, [$(call comma_list,$(2))]},
48934846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
48944847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4895 {mod, {$(PROJECT_MOD), []}}
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48964850 ]}.
48974851 endef
48984852 endif
49024856
49034857 # Source files.
49044858
4905 ERL_FILES = $(sort $(call core_find,src/,*.erl))
4906 CORE_FILES = $(sort $(call core_find,src/,*.core))
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
49074863
49084864 # ASN.1 files.
49094865
49134869
49144870 define compile_asn1
49154871 $(verbose) mkdir -p include/
4916 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
49174873 $(verbose) mv asn1/*.erl src/
49184874 $(verbose) mv asn1/*.hrl include/
49194875 $(verbose) mv asn1/*.asn1db include/
49364892
49374893 # Leex and Yecc files.
49384894
4939 XRL_FILES = $(sort $(call core_find,src/,*.xrl))
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
49404896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
49414897 ERL_FILES += $(XRL_ERL_FILES)
49424898
4943 YRL_FILES = $(sort $(call core_find,src/,*.yrl))
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
49444900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
49454901 ERL_FILES += $(YRL_ERL_FILES)
49464902
49474903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4948 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
49494905
49504906 # Erlang and Core Erlang files.
49514907
49954951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49964952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49974953 (F, Mod, import, {Imp, _}) ->
4998 case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
49994960 false -> ok;
50004961 true -> Add(Mod, Imp)
50014962 end;
50194980 end || F <- ErlFiles],
50204981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
50214982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
50224991 ok = file:write_file("$(1)", [
50234992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
5024 "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n"
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
50254994 ]),
50264995 halt()
50274996 endef
50345003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
50355004 # Rebuild everything when the Makefile changes.
50365005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5037 @mkdir -p $(ERLANG_MK_TMP)
5038 @if test -f $@; then \
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
50395008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
50405009 touch -c $(PROJECT).d; \
50415010 fi
5042 @touch $@
5011 $(verbose) touch $@
50435012
50445013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
50455014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
50465015 endif
50475016
5048 -include $(PROJECT).d
5017 include $(wildcard $(PROJECT).d)
50495018
50505019 ebin/$(PROJECT).app:: ebin/
50515020
50645033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
50655034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
50665035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5067 $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
50685037 > ebin/$(PROJECT).app
50695038 else
50705039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
50885057
50895058 endif
50905059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
50915061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
50925062 # This file is part of erlang.mk and subject to the terms of the ISC License.
50935063
51055075 doc-deps:
51065076 else
51075077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5108 $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5109 endif
5110
5111 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51125082 # This file is part of erlang.mk and subject to the terms of the ISC License.
51135083
51145084 .PHONY: rel-deps
51255095 rel-deps:
51265096 else
51275097 rel-deps: $(ALL_REL_DEPS_DIRS)
5128 $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5129 endif
5130
5131 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51325102 # This file is part of erlang.mk and subject to the terms of the ISC License.
51335103
51345104 .PHONY: test-deps test-dir test-build clean-test-dir
51505120 test-deps:
51515121 else
51525122 test-deps: $(ALL_TEST_DEPS_DIRS)
5153 $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
51545124 endif
51555125
51565126 ifneq ($(wildcard $(TEST_DIR)),)
51835153 endif
51845154 endif
51855155
5186 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51875157 # This file is part of erlang.mk and subject to the terms of the ISC License.
51885158
51895159 .PHONY: rebar.config
52195189 rebar.config:
52205190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
52215191
5222 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
52235193 # This file is part of erlang.mk and subject to the terms of the ISC License.
52245194
5225 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
5226
5227 MAN_INSTALL_PATH ?= /usr/local/share/man
5228 MAN_SECTIONS ?= 3 7
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
52295200
52305201 docs:: asciidoc
52315202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
52325207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
52335210
52345211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
52355212 asciidoc-guide:
52365213 else
5237 asciidoc-guide: distclean-asciidoc doc-deps
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
52385215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
52395216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5240 endif
5241
5242 ifeq ($(wildcard doc/src/manual/*.asciidoc),)
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
52435227 asciidoc-manual:
52445228 else
5245 asciidoc-manual: distclean-asciidoc doc-deps
5246 for f in doc/src/manual/*.asciidoc ; do \
5247 a2x -v -f manpage $$f ; \
5248 done
5249 for s in $(MAN_SECTIONS); do \
5250 mkdir -p doc/man$$s/ ; \
5251 mv doc/src/manual/*.$$s doc/man$$s/ ; \
5252 gzip doc/man$$s/*.$$s ; \
5253 done
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
52545262
52555263 install-docs:: install-asciidoc
52565264
52575265 install-asciidoc: asciidoc-manual
5258 for s in $(MAN_SECTIONS); do \
5259 mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
5260 install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
5261 done
5262 endif
5263
5264 distclean:: distclean-asciidoc
5265
5266 distclean-asciidoc:
5267 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
5268
5269 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
52705276 # This file is part of erlang.mk and subject to the terms of the ISC License.
52715277
52725278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
53235329 define bs_Makefile
53245330 PROJECT = $p
53255331 PROJECT_DESCRIPTION = New project
5326 PROJECT_VERSION = 0.0.1
5332 PROJECT_VERSION = 0.1.0
53275333
53285334 # Whitespace to be used when creating files from templates.
53295335 SP = $(SP)
53335339 define bs_Makefile
53345340 PROJECT = $p
53355341 PROJECT_DESCRIPTION = New project
5336 PROJECT_VERSION = 0.0.1
5342 PROJECT_VERSION = 0.1.0
53375343
53385344 endef
53395345 endif
53415347 define bs_apps_Makefile
53425348 PROJECT = $p
53435349 PROJECT_DESCRIPTION = New project
5344 PROJECT_VERSION = 0.0.1
5350 PROJECT_VERSION = 0.1.0
53455351
53465352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
53475353 endef
53615367 endef
53625368
53635369 define bs_relx_config
5364 {release, {$p_release, "1"}, [$p]}.
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
53655371 {extended_start_script, true}.
53665372 {sys_config, "rel/sys.config"}.
53675373 {vm_args, "rel/vm.args"}.
57125718 ifndef t
57135719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57145720 endif
5715 ifndef tpl_$(t)
5716 $(error Unknown template)
5717 endif
57185721 ifndef n
57195722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57205723 endif
57275730 list-templates:
57285731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
57295732
5730 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
57315734 # This file is part of erlang.mk and subject to the terms of the ISC License.
57325735
57335736 .PHONY: clean-c_src distclean-c_src-env
59625965 $(call render_template,bs_erl_nif,src/$n.erl)
59635966 endif
59645967
5965 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
59665969 # This file is part of erlang.mk and subject to the terms of the ISC License.
59675970
5968 .PHONY: ci ci-setup distclean-kerl
5969
5970 KERL ?= $(CURDIR)/kerl
5971 export KERL
5972
5973 KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
5974
5975 OTP_GIT ?= https://github.com/erlang/otp
5976
5977 CI_INSTALL_DIR ?= $(HOME)/erlang
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
59785973 CI_OTP ?=
5979
5980 ifeq ($(strip $(CI_OTP)),)
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
59815986 ci::
59825987 else
5983 ci:: $(addprefix ci-,$(CI_OTP))
5984
5985 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
59866007
59876008 ci-setup::
6009
6010 ci-extra::
59886011
59896012 ci_verbose_0 = @echo " CI " $(1);
59906013 ci_verbose = $(ci_verbose_$(V))
59916014
59926015 define ci_target
5993 ci-$(1): $(CI_INSTALL_DIR)/$(1)
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
59946018 $(ci_verbose) \
5995 PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
5996 CI_OTP_RELEASE="$(1)" \
5997 CT_OPTS="-label $(1)" \
5998 $(MAKE) clean ci-setup tests
5999 endef
6000
6001 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
60026030
60036031 define ci_otp_target
60046032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
60056033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6006 $(KERL) build git $(OTP_GIT) $(1) $(1)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
60076035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
60086036 endif
60096037 endef
60106038
60116039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
60126040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
60136052 $(KERL):
6014 $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
60156056 $(verbose) chmod +x $(KERL)
60166057
60176058 help::
60286069 $(gen_verbose) rm -rf $(KERL)
60296070 endif
60306071
6031 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
60326073 # This file is part of erlang.mk and subject to the terms of the ISC License.
60336074
60346075 .PHONY: ct apps-ct distclean-ct
60366077 # Configuration.
60376078
60386079 CT_OPTS ?=
6080
60396081 ifneq ($(wildcard $(TEST_DIR)),)
6040 CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6041 else
6042 CT_SUITES ?=
6043 endif
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
60446088
60456089 # Core targets.
60466090
60636107 -noinput \
60646108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
60656109 -dir $(TEST_DIR) \
6066 -logdir $(CURDIR)/logs
6110 -logdir $(CT_LOGS_DIR)
60676111
60686112 ifeq ($(CT_SUITES),)
60696113 ct: $(if $(IS_APP),,apps-ct)
60706114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
60716117 ct: test-build $(if $(IS_APP),,apps-ct)
6072 $(verbose) mkdir -p $(CURDIR)/logs/
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
60736119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
60746121 endif
60756122
60766123 ifneq ($(ALL_APPS_DIRS),)
60976144
60986145 define ct_suite_target
60996146 ct-$(1): test-build
6100 $(verbose) mkdir -p $(CURDIR)/logs/
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
61016148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
61026149 endef
61036150
61046151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
61056152
61066153 distclean-ct:
6107 $(gen_verbose) rm -rf $(CURDIR)/logs/
6108
6109 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61106157 # This file is part of erlang.mk and subject to the terms of the ISC License.
61116158
61126159 .PHONY: plt distclean-plt dialyze
61506197 endef
61516198
61526199 $(DIALYZER_PLT): deps app
6153 $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
61546204
61556205 plt: $(DIALYZER_PLT)
61566206
61646214 endif
61656215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
61666216
6167 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61686218 # This file is part of erlang.mk and subject to the terms of the ISC License.
61696219
61706220 .PHONY: distclean-edoc edoc
61726222 # Configuration.
61736223
61746224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
61756235
61766236 # Core targets.
61776237
6178 ifneq ($(wildcard doc/overview.edoc),)
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
61796239 docs:: edoc
61806240 endif
61816241
61846244 # Plugin-specific targets.
61856245
61866246 edoc: distclean-edoc doc-deps
6187 $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
61886248
61896249 distclean-edoc:
61906250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
61916251
6192 # Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61936253 # This file is part of erlang.mk and subject to the terms of the ISC License.
61946254
6195 .PHONY: distclean-escript escript
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
61966320
61976321 # Configuration.
61986322
61996323 ESCRIPT_NAME ?= $(PROJECT)
62006324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
62016325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
62026327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6203
6204 ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
6205 ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
6206 ESCRIPT_EMU_ARGS ?= -pa . \
6207 -sasl errlog_type error \
6208 -escript main $(ESCRIPT_NAME)
6209 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6210 ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
62116332
62126333 # Core targets.
62136334
62206341
62216342 # Plugin-specific targets.
62226343
6223 # Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
6224 # Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
6225 # Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
6226 # Software may only be used for the great good and the true happiness of all
6227 # sentient beings.
6228
6229 define ESCRIPT_RAW
6230 'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
6231 'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
6232 ' [F || F <- A, not filelib:is_dir(F) ] end,'\
6233 'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
6234 'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
6235 'Ez = fun(Escript) ->'\
6236 ' Static = Files([$(ESCRIPT_STATIC)]),'\
6237 ' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
6238 ' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
6239 ' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
6240 ' {archive, Archive, [memory]},'\
6241 ' {shebang, "$(ESCRIPT_SHEBANG)"},'\
6242 ' {comment, "$(ESCRIPT_COMMENT)"},'\
6243 ' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
6244 ' ]),'\
6245 ' file:change_mode(Escript, 8#755)'\
6246 'end,'\
6247 'Ez("$(ESCRIPT_FILE)"),'\
6248 'halt().'
6249 endef
6250
6251 ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
6252
6253 escript:: distclean-escript deps app
6254 $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
62556360
62566361 distclean-escript:
6257 $(gen_verbose) rm -f $(ESCRIPT_NAME)
6258
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
62596365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6260 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
62616366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
62626367
62636368 .PHONY: eunit apps-eunit
63216426
63226427 ifneq ($(ALL_APPS_DIRS),)
63236428 apps-eunit:
6324 $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done
6325 endif
6326 endif
6327
6328 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
63296436 # This file is part of erlang.mk and subject to the terms of the ISC License.
63306437
6331 .PHONY: relx-rel distclean-relx-rel distclean-relx run
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
63326439
63336440 # Configuration.
63346441
6335 RELX ?= $(CURDIR)/relx
6442 RELX ?= $(ERLANG_MK_TMP)/relx
63366443 RELX_CONFIG ?= $(CURDIR)/relx.config
63376444
63386445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
63396446 RELX_OPTS ?=
63406447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
63416454
63426455 ifeq ($(firstword $(RELX_OPTS)),-o)
63436456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
63506463 ifeq ($(IS_DEP),)
63516464 ifneq ($(wildcard $(RELX_CONFIG)),)
63526465 rel:: relx-rel
6353 endif
6354 endif
6355
6356 distclean:: distclean-relx-rel distclean-relx
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
63576472
63586473 # Plugin-specific targets.
63596474
63626477 $(verbose) chmod +x $(RELX)
63636478
63646479 relx-rel: $(RELX) rel-deps app
6365 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
63666484
63676485 distclean-relx-rel:
63686486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6369
6370 distclean-relx:
6371 $(gen_verbose) rm -rf $(RELX)
63726487
63736488 # Run target.
63746489
63776492 else
63786493
63796494 define get_relx_release.erl
6380 {ok, Config} = file:consult("$(RELX_CONFIG)"),
6381 {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
6382 io:format("~s", [Name]),
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
63836504 halt(0).
63846505 endef
63856506
6386 RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
63876514
63886515 run: all
6389 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
63906517
63916518 help::
63926519 $(verbose) printf "%s\n" "" \
63956522
63966523 endif
63976524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
63986526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6399 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
64006527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
64016528
64026529 .PHONY: shell
64216548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
64226549
64236550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6424 $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
64256552
64266553 shell: build-shell-deps
64276554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
64286555
6429 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
64306571 # This file is part of erlang.mk and subject to the terms of the ISC License.
64316572
64326573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
64376578 tests:: triq
64386579
64396580 define triq_check.erl
6440 code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
64416582 try
64426583 case $(1) of
64436584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
64696610 endif
64706611 endif
64716612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
64726614 # Copyright (c) 2015, Erlang Solutions Ltd.
64736615 # This file is part of erlang.mk and subject to the terms of the ISC License.
64746616
64856627 XREFR ?= $(CURDIR)/xrefr
64866628 export XREFR
64876629
6488 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
64896631
64906632 # Core targets.
64916633
64926634 help::
6493 $(verbose) printf "%s\n" "" \
6494 "Xref targets:" \
6495 " xref Run Xrefr using $XREF_CONFIG as config file if defined"
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
64966638
64976639 distclean:: distclean-xref
64986640
65086650 distclean-xref:
65096651 $(gen_verbose) rm -rf $(XREFR)
65106652
6511 # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
65126655 # This file is part of erlang.mk and subject to the terms of the ISC License.
65136656
65146657 COVER_REPORT_DIR = cover
65176660
65186661 ifdef COVER
65196662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
65206664 # All modules in 'ebin'
65216665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
65226666
65316675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
65326676 endif
65336677 endif
6678 endif
65346679
65356680 # Core targets
65366681
65526697 "Cover targets:" \
65536698 " cover-report Generate a HTML coverage report from previously collected" \
65546699 " cover data." \
6555 " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
65566701 "" \
65576702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
65586703 "target tests additionally generates a HTML coverage report from the combined" \
65656710
65666711 .PHONY: coverdata-clean
65676712 coverdata-clean:
6568 $(gen_verbose) rm -f *.coverdata ct.cover.spec
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
65696714
65706715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
65716721 all.coverdata: $(COVERDATA)
6572 $(gen_verbose) $(ERL) -eval ' \
6573 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
6574 cover:export("$@"), halt(0).'
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
65756723
65766724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
65776725 # empty if you want the coverdata files but not the HTML report.
65896737 # Modules which include eunit.hrl always contain one line without coverage
65906738 # because eunit defines test/0 which is never called. We compensate for this.
65916739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6592 grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
65936741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
65946742
65956743 define cover_report.erl
66246772 endef
66256773
66266774 cover-report:
6627 $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
66286776 $(gen_verbose) $(call erlang,$(cover_report.erl))
66296777
66306778 endif
66316779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
66326839
66336840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
66346841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
66976904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
66986905 endif
66996906 ifndef IS_APP
6700 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
67016908 $(MAKE) -C $$dep $@ \
67026909 IS_APP=1 \
6703 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6704 || exit $$?; \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67056911 done
67066912 endif
6707 $(verbose) for dep in $^ ; do \
6913 $(verbose) set -e; for dep in $^ ; do \
67086914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
67096915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6710 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
67116917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
67126918 $(MAKE) -C $$dep fetch-deps \
67136919 IS_DEP=1 \
6714 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6715 || exit $$?; \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67166921 fi \
67176922 fi \
67186923 done
22 # before the inclusion of erlang.mk leading to the wrong target becoming
33 # the default.
44 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
526 endif
627
728 # --------------------------------------------------------------------
2041 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
2142 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
2243 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
2345 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
2446 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
2547 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
2648 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
2749 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
2851 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
2952 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
3054 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
3155 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
3256 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
3559 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
3660 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
3761 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
3863 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
3964 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
4065 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
7095
7196 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
7297
73 # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
74 # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
75 # needs to add "ranch" as a BUILD_DEPS. The list of projects needing
76 # this workaround are:
77 # o rabbitmq-web-stomp
78 dep_ranch = git https://github.com/ninenines/ranch 1.2.1
98 # Third-party dependencies version pinning.
99 #
100 # We do that in this file, which is copied in all projects, to ensure
101 # all projects use the same versions. It avoids conflicts and makes it
102 # possible to work with rabbitmq-public-umbrella.
103
104 dep_cowboy_commit = 1.0.4
105 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
106 dep_ranch_commit = 1.3.1
107 dep_webmachine_commit = 1.10.8p2
79108
80109 RABBITMQ_COMPONENTS = amqp_client \
81110 rabbit \
82111 rabbit_common \
83112 rabbitmq_amqp1_0 \
84113 rabbitmq_auth_backend_amqp \
114 rabbitmq_auth_backend_cache \
85115 rabbitmq_auth_backend_http \
86116 rabbitmq_auth_backend_ldap \
87117 rabbitmq_auth_mechanism_ssl \
88118 rabbitmq_boot_steps_visualiser \
89119 rabbitmq_clusterer \
120 rabbitmq_cli \
90121 rabbitmq_codegen \
91122 rabbitmq_consistent_hash_exchange \
123 rabbitmq_ct_client_helpers \
92124 rabbitmq_ct_helpers \
93125 rabbitmq_delayed_message_exchange \
94126 rabbitmq_dotnet_client \
97129 rabbitmq_federation_management \
98130 rabbitmq_java_client \
99131 rabbitmq_jms_client \
132 rabbitmq_jms_cts \
100133 rabbitmq_jms_topic_exchange \
101134 rabbitmq_lvc \
102135 rabbitmq_management \
1010 %% The Original Code is RabbitMQ Consistent Hash Exchange.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_exchange_type_consistent_hash).
+0
-7
deps/rabbitmq_consistent_hash_exchange/src/rabbitmq_consistent_hash_exchange.app.src less more
0 {application, rabbitmq_consistent_hash_exchange,
1 [{description, "Consistent Hash Exchange Type"},
2 {vsn, "3.6.6"},
3 {modules, []},
4 {registered, []},
5 {env, []},
6 {applications, [kernel, stdlib, rabbit_common, rabbit]}]}.
00 PROJECT = rabbitmq_event_exchange
1 PROJECT_DESCRIPTION = Event Exchange Type
12
23 DEPS = rabbit_common rabbit
3 TEST_DEPS = rabbitmq_ct_helpers amqp_client
4 TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers amqp_client
45
6 DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
57 DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk
68
79 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
2424 rabbitmq-plugins enable rabbitmq_event_exchange
2525
2626
27 ## Downloading
27 ## Downloading & Installation
28
29 ### With RabbitMQ 3.6.0 or Later
30
31 Most recent RabbitMQ version ships with this plugin.
32
33 ### With RabbitMQ 3.5.x
2834
2935 You can download a pre-built binary of this plugin from
3036 the [RabbitMQ Community Plugins](http://www.rabbitmq.com/community-plugins.html) page.
3137
32
33 ## Building
34
35 Building is no different from [building other RabbitMQ plugins](http://www.rabbitmq.com/plugin-development.html).
36
37 TL;DR:
38
39 git clone https://github.com.com/rabbitmq/rabbitmq-public-umbrella.git
40 cd rabbitmq-public-umbrella
41 make co
42 git clone https://github.com/rabbitmq/rabbitmq-event-exchange.git
43 cd rabbitmq-event-exchange
44 make -j
4538
4639 ## Event format
4740
5750
5851 Queue, Exchange and Binding events:
5952
60 - `queue.deleted`
61 - `queue.created`
62 - `exchange.created`
63 - `exchange.deleted`
64 - `binding.created`
65 - `binding.deleted`
53 * `queue.deleted`
54 * `queue.created`
55 * `exchange.created`
56 * `exchange.deleted`
57 * `binding.created`
58 * `binding.deleted`
6659
6760 Connection and Channel events:
6861
69 - `connection.created`
70 - `connection.closed`
71 - `channel.created`
72 - `channel.closed`
62 * `connection.created`
63 * `connection.closed`
64 * `channel.created`
65 * `channel.closed`
7366
7467 Consumer events:
7568
76 - `consumer.created`
77 - `consumer.deleted`
69 * `consumer.created`
70 * `consumer.deleted`
7871
7972 Policy and Parameter events:
8073
81 - `policy.set`
82 - `policy.cleared`
83 - `parameter.set`
84 - `parameter.cleared`
74 * `policy.set`
75 * `policy.cleared`
76 * `parameter.set`
77 * `parameter.cleared`
8578
8679 Virtual host events:
8780
88 - `vhost.created`
89 - `vhost.deleted`
81 * `vhost.created`
82 * `vhost.deleted`
9083
9184 User related events:
9285
93 - `user.authentication.success`
94 - `user.authentication.failure`
95 - `user.created`
96 - `user.deleted`
97 - `user.password.changed`
98 - `user.password.cleared`
99 - `user.tags.set`
86 * `user.authentication.success`
87 * `user.authentication.failure`
88 * `user.created`
89 * `user.deleted`
90 * `user.password.changed`
91 * `user.password.cleared`
92 * `user.tags.set`
10093
10194 Permission events:
10295
103 - `permission.created`
104 - `permission.deleted`
96 * `permission.created`
97 * `permission.deleted`
98
99 Alarm events:
100
101 * `alarm.set`
102 * `alarm.cleared`
105103
106104 ### Shovel Plugin
107105
108106 Worker events:
109107
110 - `shovel.worker.status`
111 - `shovel.worker.removed`
108 * `shovel.worker.status`
109 * `shovel.worker.removed`
112110
113111 ### Federation Plugin
114112
115113 Link events:
116114
117 - `federation.link.status`
118 - `federation.link.removed`
115 * `federation.link.status`
116 * `federation.link.removed`
119117
120118 ## Example
121119
129127
130128 rabbitmqctl eval 'rabbit_exchange:delete(rabbit_misc:r(<<"/">>, exchange, <<"amq.rabbitmq.event">>), false).'
131129
130
131 ## Building from Source
132
133 Building is no different from [building other RabbitMQ plugins](http://www.rabbitmq.com/plugin-development.html).
134
135 TL;DR:
136
137 git clone https://github.com.com/rabbitmq/rabbitmq-public-umbrella.git
138 cd rabbitmq-public-umbrella
139 make co
140 make up BRANCH=stable
141 cd deps
142 git clone https://github.com/rabbitmq/rabbitmq-event-exchange.git rabbitmq_event_exchange
143 cd rabbitmq_event_exchange
144 make dist
145
146
132147 ## License
133148
134149 Released under the Mozilla Public License 1.1,
0 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
11 #
22 # Permission to use, copy, modify, and/or distribute this software for any
33 # purpose with or without fee is hereby granted, provided that the above
1111 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1212 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1313
14 .PHONY: all app apps deps search rel docs install-docs check tests clean distclean help erlang-mk
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
1515
1616 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17
18 ERLANG_MK_VERSION = 2.0.0-pre.2-144-g647ffd1
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
1931
2032 # Core configuration.
2133
2436
2537 PROJECT_VERSION ?= rolling
2638 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
2740
2841 # Verbosity.
2942
8497 rel::
8598 $(verbose) :
8699
100 relup:: deps app
101
87102 check:: tests
88103
89104 clean:: clean-crashdump
101116 help::
102117 $(verbose) printf "%s\n" \
103118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
104 "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
105120 "" \
106121 "Usage: [V=1] $(MAKE) [target]..." \
107122 "" \
149164 core_native_path = $1
150165 endif
151166
152 ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
153 define core_http_get
154 wget --no-check-certificate -O $(1) $(2)|| rm $(1)
155 endef
156 else
157 define core_http_get.erl
158 ssl:start(),
159 inets:start(),
160 case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
161 {ok, {{_, 200, _}, _, Body}} ->
162 case file:write_file("$(1)", Body) of
163 ok -> ok;
164 {error, R1} -> halt(R1)
165 end;
166 {error, R2} ->
167 halt(R2)
168 end,
169 halt(0).
170 endef
171
172 define core_http_get
173 $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
174 endef
175 endif
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
176168
177169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
178170
192184 ERLANG_MK_BUILD_CONFIG ?= build.config
193185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
194186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
195188 erlang-mk:
196189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
197190 ifdef ERLANG_MK_COMMIT
198191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
199192 endif
200193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
201 $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
202195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
203196 rm -rf $(ERLANG_MK_BUILD_DIR)
204197
285278 pkg_apns_repo = https://github.com/inaka/apns4erl
286279 pkg_apns_commit = master
287280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
288289 PACKAGES += azdht
289290 pkg_azdht_name = azdht
290291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
405406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
406407 pkg_bootstrap_commit = master
407408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
408417 PACKAGES += boss_db
409418 pkg_boss_db_name = boss_db
410419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
413422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
414423 pkg_boss_db_commit = master
415424
416 PACKAGES += boss
417 pkg_boss_name = boss
418 pkg_boss_description = Erlang web MVC, now featuring Comet
419 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
420 pkg_boss_fetch = git
421 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
422 pkg_boss_commit = master
423
424425 PACKAGES += brod
425426 pkg_brod_name = brod
426427 pkg_brod_description = Kafka client in Erlang
565566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
566567 pkg_cloudi_service_api_requests_commit = master
567568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
568585 PACKAGES += cloudi_service_db_cassandra_cql
569586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
570587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
573590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
574591 pkg_cloudi_service_db_cassandra_cql_commit = master
575592
576 PACKAGES += cloudi_service_db_cassandra
577 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
579 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
580 pkg_cloudi_service_db_cassandra_fetch = git
581 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
582 pkg_cloudi_service_db_cassandra_commit = master
583
584593 PACKAGES += cloudi_service_db_couchdb
585594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
586595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
637646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
638647 pkg_cloudi_service_db_tokyotyrant_commit = master
639648
640 PACKAGES += cloudi_service_db
641 pkg_cloudi_service_db_name = cloudi_service_db
642 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
643 pkg_cloudi_service_db_homepage = http://cloudi.org/
644 pkg_cloudi_service_db_fetch = git
645 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
646 pkg_cloudi_service_db_commit = master
647
648649 PACKAGES += cloudi_service_filesystem
649650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
650651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
10371038 pkg_edown_repo = https://github.com/uwiger/edown
10381039 pkg_edown_commit = master
10391040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
10401049 PACKAGES += eep_app
10411050 pkg_eep_app_name = eep_app
10421051 pkg_eep_app_description = Embedded Event Processing
10451054 pkg_eep_app_repo = https://github.com/darach/eep-erl
10461055 pkg_eep_app_commit = master
10471056
1048 PACKAGES += eep
1049 pkg_eep_name = eep
1050 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1051 pkg_eep_homepage = https://github.com/virtan/eep
1052 pkg_eep_fetch = git
1053 pkg_eep_repo = https://github.com/virtan/eep
1054 pkg_eep_commit = master
1055
10561057 PACKAGES += efene
10571058 pkg_efene_name = efene
10581059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
12371238 pkg_eqm_repo = https://github.com/loucash/eqm
12381239 pkg_eqm_commit = master
12391240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
12401249 PACKAGES += eredis_pool
12411250 pkg_eredis_pool_name = eredis_pool
12421251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
12451254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
12461255 pkg_eredis_pool_commit = master
12471256
1248 PACKAGES += eredis
1249 pkg_eredis_name = eredis
1250 pkg_eredis_description = Erlang Redis client
1251 pkg_eredis_homepage = https://github.com/wooga/eredis
1252 pkg_eredis_fetch = git
1253 pkg_eredis_repo = https://github.com/wooga/eredis
1254 pkg_eredis_commit = master
1255
12561257 PACKAGES += erl_streams
12571258 pkg_erl_streams_name = erl_streams
12581259 pkg_erl_streams_description = Streams in Erlang
15411542 pkg_etap_repo = https://github.com/ngerakines/etap
15421543 pkg_etap_commit = master
15431544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
15441553 PACKAGES += etest_http
15451554 pkg_etest_http_name = etest_http
15461555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
15491558 pkg_etest_http_repo = https://github.com/wooga/etest_http
15501559 pkg_etest_http_commit = master
15511560
1552 PACKAGES += etest
1553 pkg_etest_name = etest
1554 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1555 pkg_etest_homepage = https://github.com/wooga/etest
1556 pkg_etest_fetch = git
1557 pkg_etest_repo = https://github.com/wooga/etest
1558 pkg_etest_commit = master
1559
15601561 PACKAGES += etoml
15611562 pkg_etoml_name = etoml
15621563 pkg_etoml_description = TOML language erlang parser
15651566 pkg_etoml_repo = https://github.com/kalta/etoml
15661567 pkg_etoml_commit = master
15671568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
15681577 PACKAGES += eunit_formatters
15691578 pkg_eunit_formatters_name = eunit_formatters
15701579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
15731582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
15741583 pkg_eunit_formatters_commit = master
15751584
1576 PACKAGES += eunit
1577 pkg_eunit_name = eunit
1578 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1579 pkg_eunit_homepage = https://github.com/richcarl/eunit
1580 pkg_eunit_fetch = git
1581 pkg_eunit_repo = https://github.com/richcarl/eunit
1582 pkg_eunit_commit = master
1583
15841585 PACKAGES += euthanasia
15851586 pkg_euthanasia_name = euthanasia
15861587 pkg_euthanasia_description = Merciful killer for your Erlang processes
15981599 pkg_evum_commit = master
15991600
16001601 PACKAGES += exec
1601 pkg_exec_name = exec
1602 pkg_exec_name = erlexec
16021603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
16031604 pkg_exec_homepage = http://saleyn.github.com/erlexec
16041605 pkg_exec_fetch = git
17171718 pkg_fn_repo = https://github.com/reiddraper/fn
17181719 pkg_fn_commit = master
17191720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
17201729 PACKAGES += folsom_cowboy
17211730 pkg_folsom_cowboy_name = folsom_cowboy
17221731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
17251734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
17261735 pkg_folsom_cowboy_commit = master
17271736
1728 PACKAGES += folsom
1729 pkg_folsom_name = folsom
1730 pkg_folsom_description = Expose Erlang Events and Metrics
1731 pkg_folsom_homepage = https://github.com/boundary/folsom
1732 pkg_folsom_fetch = git
1733 pkg_folsom_repo = https://github.com/boundary/folsom
1734 pkg_folsom_commit = master
1735
17361737 PACKAGES += folsomite
17371738 pkg_folsomite_name = folsomite
17381739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
20932094 pkg_jesse_repo = https://github.com/for-GET/jesse
20942095 pkg_jesse_commit = master
20952096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
20962105 PACKAGES += jiffy_v
20972106 pkg_jiffy_v_name = jiffy_v
20982107 pkg_jiffy_v_description = JSON validation utility
21012110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
21022111 pkg_jiffy_v_commit = master
21032112
2104 PACKAGES += jiffy
2105 pkg_jiffy_name = jiffy
2106 pkg_jiffy_description = JSON NIFs for Erlang.
2107 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2108 pkg_jiffy_fetch = git
2109 pkg_jiffy_repo = https://github.com/davisp/jiffy
2110 pkg_jiffy_commit = master
2111
21122113 PACKAGES += jobs
21132114 pkg_jobs_name = jobs
21142115 pkg_jobs_description = a Job scheduler for load regulation
21252126 pkg_joxa_repo = https://github.com/joxa/joxa
21262127 pkg_joxa_commit = master
21272128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
21282137 PACKAGES += json_rec
21292138 pkg_json_rec_name = json_rec
21302139 pkg_json_rec_description = JSON to erlang record
21332142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
21342143 pkg_json_rec_commit = master
21352144
2136 PACKAGES += json
2137 pkg_json_name = json
2138 pkg_json_description = a high level json library for erlang (17.0+)
2139 pkg_json_homepage = https://github.com/talentdeficit/json
2140 pkg_json_fetch = git
2141 pkg_json_repo = https://github.com/talentdeficit/json
2142 pkg_json_commit = master
2143
21442145 PACKAGES += jsone
21452146 pkg_jsone_name = jsone
21462147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
21812182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
21822183 pkg_jsx_commit = master
21832184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
21842193 PACKAGES += kafka_protocol
21852194 pkg_kafka_protocol_name = kafka_protocol
21862195 pkg_kafka_protocol_description = Kafka protocol Erlang library
21892198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
21902199 pkg_kafka_protocol_commit = master
21912200
2192 PACKAGES += kafka
2193 pkg_kafka_name = kafka
2194 pkg_kafka_description = Kafka consumer and producer in Erlang
2195 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2196 pkg_kafka_fetch = git
2197 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2198 pkg_kafka_commit = master
2199
22002201 PACKAGES += kai
22012202 pkg_kai_name = kai
22022203 pkg_kai_description = DHT storage by Takeshi Inoue
22932294 pkg_kvs_repo = https://github.com/synrc/kvs
22942295 pkg_kvs_commit = master
22952296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
22962305 PACKAGES += lager_amqp_backend
22972306 pkg_lager_amqp_backend_name = lager_amqp_backend
22982307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
23092318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
23102319 pkg_lager_syslog_commit = master
23112320
2312 PACKAGES += lager
2313 pkg_lager_name = lager
2314 pkg_lager_description = A logging framework for Erlang/OTP.
2315 pkg_lager_homepage = https://github.com/basho/lager
2316 pkg_lager_fetch = git
2317 pkg_lager_repo = https://github.com/basho/lager
2318 pkg_lager_commit = master
2319
23202321 PACKAGES += lambdapad
23212322 pkg_lambdapad_name = lambdapad
23222323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
25732574 pkg_mixer_repo = https://github.com/chef/mixer
25742575 pkg_mixer_commit = master
25752576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
25762585 PACKAGES += mochiweb_xpath
25772586 pkg_mochiweb_xpath_name = mochiweb_xpath
25782587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
25812590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
25822591 pkg_mochiweb_xpath_commit = master
25832592
2584 PACKAGES += mochiweb
2585 pkg_mochiweb_name = mochiweb
2586 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2587 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2588 pkg_mochiweb_fetch = git
2589 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2590 pkg_mochiweb_commit = master
2591
25922593 PACKAGES += mockgyver
25932594 pkg_mockgyver_name = mockgyver
25942595 pkg_mockgyver_description = A mocking library for Erlang
30613062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
30623063 pkg_quickrand_commit = master
30633064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
30643073 PACKAGES += rabbit_exchange_type_riak
30653074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
30663075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
30693078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
30703079 pkg_rabbit_exchange_type_riak_commit = master
30713080
3072 PACKAGES += rabbit
3073 pkg_rabbit_name = rabbit
3074 pkg_rabbit_description = RabbitMQ Server
3075 pkg_rabbit_homepage = https://www.rabbitmq.com/
3076 pkg_rabbit_fetch = git
3077 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3078 pkg_rabbit_commit = master
3079
30803081 PACKAGES += rack
30813082 pkg_rack_name = rack
30823083 pkg_rack_description = Rack handler for erlang
34933494 pkg_smother_repo = https://github.com/ramsay-t/Smother
34943495 pkg_smother_commit = master
34953496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
34963505 PACKAGES += social
34973506 pkg_social_name = social
34983507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
35413550 pkg_stable_repo = https://github.com/dvv/stable
35423551 pkg_stable_commit = master
35433552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
35443561 PACKAGES += statebox_riak
35453562 pkg_statebox_riak_name = statebox_riak
35463563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
35493566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
35503567 pkg_statebox_riak_commit = master
35513568
3552 PACKAGES += statebox
3553 pkg_statebox_name = statebox
3554 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3555 pkg_statebox_homepage = https://github.com/mochi/statebox
3556 pkg_statebox_fetch = git
3557 pkg_statebox_repo = https://github.com/mochi/statebox
3558 pkg_statebox_commit = master
3559
35603569 PACKAGES += statman
35613570 pkg_statman_name = statman
35623571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
37253734 pkg_tirerl_repo = https://github.com/inaka/tirerl
37263735 pkg_tirerl_commit = master
37273736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
37283745 PACKAGES += traffic_tools
37293746 pkg_traffic_tools_name = traffic_tools
37303747 pkg_traffic_tools_description = Simple traffic limiting library
40614078 pkg_zucchini_repo = https://github.com/devinus/zucchini
40624079 pkg_zucchini_commit = master
40634080
4064 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
40654082 # This file is part of erlang.mk and subject to the terms of the ISC License.
40664083
40674084 .PHONY: search
40884105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
40894106 endif
40904107
4091 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
40924109 # This file is part of erlang.mk and subject to the terms of the ISC License.
40934110
4094 .PHONY: distclean-deps
4111 .PHONY: distclean-deps clean-tmp-deps.log
40954112
40964113 # Configuration.
40974114
41104127
41114128 REBAR_DEPS_DIR = $(DEPS_DIR)
41124129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
41134150
41144151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
41154152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
41164153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
41174154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
41184155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
41194157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
41204158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
41214159
41384176
41394177 # Core targets.
41404178
4141 ifdef IS_APP
4142 apps::
4143 else
4144 apps:: $(ALL_APPS_DIRS)
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
41454180 ifeq ($(IS_APP)$(IS_DEP),)
41464181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
41474182 endif
41494184 # Create ebin directory for all apps to make sure Erlang recognizes them
41504185 # as proper OTP applications when using -include_lib. This is a temporary
41514186 # fix, a proper fix would be to compile apps/* in the right order.
4152 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4153 mkdir -p $$dep/ebin || exit $$?; \
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
41544190 done
4155 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
41564196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
41574197 :; \
41584198 else \
41594199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4160 $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
4200 $(MAKE) -C $$dep IS_APP=1; \
41614201 fi \
41624202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
41634207 endif
41644208
41654209 ifneq ($(SKIP_DEPS),)
41664210 deps::
41674211 else
4168 deps:: $(ALL_DEPS_DIRS) apps
4169 ifeq ($(IS_APP)$(IS_DEP),)
4170 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4171 endif
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
41724213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4173 $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
41744215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
41754216 :; \
41764217 else \
41774218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
41784219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4179 $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
4220 $(MAKE) -C $$dep IS_DEP=1; \
41804221 else \
4181 echo "Error: No Makefile to build dependency $$dep."; \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
41824223 exit 2; \
41834224 fi \
41844225 fi \
41924233 # in practice only Makefile is needed so far.
41934234 define dep_autopatch
41944235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
41954237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
41964238 $(call dep_autopatch_erlang_mk,$(1)); \
41974239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4198 if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
41994243 $(call dep_autopatch2,$(1)); \
4200 elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
42014245 $(call dep_autopatch2,$(1)); \
4202 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
42034247 $(call dep_autopatch2,$(1)); \
4204 else \
4205 $(call erlang,$(call dep_autopatch_app.erl,$(1))); \
42064248 fi \
42074249 else \
42084250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
42144256 endef
42154257
42164258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
42174262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
42184263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
42194264 fi; \
42204265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4221 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
42224267 $(call dep_autopatch_fetch_rebar); \
42234268 $(call dep_autopatch_rebar,$(1)); \
42244269 else \
42304275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
42314276 endef
42324277
4233 # Overwrite erlang.mk with the current file by default.
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
42344280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
42354281 define dep_autopatch_erlang_mk
4236 echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \
4237 > $(DEPS_DIR)/$1/erlang.mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
42384287 endef
42394288 else
42404289 define dep_autopatch_erlang_mk
42534302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
42544303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
42554304 cd $(ERLANG_MK_TMP)/rebar; \
4256 git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
42574306 $(MAKE); \
42584307 cd -; \
42594308 fi
42704319 define dep_autopatch_rebar.erl
42714320 application:load(rebar),
42724321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
42734323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
42744324 {ok, Conf0} -> Conf0;
42754325 _ -> []
44234473 [] -> ok;
44244474 _ ->
44254475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4426 PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
44274477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4428 PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L \\"~s\\" -lerl_interface -lei\n",
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
44294479 [code:lib_dir(erl_interface, lib)])),
44304480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
44314481 FilterEnv = fun(Env) ->
44644514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44654515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44664516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4467 [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
44684518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
44694519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
44704520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
44764526 end,
44774527 [PortSpec(S) || S <- PortSpecs]
44784528 end,
4479 Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"),
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
44804530 RunPlugin = fun(Plugin, Step) ->
44814531 case erlang:function_exported(Plugin, Step, 2) of
44824532 false -> ok;
45244574 halt()
45254575 endef
45264576
4527 define dep_autopatch_app.erl
4528 UpdateModules = fun(App) ->
4529 case filelib:is_regular(App) of
4530 false -> ok;
4531 true ->
4532 {ok, [{application, '$(1)', L0}]} = file:consult(App),
4533 Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true,
4534 fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
4535 L = lists:keystore(modules, 1, L0, {modules, Mods}),
4536 ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}]))
4537 end
4538 end,
4539 UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"),
4540 halt()
4541 endef
4542
45434577 define dep_autopatch_appsrc_script.erl
45444578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
45454579 AppSrcScript = AppSrc ++ ".script",
45874621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
45884622 endef
45894623
4590 define dep_fetch_hex.erl
4591 ssl:start(),
4592 inets:start(),
4593 {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
4594 {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
4595 [], [{body_format, binary}]),
4596 {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
4597 {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
4598 ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
4599 halt()
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
46004626 endef
46014627
46024628 # Hex only has a package version. No need to look in the Erlang.mk packages.
46034629 define dep_fetch_hex
4604 $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
46054634 endef
46064635
46074636 define dep_fetch_fail
46314660 $(eval DEP_NAME := $(call dep_name,$1))
46324661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
46334662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4634 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
46354664 exit 17; \
46364665 fi
46374666 $(verbose) mkdir -p $(DEPS_DIR)
46734702 clean:: clean-apps
46744703
46754704 clean-apps:
4676 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4677 $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
46784707 done
46794708
46804709 distclean:: distclean-apps
46814710
46824711 distclean-apps:
4683 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4684 $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
46854714 done
46864715 endif
46874716
47014730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
47024731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
47034732
4704 # External plugins.
4705
4706 DEP_PLUGINS ?=
4707
4708 define core_dep_plugin
4709 -include $(DEPS_DIR)/$(1)
4710
4711 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4712 endef
4713
4714 $(foreach p,$(DEP_PLUGINS),\
4715 $(eval $(if $(findstring /,$p),\
4716 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4717 $(call core_dep_plugin,$p/plugins.mk,$p))))
4718
4719 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4720 # This file is part of erlang.mk and subject to the terms of the ISC License.
4721
4722 # Configuration.
4723
4724 DTL_FULL_PATH ?=
4725 DTL_PATH ?= templates/
4726 DTL_SUFFIX ?= _dtl
4727 DTL_OPTS ?=
4728
4729 # Verbosity.
4730
4731 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
4732 dtl_verbose = $(dtl_verbose_$(V))
4733
4734 # Core targets.
4735
4736 DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl))
4737
4738 ifneq ($(DTL_FILES),)
4739
4740 ifdef DTL_FULL_PATH
4741 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
4742 else
4743 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
4744 endif
4745
4746 ifneq ($(words $(DTL_FILES)),0)
4747 # Rebuild templates when the Makefile changes.
4748 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
4749 @mkdir -p $(ERLANG_MK_TMP)
4750 @if test -f $@; then \
4751 touch $(DTL_FILES); \
4752 fi
4753 @touch $@
4754
4755 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
4756 endif
4757
4758 define erlydtl_compile.erl
4759 [begin
4760 Module0 = case "$(strip $(DTL_FULL_PATH))" of
4761 "" ->
4762 filename:basename(F, ".dtl");
4763 _ ->
4764 "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
4765 re:replace(F2, "/", "_", [{return, list}, global])
4766 end,
4767 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
4768 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
4769 ok -> ok;
4770 {ok, _} -> ok
4771 end
4772 end || F <- string:tokens("$(1)", " ")],
4773 halt().
4774 endef
4775
4776 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
4777 $(if $(strip $?),\
4778 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
4779
4780 endif
4781
4782 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
47834734 # This file is part of erlang.mk and subject to the terms of the ISC License.
47844735
47854736 # Verbosity.
47984749
47994750 define compile_proto.erl
48004751 [begin
4801 Dir = filename:dirname(filename:dirname(F)),
48024752 protobuffs_compile:generate_source(F,
4803 [{output_include_dir, Dir ++ "/include"},
4804 {output_src_dir, Dir ++ "/ebin"}])
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
48054755 end || F <- string:tokens("$(1)", " ")],
48064756 halt().
48074757 endef
48114761 $(if $(strip $?),$(call compile_proto,$?))
48124762 endif
48134763
4814 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
48154765 # This file is part of erlang.mk and subject to the terms of the ISC License.
48164766
48174767 .PHONY: clean-app
48254775 ERLC_EXCLUDE ?=
48264776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
48274777
4778 ERLC_ASN1_OPTS ?=
4779
48284780 ERLC_MIB_OPTS ?=
48294781 COMPILE_MIB_FIRST ?=
48304782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
48744826
48754827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
48764828 define app_file
4877 {application, $(PROJECT), [
4829 {application, '$(PROJECT)', [
48784830 {description, "$(PROJECT_DESCRIPTION)"},
48794831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48804832 {id$(comma)$(space)"$(1)"}$(comma))
48814833 {modules, [$(call comma_list,$(2))]},
48824834 {registered, []},
4883 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48844837 ]}.
48854838 endef
48864839 else
48874840 define app_file
4888 {application, $(PROJECT), [
4841 {application, '$(PROJECT)', [
48894842 {description, "$(PROJECT_DESCRIPTION)"},
48904843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48914844 {id$(comma)$(space)"$(1)"}$(comma))
48924845 {modules, [$(call comma_list,$(2))]},
48934846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
48944847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4895 {mod, {$(PROJECT_MOD), []}}
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48964850 ]}.
48974851 endef
48984852 endif
49024856
49034857 # Source files.
49044858
4905 ERL_FILES = $(sort $(call core_find,src/,*.erl))
4906 CORE_FILES = $(sort $(call core_find,src/,*.core))
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
49074863
49084864 # ASN.1 files.
49094865
49134869
49144870 define compile_asn1
49154871 $(verbose) mkdir -p include/
4916 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
49174873 $(verbose) mv asn1/*.erl src/
49184874 $(verbose) mv asn1/*.hrl include/
49194875 $(verbose) mv asn1/*.asn1db include/
49364892
49374893 # Leex and Yecc files.
49384894
4939 XRL_FILES = $(sort $(call core_find,src/,*.xrl))
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
49404896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
49414897 ERL_FILES += $(XRL_ERL_FILES)
49424898
4943 YRL_FILES = $(sort $(call core_find,src/,*.yrl))
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
49444900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
49454901 ERL_FILES += $(YRL_ERL_FILES)
49464902
49474903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4948 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
49494905
49504906 # Erlang and Core Erlang files.
49514907
49954951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49964952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49974953 (F, Mod, import, {Imp, _}) ->
4998 case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
49994960 false -> ok;
50004961 true -> Add(Mod, Imp)
50014962 end;
50194980 end || F <- ErlFiles],
50204981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
50214982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
50224991 ok = file:write_file("$(1)", [
50234992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
5024 "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n"
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
50254994 ]),
50264995 halt()
50274996 endef
50345003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
50355004 # Rebuild everything when the Makefile changes.
50365005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5037 @mkdir -p $(ERLANG_MK_TMP)
5038 @if test -f $@; then \
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
50395008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
50405009 touch -c $(PROJECT).d; \
50415010 fi
5042 @touch $@
5011 $(verbose) touch $@
50435012
50445013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
50455014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
50465015 endif
50475016
5048 -include $(PROJECT).d
5017 include $(wildcard $(PROJECT).d)
50495018
50505019 ebin/$(PROJECT).app:: ebin/
50515020
50645033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
50655034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
50665035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5067 $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
50685037 > ebin/$(PROJECT).app
50695038 else
50705039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
50885057
50895058 endif
50905059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
50915061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
50925062 # This file is part of erlang.mk and subject to the terms of the ISC License.
50935063
51055075 doc-deps:
51065076 else
51075077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5108 $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5109 endif
5110
5111 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51125082 # This file is part of erlang.mk and subject to the terms of the ISC License.
51135083
51145084 .PHONY: rel-deps
51255095 rel-deps:
51265096 else
51275097 rel-deps: $(ALL_REL_DEPS_DIRS)
5128 $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5129 endif
5130
5131 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51325102 # This file is part of erlang.mk and subject to the terms of the ISC License.
51335103
51345104 .PHONY: test-deps test-dir test-build clean-test-dir
51505120 test-deps:
51515121 else
51525122 test-deps: $(ALL_TEST_DEPS_DIRS)
5153 $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
51545124 endif
51555125
51565126 ifneq ($(wildcard $(TEST_DIR)),)
51835153 endif
51845154 endif
51855155
5186 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51875157 # This file is part of erlang.mk and subject to the terms of the ISC License.
51885158
51895159 .PHONY: rebar.config
52195189 rebar.config:
52205190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
52215191
5222 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
52235193 # This file is part of erlang.mk and subject to the terms of the ISC License.
52245194
5225 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
5226
5227 MAN_INSTALL_PATH ?= /usr/local/share/man
5228 MAN_SECTIONS ?= 3 7
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
52295200
52305201 docs:: asciidoc
52315202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
52325207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
52335210
52345211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
52355212 asciidoc-guide:
52365213 else
5237 asciidoc-guide: distclean-asciidoc doc-deps
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
52385215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
52395216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5240 endif
5241
5242 ifeq ($(wildcard doc/src/manual/*.asciidoc),)
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
52435227 asciidoc-manual:
52445228 else
5245 asciidoc-manual: distclean-asciidoc doc-deps
5246 for f in doc/src/manual/*.asciidoc ; do \
5247 a2x -v -f manpage $$f ; \
5248 done
5249 for s in $(MAN_SECTIONS); do \
5250 mkdir -p doc/man$$s/ ; \
5251 mv doc/src/manual/*.$$s doc/man$$s/ ; \
5252 gzip doc/man$$s/*.$$s ; \
5253 done
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
52545262
52555263 install-docs:: install-asciidoc
52565264
52575265 install-asciidoc: asciidoc-manual
5258 for s in $(MAN_SECTIONS); do \
5259 mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
5260 install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
5261 done
5262 endif
5263
5264 distclean:: distclean-asciidoc
5265
5266 distclean-asciidoc:
5267 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
5268
5269 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
52705276 # This file is part of erlang.mk and subject to the terms of the ISC License.
52715277
52725278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
53235329 define bs_Makefile
53245330 PROJECT = $p
53255331 PROJECT_DESCRIPTION = New project
5326 PROJECT_VERSION = 0.0.1
5332 PROJECT_VERSION = 0.1.0
53275333
53285334 # Whitespace to be used when creating files from templates.
53295335 SP = $(SP)
53335339 define bs_Makefile
53345340 PROJECT = $p
53355341 PROJECT_DESCRIPTION = New project
5336 PROJECT_VERSION = 0.0.1
5342 PROJECT_VERSION = 0.1.0
53375343
53385344 endef
53395345 endif
53415347 define bs_apps_Makefile
53425348 PROJECT = $p
53435349 PROJECT_DESCRIPTION = New project
5344 PROJECT_VERSION = 0.0.1
5350 PROJECT_VERSION = 0.1.0
53455351
53465352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
53475353 endef
53615367 endef
53625368
53635369 define bs_relx_config
5364 {release, {$p_release, "1"}, [$p]}.
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
53655371 {extended_start_script, true}.
53665372 {sys_config, "rel/sys.config"}.
53675373 {vm_args, "rel/vm.args"}.
57125718 ifndef t
57135719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57145720 endif
5715 ifndef tpl_$(t)
5716 $(error Unknown template)
5717 endif
57185721 ifndef n
57195722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57205723 endif
57275730 list-templates:
57285731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
57295732
5730 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
57315734 # This file is part of erlang.mk and subject to the terms of the ISC License.
57325735
57335736 .PHONY: clean-c_src distclean-c_src-env
59625965 $(call render_template,bs_erl_nif,src/$n.erl)
59635966 endif
59645967
5965 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
59665969 # This file is part of erlang.mk and subject to the terms of the ISC License.
59675970
5968 .PHONY: ci ci-setup distclean-kerl
5969
5970 KERL ?= $(CURDIR)/kerl
5971 export KERL
5972
5973 KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
5974
5975 OTP_GIT ?= https://github.com/erlang/otp
5976
5977 CI_INSTALL_DIR ?= $(HOME)/erlang
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
59785973 CI_OTP ?=
5979
5980 ifeq ($(strip $(CI_OTP)),)
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
59815986 ci::
59825987 else
5983 ci:: $(addprefix ci-,$(CI_OTP))
5984
5985 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
59866007
59876008 ci-setup::
6009
6010 ci-extra::
59886011
59896012 ci_verbose_0 = @echo " CI " $(1);
59906013 ci_verbose = $(ci_verbose_$(V))
59916014
59926015 define ci_target
5993 ci-$(1): $(CI_INSTALL_DIR)/$(1)
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
59946018 $(ci_verbose) \
5995 PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
5996 CI_OTP_RELEASE="$(1)" \
5997 CT_OPTS="-label $(1)" \
5998 $(MAKE) clean ci-setup tests
5999 endef
6000
6001 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
60026030
60036031 define ci_otp_target
60046032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
60056033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6006 $(KERL) build git $(OTP_GIT) $(1) $(1)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
60076035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
60086036 endif
60096037 endef
60106038
60116039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
60126040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
60136052 $(KERL):
6014 $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
60156056 $(verbose) chmod +x $(KERL)
60166057
60176058 help::
60286069 $(gen_verbose) rm -rf $(KERL)
60296070 endif
60306071
6031 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
60326073 # This file is part of erlang.mk and subject to the terms of the ISC License.
60336074
60346075 .PHONY: ct apps-ct distclean-ct
60366077 # Configuration.
60376078
60386079 CT_OPTS ?=
6080
60396081 ifneq ($(wildcard $(TEST_DIR)),)
6040 CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6041 else
6042 CT_SUITES ?=
6043 endif
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
60446088
60456089 # Core targets.
60466090
60636107 -noinput \
60646108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
60656109 -dir $(TEST_DIR) \
6066 -logdir $(CURDIR)/logs
6110 -logdir $(CT_LOGS_DIR)
60676111
60686112 ifeq ($(CT_SUITES),)
60696113 ct: $(if $(IS_APP),,apps-ct)
60706114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
60716117 ct: test-build $(if $(IS_APP),,apps-ct)
6072 $(verbose) mkdir -p $(CURDIR)/logs/
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
60736119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
60746121 endif
60756122
60766123 ifneq ($(ALL_APPS_DIRS),)
60976144
60986145 define ct_suite_target
60996146 ct-$(1): test-build
6100 $(verbose) mkdir -p $(CURDIR)/logs/
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
61016148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
61026149 endef
61036150
61046151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
61056152
61066153 distclean-ct:
6107 $(gen_verbose) rm -rf $(CURDIR)/logs/
6108
6109 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61106157 # This file is part of erlang.mk and subject to the terms of the ISC License.
61116158
61126159 .PHONY: plt distclean-plt dialyze
61506197 endef
61516198
61526199 $(DIALYZER_PLT): deps app
6153 $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
61546204
61556205 plt: $(DIALYZER_PLT)
61566206
61646214 endif
61656215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
61666216
6167 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61686218 # This file is part of erlang.mk and subject to the terms of the ISC License.
61696219
61706220 .PHONY: distclean-edoc edoc
61726222 # Configuration.
61736223
61746224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
61756235
61766236 # Core targets.
61776237
6178 ifneq ($(wildcard doc/overview.edoc),)
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
61796239 docs:: edoc
61806240 endif
61816241
61846244 # Plugin-specific targets.
61856245
61866246 edoc: distclean-edoc doc-deps
6187 $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
61886248
61896249 distclean-edoc:
61906250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
61916251
6192 # Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61936253 # This file is part of erlang.mk and subject to the terms of the ISC License.
61946254
6195 .PHONY: distclean-escript escript
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
61966320
61976321 # Configuration.
61986322
61996323 ESCRIPT_NAME ?= $(PROJECT)
62006324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
62016325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
62026327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6203
6204 ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
6205 ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
6206 ESCRIPT_EMU_ARGS ?= -pa . \
6207 -sasl errlog_type error \
6208 -escript main $(ESCRIPT_NAME)
6209 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6210 ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
62116332
62126333 # Core targets.
62136334
62206341
62216342 # Plugin-specific targets.
62226343
6223 # Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
6224 # Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
6225 # Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
6226 # Software may only be used for the great good and the true happiness of all
6227 # sentient beings.
6228
6229 define ESCRIPT_RAW
6230 'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
6231 'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
6232 ' [F || F <- A, not filelib:is_dir(F) ] end,'\
6233 'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
6234 'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
6235 'Ez = fun(Escript) ->'\
6236 ' Static = Files([$(ESCRIPT_STATIC)]),'\
6237 ' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
6238 ' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
6239 ' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
6240 ' {archive, Archive, [memory]},'\
6241 ' {shebang, "$(ESCRIPT_SHEBANG)"},'\
6242 ' {comment, "$(ESCRIPT_COMMENT)"},'\
6243 ' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
6244 ' ]),'\
6245 ' file:change_mode(Escript, 8#755)'\
6246 'end,'\
6247 'Ez("$(ESCRIPT_FILE)"),'\
6248 'halt().'
6249 endef
6250
6251 ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
6252
6253 escript:: distclean-escript deps app
6254 $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
62556360
62566361 distclean-escript:
6257 $(gen_verbose) rm -f $(ESCRIPT_NAME)
6258
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
62596365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6260 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
62616366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
62626367
62636368 .PHONY: eunit apps-eunit
63216426
63226427 ifneq ($(ALL_APPS_DIRS),)
63236428 apps-eunit:
6324 $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done
6325 endif
6326 endif
6327
6328 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
63296436 # This file is part of erlang.mk and subject to the terms of the ISC License.
63306437
6331 .PHONY: relx-rel distclean-relx-rel distclean-relx run
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
63326439
63336440 # Configuration.
63346441
6335 RELX ?= $(CURDIR)/relx
6442 RELX ?= $(ERLANG_MK_TMP)/relx
63366443 RELX_CONFIG ?= $(CURDIR)/relx.config
63376444
63386445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
63396446 RELX_OPTS ?=
63406447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
63416454
63426455 ifeq ($(firstword $(RELX_OPTS)),-o)
63436456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
63506463 ifeq ($(IS_DEP),)
63516464 ifneq ($(wildcard $(RELX_CONFIG)),)
63526465 rel:: relx-rel
6353 endif
6354 endif
6355
6356 distclean:: distclean-relx-rel distclean-relx
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
63576472
63586473 # Plugin-specific targets.
63596474
63626477 $(verbose) chmod +x $(RELX)
63636478
63646479 relx-rel: $(RELX) rel-deps app
6365 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
63666484
63676485 distclean-relx-rel:
63686486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6369
6370 distclean-relx:
6371 $(gen_verbose) rm -rf $(RELX)
63726487
63736488 # Run target.
63746489
63776492 else
63786493
63796494 define get_relx_release.erl
6380 {ok, Config} = file:consult("$(RELX_CONFIG)"),
6381 {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
6382 io:format("~s", [Name]),
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
63836504 halt(0).
63846505 endef
63856506
6386 RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
63876514
63886515 run: all
6389 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
63906517
63916518 help::
63926519 $(verbose) printf "%s\n" "" \
63956522
63966523 endif
63976524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
63986526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6399 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
64006527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
64016528
64026529 .PHONY: shell
64216548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
64226549
64236550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6424 $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
64256552
64266553 shell: build-shell-deps
64276554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
64286555
6429 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
64306571 # This file is part of erlang.mk and subject to the terms of the ISC License.
64316572
64326573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
64376578 tests:: triq
64386579
64396580 define triq_check.erl
6440 code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
64416582 try
64426583 case $(1) of
64436584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
64696610 endif
64706611 endif
64716612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
64726614 # Copyright (c) 2015, Erlang Solutions Ltd.
64736615 # This file is part of erlang.mk and subject to the terms of the ISC License.
64746616
64856627 XREFR ?= $(CURDIR)/xrefr
64866628 export XREFR
64876629
6488 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
64896631
64906632 # Core targets.
64916633
64926634 help::
6493 $(verbose) printf "%s\n" "" \
6494 "Xref targets:" \
6495 " xref Run Xrefr using $XREF_CONFIG as config file if defined"
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
64966638
64976639 distclean:: distclean-xref
64986640
65086650 distclean-xref:
65096651 $(gen_verbose) rm -rf $(XREFR)
65106652
6511 # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
65126655 # This file is part of erlang.mk and subject to the terms of the ISC License.
65136656
65146657 COVER_REPORT_DIR = cover
65176660
65186661 ifdef COVER
65196662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
65206664 # All modules in 'ebin'
65216665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
65226666
65316675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
65326676 endif
65336677 endif
6678 endif
65346679
65356680 # Core targets
65366681
65526697 "Cover targets:" \
65536698 " cover-report Generate a HTML coverage report from previously collected" \
65546699 " cover data." \
6555 " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
65566701 "" \
65576702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
65586703 "target tests additionally generates a HTML coverage report from the combined" \
65656710
65666711 .PHONY: coverdata-clean
65676712 coverdata-clean:
6568 $(gen_verbose) rm -f *.coverdata ct.cover.spec
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
65696714
65706715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
65716721 all.coverdata: $(COVERDATA)
6572 $(gen_verbose) $(ERL) -eval ' \
6573 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
6574 cover:export("$@"), halt(0).'
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
65756723
65766724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
65776725 # empty if you want the coverdata files but not the HTML report.
65896737 # Modules which include eunit.hrl always contain one line without coverage
65906738 # because eunit defines test/0 which is never called. We compensate for this.
65916739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6592 grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
65936741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
65946742
65956743 define cover_report.erl
66246772 endef
66256773
66266774 cover-report:
6627 $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
66286776 $(gen_verbose) $(call erlang,$(cover_report.erl))
66296777
66306778 endif
66316779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
66326839
66336840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
66346841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
66976904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
66986905 endif
66996906 ifndef IS_APP
6700 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
67016908 $(MAKE) -C $$dep $@ \
67026909 IS_APP=1 \
6703 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6704 || exit $$?; \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67056911 done
67066912 endif
6707 $(verbose) for dep in $^ ; do \
6913 $(verbose) set -e; for dep in $^ ; do \
67086914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
67096915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6710 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
67116917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
67126918 $(MAKE) -C $$dep fetch-deps \
67136919 IS_DEP=1 \
6714 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6715 || exit $$?; \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67166921 fi \
67176922 fi \
67186923 done
22 # before the inclusion of erlang.mk leading to the wrong target becoming
33 # the default.
44 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
526 endif
627
728 # --------------------------------------------------------------------
2041 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
2142 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
2243 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
2345 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
2446 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
2547 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
2648 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
2749 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
2851 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
2952 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
3054 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
3155 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
3256 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
3559 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
3660 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
3761 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
3863 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
3964 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
4065 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
7095
7196 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
7297
73 # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
74 # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
75 # needs to add "ranch" as a BUILD_DEPS. The list of projects needing
76 # this workaround are:
77 # o rabbitmq-web-stomp
78 dep_ranch = git https://github.com/ninenines/ranch 1.2.1
98 # Third-party dependencies version pinning.
99 #
100 # We do that in this file, which is copied in all projects, to ensure
101 # all projects use the same versions. It avoids conflicts and makes it
102 # possible to work with rabbitmq-public-umbrella.
103
104 dep_cowboy_commit = 1.0.4
105 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
106 dep_ranch_commit = 1.3.1
107 dep_webmachine_commit = 1.10.8p2
79108
80109 RABBITMQ_COMPONENTS = amqp_client \
81110 rabbit \
82111 rabbit_common \
83112 rabbitmq_amqp1_0 \
84113 rabbitmq_auth_backend_amqp \
114 rabbitmq_auth_backend_cache \
85115 rabbitmq_auth_backend_http \
86116 rabbitmq_auth_backend_ldap \
87117 rabbitmq_auth_mechanism_ssl \
88118 rabbitmq_boot_steps_visualiser \
89119 rabbitmq_clusterer \
120 rabbitmq_cli \
90121 rabbitmq_codegen \
91122 rabbitmq_consistent_hash_exchange \
123 rabbitmq_ct_client_helpers \
92124 rabbitmq_ct_helpers \
93125 rabbitmq_delayed_message_exchange \
94126 rabbitmq_dotnet_client \
97129 rabbitmq_federation_management \
98130 rabbitmq_java_client \
99131 rabbitmq_jms_client \
132 rabbitmq_jms_cts \
100133 rabbitmq_jms_topic_exchange \
101134 rabbitmq_lvc \
102135 rabbitmq_management \
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_exchange_type_event).
+0
-7
deps/rabbitmq_event_exchange/src/rabbitmq_event_exchange.app.src less more
0 {application, rabbitmq_event_exchange,
1 [{description, "Event Exchange Type"},
2 {vsn, "3.6.6"},
3 {modules, []},
4 {registered, []},
5 {env, []},
6 {applications, [kernel, stdlib, rabbit_common, rabbit]}]}.
00 PROJECT = rabbitmq_federation
1 PROJECT_DESCRIPTION = RabbitMQ Federation
2 PROJECT_MOD = rabbit_federation_app
3
4 define PROJECT_ENV
5 [
6 {pgroup_name_cluster_id, false}
7 ]
8 endef
19
210 DEPS = rabbit_common rabbit amqp_client
3 TEST_DEPS = rabbitmq_ct_helpers
11 TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers
412
13 DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
514 DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk
615
716 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
0 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
11 #
22 # Permission to use, copy, modify, and/or distribute this software for any
33 # purpose with or without fee is hereby granted, provided that the above
1111 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1212 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1313
14 .PHONY: all app apps deps search rel docs install-docs check tests clean distclean help erlang-mk
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
1515
1616 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17
18 ERLANG_MK_VERSION = 2.0.0-pre.2-144-g647ffd1
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
1931
2032 # Core configuration.
2133
2436
2537 PROJECT_VERSION ?= rolling
2638 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
2740
2841 # Verbosity.
2942
8497 rel::
8598 $(verbose) :
8699
100 relup:: deps app
101
87102 check:: tests
88103
89104 clean:: clean-crashdump
101116 help::
102117 $(verbose) printf "%s\n" \
103118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
104 "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
105120 "" \
106121 "Usage: [V=1] $(MAKE) [target]..." \
107122 "" \
149164 core_native_path = $1
150165 endif
151166
152 ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
153 define core_http_get
154 wget --no-check-certificate -O $(1) $(2)|| rm $(1)
155 endef
156 else
157 define core_http_get.erl
158 ssl:start(),
159 inets:start(),
160 case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
161 {ok, {{_, 200, _}, _, Body}} ->
162 case file:write_file("$(1)", Body) of
163 ok -> ok;
164 {error, R1} -> halt(R1)
165 end;
166 {error, R2} ->
167 halt(R2)
168 end,
169 halt(0).
170 endef
171
172 define core_http_get
173 $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
174 endef
175 endif
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
176168
177169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
178170
192184 ERLANG_MK_BUILD_CONFIG ?= build.config
193185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
194186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
195188 erlang-mk:
196189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
197190 ifdef ERLANG_MK_COMMIT
198191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
199192 endif
200193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
201 $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
202195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
203196 rm -rf $(ERLANG_MK_BUILD_DIR)
204197
285278 pkg_apns_repo = https://github.com/inaka/apns4erl
286279 pkg_apns_commit = master
287280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
288289 PACKAGES += azdht
289290 pkg_azdht_name = azdht
290291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
405406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
406407 pkg_bootstrap_commit = master
407408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
408417 PACKAGES += boss_db
409418 pkg_boss_db_name = boss_db
410419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
413422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
414423 pkg_boss_db_commit = master
415424
416 PACKAGES += boss
417 pkg_boss_name = boss
418 pkg_boss_description = Erlang web MVC, now featuring Comet
419 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
420 pkg_boss_fetch = git
421 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
422 pkg_boss_commit = master
423
424425 PACKAGES += brod
425426 pkg_brod_name = brod
426427 pkg_brod_description = Kafka client in Erlang
565566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
566567 pkg_cloudi_service_api_requests_commit = master
567568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
568585 PACKAGES += cloudi_service_db_cassandra_cql
569586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
570587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
573590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
574591 pkg_cloudi_service_db_cassandra_cql_commit = master
575592
576 PACKAGES += cloudi_service_db_cassandra
577 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
579 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
580 pkg_cloudi_service_db_cassandra_fetch = git
581 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
582 pkg_cloudi_service_db_cassandra_commit = master
583
584593 PACKAGES += cloudi_service_db_couchdb
585594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
586595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
637646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
638647 pkg_cloudi_service_db_tokyotyrant_commit = master
639648
640 PACKAGES += cloudi_service_db
641 pkg_cloudi_service_db_name = cloudi_service_db
642 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
643 pkg_cloudi_service_db_homepage = http://cloudi.org/
644 pkg_cloudi_service_db_fetch = git
645 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
646 pkg_cloudi_service_db_commit = master
647
648649 PACKAGES += cloudi_service_filesystem
649650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
650651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
10371038 pkg_edown_repo = https://github.com/uwiger/edown
10381039 pkg_edown_commit = master
10391040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
10401049 PACKAGES += eep_app
10411050 pkg_eep_app_name = eep_app
10421051 pkg_eep_app_description = Embedded Event Processing
10451054 pkg_eep_app_repo = https://github.com/darach/eep-erl
10461055 pkg_eep_app_commit = master
10471056
1048 PACKAGES += eep
1049 pkg_eep_name = eep
1050 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1051 pkg_eep_homepage = https://github.com/virtan/eep
1052 pkg_eep_fetch = git
1053 pkg_eep_repo = https://github.com/virtan/eep
1054 pkg_eep_commit = master
1055
10561057 PACKAGES += efene
10571058 pkg_efene_name = efene
10581059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
12371238 pkg_eqm_repo = https://github.com/loucash/eqm
12381239 pkg_eqm_commit = master
12391240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
12401249 PACKAGES += eredis_pool
12411250 pkg_eredis_pool_name = eredis_pool
12421251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
12451254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
12461255 pkg_eredis_pool_commit = master
12471256
1248 PACKAGES += eredis
1249 pkg_eredis_name = eredis
1250 pkg_eredis_description = Erlang Redis client
1251 pkg_eredis_homepage = https://github.com/wooga/eredis
1252 pkg_eredis_fetch = git
1253 pkg_eredis_repo = https://github.com/wooga/eredis
1254 pkg_eredis_commit = master
1255
12561257 PACKAGES += erl_streams
12571258 pkg_erl_streams_name = erl_streams
12581259 pkg_erl_streams_description = Streams in Erlang
15411542 pkg_etap_repo = https://github.com/ngerakines/etap
15421543 pkg_etap_commit = master
15431544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
15441553 PACKAGES += etest_http
15451554 pkg_etest_http_name = etest_http
15461555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
15491558 pkg_etest_http_repo = https://github.com/wooga/etest_http
15501559 pkg_etest_http_commit = master
15511560
1552 PACKAGES += etest
1553 pkg_etest_name = etest
1554 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1555 pkg_etest_homepage = https://github.com/wooga/etest
1556 pkg_etest_fetch = git
1557 pkg_etest_repo = https://github.com/wooga/etest
1558 pkg_etest_commit = master
1559
15601561 PACKAGES += etoml
15611562 pkg_etoml_name = etoml
15621563 pkg_etoml_description = TOML language erlang parser
15651566 pkg_etoml_repo = https://github.com/kalta/etoml
15661567 pkg_etoml_commit = master
15671568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
15681577 PACKAGES += eunit_formatters
15691578 pkg_eunit_formatters_name = eunit_formatters
15701579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
15731582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
15741583 pkg_eunit_formatters_commit = master
15751584
1576 PACKAGES += eunit
1577 pkg_eunit_name = eunit
1578 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1579 pkg_eunit_homepage = https://github.com/richcarl/eunit
1580 pkg_eunit_fetch = git
1581 pkg_eunit_repo = https://github.com/richcarl/eunit
1582 pkg_eunit_commit = master
1583
15841585 PACKAGES += euthanasia
15851586 pkg_euthanasia_name = euthanasia
15861587 pkg_euthanasia_description = Merciful killer for your Erlang processes
15981599 pkg_evum_commit = master
15991600
16001601 PACKAGES += exec
1601 pkg_exec_name = exec
1602 pkg_exec_name = erlexec
16021603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
16031604 pkg_exec_homepage = http://saleyn.github.com/erlexec
16041605 pkg_exec_fetch = git
17171718 pkg_fn_repo = https://github.com/reiddraper/fn
17181719 pkg_fn_commit = master
17191720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
17201729 PACKAGES += folsom_cowboy
17211730 pkg_folsom_cowboy_name = folsom_cowboy
17221731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
17251734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
17261735 pkg_folsom_cowboy_commit = master
17271736
1728 PACKAGES += folsom
1729 pkg_folsom_name = folsom
1730 pkg_folsom_description = Expose Erlang Events and Metrics
1731 pkg_folsom_homepage = https://github.com/boundary/folsom
1732 pkg_folsom_fetch = git
1733 pkg_folsom_repo = https://github.com/boundary/folsom
1734 pkg_folsom_commit = master
1735
17361737 PACKAGES += folsomite
17371738 pkg_folsomite_name = folsomite
17381739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
20932094 pkg_jesse_repo = https://github.com/for-GET/jesse
20942095 pkg_jesse_commit = master
20952096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
20962105 PACKAGES += jiffy_v
20972106 pkg_jiffy_v_name = jiffy_v
20982107 pkg_jiffy_v_description = JSON validation utility
21012110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
21022111 pkg_jiffy_v_commit = master
21032112
2104 PACKAGES += jiffy
2105 pkg_jiffy_name = jiffy
2106 pkg_jiffy_description = JSON NIFs for Erlang.
2107 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2108 pkg_jiffy_fetch = git
2109 pkg_jiffy_repo = https://github.com/davisp/jiffy
2110 pkg_jiffy_commit = master
2111
21122113 PACKAGES += jobs
21132114 pkg_jobs_name = jobs
21142115 pkg_jobs_description = a Job scheduler for load regulation
21252126 pkg_joxa_repo = https://github.com/joxa/joxa
21262127 pkg_joxa_commit = master
21272128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
21282137 PACKAGES += json_rec
21292138 pkg_json_rec_name = json_rec
21302139 pkg_json_rec_description = JSON to erlang record
21332142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
21342143 pkg_json_rec_commit = master
21352144
2136 PACKAGES += json
2137 pkg_json_name = json
2138 pkg_json_description = a high level json library for erlang (17.0+)
2139 pkg_json_homepage = https://github.com/talentdeficit/json
2140 pkg_json_fetch = git
2141 pkg_json_repo = https://github.com/talentdeficit/json
2142 pkg_json_commit = master
2143
21442145 PACKAGES += jsone
21452146 pkg_jsone_name = jsone
21462147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
21812182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
21822183 pkg_jsx_commit = master
21832184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
21842193 PACKAGES += kafka_protocol
21852194 pkg_kafka_protocol_name = kafka_protocol
21862195 pkg_kafka_protocol_description = Kafka protocol Erlang library
21892198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
21902199 pkg_kafka_protocol_commit = master
21912200
2192 PACKAGES += kafka
2193 pkg_kafka_name = kafka
2194 pkg_kafka_description = Kafka consumer and producer in Erlang
2195 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2196 pkg_kafka_fetch = git
2197 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2198 pkg_kafka_commit = master
2199
22002201 PACKAGES += kai
22012202 pkg_kai_name = kai
22022203 pkg_kai_description = DHT storage by Takeshi Inoue
22932294 pkg_kvs_repo = https://github.com/synrc/kvs
22942295 pkg_kvs_commit = master
22952296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
22962305 PACKAGES += lager_amqp_backend
22972306 pkg_lager_amqp_backend_name = lager_amqp_backend
22982307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
23092318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
23102319 pkg_lager_syslog_commit = master
23112320
2312 PACKAGES += lager
2313 pkg_lager_name = lager
2314 pkg_lager_description = A logging framework for Erlang/OTP.
2315 pkg_lager_homepage = https://github.com/basho/lager
2316 pkg_lager_fetch = git
2317 pkg_lager_repo = https://github.com/basho/lager
2318 pkg_lager_commit = master
2319
23202321 PACKAGES += lambdapad
23212322 pkg_lambdapad_name = lambdapad
23222323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
25732574 pkg_mixer_repo = https://github.com/chef/mixer
25742575 pkg_mixer_commit = master
25752576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
25762585 PACKAGES += mochiweb_xpath
25772586 pkg_mochiweb_xpath_name = mochiweb_xpath
25782587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
25812590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
25822591 pkg_mochiweb_xpath_commit = master
25832592
2584 PACKAGES += mochiweb
2585 pkg_mochiweb_name = mochiweb
2586 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2587 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2588 pkg_mochiweb_fetch = git
2589 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2590 pkg_mochiweb_commit = master
2591
25922593 PACKAGES += mockgyver
25932594 pkg_mockgyver_name = mockgyver
25942595 pkg_mockgyver_description = A mocking library for Erlang
30613062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
30623063 pkg_quickrand_commit = master
30633064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
30643073 PACKAGES += rabbit_exchange_type_riak
30653074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
30663075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
30693078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
30703079 pkg_rabbit_exchange_type_riak_commit = master
30713080
3072 PACKAGES += rabbit
3073 pkg_rabbit_name = rabbit
3074 pkg_rabbit_description = RabbitMQ Server
3075 pkg_rabbit_homepage = https://www.rabbitmq.com/
3076 pkg_rabbit_fetch = git
3077 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3078 pkg_rabbit_commit = master
3079
30803081 PACKAGES += rack
30813082 pkg_rack_name = rack
30823083 pkg_rack_description = Rack handler for erlang
34933494 pkg_smother_repo = https://github.com/ramsay-t/Smother
34943495 pkg_smother_commit = master
34953496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
34963505 PACKAGES += social
34973506 pkg_social_name = social
34983507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
35413550 pkg_stable_repo = https://github.com/dvv/stable
35423551 pkg_stable_commit = master
35433552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
35443561 PACKAGES += statebox_riak
35453562 pkg_statebox_riak_name = statebox_riak
35463563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
35493566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
35503567 pkg_statebox_riak_commit = master
35513568
3552 PACKAGES += statebox
3553 pkg_statebox_name = statebox
3554 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3555 pkg_statebox_homepage = https://github.com/mochi/statebox
3556 pkg_statebox_fetch = git
3557 pkg_statebox_repo = https://github.com/mochi/statebox
3558 pkg_statebox_commit = master
3559
35603569 PACKAGES += statman
35613570 pkg_statman_name = statman
35623571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
37253734 pkg_tirerl_repo = https://github.com/inaka/tirerl
37263735 pkg_tirerl_commit = master
37273736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
37283745 PACKAGES += traffic_tools
37293746 pkg_traffic_tools_name = traffic_tools
37303747 pkg_traffic_tools_description = Simple traffic limiting library
40614078 pkg_zucchini_repo = https://github.com/devinus/zucchini
40624079 pkg_zucchini_commit = master
40634080
4064 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
40654082 # This file is part of erlang.mk and subject to the terms of the ISC License.
40664083
40674084 .PHONY: search
40884105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
40894106 endif
40904107
4091 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
40924109 # This file is part of erlang.mk and subject to the terms of the ISC License.
40934110
4094 .PHONY: distclean-deps
4111 .PHONY: distclean-deps clean-tmp-deps.log
40954112
40964113 # Configuration.
40974114
41104127
41114128 REBAR_DEPS_DIR = $(DEPS_DIR)
41124129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
41134150
41144151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
41154152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
41164153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
41174154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
41184155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
41194157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
41204158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
41214159
41384176
41394177 # Core targets.
41404178
4141 ifdef IS_APP
4142 apps::
4143 else
4144 apps:: $(ALL_APPS_DIRS)
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
41454180 ifeq ($(IS_APP)$(IS_DEP),)
41464181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
41474182 endif
41494184 # Create ebin directory for all apps to make sure Erlang recognizes them
41504185 # as proper OTP applications when using -include_lib. This is a temporary
41514186 # fix, a proper fix would be to compile apps/* in the right order.
4152 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4153 mkdir -p $$dep/ebin || exit $$?; \
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
41544190 done
4155 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
41564196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
41574197 :; \
41584198 else \
41594199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4160 $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
4200 $(MAKE) -C $$dep IS_APP=1; \
41614201 fi \
41624202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
41634207 endif
41644208
41654209 ifneq ($(SKIP_DEPS),)
41664210 deps::
41674211 else
4168 deps:: $(ALL_DEPS_DIRS) apps
4169 ifeq ($(IS_APP)$(IS_DEP),)
4170 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4171 endif
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
41724213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4173 $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
41744215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
41754216 :; \
41764217 else \
41774218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
41784219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4179 $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
4220 $(MAKE) -C $$dep IS_DEP=1; \
41804221 else \
4181 echo "Error: No Makefile to build dependency $$dep."; \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
41824223 exit 2; \
41834224 fi \
41844225 fi \
41924233 # in practice only Makefile is needed so far.
41934234 define dep_autopatch
41944235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
41954237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
41964238 $(call dep_autopatch_erlang_mk,$(1)); \
41974239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4198 if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
41994243 $(call dep_autopatch2,$(1)); \
4200 elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
42014245 $(call dep_autopatch2,$(1)); \
4202 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
42034247 $(call dep_autopatch2,$(1)); \
4204 else \
4205 $(call erlang,$(call dep_autopatch_app.erl,$(1))); \
42064248 fi \
42074249 else \
42084250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
42144256 endef
42154257
42164258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
42174262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
42184263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
42194264 fi; \
42204265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4221 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
42224267 $(call dep_autopatch_fetch_rebar); \
42234268 $(call dep_autopatch_rebar,$(1)); \
42244269 else \
42304275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
42314276 endef
42324277
4233 # Overwrite erlang.mk with the current file by default.
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
42344280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
42354281 define dep_autopatch_erlang_mk
4236 echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \
4237 > $(DEPS_DIR)/$1/erlang.mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
42384287 endef
42394288 else
42404289 define dep_autopatch_erlang_mk
42534302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
42544303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
42554304 cd $(ERLANG_MK_TMP)/rebar; \
4256 git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
42574306 $(MAKE); \
42584307 cd -; \
42594308 fi
42704319 define dep_autopatch_rebar.erl
42714320 application:load(rebar),
42724321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
42734323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
42744324 {ok, Conf0} -> Conf0;
42754325 _ -> []
44234473 [] -> ok;
44244474 _ ->
44254475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4426 PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
44274477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4428 PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L \\"~s\\" -lerl_interface -lei\n",
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
44294479 [code:lib_dir(erl_interface, lib)])),
44304480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
44314481 FilterEnv = fun(Env) ->
44644514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44654515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44664516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4467 [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
44684518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
44694519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
44704520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
44764526 end,
44774527 [PortSpec(S) || S <- PortSpecs]
44784528 end,
4479 Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"),
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
44804530 RunPlugin = fun(Plugin, Step) ->
44814531 case erlang:function_exported(Plugin, Step, 2) of
44824532 false -> ok;
45244574 halt()
45254575 endef
45264576
4527 define dep_autopatch_app.erl
4528 UpdateModules = fun(App) ->
4529 case filelib:is_regular(App) of
4530 false -> ok;
4531 true ->
4532 {ok, [{application, '$(1)', L0}]} = file:consult(App),
4533 Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true,
4534 fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
4535 L = lists:keystore(modules, 1, L0, {modules, Mods}),
4536 ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}]))
4537 end
4538 end,
4539 UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"),
4540 halt()
4541 endef
4542
45434577 define dep_autopatch_appsrc_script.erl
45444578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
45454579 AppSrcScript = AppSrc ++ ".script",
45874621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
45884622 endef
45894623
4590 define dep_fetch_hex.erl
4591 ssl:start(),
4592 inets:start(),
4593 {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
4594 {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
4595 [], [{body_format, binary}]),
4596 {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
4597 {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
4598 ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
4599 halt()
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
46004626 endef
46014627
46024628 # Hex only has a package version. No need to look in the Erlang.mk packages.
46034629 define dep_fetch_hex
4604 $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
46054634 endef
46064635
46074636 define dep_fetch_fail
46314660 $(eval DEP_NAME := $(call dep_name,$1))
46324661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
46334662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4634 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
46354664 exit 17; \
46364665 fi
46374666 $(verbose) mkdir -p $(DEPS_DIR)
46734702 clean:: clean-apps
46744703
46754704 clean-apps:
4676 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4677 $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
46784707 done
46794708
46804709 distclean:: distclean-apps
46814710
46824711 distclean-apps:
4683 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4684 $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
46854714 done
46864715 endif
46874716
47014730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
47024731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
47034732
4704 # External plugins.
4705
4706 DEP_PLUGINS ?=
4707
4708 define core_dep_plugin
4709 -include $(DEPS_DIR)/$(1)
4710
4711 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4712 endef
4713
4714 $(foreach p,$(DEP_PLUGINS),\
4715 $(eval $(if $(findstring /,$p),\
4716 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4717 $(call core_dep_plugin,$p/plugins.mk,$p))))
4718
4719 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4720 # This file is part of erlang.mk and subject to the terms of the ISC License.
4721
4722 # Configuration.
4723
4724 DTL_FULL_PATH ?=
4725 DTL_PATH ?= templates/
4726 DTL_SUFFIX ?= _dtl
4727 DTL_OPTS ?=
4728
4729 # Verbosity.
4730
4731 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
4732 dtl_verbose = $(dtl_verbose_$(V))
4733
4734 # Core targets.
4735
4736 DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl))
4737
4738 ifneq ($(DTL_FILES),)
4739
4740 ifdef DTL_FULL_PATH
4741 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
4742 else
4743 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
4744 endif
4745
4746 ifneq ($(words $(DTL_FILES)),0)
4747 # Rebuild templates when the Makefile changes.
4748 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
4749 @mkdir -p $(ERLANG_MK_TMP)
4750 @if test -f $@; then \
4751 touch $(DTL_FILES); \
4752 fi
4753 @touch $@
4754
4755 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
4756 endif
4757
4758 define erlydtl_compile.erl
4759 [begin
4760 Module0 = case "$(strip $(DTL_FULL_PATH))" of
4761 "" ->
4762 filename:basename(F, ".dtl");
4763 _ ->
4764 "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
4765 re:replace(F2, "/", "_", [{return, list}, global])
4766 end,
4767 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
4768 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
4769 ok -> ok;
4770 {ok, _} -> ok
4771 end
4772 end || F <- string:tokens("$(1)", " ")],
4773 halt().
4774 endef
4775
4776 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
4777 $(if $(strip $?),\
4778 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
4779
4780 endif
4781
4782 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
47834734 # This file is part of erlang.mk and subject to the terms of the ISC License.
47844735
47854736 # Verbosity.
47984749
47994750 define compile_proto.erl
48004751 [begin
4801 Dir = filename:dirname(filename:dirname(F)),
48024752 protobuffs_compile:generate_source(F,
4803 [{output_include_dir, Dir ++ "/include"},
4804 {output_src_dir, Dir ++ "/ebin"}])
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
48054755 end || F <- string:tokens("$(1)", " ")],
48064756 halt().
48074757 endef
48114761 $(if $(strip $?),$(call compile_proto,$?))
48124762 endif
48134763
4814 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
48154765 # This file is part of erlang.mk and subject to the terms of the ISC License.
48164766
48174767 .PHONY: clean-app
48254775 ERLC_EXCLUDE ?=
48264776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
48274777
4778 ERLC_ASN1_OPTS ?=
4779
48284780 ERLC_MIB_OPTS ?=
48294781 COMPILE_MIB_FIRST ?=
48304782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
48744826
48754827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
48764828 define app_file
4877 {application, $(PROJECT), [
4829 {application, '$(PROJECT)', [
48784830 {description, "$(PROJECT_DESCRIPTION)"},
48794831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48804832 {id$(comma)$(space)"$(1)"}$(comma))
48814833 {modules, [$(call comma_list,$(2))]},
48824834 {registered, []},
4883 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48844837 ]}.
48854838 endef
48864839 else
48874840 define app_file
4888 {application, $(PROJECT), [
4841 {application, '$(PROJECT)', [
48894842 {description, "$(PROJECT_DESCRIPTION)"},
48904843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48914844 {id$(comma)$(space)"$(1)"}$(comma))
48924845 {modules, [$(call comma_list,$(2))]},
48934846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
48944847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4895 {mod, {$(PROJECT_MOD), []}}
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48964850 ]}.
48974851 endef
48984852 endif
49024856
49034857 # Source files.
49044858
4905 ERL_FILES = $(sort $(call core_find,src/,*.erl))
4906 CORE_FILES = $(sort $(call core_find,src/,*.core))
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
49074863
49084864 # ASN.1 files.
49094865
49134869
49144870 define compile_asn1
49154871 $(verbose) mkdir -p include/
4916 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
49174873 $(verbose) mv asn1/*.erl src/
49184874 $(verbose) mv asn1/*.hrl include/
49194875 $(verbose) mv asn1/*.asn1db include/
49364892
49374893 # Leex and Yecc files.
49384894
4939 XRL_FILES = $(sort $(call core_find,src/,*.xrl))
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
49404896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
49414897 ERL_FILES += $(XRL_ERL_FILES)
49424898
4943 YRL_FILES = $(sort $(call core_find,src/,*.yrl))
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
49444900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
49454901 ERL_FILES += $(YRL_ERL_FILES)
49464902
49474903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4948 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
49494905
49504906 # Erlang and Core Erlang files.
49514907
49954951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49964952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49974953 (F, Mod, import, {Imp, _}) ->
4998 case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
49994960 false -> ok;
50004961 true -> Add(Mod, Imp)
50014962 end;
50194980 end || F <- ErlFiles],
50204981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
50214982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
50224991 ok = file:write_file("$(1)", [
50234992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
5024 "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n"
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
50254994 ]),
50264995 halt()
50274996 endef
50345003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
50355004 # Rebuild everything when the Makefile changes.
50365005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5037 @mkdir -p $(ERLANG_MK_TMP)
5038 @if test -f $@; then \
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
50395008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
50405009 touch -c $(PROJECT).d; \
50415010 fi
5042 @touch $@
5011 $(verbose) touch $@
50435012
50445013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
50455014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
50465015 endif
50475016
5048 -include $(PROJECT).d
5017 include $(wildcard $(PROJECT).d)
50495018
50505019 ebin/$(PROJECT).app:: ebin/
50515020
50645033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
50655034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
50665035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5067 $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
50685037 > ebin/$(PROJECT).app
50695038 else
50705039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
50885057
50895058 endif
50905059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
50915061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
50925062 # This file is part of erlang.mk and subject to the terms of the ISC License.
50935063
51055075 doc-deps:
51065076 else
51075077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5108 $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5109 endif
5110
5111 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51125082 # This file is part of erlang.mk and subject to the terms of the ISC License.
51135083
51145084 .PHONY: rel-deps
51255095 rel-deps:
51265096 else
51275097 rel-deps: $(ALL_REL_DEPS_DIRS)
5128 $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5129 endif
5130
5131 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51325102 # This file is part of erlang.mk and subject to the terms of the ISC License.
51335103
51345104 .PHONY: test-deps test-dir test-build clean-test-dir
51505120 test-deps:
51515121 else
51525122 test-deps: $(ALL_TEST_DEPS_DIRS)
5153 $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
51545124 endif
51555125
51565126 ifneq ($(wildcard $(TEST_DIR)),)
51835153 endif
51845154 endif
51855155
5186 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51875157 # This file is part of erlang.mk and subject to the terms of the ISC License.
51885158
51895159 .PHONY: rebar.config
52195189 rebar.config:
52205190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
52215191
5222 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
52235193 # This file is part of erlang.mk and subject to the terms of the ISC License.
52245194
5225 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
5226
5227 MAN_INSTALL_PATH ?= /usr/local/share/man
5228 MAN_SECTIONS ?= 3 7
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
52295200
52305201 docs:: asciidoc
52315202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
52325207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
52335210
52345211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
52355212 asciidoc-guide:
52365213 else
5237 asciidoc-guide: distclean-asciidoc doc-deps
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
52385215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
52395216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5240 endif
5241
5242 ifeq ($(wildcard doc/src/manual/*.asciidoc),)
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
52435227 asciidoc-manual:
52445228 else
5245 asciidoc-manual: distclean-asciidoc doc-deps
5246 for f in doc/src/manual/*.asciidoc ; do \
5247 a2x -v -f manpage $$f ; \
5248 done
5249 for s in $(MAN_SECTIONS); do \
5250 mkdir -p doc/man$$s/ ; \
5251 mv doc/src/manual/*.$$s doc/man$$s/ ; \
5252 gzip doc/man$$s/*.$$s ; \
5253 done
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
52545262
52555263 install-docs:: install-asciidoc
52565264
52575265 install-asciidoc: asciidoc-manual
5258 for s in $(MAN_SECTIONS); do \
5259 mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
5260 install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
5261 done
5262 endif
5263
5264 distclean:: distclean-asciidoc
5265
5266 distclean-asciidoc:
5267 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
5268
5269 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
52705276 # This file is part of erlang.mk and subject to the terms of the ISC License.
52715277
52725278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
53235329 define bs_Makefile
53245330 PROJECT = $p
53255331 PROJECT_DESCRIPTION = New project
5326 PROJECT_VERSION = 0.0.1
5332 PROJECT_VERSION = 0.1.0
53275333
53285334 # Whitespace to be used when creating files from templates.
53295335 SP = $(SP)
53335339 define bs_Makefile
53345340 PROJECT = $p
53355341 PROJECT_DESCRIPTION = New project
5336 PROJECT_VERSION = 0.0.1
5342 PROJECT_VERSION = 0.1.0
53375343
53385344 endef
53395345 endif
53415347 define bs_apps_Makefile
53425348 PROJECT = $p
53435349 PROJECT_DESCRIPTION = New project
5344 PROJECT_VERSION = 0.0.1
5350 PROJECT_VERSION = 0.1.0
53455351
53465352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
53475353 endef
53615367 endef
53625368
53635369 define bs_relx_config
5364 {release, {$p_release, "1"}, [$p]}.
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
53655371 {extended_start_script, true}.
53665372 {sys_config, "rel/sys.config"}.
53675373 {vm_args, "rel/vm.args"}.
57125718 ifndef t
57135719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57145720 endif
5715 ifndef tpl_$(t)
5716 $(error Unknown template)
5717 endif
57185721 ifndef n
57195722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57205723 endif
57275730 list-templates:
57285731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
57295732
5730 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
57315734 # This file is part of erlang.mk and subject to the terms of the ISC License.
57325735
57335736 .PHONY: clean-c_src distclean-c_src-env
59625965 $(call render_template,bs_erl_nif,src/$n.erl)
59635966 endif
59645967
5965 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
59665969 # This file is part of erlang.mk and subject to the terms of the ISC License.
59675970
5968 .PHONY: ci ci-setup distclean-kerl
5969
5970 KERL ?= $(CURDIR)/kerl
5971 export KERL
5972
5973 KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
5974
5975 OTP_GIT ?= https://github.com/erlang/otp
5976
5977 CI_INSTALL_DIR ?= $(HOME)/erlang
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
59785973 CI_OTP ?=
5979
5980 ifeq ($(strip $(CI_OTP)),)
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
59815986 ci::
59825987 else
5983 ci:: $(addprefix ci-,$(CI_OTP))
5984
5985 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
59866007
59876008 ci-setup::
6009
6010 ci-extra::
59886011
59896012 ci_verbose_0 = @echo " CI " $(1);
59906013 ci_verbose = $(ci_verbose_$(V))
59916014
59926015 define ci_target
5993 ci-$(1): $(CI_INSTALL_DIR)/$(1)
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
59946018 $(ci_verbose) \
5995 PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
5996 CI_OTP_RELEASE="$(1)" \
5997 CT_OPTS="-label $(1)" \
5998 $(MAKE) clean ci-setup tests
5999 endef
6000
6001 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
60026030
60036031 define ci_otp_target
60046032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
60056033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6006 $(KERL) build git $(OTP_GIT) $(1) $(1)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
60076035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
60086036 endif
60096037 endef
60106038
60116039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
60126040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
60136052 $(KERL):
6014 $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
60156056 $(verbose) chmod +x $(KERL)
60166057
60176058 help::
60286069 $(gen_verbose) rm -rf $(KERL)
60296070 endif
60306071
6031 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
60326073 # This file is part of erlang.mk and subject to the terms of the ISC License.
60336074
60346075 .PHONY: ct apps-ct distclean-ct
60366077 # Configuration.
60376078
60386079 CT_OPTS ?=
6080
60396081 ifneq ($(wildcard $(TEST_DIR)),)
6040 CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6041 else
6042 CT_SUITES ?=
6043 endif
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
60446088
60456089 # Core targets.
60466090
60636107 -noinput \
60646108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
60656109 -dir $(TEST_DIR) \
6066 -logdir $(CURDIR)/logs
6110 -logdir $(CT_LOGS_DIR)
60676111
60686112 ifeq ($(CT_SUITES),)
60696113 ct: $(if $(IS_APP),,apps-ct)
60706114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
60716117 ct: test-build $(if $(IS_APP),,apps-ct)
6072 $(verbose) mkdir -p $(CURDIR)/logs/
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
60736119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
60746121 endif
60756122
60766123 ifneq ($(ALL_APPS_DIRS),)
60976144
60986145 define ct_suite_target
60996146 ct-$(1): test-build
6100 $(verbose) mkdir -p $(CURDIR)/logs/
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
61016148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
61026149 endef
61036150
61046151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
61056152
61066153 distclean-ct:
6107 $(gen_verbose) rm -rf $(CURDIR)/logs/
6108
6109 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61106157 # This file is part of erlang.mk and subject to the terms of the ISC License.
61116158
61126159 .PHONY: plt distclean-plt dialyze
61506197 endef
61516198
61526199 $(DIALYZER_PLT): deps app
6153 $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
61546204
61556205 plt: $(DIALYZER_PLT)
61566206
61646214 endif
61656215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
61666216
6167 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61686218 # This file is part of erlang.mk and subject to the terms of the ISC License.
61696219
61706220 .PHONY: distclean-edoc edoc
61726222 # Configuration.
61736223
61746224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
61756235
61766236 # Core targets.
61776237
6178 ifneq ($(wildcard doc/overview.edoc),)
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
61796239 docs:: edoc
61806240 endif
61816241
61846244 # Plugin-specific targets.
61856245
61866246 edoc: distclean-edoc doc-deps
6187 $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
61886248
61896249 distclean-edoc:
61906250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
61916251
6192 # Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61936253 # This file is part of erlang.mk and subject to the terms of the ISC License.
61946254
6195 .PHONY: distclean-escript escript
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
61966320
61976321 # Configuration.
61986322
61996323 ESCRIPT_NAME ?= $(PROJECT)
62006324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
62016325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
62026327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6203
6204 ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
6205 ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
6206 ESCRIPT_EMU_ARGS ?= -pa . \
6207 -sasl errlog_type error \
6208 -escript main $(ESCRIPT_NAME)
6209 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6210 ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
62116332
62126333 # Core targets.
62136334
62206341
62216342 # Plugin-specific targets.
62226343
6223 # Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
6224 # Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
6225 # Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
6226 # Software may only be used for the great good and the true happiness of all
6227 # sentient beings.
6228
6229 define ESCRIPT_RAW
6230 'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
6231 'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
6232 ' [F || F <- A, not filelib:is_dir(F) ] end,'\
6233 'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
6234 'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
6235 'Ez = fun(Escript) ->'\
6236 ' Static = Files([$(ESCRIPT_STATIC)]),'\
6237 ' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
6238 ' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
6239 ' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
6240 ' {archive, Archive, [memory]},'\
6241 ' {shebang, "$(ESCRIPT_SHEBANG)"},'\
6242 ' {comment, "$(ESCRIPT_COMMENT)"},'\
6243 ' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
6244 ' ]),'\
6245 ' file:change_mode(Escript, 8#755)'\
6246 'end,'\
6247 'Ez("$(ESCRIPT_FILE)"),'\
6248 'halt().'
6249 endef
6250
6251 ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
6252
6253 escript:: distclean-escript deps app
6254 $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
62556360
62566361 distclean-escript:
6257 $(gen_verbose) rm -f $(ESCRIPT_NAME)
6258
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
62596365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6260 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
62616366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
62626367
62636368 .PHONY: eunit apps-eunit
63216426
63226427 ifneq ($(ALL_APPS_DIRS),)
63236428 apps-eunit:
6324 $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done
6325 endif
6326 endif
6327
6328 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
63296436 # This file is part of erlang.mk and subject to the terms of the ISC License.
63306437
6331 .PHONY: relx-rel distclean-relx-rel distclean-relx run
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
63326439
63336440 # Configuration.
63346441
6335 RELX ?= $(CURDIR)/relx
6442 RELX ?= $(ERLANG_MK_TMP)/relx
63366443 RELX_CONFIG ?= $(CURDIR)/relx.config
63376444
63386445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
63396446 RELX_OPTS ?=
63406447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
63416454
63426455 ifeq ($(firstword $(RELX_OPTS)),-o)
63436456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
63506463 ifeq ($(IS_DEP),)
63516464 ifneq ($(wildcard $(RELX_CONFIG)),)
63526465 rel:: relx-rel
6353 endif
6354 endif
6355
6356 distclean:: distclean-relx-rel distclean-relx
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
63576472
63586473 # Plugin-specific targets.
63596474
63626477 $(verbose) chmod +x $(RELX)
63636478
63646479 relx-rel: $(RELX) rel-deps app
6365 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
63666484
63676485 distclean-relx-rel:
63686486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6369
6370 distclean-relx:
6371 $(gen_verbose) rm -rf $(RELX)
63726487
63736488 # Run target.
63746489
63776492 else
63786493
63796494 define get_relx_release.erl
6380 {ok, Config} = file:consult("$(RELX_CONFIG)"),
6381 {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
6382 io:format("~s", [Name]),
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
63836504 halt(0).
63846505 endef
63856506
6386 RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
63876514
63886515 run: all
6389 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
63906517
63916518 help::
63926519 $(verbose) printf "%s\n" "" \
63956522
63966523 endif
63976524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
63986526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6399 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
64006527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
64016528
64026529 .PHONY: shell
64216548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
64226549
64236550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6424 $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
64256552
64266553 shell: build-shell-deps
64276554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
64286555
6429 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
64306571 # This file is part of erlang.mk and subject to the terms of the ISC License.
64316572
64326573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
64376578 tests:: triq
64386579
64396580 define triq_check.erl
6440 code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
64416582 try
64426583 case $(1) of
64436584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
64696610 endif
64706611 endif
64716612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
64726614 # Copyright (c) 2015, Erlang Solutions Ltd.
64736615 # This file is part of erlang.mk and subject to the terms of the ISC License.
64746616
64856627 XREFR ?= $(CURDIR)/xrefr
64866628 export XREFR
64876629
6488 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
64896631
64906632 # Core targets.
64916633
64926634 help::
6493 $(verbose) printf "%s\n" "" \
6494 "Xref targets:" \
6495 " xref Run Xrefr using $XREF_CONFIG as config file if defined"
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
64966638
64976639 distclean:: distclean-xref
64986640
65086650 distclean-xref:
65096651 $(gen_verbose) rm -rf $(XREFR)
65106652
6511 # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
65126655 # This file is part of erlang.mk and subject to the terms of the ISC License.
65136656
65146657 COVER_REPORT_DIR = cover
65176660
65186661 ifdef COVER
65196662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
65206664 # All modules in 'ebin'
65216665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
65226666
65316675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
65326676 endif
65336677 endif
6678 endif
65346679
65356680 # Core targets
65366681
65526697 "Cover targets:" \
65536698 " cover-report Generate a HTML coverage report from previously collected" \
65546699 " cover data." \
6555 " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
65566701 "" \
65576702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
65586703 "target tests additionally generates a HTML coverage report from the combined" \
65656710
65666711 .PHONY: coverdata-clean
65676712 coverdata-clean:
6568 $(gen_verbose) rm -f *.coverdata ct.cover.spec
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
65696714
65706715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
65716721 all.coverdata: $(COVERDATA)
6572 $(gen_verbose) $(ERL) -eval ' \
6573 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
6574 cover:export("$@"), halt(0).'
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
65756723
65766724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
65776725 # empty if you want the coverdata files but not the HTML report.
65896737 # Modules which include eunit.hrl always contain one line without coverage
65906738 # because eunit defines test/0 which is never called. We compensate for this.
65916739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6592 grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
65936741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
65946742
65956743 define cover_report.erl
66246772 endef
66256773
66266774 cover-report:
6627 $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
66286776 $(gen_verbose) $(call erlang,$(cover_report.erl))
66296777
66306778 endif
66316779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
66326839
66336840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
66346841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
66976904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
66986905 endif
66996906 ifndef IS_APP
6700 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
67016908 $(MAKE) -C $$dep $@ \
67026909 IS_APP=1 \
6703 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6704 || exit $$?; \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67056911 done
67066912 endif
6707 $(verbose) for dep in $^ ; do \
6913 $(verbose) set -e; for dep in $^ ; do \
67086914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
67096915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6710 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
67116917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
67126918 $(MAKE) -C $$dep fetch-deps \
67136919 IS_DEP=1 \
6714 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6715 || exit $$?; \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67166921 fi \
67176922 fi \
67186923 done
22 # before the inclusion of erlang.mk leading to the wrong target becoming
33 # the default.
44 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
526 endif
627
728 # --------------------------------------------------------------------
2041 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
2142 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
2243 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
2345 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
2446 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
2547 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
2648 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
2749 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
2851 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
2952 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
3054 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
3155 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
3256 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
3559 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
3660 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
3761 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
3863 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
3964 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
4065 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
7095
7196 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
7297
73 # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
74 # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
75 # needs to add "ranch" as a BUILD_DEPS. The list of projects needing
76 # this workaround are:
77 # o rabbitmq-web-stomp
78 dep_ranch = git https://github.com/ninenines/ranch 1.2.1
98 # Third-party dependencies version pinning.
99 #
100 # We do that in this file, which is copied in all projects, to ensure
101 # all projects use the same versions. It avoids conflicts and makes it
102 # possible to work with rabbitmq-public-umbrella.
103
104 dep_cowboy_commit = 1.0.4
105 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
106 dep_ranch_commit = 1.3.1
107 dep_webmachine_commit = 1.10.8p2
79108
80109 RABBITMQ_COMPONENTS = amqp_client \
81110 rabbit \
82111 rabbit_common \
83112 rabbitmq_amqp1_0 \
84113 rabbitmq_auth_backend_amqp \
114 rabbitmq_auth_backend_cache \
85115 rabbitmq_auth_backend_http \
86116 rabbitmq_auth_backend_ldap \
87117 rabbitmq_auth_mechanism_ssl \
88118 rabbitmq_boot_steps_visualiser \
89119 rabbitmq_clusterer \
120 rabbitmq_cli \
90121 rabbitmq_codegen \
91122 rabbitmq_consistent_hash_exchange \
123 rabbitmq_ct_client_helpers \
92124 rabbitmq_ct_helpers \
93125 rabbitmq_delayed_message_exchange \
94126 rabbitmq_dotnet_client \
97129 rabbitmq_federation_management \
98130 rabbitmq_java_client \
99131 rabbitmq_jms_client \
132 rabbitmq_jms_cts \
100133 rabbitmq_jms_topic_exchange \
101134 rabbitmq_lvc \
102135 rabbitmq_management \
1010 %% The Original Code is RabbitMQ Federation.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_federation_app).
1010 %% The Original Code is RabbitMQ Federation.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_federation_db).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_federation_event).
1010 %% The Original Code is RabbitMQ Federation.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% TODO rename this
1010 %% The Original Code is RabbitMQ Federation.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_federation_exchange_link).
7979 gen_server2:cast(self(), maybe_go),
8080 {ok, {not_started, {Upstream, UParams, XName}}};
8181 {error, not_found} ->
82 rabbit_federation_link_util:log_warning(XName, "not found, stopping link~n", []),
8283 {stop, gone}
8384 end.
8485
105106 handle_cast({enqueue, _, _}, State = {not_started, _}) ->
106107 {noreply, State};
107108
108 handle_cast({enqueue, Serial, Cmd}, State = #state{waiting_cmds = Waiting}) ->
109 handle_cast({enqueue, Serial, Cmd},
110 State = #state{waiting_cmds = Waiting,
111 downstream_exchange = XName}) ->
109112 Waiting1 = gb_trees:insert(Serial, Cmd, Waiting),
110 {noreply, play_back_commands(State#state{waiting_cmds = Waiting1})};
113 try
114 {noreply, play_back_commands(State#state{waiting_cmds = Waiting1})}
115 catch exit:{{shutdown, {server_initiated_close, 404, Text}}, _} ->
116 rabbit_federation_link_util:log_warning(
117 XName, "detected upstream changes, restarting link: ~p~n", [Text]),
118 {stop, {shutdown, restart}, State}
119 end;
111120
112121 handle_cast(Msg, State) ->
113122 {stop, {unexpected_cast, Msg}, State}.
344353 Hops = case rabbit_misc:table_lookup(Args, ?BINDING_HEADER) of
345354 undefined -> MaxHops;
346355 {array, All} -> [{table, Prev} | _] = All,
347 {short, PrevHops} =
348 rabbit_misc:table_lookup(Prev, <<"hops">>),
356 PrevHops = get_hops(Prev),
349357 case rabbit_federation_util:already_seen(
350358 UName, All) of
351359 true -> 0;
365373 {<<"hops">>, short, Hops}],
366374 rabbit_basic:prepend_table_header(?BINDING_HEADER, Info, Args)
367375 end.
376
377
368378
369379 key(#binding{key = Key, args = Args}) -> {Key, Args}.
370380
541551
542552 header_for_name(unknown) -> [];
543553 header_for_name(Name) -> [{<<"cluster-name">>, longstr, Name}].
554
555 get_hops(Table) ->
556 case rabbit_misc:table_lookup(Table, <<"hops">>) of
557 %% see rabbit_binary_generator
558 {short, N} -> N;
559 {long, N} -> N;
560 {byte, N} -> N;
561 {signedint, N} -> N;
562 {unsignedbyte, N} -> N;
563 {unsignedshort, N} -> N;
564 {unsignedint, N} -> N;
565 {_, N} when is_integer(N) andalso N >= 0 -> N
566 end.
1010 %% The Original Code is RabbitMQ Federation.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_federation_exchange_link_sup_sup).
5858 %%----------------------------------------------------------------------------
5959
6060 init([]) ->
61 {ok, {{one_for_one, 3, 10}, []}}.
61 {ok, {{one_for_one, 1200, 60}, []}}.
6262
6363 %% See comment in rabbit_federation_queue_link_sup_sup:id/1
6464 id(X = #exchange{policy = Policy}) -> X1 = rabbit_exchange:immutable(X),
1010 %% The Original Code is RabbitMQ Federation.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_federation_link_sup).
2222
2323 %% Supervises the upstream links for an exchange or queue.
2424
25 -export([start_link/1, adjust/3]).
25 -export([start_link/1, adjust/3, restart/2]).
2626 -export([init/1]).
2727
2828 start_link(XorQ) ->
6969 adjust(Sup, XorQ, {clear_upstream_set, _}) ->
7070 adjust(Sup, XorQ, everything).
7171
72 restart(Sup, Upstream) ->
73 ok = supervisor2:terminate_child(Sup, Upstream),
74 {ok, _Pid} = supervisor2:restart_child(Sup, Upstream),
75 ok.
76
7277 start(Sup, Upstream, XorQ) ->
7378 {ok, _Pid} = supervisor2:start_child(Sup, spec(Upstream, XorQ)),
7479 ok.
1010 %% The Original Code is RabbitMQ Federation.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_federation_link_util).
2222 -export([start_conn_ch/5, disposable_channel_call/2, disposable_channel_call/3,
2323 disposable_connection_call/3, ensure_connection_closed/1,
2424 log_terminate/4, unacked_new/0, ack/3, nack/3, forward/9,
25 handle_down/6, get_connection_name/2]).
25 handle_down/6, get_connection_name/2, log_warning/3]).
2626
2727 %% temp
2828 -export([connection_error/6]).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_federation_parameters).
1010 %% The Original Code is RabbitMQ Federation.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_federation_queue).
1010 %% The Original Code is RabbitMQ Federation.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_federation_queue_link).
7676 upstream = Upstream,
7777 upstream_params = UParams}};
7878 {error, not_found} ->
79 rabbit_federation_link_util:log_warning(QName, "not found, stopping link~n", []),
7980 {stop, gone}
8081 end.
8182
1010 %% The Original Code is RabbitMQ Federation.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_federation_queue_link_sup_sup).
5656 %%----------------------------------------------------------------------------
5757
5858 init([]) ->
59 {ok, {{one_for_one, 3, 10}, []}}.
59 {ok, {{one_for_one, 1200, 60}, []}}.
6060
6161 %% Clean out all mutable aspects of the queue except policy. We need
6262 %% to keep the entire queue around rather than just take its name
1010 %% The Original Code is RabbitMQ Federation.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_federation_status).
2121
2222 -export([start_link/0]).
2323
24 -export([report/4, remove_exchange_or_queue/1, remove/2, status/0]).
24 -export([report/4, remove_exchange_or_queue/1, remove/2, status/0, lookup/1]).
2525
2626 -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
2727 terminate/2, code_change/3]).
3232 -define(ETS_NAME, ?MODULE).
3333
3434 -record(state, {}).
35 -record(entry, {key, uri, status, timestamp}).
35 -record(entry, {key, uri, status, timestamp, id, supervisor, upstream}).
3636
3737 start_link() ->
3838 gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
3939
4040 report(Upstream, UParams, XorQName, Status) ->
41 gen_server:cast(?SERVER, {report, Upstream, UParams, XorQName, Status,
42 calendar:local_time()}).
41 [Supervisor | _] = get('$ancestors'),
42 gen_server:cast(?SERVER, {report, Supervisor, Upstream, UParams, XorQName,
43 Status, calendar:local_time()}).
4344
4445 remove_exchange_or_queue(XorQName) ->
4546 gen_server:call(?SERVER, {remove_exchange_or_queue, XorQName}, infinity).
4950
5051 status() ->
5152 gen_server:call(?SERVER, status, infinity).
53
54 lookup(Id) ->
55 gen_server:call(?SERVER, {lookup, Id}, infinity).
5256
5357 init([]) ->
5458 ?ETS_NAME = ets:new(?ETS_NAME,
6771 end,
6872 {reply, ok, State};
6973
74 handle_call({lookup, Id}, _From, State) ->
75 Link = case ets:match_object(?ETS_NAME, match_id(Id)) of
76 [Entry] -> [{key, Entry#entry.key},
77 {uri, Entry#entry.uri},
78 {status, Entry#entry.status},
79 {timestamp, Entry#entry.timestamp},
80 {id, Entry#entry.id},
81 {supervisor, Entry#entry.supervisor},
82 {upstream, Entry#entry.upstream}];
83 [] -> not_found
84 end,
85 {reply, Link, State};
86
7087 handle_call(status, _From, State) ->
7188 Entries = ets:tab2list(?ETS_NAME),
7289 {reply, [format(Entry) || Entry <- Entries], State}.
7390
74 handle_cast({report, Upstream, #upstream_params{safe_uri = URI},
91 handle_cast({report, Supervisor, Upstream, #upstream_params{safe_uri = URI},
7592 XorQName, Status, Timestamp}, State) ->
76 Entry = #entry{key = key(XorQName, Upstream),
93 Key = key(XorQName, Upstream),
94 Entry = #entry{key = Key,
7795 status = Status,
7896 uri = URI,
79 timestamp = Timestamp},
97 timestamp = Timestamp,
98 supervisor = Supervisor,
99 upstream = Upstream,
100 id = unique_id(Key)},
80101 true = ets:insert(?ETS_NAME, Entry),
81102 rabbit_event:notify(federation_link_status, format(Entry)),
82103 {noreply, State}.
99120 identity(#entry{key = {#resource{virtual_host = VHost,
100121 kind = Type,
101122 name = XorQNameBin},
102 UpstreamName, UXorQNameBin}}) ->
123 UpstreamName, UXorQNameBin},
124 id = Id}) ->
103125 case Type of
104126 exchange -> [{exchange, XorQNameBin},
105127 {upstream_exchange, UXorQNameBin}];
107129 {upstream_queue, UXorQNameBin}]
108130 end ++ [{type, Type},
109131 {vhost, VHost},
110 {upstream, UpstreamName}].
132 {upstream, UpstreamName},
133 {id, Id}].
134
135 unique_id(Key) ->
136 << << case N >= 10 of
137 true -> N - 10 + $a;
138 false -> N + $0 end >>
139 || <<N:4>> <= crypto:hash(sha, term_to_binary(Key)) >>.
111140
112141 split_status({running, ConnName}) -> [{status, running},
113142 {local_connection, ConnName}];
135164 #entry{key = Key,
136165 uri = '_',
137166 status = '_',
138 timestamp = '_'}.
167 timestamp = '_',
168 id = '_',
169 supervisor = '_',
170 upstream = '_'}.
171
172 match_id(Id) ->
173 #entry{key = '_',
174 uri = '_',
175 status = '_',
176 timestamp = '_',
177 id = Id,
178 supervisor = '_',
179 upstream = '_'}.
1010 %% The Original Code is RabbitMQ Federation.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_federation_sup).
6464 {rabbit_federation_queue_link_sup_sup, start_link, []},
6565 transient, ?SUPERVISOR_WAIT, supervisor,
6666 [rabbit_federation_queue_link_sup_sup]},
67 {ok, {{one_for_one, 3, 10}, [Status, XLinkSupSup, QLinkSupSup]}}.
67 %% with default reconnect-delay of 5 second, this supports up to
68 %% 100 links constantly failing and being restarted a minute
69 %% (or 200 links if reconnect-delay is 10 seconds, 600 with 30 seconds,
70 %% etc: N * (60/reconnect-delay) <= 1200)
71 {ok, {{one_for_one, 1200, 60}, [Status, XLinkSupSup, QLinkSupSup]}}.
1010 %% The Original Code is RabbitMQ Federation.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_federation_upstream).
1010 %% The Original Code is RabbitMQ Federation.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_federation_upstream_exchange).
1010 %% The Original Code is RabbitMQ Federation.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_federation_util).
+0
-8
deps/rabbitmq_federation/src/rabbitmq_federation.app.src less more
0 {application, rabbitmq_federation,
1 [{description, "RabbitMQ Federation"},
2 {vsn, "3.6.6"},
3 {modules, []},
4 {registered, []},
5 {mod, {rabbit_federation_app, []}},
6 {env, [{pgroup_name_cluster_id, false}]},
7 {applications, [kernel, stdlib, rabbit_common, rabbit, amqp_client]}]}.
00 PROJECT = rabbitmq_federation_management
1 PROJECT_DESCRIPTION = RabbitMQ Federation Management
12
2 DEPS = rabbit_common rabbit rabbitmq_management webmachine
3 DEPS = rabbit_common rabbit rabbitmq_management rabbitmq_federation
4 TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers
35
6 DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
47 DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk
58
69 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
3232
3333 ## Copyright and License
3434
35 (c) Pivotal Software Inc, 2007-2016.
35 (c) Pivotal Software Inc, 2007-2017.
3636
3737 See `LICENSE` for license information.
0 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
11 #
22 # Permission to use, copy, modify, and/or distribute this software for any
33 # purpose with or without fee is hereby granted, provided that the above
1111 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1212 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1313
14 .PHONY: all app apps deps search rel docs install-docs check tests clean distclean help erlang-mk
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
1515
1616 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17
18 ERLANG_MK_VERSION = 2.0.0-pre.2-144-g647ffd1
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
1931
2032 # Core configuration.
2133
2436
2537 PROJECT_VERSION ?= rolling
2638 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
2740
2841 # Verbosity.
2942
8497 rel::
8598 $(verbose) :
8699
100 relup:: deps app
101
87102 check:: tests
88103
89104 clean:: clean-crashdump
101116 help::
102117 $(verbose) printf "%s\n" \
103118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
104 "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
105120 "" \
106121 "Usage: [V=1] $(MAKE) [target]..." \
107122 "" \
149164 core_native_path = $1
150165 endif
151166
152 ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
153 define core_http_get
154 wget --no-check-certificate -O $(1) $(2)|| rm $(1)
155 endef
156 else
157 define core_http_get.erl
158 ssl:start(),
159 inets:start(),
160 case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
161 {ok, {{_, 200, _}, _, Body}} ->
162 case file:write_file("$(1)", Body) of
163 ok -> ok;
164 {error, R1} -> halt(R1)
165 end;
166 {error, R2} ->
167 halt(R2)
168 end,
169 halt(0).
170 endef
171
172 define core_http_get
173 $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
174 endef
175 endif
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
176168
177169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
178170
192184 ERLANG_MK_BUILD_CONFIG ?= build.config
193185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
194186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
195188 erlang-mk:
196189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
197190 ifdef ERLANG_MK_COMMIT
198191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
199192 endif
200193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
201 $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
202195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
203196 rm -rf $(ERLANG_MK_BUILD_DIR)
204197
285278 pkg_apns_repo = https://github.com/inaka/apns4erl
286279 pkg_apns_commit = master
287280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
288289 PACKAGES += azdht
289290 pkg_azdht_name = azdht
290291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
405406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
406407 pkg_bootstrap_commit = master
407408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
408417 PACKAGES += boss_db
409418 pkg_boss_db_name = boss_db
410419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
413422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
414423 pkg_boss_db_commit = master
415424
416 PACKAGES += boss
417 pkg_boss_name = boss
418 pkg_boss_description = Erlang web MVC, now featuring Comet
419 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
420 pkg_boss_fetch = git
421 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
422 pkg_boss_commit = master
423
424425 PACKAGES += brod
425426 pkg_brod_name = brod
426427 pkg_brod_description = Kafka client in Erlang
565566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
566567 pkg_cloudi_service_api_requests_commit = master
567568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
568585 PACKAGES += cloudi_service_db_cassandra_cql
569586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
570587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
573590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
574591 pkg_cloudi_service_db_cassandra_cql_commit = master
575592
576 PACKAGES += cloudi_service_db_cassandra
577 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
579 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
580 pkg_cloudi_service_db_cassandra_fetch = git
581 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
582 pkg_cloudi_service_db_cassandra_commit = master
583
584593 PACKAGES += cloudi_service_db_couchdb
585594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
586595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
637646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
638647 pkg_cloudi_service_db_tokyotyrant_commit = master
639648
640 PACKAGES += cloudi_service_db
641 pkg_cloudi_service_db_name = cloudi_service_db
642 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
643 pkg_cloudi_service_db_homepage = http://cloudi.org/
644 pkg_cloudi_service_db_fetch = git
645 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
646 pkg_cloudi_service_db_commit = master
647
648649 PACKAGES += cloudi_service_filesystem
649650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
650651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
10371038 pkg_edown_repo = https://github.com/uwiger/edown
10381039 pkg_edown_commit = master
10391040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
10401049 PACKAGES += eep_app
10411050 pkg_eep_app_name = eep_app
10421051 pkg_eep_app_description = Embedded Event Processing
10451054 pkg_eep_app_repo = https://github.com/darach/eep-erl
10461055 pkg_eep_app_commit = master
10471056
1048 PACKAGES += eep
1049 pkg_eep_name = eep
1050 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1051 pkg_eep_homepage = https://github.com/virtan/eep
1052 pkg_eep_fetch = git
1053 pkg_eep_repo = https://github.com/virtan/eep
1054 pkg_eep_commit = master
1055
10561057 PACKAGES += efene
10571058 pkg_efene_name = efene
10581059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
12371238 pkg_eqm_repo = https://github.com/loucash/eqm
12381239 pkg_eqm_commit = master
12391240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
12401249 PACKAGES += eredis_pool
12411250 pkg_eredis_pool_name = eredis_pool
12421251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
12451254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
12461255 pkg_eredis_pool_commit = master
12471256
1248 PACKAGES += eredis
1249 pkg_eredis_name = eredis
1250 pkg_eredis_description = Erlang Redis client
1251 pkg_eredis_homepage = https://github.com/wooga/eredis
1252 pkg_eredis_fetch = git
1253 pkg_eredis_repo = https://github.com/wooga/eredis
1254 pkg_eredis_commit = master
1255
12561257 PACKAGES += erl_streams
12571258 pkg_erl_streams_name = erl_streams
12581259 pkg_erl_streams_description = Streams in Erlang
15411542 pkg_etap_repo = https://github.com/ngerakines/etap
15421543 pkg_etap_commit = master
15431544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
15441553 PACKAGES += etest_http
15451554 pkg_etest_http_name = etest_http
15461555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
15491558 pkg_etest_http_repo = https://github.com/wooga/etest_http
15501559 pkg_etest_http_commit = master
15511560
1552 PACKAGES += etest
1553 pkg_etest_name = etest
1554 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1555 pkg_etest_homepage = https://github.com/wooga/etest
1556 pkg_etest_fetch = git
1557 pkg_etest_repo = https://github.com/wooga/etest
1558 pkg_etest_commit = master
1559
15601561 PACKAGES += etoml
15611562 pkg_etoml_name = etoml
15621563 pkg_etoml_description = TOML language erlang parser
15651566 pkg_etoml_repo = https://github.com/kalta/etoml
15661567 pkg_etoml_commit = master
15671568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
15681577 PACKAGES += eunit_formatters
15691578 pkg_eunit_formatters_name = eunit_formatters
15701579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
15731582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
15741583 pkg_eunit_formatters_commit = master
15751584
1576 PACKAGES += eunit
1577 pkg_eunit_name = eunit
1578 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1579 pkg_eunit_homepage = https://github.com/richcarl/eunit
1580 pkg_eunit_fetch = git
1581 pkg_eunit_repo = https://github.com/richcarl/eunit
1582 pkg_eunit_commit = master
1583
15841585 PACKAGES += euthanasia
15851586 pkg_euthanasia_name = euthanasia
15861587 pkg_euthanasia_description = Merciful killer for your Erlang processes
15981599 pkg_evum_commit = master
15991600
16001601 PACKAGES += exec
1601 pkg_exec_name = exec
1602 pkg_exec_name = erlexec
16021603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
16031604 pkg_exec_homepage = http://saleyn.github.com/erlexec
16041605 pkg_exec_fetch = git
17171718 pkg_fn_repo = https://github.com/reiddraper/fn
17181719 pkg_fn_commit = master
17191720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
17201729 PACKAGES += folsom_cowboy
17211730 pkg_folsom_cowboy_name = folsom_cowboy
17221731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
17251734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
17261735 pkg_folsom_cowboy_commit = master
17271736
1728 PACKAGES += folsom
1729 pkg_folsom_name = folsom
1730 pkg_folsom_description = Expose Erlang Events and Metrics
1731 pkg_folsom_homepage = https://github.com/boundary/folsom
1732 pkg_folsom_fetch = git
1733 pkg_folsom_repo = https://github.com/boundary/folsom
1734 pkg_folsom_commit = master
1735
17361737 PACKAGES += folsomite
17371738 pkg_folsomite_name = folsomite
17381739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
20932094 pkg_jesse_repo = https://github.com/for-GET/jesse
20942095 pkg_jesse_commit = master
20952096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
20962105 PACKAGES += jiffy_v
20972106 pkg_jiffy_v_name = jiffy_v
20982107 pkg_jiffy_v_description = JSON validation utility
21012110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
21022111 pkg_jiffy_v_commit = master
21032112
2104 PACKAGES += jiffy
2105 pkg_jiffy_name = jiffy
2106 pkg_jiffy_description = JSON NIFs for Erlang.
2107 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2108 pkg_jiffy_fetch = git
2109 pkg_jiffy_repo = https://github.com/davisp/jiffy
2110 pkg_jiffy_commit = master
2111
21122113 PACKAGES += jobs
21132114 pkg_jobs_name = jobs
21142115 pkg_jobs_description = a Job scheduler for load regulation
21252126 pkg_joxa_repo = https://github.com/joxa/joxa
21262127 pkg_joxa_commit = master
21272128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
21282137 PACKAGES += json_rec
21292138 pkg_json_rec_name = json_rec
21302139 pkg_json_rec_description = JSON to erlang record
21332142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
21342143 pkg_json_rec_commit = master
21352144
2136 PACKAGES += json
2137 pkg_json_name = json
2138 pkg_json_description = a high level json library for erlang (17.0+)
2139 pkg_json_homepage = https://github.com/talentdeficit/json
2140 pkg_json_fetch = git
2141 pkg_json_repo = https://github.com/talentdeficit/json
2142 pkg_json_commit = master
2143
21442145 PACKAGES += jsone
21452146 pkg_jsone_name = jsone
21462147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
21812182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
21822183 pkg_jsx_commit = master
21832184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
21842193 PACKAGES += kafka_protocol
21852194 pkg_kafka_protocol_name = kafka_protocol
21862195 pkg_kafka_protocol_description = Kafka protocol Erlang library
21892198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
21902199 pkg_kafka_protocol_commit = master
21912200
2192 PACKAGES += kafka
2193 pkg_kafka_name = kafka
2194 pkg_kafka_description = Kafka consumer and producer in Erlang
2195 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2196 pkg_kafka_fetch = git
2197 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2198 pkg_kafka_commit = master
2199
22002201 PACKAGES += kai
22012202 pkg_kai_name = kai
22022203 pkg_kai_description = DHT storage by Takeshi Inoue
22932294 pkg_kvs_repo = https://github.com/synrc/kvs
22942295 pkg_kvs_commit = master
22952296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
22962305 PACKAGES += lager_amqp_backend
22972306 pkg_lager_amqp_backend_name = lager_amqp_backend
22982307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
23092318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
23102319 pkg_lager_syslog_commit = master
23112320
2312 PACKAGES += lager
2313 pkg_lager_name = lager
2314 pkg_lager_description = A logging framework for Erlang/OTP.
2315 pkg_lager_homepage = https://github.com/basho/lager
2316 pkg_lager_fetch = git
2317 pkg_lager_repo = https://github.com/basho/lager
2318 pkg_lager_commit = master
2319
23202321 PACKAGES += lambdapad
23212322 pkg_lambdapad_name = lambdapad
23222323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
25732574 pkg_mixer_repo = https://github.com/chef/mixer
25742575 pkg_mixer_commit = master
25752576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
25762585 PACKAGES += mochiweb_xpath
25772586 pkg_mochiweb_xpath_name = mochiweb_xpath
25782587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
25812590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
25822591 pkg_mochiweb_xpath_commit = master
25832592
2584 PACKAGES += mochiweb
2585 pkg_mochiweb_name = mochiweb
2586 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2587 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2588 pkg_mochiweb_fetch = git
2589 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2590 pkg_mochiweb_commit = master
2591
25922593 PACKAGES += mockgyver
25932594 pkg_mockgyver_name = mockgyver
25942595 pkg_mockgyver_description = A mocking library for Erlang
30613062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
30623063 pkg_quickrand_commit = master
30633064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
30643073 PACKAGES += rabbit_exchange_type_riak
30653074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
30663075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
30693078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
30703079 pkg_rabbit_exchange_type_riak_commit = master
30713080
3072 PACKAGES += rabbit
3073 pkg_rabbit_name = rabbit
3074 pkg_rabbit_description = RabbitMQ Server
3075 pkg_rabbit_homepage = https://www.rabbitmq.com/
3076 pkg_rabbit_fetch = git
3077 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3078 pkg_rabbit_commit = master
3079
30803081 PACKAGES += rack
30813082 pkg_rack_name = rack
30823083 pkg_rack_description = Rack handler for erlang
34933494 pkg_smother_repo = https://github.com/ramsay-t/Smother
34943495 pkg_smother_commit = master
34953496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
34963505 PACKAGES += social
34973506 pkg_social_name = social
34983507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
35413550 pkg_stable_repo = https://github.com/dvv/stable
35423551 pkg_stable_commit = master
35433552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
35443561 PACKAGES += statebox_riak
35453562 pkg_statebox_riak_name = statebox_riak
35463563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
35493566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
35503567 pkg_statebox_riak_commit = master
35513568
3552 PACKAGES += statebox
3553 pkg_statebox_name = statebox
3554 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3555 pkg_statebox_homepage = https://github.com/mochi/statebox
3556 pkg_statebox_fetch = git
3557 pkg_statebox_repo = https://github.com/mochi/statebox
3558 pkg_statebox_commit = master
3559
35603569 PACKAGES += statman
35613570 pkg_statman_name = statman
35623571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
37253734 pkg_tirerl_repo = https://github.com/inaka/tirerl
37263735 pkg_tirerl_commit = master
37273736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
37283745 PACKAGES += traffic_tools
37293746 pkg_traffic_tools_name = traffic_tools
37303747 pkg_traffic_tools_description = Simple traffic limiting library
40614078 pkg_zucchini_repo = https://github.com/devinus/zucchini
40624079 pkg_zucchini_commit = master
40634080
4064 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
40654082 # This file is part of erlang.mk and subject to the terms of the ISC License.
40664083
40674084 .PHONY: search
40884105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
40894106 endif
40904107
4091 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
40924109 # This file is part of erlang.mk and subject to the terms of the ISC License.
40934110
4094 .PHONY: distclean-deps
4111 .PHONY: distclean-deps clean-tmp-deps.log
40954112
40964113 # Configuration.
40974114
41104127
41114128 REBAR_DEPS_DIR = $(DEPS_DIR)
41124129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
41134150
41144151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
41154152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
41164153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
41174154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
41184155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
41194157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
41204158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
41214159
41384176
41394177 # Core targets.
41404178
4141 ifdef IS_APP
4142 apps::
4143 else
4144 apps:: $(ALL_APPS_DIRS)
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
41454180 ifeq ($(IS_APP)$(IS_DEP),)
41464181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
41474182 endif
41494184 # Create ebin directory for all apps to make sure Erlang recognizes them
41504185 # as proper OTP applications when using -include_lib. This is a temporary
41514186 # fix, a proper fix would be to compile apps/* in the right order.
4152 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4153 mkdir -p $$dep/ebin || exit $$?; \
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
41544190 done
4155 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
41564196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
41574197 :; \
41584198 else \
41594199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4160 $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
4200 $(MAKE) -C $$dep IS_APP=1; \
41614201 fi \
41624202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
41634207 endif
41644208
41654209 ifneq ($(SKIP_DEPS),)
41664210 deps::
41674211 else
4168 deps:: $(ALL_DEPS_DIRS) apps
4169 ifeq ($(IS_APP)$(IS_DEP),)
4170 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4171 endif
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
41724213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4173 $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
41744215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
41754216 :; \
41764217 else \
41774218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
41784219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4179 $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
4220 $(MAKE) -C $$dep IS_DEP=1; \
41804221 else \
4181 echo "Error: No Makefile to build dependency $$dep."; \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
41824223 exit 2; \
41834224 fi \
41844225 fi \
41924233 # in practice only Makefile is needed so far.
41934234 define dep_autopatch
41944235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
41954237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
41964238 $(call dep_autopatch_erlang_mk,$(1)); \
41974239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4198 if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
41994243 $(call dep_autopatch2,$(1)); \
4200 elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
42014245 $(call dep_autopatch2,$(1)); \
4202 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
42034247 $(call dep_autopatch2,$(1)); \
4204 else \
4205 $(call erlang,$(call dep_autopatch_app.erl,$(1))); \
42064248 fi \
42074249 else \
42084250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
42144256 endef
42154257
42164258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
42174262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
42184263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
42194264 fi; \
42204265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4221 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
42224267 $(call dep_autopatch_fetch_rebar); \
42234268 $(call dep_autopatch_rebar,$(1)); \
42244269 else \
42304275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
42314276 endef
42324277
4233 # Overwrite erlang.mk with the current file by default.
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
42344280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
42354281 define dep_autopatch_erlang_mk
4236 echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \
4237 > $(DEPS_DIR)/$1/erlang.mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
42384287 endef
42394288 else
42404289 define dep_autopatch_erlang_mk
42534302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
42544303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
42554304 cd $(ERLANG_MK_TMP)/rebar; \
4256 git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
42574306 $(MAKE); \
42584307 cd -; \
42594308 fi
42704319 define dep_autopatch_rebar.erl
42714320 application:load(rebar),
42724321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
42734323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
42744324 {ok, Conf0} -> Conf0;
42754325 _ -> []
44234473 [] -> ok;
44244474 _ ->
44254475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4426 PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
44274477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4428 PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L \\"~s\\" -lerl_interface -lei\n",
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
44294479 [code:lib_dir(erl_interface, lib)])),
44304480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
44314481 FilterEnv = fun(Env) ->
44644514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44654515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44664516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4467 [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
44684518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
44694519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
44704520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
44764526 end,
44774527 [PortSpec(S) || S <- PortSpecs]
44784528 end,
4479 Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"),
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
44804530 RunPlugin = fun(Plugin, Step) ->
44814531 case erlang:function_exported(Plugin, Step, 2) of
44824532 false -> ok;
45244574 halt()
45254575 endef
45264576
4527 define dep_autopatch_app.erl
4528 UpdateModules = fun(App) ->
4529 case filelib:is_regular(App) of
4530 false -> ok;
4531 true ->
4532 {ok, [{application, '$(1)', L0}]} = file:consult(App),
4533 Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true,
4534 fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
4535 L = lists:keystore(modules, 1, L0, {modules, Mods}),
4536 ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}]))
4537 end
4538 end,
4539 UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"),
4540 halt()
4541 endef
4542
45434577 define dep_autopatch_appsrc_script.erl
45444578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
45454579 AppSrcScript = AppSrc ++ ".script",
45874621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
45884622 endef
45894623
4590 define dep_fetch_hex.erl
4591 ssl:start(),
4592 inets:start(),
4593 {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
4594 {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
4595 [], [{body_format, binary}]),
4596 {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
4597 {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
4598 ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
4599 halt()
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
46004626 endef
46014627
46024628 # Hex only has a package version. No need to look in the Erlang.mk packages.
46034629 define dep_fetch_hex
4604 $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
46054634 endef
46064635
46074636 define dep_fetch_fail
46314660 $(eval DEP_NAME := $(call dep_name,$1))
46324661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
46334662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4634 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
46354664 exit 17; \
46364665 fi
46374666 $(verbose) mkdir -p $(DEPS_DIR)
46734702 clean:: clean-apps
46744703
46754704 clean-apps:
4676 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4677 $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
46784707 done
46794708
46804709 distclean:: distclean-apps
46814710
46824711 distclean-apps:
4683 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4684 $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
46854714 done
46864715 endif
46874716
47014730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
47024731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
47034732
4704 # External plugins.
4705
4706 DEP_PLUGINS ?=
4707
4708 define core_dep_plugin
4709 -include $(DEPS_DIR)/$(1)
4710
4711 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4712 endef
4713
4714 $(foreach p,$(DEP_PLUGINS),\
4715 $(eval $(if $(findstring /,$p),\
4716 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4717 $(call core_dep_plugin,$p/plugins.mk,$p))))
4718
4719 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4720 # This file is part of erlang.mk and subject to the terms of the ISC License.
4721
4722 # Configuration.
4723
4724 DTL_FULL_PATH ?=
4725 DTL_PATH ?= templates/
4726 DTL_SUFFIX ?= _dtl
4727 DTL_OPTS ?=
4728
4729 # Verbosity.
4730
4731 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
4732 dtl_verbose = $(dtl_verbose_$(V))
4733
4734 # Core targets.
4735
4736 DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl))
4737
4738 ifneq ($(DTL_FILES),)
4739
4740 ifdef DTL_FULL_PATH
4741 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
4742 else
4743 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
4744 endif
4745
4746 ifneq ($(words $(DTL_FILES)),0)
4747 # Rebuild templates when the Makefile changes.
4748 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
4749 @mkdir -p $(ERLANG_MK_TMP)
4750 @if test -f $@; then \
4751 touch $(DTL_FILES); \
4752 fi
4753 @touch $@
4754
4755 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
4756 endif
4757
4758 define erlydtl_compile.erl
4759 [begin
4760 Module0 = case "$(strip $(DTL_FULL_PATH))" of
4761 "" ->
4762 filename:basename(F, ".dtl");
4763 _ ->
4764 "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
4765 re:replace(F2, "/", "_", [{return, list}, global])
4766 end,
4767 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
4768 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
4769 ok -> ok;
4770 {ok, _} -> ok
4771 end
4772 end || F <- string:tokens("$(1)", " ")],
4773 halt().
4774 endef
4775
4776 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
4777 $(if $(strip $?),\
4778 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
4779
4780 endif
4781
4782 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
47834734 # This file is part of erlang.mk and subject to the terms of the ISC License.
47844735
47854736 # Verbosity.
47984749
47994750 define compile_proto.erl
48004751 [begin
4801 Dir = filename:dirname(filename:dirname(F)),
48024752 protobuffs_compile:generate_source(F,
4803 [{output_include_dir, Dir ++ "/include"},
4804 {output_src_dir, Dir ++ "/ebin"}])
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
48054755 end || F <- string:tokens("$(1)", " ")],
48064756 halt().
48074757 endef
48114761 $(if $(strip $?),$(call compile_proto,$?))
48124762 endif
48134763
4814 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
48154765 # This file is part of erlang.mk and subject to the terms of the ISC License.
48164766
48174767 .PHONY: clean-app
48254775 ERLC_EXCLUDE ?=
48264776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
48274777
4778 ERLC_ASN1_OPTS ?=
4779
48284780 ERLC_MIB_OPTS ?=
48294781 COMPILE_MIB_FIRST ?=
48304782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
48744826
48754827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
48764828 define app_file
4877 {application, $(PROJECT), [
4829 {application, '$(PROJECT)', [
48784830 {description, "$(PROJECT_DESCRIPTION)"},
48794831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48804832 {id$(comma)$(space)"$(1)"}$(comma))
48814833 {modules, [$(call comma_list,$(2))]},
48824834 {registered, []},
4883 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48844837 ]}.
48854838 endef
48864839 else
48874840 define app_file
4888 {application, $(PROJECT), [
4841 {application, '$(PROJECT)', [
48894842 {description, "$(PROJECT_DESCRIPTION)"},
48904843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48914844 {id$(comma)$(space)"$(1)"}$(comma))
48924845 {modules, [$(call comma_list,$(2))]},
48934846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
48944847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4895 {mod, {$(PROJECT_MOD), []}}
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48964850 ]}.
48974851 endef
48984852 endif
49024856
49034857 # Source files.
49044858
4905 ERL_FILES = $(sort $(call core_find,src/,*.erl))
4906 CORE_FILES = $(sort $(call core_find,src/,*.core))
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
49074863
49084864 # ASN.1 files.
49094865
49134869
49144870 define compile_asn1
49154871 $(verbose) mkdir -p include/
4916 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
49174873 $(verbose) mv asn1/*.erl src/
49184874 $(verbose) mv asn1/*.hrl include/
49194875 $(verbose) mv asn1/*.asn1db include/
49364892
49374893 # Leex and Yecc files.
49384894
4939 XRL_FILES = $(sort $(call core_find,src/,*.xrl))
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
49404896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
49414897 ERL_FILES += $(XRL_ERL_FILES)
49424898
4943 YRL_FILES = $(sort $(call core_find,src/,*.yrl))
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
49444900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
49454901 ERL_FILES += $(YRL_ERL_FILES)
49464902
49474903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4948 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
49494905
49504906 # Erlang and Core Erlang files.
49514907
49954951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49964952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49974953 (F, Mod, import, {Imp, _}) ->
4998 case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
49994960 false -> ok;
50004961 true -> Add(Mod, Imp)
50014962 end;
50194980 end || F <- ErlFiles],
50204981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
50214982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
50224991 ok = file:write_file("$(1)", [
50234992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
5024 "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n"
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
50254994 ]),
50264995 halt()
50274996 endef
50345003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
50355004 # Rebuild everything when the Makefile changes.
50365005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5037 @mkdir -p $(ERLANG_MK_TMP)
5038 @if test -f $@; then \
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
50395008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
50405009 touch -c $(PROJECT).d; \
50415010 fi
5042 @touch $@
5011 $(verbose) touch $@
50435012
50445013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
50455014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
50465015 endif
50475016
5048 -include $(PROJECT).d
5017 include $(wildcard $(PROJECT).d)
50495018
50505019 ebin/$(PROJECT).app:: ebin/
50515020
50645033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
50655034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
50665035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5067 $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
50685037 > ebin/$(PROJECT).app
50695038 else
50705039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
50885057
50895058 endif
50905059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
50915061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
50925062 # This file is part of erlang.mk and subject to the terms of the ISC License.
50935063
51055075 doc-deps:
51065076 else
51075077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5108 $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5109 endif
5110
5111 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51125082 # This file is part of erlang.mk and subject to the terms of the ISC License.
51135083
51145084 .PHONY: rel-deps
51255095 rel-deps:
51265096 else
51275097 rel-deps: $(ALL_REL_DEPS_DIRS)
5128 $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5129 endif
5130
5131 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51325102 # This file is part of erlang.mk and subject to the terms of the ISC License.
51335103
51345104 .PHONY: test-deps test-dir test-build clean-test-dir
51505120 test-deps:
51515121 else
51525122 test-deps: $(ALL_TEST_DEPS_DIRS)
5153 $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
51545124 endif
51555125
51565126 ifneq ($(wildcard $(TEST_DIR)),)
51835153 endif
51845154 endif
51855155
5186 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51875157 # This file is part of erlang.mk and subject to the terms of the ISC License.
51885158
51895159 .PHONY: rebar.config
52195189 rebar.config:
52205190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
52215191
5222 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
52235193 # This file is part of erlang.mk and subject to the terms of the ISC License.
52245194
5225 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
5226
5227 MAN_INSTALL_PATH ?= /usr/local/share/man
5228 MAN_SECTIONS ?= 3 7
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
52295200
52305201 docs:: asciidoc
52315202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
52325207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
52335210
52345211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
52355212 asciidoc-guide:
52365213 else
5237 asciidoc-guide: distclean-asciidoc doc-deps
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
52385215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
52395216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5240 endif
5241
5242 ifeq ($(wildcard doc/src/manual/*.asciidoc),)
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
52435227 asciidoc-manual:
52445228 else
5245 asciidoc-manual: distclean-asciidoc doc-deps
5246 for f in doc/src/manual/*.asciidoc ; do \
5247 a2x -v -f manpage $$f ; \
5248 done
5249 for s in $(MAN_SECTIONS); do \
5250 mkdir -p doc/man$$s/ ; \
5251 mv doc/src/manual/*.$$s doc/man$$s/ ; \
5252 gzip doc/man$$s/*.$$s ; \
5253 done
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
52545262
52555263 install-docs:: install-asciidoc
52565264
52575265 install-asciidoc: asciidoc-manual
5258 for s in $(MAN_SECTIONS); do \
5259 mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
5260 install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
5261 done
5262 endif
5263
5264 distclean:: distclean-asciidoc
5265
5266 distclean-asciidoc:
5267 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
5268
5269 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
52705276 # This file is part of erlang.mk and subject to the terms of the ISC License.
52715277
52725278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
53235329 define bs_Makefile
53245330 PROJECT = $p
53255331 PROJECT_DESCRIPTION = New project
5326 PROJECT_VERSION = 0.0.1
5332 PROJECT_VERSION = 0.1.0
53275333
53285334 # Whitespace to be used when creating files from templates.
53295335 SP = $(SP)
53335339 define bs_Makefile
53345340 PROJECT = $p
53355341 PROJECT_DESCRIPTION = New project
5336 PROJECT_VERSION = 0.0.1
5342 PROJECT_VERSION = 0.1.0
53375343
53385344 endef
53395345 endif
53415347 define bs_apps_Makefile
53425348 PROJECT = $p
53435349 PROJECT_DESCRIPTION = New project
5344 PROJECT_VERSION = 0.0.1
5350 PROJECT_VERSION = 0.1.0
53455351
53465352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
53475353 endef
53615367 endef
53625368
53635369 define bs_relx_config
5364 {release, {$p_release, "1"}, [$p]}.
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
53655371 {extended_start_script, true}.
53665372 {sys_config, "rel/sys.config"}.
53675373 {vm_args, "rel/vm.args"}.
57125718 ifndef t
57135719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57145720 endif
5715 ifndef tpl_$(t)
5716 $(error Unknown template)
5717 endif
57185721 ifndef n
57195722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57205723 endif
57275730 list-templates:
57285731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
57295732
5730 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
57315734 # This file is part of erlang.mk and subject to the terms of the ISC License.
57325735
57335736 .PHONY: clean-c_src distclean-c_src-env
59625965 $(call render_template,bs_erl_nif,src/$n.erl)
59635966 endif
59645967
5965 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
59665969 # This file is part of erlang.mk and subject to the terms of the ISC License.
59675970
5968 .PHONY: ci ci-setup distclean-kerl
5969
5970 KERL ?= $(CURDIR)/kerl
5971 export KERL
5972
5973 KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
5974
5975 OTP_GIT ?= https://github.com/erlang/otp
5976
5977 CI_INSTALL_DIR ?= $(HOME)/erlang
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
59785973 CI_OTP ?=
5979
5980 ifeq ($(strip $(CI_OTP)),)
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
59815986 ci::
59825987 else
5983 ci:: $(addprefix ci-,$(CI_OTP))
5984
5985 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
59866007
59876008 ci-setup::
6009
6010 ci-extra::
59886011
59896012 ci_verbose_0 = @echo " CI " $(1);
59906013 ci_verbose = $(ci_verbose_$(V))
59916014
59926015 define ci_target
5993 ci-$(1): $(CI_INSTALL_DIR)/$(1)
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
59946018 $(ci_verbose) \
5995 PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
5996 CI_OTP_RELEASE="$(1)" \
5997 CT_OPTS="-label $(1)" \
5998 $(MAKE) clean ci-setup tests
5999 endef
6000
6001 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
60026030
60036031 define ci_otp_target
60046032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
60056033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6006 $(KERL) build git $(OTP_GIT) $(1) $(1)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
60076035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
60086036 endif
60096037 endef
60106038
60116039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
60126040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
60136052 $(KERL):
6014 $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
60156056 $(verbose) chmod +x $(KERL)
60166057
60176058 help::
60286069 $(gen_verbose) rm -rf $(KERL)
60296070 endif
60306071
6031 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
60326073 # This file is part of erlang.mk and subject to the terms of the ISC License.
60336074
60346075 .PHONY: ct apps-ct distclean-ct
60366077 # Configuration.
60376078
60386079 CT_OPTS ?=
6080
60396081 ifneq ($(wildcard $(TEST_DIR)),)
6040 CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6041 else
6042 CT_SUITES ?=
6043 endif
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
60446088
60456089 # Core targets.
60466090
60636107 -noinput \
60646108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
60656109 -dir $(TEST_DIR) \
6066 -logdir $(CURDIR)/logs
6110 -logdir $(CT_LOGS_DIR)
60676111
60686112 ifeq ($(CT_SUITES),)
60696113 ct: $(if $(IS_APP),,apps-ct)
60706114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
60716117 ct: test-build $(if $(IS_APP),,apps-ct)
6072 $(verbose) mkdir -p $(CURDIR)/logs/
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
60736119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
60746121 endif
60756122
60766123 ifneq ($(ALL_APPS_DIRS),)
60976144
60986145 define ct_suite_target
60996146 ct-$(1): test-build
6100 $(verbose) mkdir -p $(CURDIR)/logs/
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
61016148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
61026149 endef
61036150
61046151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
61056152
61066153 distclean-ct:
6107 $(gen_verbose) rm -rf $(CURDIR)/logs/
6108
6109 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61106157 # This file is part of erlang.mk and subject to the terms of the ISC License.
61116158
61126159 .PHONY: plt distclean-plt dialyze
61506197 endef
61516198
61526199 $(DIALYZER_PLT): deps app
6153 $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
61546204
61556205 plt: $(DIALYZER_PLT)
61566206
61646214 endif
61656215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
61666216
6167 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61686218 # This file is part of erlang.mk and subject to the terms of the ISC License.
61696219
61706220 .PHONY: distclean-edoc edoc
61726222 # Configuration.
61736223
61746224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
61756235
61766236 # Core targets.
61776237
6178 ifneq ($(wildcard doc/overview.edoc),)
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
61796239 docs:: edoc
61806240 endif
61816241
61846244 # Plugin-specific targets.
61856245
61866246 edoc: distclean-edoc doc-deps
6187 $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
61886248
61896249 distclean-edoc:
61906250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
61916251
6192 # Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61936253 # This file is part of erlang.mk and subject to the terms of the ISC License.
61946254
6195 .PHONY: distclean-escript escript
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
61966320
61976321 # Configuration.
61986322
61996323 ESCRIPT_NAME ?= $(PROJECT)
62006324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
62016325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
62026327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6203
6204 ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
6205 ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
6206 ESCRIPT_EMU_ARGS ?= -pa . \
6207 -sasl errlog_type error \
6208 -escript main $(ESCRIPT_NAME)
6209 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6210 ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
62116332
62126333 # Core targets.
62136334
62206341
62216342 # Plugin-specific targets.
62226343
6223 # Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
6224 # Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
6225 # Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
6226 # Software may only be used for the great good and the true happiness of all
6227 # sentient beings.
6228
6229 define ESCRIPT_RAW
6230 'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
6231 'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
6232 ' [F || F <- A, not filelib:is_dir(F) ] end,'\
6233 'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
6234 'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
6235 'Ez = fun(Escript) ->'\
6236 ' Static = Files([$(ESCRIPT_STATIC)]),'\
6237 ' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
6238 ' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
6239 ' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
6240 ' {archive, Archive, [memory]},'\
6241 ' {shebang, "$(ESCRIPT_SHEBANG)"},'\
6242 ' {comment, "$(ESCRIPT_COMMENT)"},'\
6243 ' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
6244 ' ]),'\
6245 ' file:change_mode(Escript, 8#755)'\
6246 'end,'\
6247 'Ez("$(ESCRIPT_FILE)"),'\
6248 'halt().'
6249 endef
6250
6251 ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
6252
6253 escript:: distclean-escript deps app
6254 $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
62556360
62566361 distclean-escript:
6257 $(gen_verbose) rm -f $(ESCRIPT_NAME)
6258
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
62596365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6260 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
62616366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
62626367
62636368 .PHONY: eunit apps-eunit
63216426
63226427 ifneq ($(ALL_APPS_DIRS),)
63236428 apps-eunit:
6324 $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done
6325 endif
6326 endif
6327
6328 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
63296436 # This file is part of erlang.mk and subject to the terms of the ISC License.
63306437
6331 .PHONY: relx-rel distclean-relx-rel distclean-relx run
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
63326439
63336440 # Configuration.
63346441
6335 RELX ?= $(CURDIR)/relx
6442 RELX ?= $(ERLANG_MK_TMP)/relx
63366443 RELX_CONFIG ?= $(CURDIR)/relx.config
63376444
63386445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
63396446 RELX_OPTS ?=
63406447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
63416454
63426455 ifeq ($(firstword $(RELX_OPTS)),-o)
63436456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
63506463 ifeq ($(IS_DEP),)
63516464 ifneq ($(wildcard $(RELX_CONFIG)),)
63526465 rel:: relx-rel
6353 endif
6354 endif
6355
6356 distclean:: distclean-relx-rel distclean-relx
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
63576472
63586473 # Plugin-specific targets.
63596474
63626477 $(verbose) chmod +x $(RELX)
63636478
63646479 relx-rel: $(RELX) rel-deps app
6365 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
63666484
63676485 distclean-relx-rel:
63686486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6369
6370 distclean-relx:
6371 $(gen_verbose) rm -rf $(RELX)
63726487
63736488 # Run target.
63746489
63776492 else
63786493
63796494 define get_relx_release.erl
6380 {ok, Config} = file:consult("$(RELX_CONFIG)"),
6381 {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
6382 io:format("~s", [Name]),
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
63836504 halt(0).
63846505 endef
63856506
6386 RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
63876514
63886515 run: all
6389 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
63906517
63916518 help::
63926519 $(verbose) printf "%s\n" "" \
63956522
63966523 endif
63976524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
63986526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6399 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
64006527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
64016528
64026529 .PHONY: shell
64216548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
64226549
64236550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6424 $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
64256552
64266553 shell: build-shell-deps
64276554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
64286555
6429 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
64306571 # This file is part of erlang.mk and subject to the terms of the ISC License.
64316572
64326573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
64376578 tests:: triq
64386579
64396580 define triq_check.erl
6440 code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
64416582 try
64426583 case $(1) of
64436584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
64696610 endif
64706611 endif
64716612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
64726614 # Copyright (c) 2015, Erlang Solutions Ltd.
64736615 # This file is part of erlang.mk and subject to the terms of the ISC License.
64746616
64856627 XREFR ?= $(CURDIR)/xrefr
64866628 export XREFR
64876629
6488 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
64896631
64906632 # Core targets.
64916633
64926634 help::
6493 $(verbose) printf "%s\n" "" \
6494 "Xref targets:" \
6495 " xref Run Xrefr using $XREF_CONFIG as config file if defined"
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
64966638
64976639 distclean:: distclean-xref
64986640
65086650 distclean-xref:
65096651 $(gen_verbose) rm -rf $(XREFR)
65106652
6511 # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
65126655 # This file is part of erlang.mk and subject to the terms of the ISC License.
65136656
65146657 COVER_REPORT_DIR = cover
65176660
65186661 ifdef COVER
65196662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
65206664 # All modules in 'ebin'
65216665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
65226666
65316675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
65326676 endif
65336677 endif
6678 endif
65346679
65356680 # Core targets
65366681
65526697 "Cover targets:" \
65536698 " cover-report Generate a HTML coverage report from previously collected" \
65546699 " cover data." \
6555 " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
65566701 "" \
65576702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
65586703 "target tests additionally generates a HTML coverage report from the combined" \
65656710
65666711 .PHONY: coverdata-clean
65676712 coverdata-clean:
6568 $(gen_verbose) rm -f *.coverdata ct.cover.spec
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
65696714
65706715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
65716721 all.coverdata: $(COVERDATA)
6572 $(gen_verbose) $(ERL) -eval ' \
6573 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
6574 cover:export("$@"), halt(0).'
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
65756723
65766724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
65776725 # empty if you want the coverdata files but not the HTML report.
65896737 # Modules which include eunit.hrl always contain one line without coverage
65906738 # because eunit defines test/0 which is never called. We compensate for this.
65916739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6592 grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
65936741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
65946742
65956743 define cover_report.erl
66246772 endef
66256773
66266774 cover-report:
6627 $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
66286776 $(gen_verbose) $(call erlang,$(cover_report.erl))
66296777
66306778 endif
66316779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
66326839
66336840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
66346841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
66976904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
66986905 endif
66996906 ifndef IS_APP
6700 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
67016908 $(MAKE) -C $$dep $@ \
67026909 IS_APP=1 \
6703 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6704 || exit $$?; \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67056911 done
67066912 endif
6707 $(verbose) for dep in $^ ; do \
6913 $(verbose) set -e; for dep in $^ ; do \
67086914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
67096915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6710 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
67116917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
67126918 $(MAKE) -C $$dep fetch-deps \
67136919 IS_DEP=1 \
6714 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6715 || exit $$?; \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67166921 fi \
67176922 fi \
67186923 done
2727 if (sync_delete(this, '/parameters/:component/:vhost/:name'))
2828 go_to('#/federation-upstreams');
2929 return false;
30 });
30 });
31 sammy.del("#/restart-link", function(){
32 if(sync_delete(this, '/federation-links/vhost/:vhost/:id/restart')){
33 update();
34 }
35 });
3136 });
3237
3338 NAVIGATION['Admin'][0]['Federation Status'] = ['#/federation', "monitoring"];
1717 <th>State</th>
1818 <th>Inbound message rate</th>
1919 <th>Last changed</th>
20 <th>ID</th>
21 <th>Operations</th>
2022 </tr>
2123 </thead>
2224 <tbody>
7779 <% } %>
7880 </td>
7981 <td><%= link.timestamp %></td>
82 <td><%= link.id %></td>
83 <td>
84 <form action="#/restart-link" method="delete" class="confirm">
85 <input type="hidden" name="id" value="<%= link.id %>"/>
86 <input type="hidden" name="vhost" value="<%= link.vhost %>"/>
87 <input type="submit" value="Restart"/>
88 </form>
89 </td>
8090 </tr>
8191 <% } %>
8292 <% } %>
22 # before the inclusion of erlang.mk leading to the wrong target becoming
33 # the default.
44 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
526 endif
627
728 # --------------------------------------------------------------------
2041 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
2142 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
2243 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
2345 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
2446 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
2547 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
2648 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
2749 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
2851 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
2952 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
3054 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
3155 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
3256 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
3559 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
3660 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
3761 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
3863 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
3964 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
4065 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
7095
7196 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
7297
73 # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
74 # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
75 # needs to add "ranch" as a BUILD_DEPS. The list of projects needing
76 # this workaround are:
77 # o rabbitmq-web-stomp
78 dep_ranch = git https://github.com/ninenines/ranch 1.2.1
98 # Third-party dependencies version pinning.
99 #
100 # We do that in this file, which is copied in all projects, to ensure
101 # all projects use the same versions. It avoids conflicts and makes it
102 # possible to work with rabbitmq-public-umbrella.
103
104 dep_cowboy_commit = 1.0.4
105 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
106 dep_ranch_commit = 1.3.1
107 dep_webmachine_commit = 1.10.8p2
79108
80109 RABBITMQ_COMPONENTS = amqp_client \
81110 rabbit \
82111 rabbit_common \
83112 rabbitmq_amqp1_0 \
84113 rabbitmq_auth_backend_amqp \
114 rabbitmq_auth_backend_cache \
85115 rabbitmq_auth_backend_http \
86116 rabbitmq_auth_backend_ldap \
87117 rabbitmq_auth_mechanism_ssl \
88118 rabbitmq_boot_steps_visualiser \
89119 rabbitmq_clusterer \
120 rabbitmq_cli \
90121 rabbitmq_codegen \
91122 rabbitmq_consistent_hash_exchange \
123 rabbitmq_ct_client_helpers \
92124 rabbitmq_ct_helpers \
93125 rabbitmq_delayed_message_exchange \
94126 rabbitmq_dotnet_client \
97129 rabbitmq_federation_management \
98130 rabbitmq_java_client \
99131 rabbitmq_jms_client \
132 rabbitmq_jms_cts \
100133 rabbitmq_jms_topic_exchange \
101134 rabbitmq_lvc \
102135 rabbitmq_management \
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_federation_mgmt).
1818 -behaviour(rabbit_mgmt_extension).
1919
2020 -export([dispatcher/0, web_ui/0]).
21 -export([init/1, to_json/2, resource_exists/2, content_types_provided/2,
22 is_authorized/2]).
21 -export([init/3, rest_init/2, to_json/2, resource_exists/2, content_types_provided/2,
22 is_authorized/2, allowed_methods/2, delete_resource/2]).
2323
2424 -import(rabbit_misc, [pget/2]).
2525
26 -include_lib("rabbitmq_management/include/rabbit_mgmt.hrl").
27 -include_lib("webmachine/include/webmachine.hrl").
26 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2827
29 dispatcher() -> [{["federation-links"], ?MODULE, []},
30 {["federation-links", vhost], ?MODULE, []}].
28 dispatcher() -> [{"/federation-links", ?MODULE, [all]},
29 {"/federation-links/:vhost", ?MODULE, [all]},
30 {"/federation-links/state/down/", ?MODULE, [down]},
31 {"/federation-links/:vhost/state/down", ?MODULE, [down]},
32 {"/federation-links/vhost/:vhost/:id/restart", ?MODULE, []}].
33
3134 web_ui() -> [{javascript, <<"federation.js">>}].
3235
3336 %%--------------------------------------------------------------------
3437
35 init(_Config) -> {ok, #context{}}.
38 init(_, _, _) ->
39 {upgrade, protocol, cowboy_rest}.
40
41 rest_init(Req, [Filter]) ->
42 {ok, Req, {Filter, #context{}}};
43 rest_init(Req, []) ->
44 {ok, Req, #context{}}.
3645
3746 content_types_provided(ReqData, Context) ->
38 {[{"application/json", to_json}], ReqData, Context}.
47 {[{<<"application/json">>, to_json}], ReqData, Context}.
48
49 allowed_methods(ReqData, Context) ->
50 {[<<"HEAD">>, <<"GET">>, <<"DELETE">>, <<"OPTIONS">>], ReqData, Context}.
3951
4052 resource_exists(ReqData, Context) ->
4153 {case rabbit_mgmt_util:vhost(ReqData) of
4254 not_found -> false;
43 _ -> true
55 _ -> case rabbit_mgmt_util:id(id, ReqData) of
56 %% Listing links
57 none -> true;
58 %% Restarting a link
59 Id -> case rabbit_federation_status:lookup(Id) of
60 not_found -> false;
61 _ -> true
62 end
63 end
4464 end, ReqData, Context}.
4565
46 to_json(ReqData, Context) ->
66 to_json(ReqData, {Filter, Context}) ->
4767 Chs = rabbit_mgmt_db:get_all_channels(
4868 rabbit_mgmt_util:range(ReqData)),
4969 rabbit_mgmt_util:reply_list(
50 filter_vhost(status(Chs, ReqData, Context), ReqData), ReqData, Context).
70 filter_vhost(status(Chs, ReqData, Context, Filter), ReqData), ReqData, Context);
71 to_json(ReqData, Context) ->
72 to_json(ReqData, {all, Context}).
5173
74 is_authorized(ReqData, {Filter, Context}) ->
75 {Res, RD, C} = rabbit_mgmt_util:is_authorized_monitor(ReqData, Context),
76 {Res, RD, {Filter, C}};
5277 is_authorized(ReqData, Context) ->
5378 rabbit_mgmt_util:is_authorized_monitor(ReqData, Context).
5479
80 delete_resource(ReqData, Context) ->
81 Reply = case rabbit_mgmt_util:id(id, ReqData) of
82 none -> false;
83 Id ->
84 case rabbit_federation_status:lookup(Id) of
85 not_found -> false;
86 Obj ->
87 Upstream = proplists:get_value(upstream, Obj),
88 Supervisor = proplists:get_value(supervisor, Obj),
89 rabbit_federation_link_sup:restart(Supervisor, Upstream),
90 true
91 end
92 end,
93 {Reply, ReqData, Context}.
94
5595 %%--------------------------------------------------------------------
56
5796 filter_vhost(List, ReqData) ->
5897 rabbit_mgmt_util:all_or_one_vhost(
5998 ReqData,
6099 fun(V) -> lists:filter(fun(I) -> pget(vhost, I) =:= V end, List) end).
61100
62 status(Chs, ReqData, Context) ->
101 status(Chs, ReqData, Context, Filter) ->
63102 rabbit_mgmt_util:filter_vhost(
64 lists:append([status(Node, Chs) || Node <- [node() | nodes()]]),
103 lists:append([status(Node, Chs, Filter) || Node <- [node() | nodes()]]),
65104 ReqData, Context).
66105
67 status(Node, Chs) ->
106 status(Node, Chs, Filter) ->
68107 case rpc:call(Node, rabbit_federation_status, status, [], infinity) of
69108 {badrpc, {'EXIT', {undef, _}}} -> [];
70109 {badrpc, {'EXIT', {noproc, _}}} -> [];
71 Status -> [format(Node, I, Chs) || I <- Status]
110 Status -> [format(Node, I, Chs) || I <- Status,
111 filter_status(I, Filter)]
72112 end.
113
114 filter_status(_, all) ->
115 true;
116 filter_status(Props, down) ->
117 Status = pget(status, Props),
118 not lists:member(Status, [running, starting]).
73119
74120 format(Node, Info, Chs) ->
75121 LocalCh = case rabbit_mgmt_format:strip_pids(
+0
-8
deps/rabbitmq_federation_management/src/rabbitmq_federation_management.app.src less more
0 {application, rabbitmq_federation_management,
1 [{description, "RabbitMQ Federation Management"},
2 {vsn, "3.6.6"},
3 {modules, []},
4 {registered, []},
5 {env, []},
6 {applications, [kernel, stdlib, rabbit_common, rabbit, rabbitmq_management]}
7 ]}.
00 PROJECT = rabbitmq_jms_topic_exchange
1 PROJECT_DESCRIPTION = RabbitMQ JMS topic selector exchange plugin
12
23 DEPS = rabbit_common rabbit
3 TEST_DEPS = rabbitmq_ct_helpers amqp_client
4 TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers amqp_client
45
6 DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
57 DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk
68
79 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
4545
4646 ## Copyright and License
4747
48 (c) Pivotal Software Inc., 2007-2016.
48 (c) Pivotal Software Inc., 2007-2017.
4949
5050 See [LICENSE](./LICENSE) for license information.
0 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
11 #
22 # Permission to use, copy, modify, and/or distribute this software for any
33 # purpose with or without fee is hereby granted, provided that the above
1111 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1212 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1313
14 .PHONY: all app apps deps search rel docs install-docs check tests clean distclean help erlang-mk
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
1515
1616 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17
18 ERLANG_MK_VERSION = 2.0.0-pre.2-144-g647ffd1
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
1931
2032 # Core configuration.
2133
2436
2537 PROJECT_VERSION ?= rolling
2638 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
2740
2841 # Verbosity.
2942
8497 rel::
8598 $(verbose) :
8699
100 relup:: deps app
101
87102 check:: tests
88103
89104 clean:: clean-crashdump
101116 help::
102117 $(verbose) printf "%s\n" \
103118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
104 "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
105120 "" \
106121 "Usage: [V=1] $(MAKE) [target]..." \
107122 "" \
149164 core_native_path = $1
150165 endif
151166
152 ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
153 define core_http_get
154 wget --no-check-certificate -O $(1) $(2)|| rm $(1)
155 endef
156 else
157 define core_http_get.erl
158 ssl:start(),
159 inets:start(),
160 case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
161 {ok, {{_, 200, _}, _, Body}} ->
162 case file:write_file("$(1)", Body) of
163 ok -> ok;
164 {error, R1} -> halt(R1)
165 end;
166 {error, R2} ->
167 halt(R2)
168 end,
169 halt(0).
170 endef
171
172 define core_http_get
173 $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
174 endef
175 endif
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
176168
177169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
178170
192184 ERLANG_MK_BUILD_CONFIG ?= build.config
193185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
194186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
195188 erlang-mk:
196189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
197190 ifdef ERLANG_MK_COMMIT
198191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
199192 endif
200193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
201 $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
202195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
203196 rm -rf $(ERLANG_MK_BUILD_DIR)
204197
285278 pkg_apns_repo = https://github.com/inaka/apns4erl
286279 pkg_apns_commit = master
287280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
288289 PACKAGES += azdht
289290 pkg_azdht_name = azdht
290291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
405406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
406407 pkg_bootstrap_commit = master
407408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
408417 PACKAGES += boss_db
409418 pkg_boss_db_name = boss_db
410419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
413422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
414423 pkg_boss_db_commit = master
415424
416 PACKAGES += boss
417 pkg_boss_name = boss
418 pkg_boss_description = Erlang web MVC, now featuring Comet
419 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
420 pkg_boss_fetch = git
421 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
422 pkg_boss_commit = master
423
424425 PACKAGES += brod
425426 pkg_brod_name = brod
426427 pkg_brod_description = Kafka client in Erlang
565566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
566567 pkg_cloudi_service_api_requests_commit = master
567568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
568585 PACKAGES += cloudi_service_db_cassandra_cql
569586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
570587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
573590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
574591 pkg_cloudi_service_db_cassandra_cql_commit = master
575592
576 PACKAGES += cloudi_service_db_cassandra
577 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
579 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
580 pkg_cloudi_service_db_cassandra_fetch = git
581 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
582 pkg_cloudi_service_db_cassandra_commit = master
583
584593 PACKAGES += cloudi_service_db_couchdb
585594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
586595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
637646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
638647 pkg_cloudi_service_db_tokyotyrant_commit = master
639648
640 PACKAGES += cloudi_service_db
641 pkg_cloudi_service_db_name = cloudi_service_db
642 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
643 pkg_cloudi_service_db_homepage = http://cloudi.org/
644 pkg_cloudi_service_db_fetch = git
645 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
646 pkg_cloudi_service_db_commit = master
647
648649 PACKAGES += cloudi_service_filesystem
649650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
650651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
10371038 pkg_edown_repo = https://github.com/uwiger/edown
10381039 pkg_edown_commit = master
10391040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
10401049 PACKAGES += eep_app
10411050 pkg_eep_app_name = eep_app
10421051 pkg_eep_app_description = Embedded Event Processing
10451054 pkg_eep_app_repo = https://github.com/darach/eep-erl
10461055 pkg_eep_app_commit = master
10471056
1048 PACKAGES += eep
1049 pkg_eep_name = eep
1050 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1051 pkg_eep_homepage = https://github.com/virtan/eep
1052 pkg_eep_fetch = git
1053 pkg_eep_repo = https://github.com/virtan/eep
1054 pkg_eep_commit = master
1055
10561057 PACKAGES += efene
10571058 pkg_efene_name = efene
10581059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
12371238 pkg_eqm_repo = https://github.com/loucash/eqm
12381239 pkg_eqm_commit = master
12391240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
12401249 PACKAGES += eredis_pool
12411250 pkg_eredis_pool_name = eredis_pool
12421251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
12451254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
12461255 pkg_eredis_pool_commit = master
12471256
1248 PACKAGES += eredis
1249 pkg_eredis_name = eredis
1250 pkg_eredis_description = Erlang Redis client
1251 pkg_eredis_homepage = https://github.com/wooga/eredis
1252 pkg_eredis_fetch = git
1253 pkg_eredis_repo = https://github.com/wooga/eredis
1254 pkg_eredis_commit = master
1255
12561257 PACKAGES += erl_streams
12571258 pkg_erl_streams_name = erl_streams
12581259 pkg_erl_streams_description = Streams in Erlang
15411542 pkg_etap_repo = https://github.com/ngerakines/etap
15421543 pkg_etap_commit = master
15431544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
15441553 PACKAGES += etest_http
15451554 pkg_etest_http_name = etest_http
15461555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
15491558 pkg_etest_http_repo = https://github.com/wooga/etest_http
15501559 pkg_etest_http_commit = master
15511560
1552 PACKAGES += etest
1553 pkg_etest_name = etest
1554 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1555 pkg_etest_homepage = https://github.com/wooga/etest
1556 pkg_etest_fetch = git
1557 pkg_etest_repo = https://github.com/wooga/etest
1558 pkg_etest_commit = master
1559
15601561 PACKAGES += etoml
15611562 pkg_etoml_name = etoml
15621563 pkg_etoml_description = TOML language erlang parser
15651566 pkg_etoml_repo = https://github.com/kalta/etoml
15661567 pkg_etoml_commit = master
15671568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
15681577 PACKAGES += eunit_formatters
15691578 pkg_eunit_formatters_name = eunit_formatters
15701579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
15731582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
15741583 pkg_eunit_formatters_commit = master
15751584
1576 PACKAGES += eunit
1577 pkg_eunit_name = eunit
1578 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1579 pkg_eunit_homepage = https://github.com/richcarl/eunit
1580 pkg_eunit_fetch = git
1581 pkg_eunit_repo = https://github.com/richcarl/eunit
1582 pkg_eunit_commit = master
1583
15841585 PACKAGES += euthanasia
15851586 pkg_euthanasia_name = euthanasia
15861587 pkg_euthanasia_description = Merciful killer for your Erlang processes
15981599 pkg_evum_commit = master
15991600
16001601 PACKAGES += exec
1601 pkg_exec_name = exec
1602 pkg_exec_name = erlexec
16021603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
16031604 pkg_exec_homepage = http://saleyn.github.com/erlexec
16041605 pkg_exec_fetch = git
17171718 pkg_fn_repo = https://github.com/reiddraper/fn
17181719 pkg_fn_commit = master
17191720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
17201729 PACKAGES += folsom_cowboy
17211730 pkg_folsom_cowboy_name = folsom_cowboy
17221731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
17251734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
17261735 pkg_folsom_cowboy_commit = master
17271736
1728 PACKAGES += folsom
1729 pkg_folsom_name = folsom
1730 pkg_folsom_description = Expose Erlang Events and Metrics
1731 pkg_folsom_homepage = https://github.com/boundary/folsom
1732 pkg_folsom_fetch = git
1733 pkg_folsom_repo = https://github.com/boundary/folsom
1734 pkg_folsom_commit = master
1735
17361737 PACKAGES += folsomite
17371738 pkg_folsomite_name = folsomite
17381739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
20932094 pkg_jesse_repo = https://github.com/for-GET/jesse
20942095 pkg_jesse_commit = master
20952096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
20962105 PACKAGES += jiffy_v
20972106 pkg_jiffy_v_name = jiffy_v
20982107 pkg_jiffy_v_description = JSON validation utility
21012110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
21022111 pkg_jiffy_v_commit = master
21032112
2104 PACKAGES += jiffy
2105 pkg_jiffy_name = jiffy
2106 pkg_jiffy_description = JSON NIFs for Erlang.
2107 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2108 pkg_jiffy_fetch = git
2109 pkg_jiffy_repo = https://github.com/davisp/jiffy
2110 pkg_jiffy_commit = master
2111
21122113 PACKAGES += jobs
21132114 pkg_jobs_name = jobs
21142115 pkg_jobs_description = a Job scheduler for load regulation
21252126 pkg_joxa_repo = https://github.com/joxa/joxa
21262127 pkg_joxa_commit = master
21272128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
21282137 PACKAGES += json_rec
21292138 pkg_json_rec_name = json_rec
21302139 pkg_json_rec_description = JSON to erlang record
21332142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
21342143 pkg_json_rec_commit = master
21352144
2136 PACKAGES += json
2137 pkg_json_name = json
2138 pkg_json_description = a high level json library for erlang (17.0+)
2139 pkg_json_homepage = https://github.com/talentdeficit/json
2140 pkg_json_fetch = git
2141 pkg_json_repo = https://github.com/talentdeficit/json
2142 pkg_json_commit = master
2143
21442145 PACKAGES += jsone
21452146 pkg_jsone_name = jsone
21462147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
21812182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
21822183 pkg_jsx_commit = master
21832184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
21842193 PACKAGES += kafka_protocol
21852194 pkg_kafka_protocol_name = kafka_protocol
21862195 pkg_kafka_protocol_description = Kafka protocol Erlang library
21892198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
21902199 pkg_kafka_protocol_commit = master
21912200
2192 PACKAGES += kafka
2193 pkg_kafka_name = kafka
2194 pkg_kafka_description = Kafka consumer and producer in Erlang
2195 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2196 pkg_kafka_fetch = git
2197 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2198 pkg_kafka_commit = master
2199
22002201 PACKAGES += kai
22012202 pkg_kai_name = kai
22022203 pkg_kai_description = DHT storage by Takeshi Inoue
22932294 pkg_kvs_repo = https://github.com/synrc/kvs
22942295 pkg_kvs_commit = master
22952296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
22962305 PACKAGES += lager_amqp_backend
22972306 pkg_lager_amqp_backend_name = lager_amqp_backend
22982307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
23092318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
23102319 pkg_lager_syslog_commit = master
23112320
2312 PACKAGES += lager
2313 pkg_lager_name = lager
2314 pkg_lager_description = A logging framework for Erlang/OTP.
2315 pkg_lager_homepage = https://github.com/basho/lager
2316 pkg_lager_fetch = git
2317 pkg_lager_repo = https://github.com/basho/lager
2318 pkg_lager_commit = master
2319
23202321 PACKAGES += lambdapad
23212322 pkg_lambdapad_name = lambdapad
23222323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
25732574 pkg_mixer_repo = https://github.com/chef/mixer
25742575 pkg_mixer_commit = master
25752576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
25762585 PACKAGES += mochiweb_xpath
25772586 pkg_mochiweb_xpath_name = mochiweb_xpath
25782587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
25812590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
25822591 pkg_mochiweb_xpath_commit = master
25832592
2584 PACKAGES += mochiweb
2585 pkg_mochiweb_name = mochiweb
2586 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2587 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2588 pkg_mochiweb_fetch = git
2589 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2590 pkg_mochiweb_commit = master
2591
25922593 PACKAGES += mockgyver
25932594 pkg_mockgyver_name = mockgyver
25942595 pkg_mockgyver_description = A mocking library for Erlang
30613062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
30623063 pkg_quickrand_commit = master
30633064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
30643073 PACKAGES += rabbit_exchange_type_riak
30653074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
30663075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
30693078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
30703079 pkg_rabbit_exchange_type_riak_commit = master
30713080
3072 PACKAGES += rabbit
3073 pkg_rabbit_name = rabbit
3074 pkg_rabbit_description = RabbitMQ Server
3075 pkg_rabbit_homepage = https://www.rabbitmq.com/
3076 pkg_rabbit_fetch = git
3077 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3078 pkg_rabbit_commit = master
3079
30803081 PACKAGES += rack
30813082 pkg_rack_name = rack
30823083 pkg_rack_description = Rack handler for erlang
34933494 pkg_smother_repo = https://github.com/ramsay-t/Smother
34943495 pkg_smother_commit = master
34953496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
34963505 PACKAGES += social
34973506 pkg_social_name = social
34983507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
35413550 pkg_stable_repo = https://github.com/dvv/stable
35423551 pkg_stable_commit = master
35433552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
35443561 PACKAGES += statebox_riak
35453562 pkg_statebox_riak_name = statebox_riak
35463563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
35493566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
35503567 pkg_statebox_riak_commit = master
35513568
3552 PACKAGES += statebox
3553 pkg_statebox_name = statebox
3554 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3555 pkg_statebox_homepage = https://github.com/mochi/statebox
3556 pkg_statebox_fetch = git
3557 pkg_statebox_repo = https://github.com/mochi/statebox
3558 pkg_statebox_commit = master
3559
35603569 PACKAGES += statman
35613570 pkg_statman_name = statman
35623571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
37253734 pkg_tirerl_repo = https://github.com/inaka/tirerl
37263735 pkg_tirerl_commit = master
37273736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
37283745 PACKAGES += traffic_tools
37293746 pkg_traffic_tools_name = traffic_tools
37303747 pkg_traffic_tools_description = Simple traffic limiting library
40614078 pkg_zucchini_repo = https://github.com/devinus/zucchini
40624079 pkg_zucchini_commit = master
40634080
4064 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
40654082 # This file is part of erlang.mk and subject to the terms of the ISC License.
40664083
40674084 .PHONY: search
40884105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
40894106 endif
40904107
4091 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
40924109 # This file is part of erlang.mk and subject to the terms of the ISC License.
40934110
4094 .PHONY: distclean-deps
4111 .PHONY: distclean-deps clean-tmp-deps.log
40954112
40964113 # Configuration.
40974114
41104127
41114128 REBAR_DEPS_DIR = $(DEPS_DIR)
41124129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
41134150
41144151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
41154152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
41164153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
41174154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
41184155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
41194157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
41204158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
41214159
41384176
41394177 # Core targets.
41404178
4141 ifdef IS_APP
4142 apps::
4143 else
4144 apps:: $(ALL_APPS_DIRS)
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
41454180 ifeq ($(IS_APP)$(IS_DEP),)
41464181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
41474182 endif
41494184 # Create ebin directory for all apps to make sure Erlang recognizes them
41504185 # as proper OTP applications when using -include_lib. This is a temporary
41514186 # fix, a proper fix would be to compile apps/* in the right order.
4152 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4153 mkdir -p $$dep/ebin || exit $$?; \
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
41544190 done
4155 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
41564196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
41574197 :; \
41584198 else \
41594199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4160 $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
4200 $(MAKE) -C $$dep IS_APP=1; \
41614201 fi \
41624202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
41634207 endif
41644208
41654209 ifneq ($(SKIP_DEPS),)
41664210 deps::
41674211 else
4168 deps:: $(ALL_DEPS_DIRS) apps
4169 ifeq ($(IS_APP)$(IS_DEP),)
4170 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4171 endif
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
41724213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4173 $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
41744215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
41754216 :; \
41764217 else \
41774218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
41784219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4179 $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
4220 $(MAKE) -C $$dep IS_DEP=1; \
41804221 else \
4181 echo "Error: No Makefile to build dependency $$dep."; \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
41824223 exit 2; \
41834224 fi \
41844225 fi \
41924233 # in practice only Makefile is needed so far.
41934234 define dep_autopatch
41944235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
41954237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
41964238 $(call dep_autopatch_erlang_mk,$(1)); \
41974239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4198 if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
41994243 $(call dep_autopatch2,$(1)); \
4200 elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
42014245 $(call dep_autopatch2,$(1)); \
4202 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
42034247 $(call dep_autopatch2,$(1)); \
4204 else \
4205 $(call erlang,$(call dep_autopatch_app.erl,$(1))); \
42064248 fi \
42074249 else \
42084250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
42144256 endef
42154257
42164258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
42174262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
42184263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
42194264 fi; \
42204265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4221 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
42224267 $(call dep_autopatch_fetch_rebar); \
42234268 $(call dep_autopatch_rebar,$(1)); \
42244269 else \
42304275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
42314276 endef
42324277
4233 # Overwrite erlang.mk with the current file by default.
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
42344280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
42354281 define dep_autopatch_erlang_mk
4236 echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \
4237 > $(DEPS_DIR)/$1/erlang.mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
42384287 endef
42394288 else
42404289 define dep_autopatch_erlang_mk
42534302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
42544303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
42554304 cd $(ERLANG_MK_TMP)/rebar; \
4256 git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
42574306 $(MAKE); \
42584307 cd -; \
42594308 fi
42704319 define dep_autopatch_rebar.erl
42714320 application:load(rebar),
42724321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
42734323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
42744324 {ok, Conf0} -> Conf0;
42754325 _ -> []
44234473 [] -> ok;
44244474 _ ->
44254475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4426 PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
44274477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4428 PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L \\"~s\\" -lerl_interface -lei\n",
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
44294479 [code:lib_dir(erl_interface, lib)])),
44304480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
44314481 FilterEnv = fun(Env) ->
44644514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44654515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44664516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4467 [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
44684518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
44694519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
44704520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
44764526 end,
44774527 [PortSpec(S) || S <- PortSpecs]
44784528 end,
4479 Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"),
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
44804530 RunPlugin = fun(Plugin, Step) ->
44814531 case erlang:function_exported(Plugin, Step, 2) of
44824532 false -> ok;
45244574 halt()
45254575 endef
45264576
4527 define dep_autopatch_app.erl
4528 UpdateModules = fun(App) ->
4529 case filelib:is_regular(App) of
4530 false -> ok;
4531 true ->
4532 {ok, [{application, '$(1)', L0}]} = file:consult(App),
4533 Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true,
4534 fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
4535 L = lists:keystore(modules, 1, L0, {modules, Mods}),
4536 ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}]))
4537 end
4538 end,
4539 UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"),
4540 halt()
4541 endef
4542
45434577 define dep_autopatch_appsrc_script.erl
45444578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
45454579 AppSrcScript = AppSrc ++ ".script",
45874621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
45884622 endef
45894623
4590 define dep_fetch_hex.erl
4591 ssl:start(),
4592 inets:start(),
4593 {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
4594 {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
4595 [], [{body_format, binary}]),
4596 {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
4597 {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
4598 ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
4599 halt()
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
46004626 endef
46014627
46024628 # Hex only has a package version. No need to look in the Erlang.mk packages.
46034629 define dep_fetch_hex
4604 $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
46054634 endef
46064635
46074636 define dep_fetch_fail
46314660 $(eval DEP_NAME := $(call dep_name,$1))
46324661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
46334662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4634 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
46354664 exit 17; \
46364665 fi
46374666 $(verbose) mkdir -p $(DEPS_DIR)
46734702 clean:: clean-apps
46744703
46754704 clean-apps:
4676 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4677 $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
46784707 done
46794708
46804709 distclean:: distclean-apps
46814710
46824711 distclean-apps:
4683 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4684 $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
46854714 done
46864715 endif
46874716
47014730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
47024731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
47034732
4704 # External plugins.
4705
4706 DEP_PLUGINS ?=
4707
4708 define core_dep_plugin
4709 -include $(DEPS_DIR)/$(1)
4710
4711 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4712 endef
4713
4714 $(foreach p,$(DEP_PLUGINS),\
4715 $(eval $(if $(findstring /,$p),\
4716 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4717 $(call core_dep_plugin,$p/plugins.mk,$p))))
4718
4719 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4720 # This file is part of erlang.mk and subject to the terms of the ISC License.
4721
4722 # Configuration.
4723
4724 DTL_FULL_PATH ?=
4725 DTL_PATH ?= templates/
4726 DTL_SUFFIX ?= _dtl
4727 DTL_OPTS ?=
4728
4729 # Verbosity.
4730
4731 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
4732 dtl_verbose = $(dtl_verbose_$(V))
4733
4734 # Core targets.
4735
4736 DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl))
4737
4738 ifneq ($(DTL_FILES),)
4739
4740 ifdef DTL_FULL_PATH
4741 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
4742 else
4743 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
4744 endif
4745
4746 ifneq ($(words $(DTL_FILES)),0)
4747 # Rebuild templates when the Makefile changes.
4748 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
4749 @mkdir -p $(ERLANG_MK_TMP)
4750 @if test -f $@; then \
4751 touch $(DTL_FILES); \
4752 fi
4753 @touch $@
4754
4755 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
4756 endif
4757
4758 define erlydtl_compile.erl
4759 [begin
4760 Module0 = case "$(strip $(DTL_FULL_PATH))" of
4761 "" ->
4762 filename:basename(F, ".dtl");
4763 _ ->
4764 "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
4765 re:replace(F2, "/", "_", [{return, list}, global])
4766 end,
4767 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
4768 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
4769 ok -> ok;
4770 {ok, _} -> ok
4771 end
4772 end || F <- string:tokens("$(1)", " ")],
4773 halt().
4774 endef
4775
4776 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
4777 $(if $(strip $?),\
4778 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
4779
4780 endif
4781
4782 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
47834734 # This file is part of erlang.mk and subject to the terms of the ISC License.
47844735
47854736 # Verbosity.
47984749
47994750 define compile_proto.erl
48004751 [begin
4801 Dir = filename:dirname(filename:dirname(F)),
48024752 protobuffs_compile:generate_source(F,
4803 [{output_include_dir, Dir ++ "/include"},
4804 {output_src_dir, Dir ++ "/ebin"}])
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
48054755 end || F <- string:tokens("$(1)", " ")],
48064756 halt().
48074757 endef
48114761 $(if $(strip $?),$(call compile_proto,$?))
48124762 endif
48134763
4814 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
48154765 # This file is part of erlang.mk and subject to the terms of the ISC License.
48164766
48174767 .PHONY: clean-app
48254775 ERLC_EXCLUDE ?=
48264776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
48274777
4778 ERLC_ASN1_OPTS ?=
4779
48284780 ERLC_MIB_OPTS ?=
48294781 COMPILE_MIB_FIRST ?=
48304782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
48744826
48754827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
48764828 define app_file
4877 {application, $(PROJECT), [
4829 {application, '$(PROJECT)', [
48784830 {description, "$(PROJECT_DESCRIPTION)"},
48794831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48804832 {id$(comma)$(space)"$(1)"}$(comma))
48814833 {modules, [$(call comma_list,$(2))]},
48824834 {registered, []},
4883 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48844837 ]}.
48854838 endef
48864839 else
48874840 define app_file
4888 {application, $(PROJECT), [
4841 {application, '$(PROJECT)', [
48894842 {description, "$(PROJECT_DESCRIPTION)"},
48904843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48914844 {id$(comma)$(space)"$(1)"}$(comma))
48924845 {modules, [$(call comma_list,$(2))]},
48934846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
48944847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4895 {mod, {$(PROJECT_MOD), []}}
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48964850 ]}.
48974851 endef
48984852 endif
49024856
49034857 # Source files.
49044858
4905 ERL_FILES = $(sort $(call core_find,src/,*.erl))
4906 CORE_FILES = $(sort $(call core_find,src/,*.core))
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
49074863
49084864 # ASN.1 files.
49094865
49134869
49144870 define compile_asn1
49154871 $(verbose) mkdir -p include/
4916 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
49174873 $(verbose) mv asn1/*.erl src/
49184874 $(verbose) mv asn1/*.hrl include/
49194875 $(verbose) mv asn1/*.asn1db include/
49364892
49374893 # Leex and Yecc files.
49384894
4939 XRL_FILES = $(sort $(call core_find,src/,*.xrl))
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
49404896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
49414897 ERL_FILES += $(XRL_ERL_FILES)
49424898
4943 YRL_FILES = $(sort $(call core_find,src/,*.yrl))
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
49444900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
49454901 ERL_FILES += $(YRL_ERL_FILES)
49464902
49474903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4948 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
49494905
49504906 # Erlang and Core Erlang files.
49514907
49954951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49964952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49974953 (F, Mod, import, {Imp, _}) ->
4998 case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
49994960 false -> ok;
50004961 true -> Add(Mod, Imp)
50014962 end;
50194980 end || F <- ErlFiles],
50204981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
50214982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
50224991 ok = file:write_file("$(1)", [
50234992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
5024 "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n"
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
50254994 ]),
50264995 halt()
50274996 endef
50345003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
50355004 # Rebuild everything when the Makefile changes.
50365005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5037 @mkdir -p $(ERLANG_MK_TMP)
5038 @if test -f $@; then \
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
50395008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
50405009 touch -c $(PROJECT).d; \
50415010 fi
5042 @touch $@
5011 $(verbose) touch $@
50435012
50445013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
50455014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
50465015 endif
50475016
5048 -include $(PROJECT).d
5017 include $(wildcard $(PROJECT).d)
50495018
50505019 ebin/$(PROJECT).app:: ebin/
50515020
50645033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
50655034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
50665035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5067 $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
50685037 > ebin/$(PROJECT).app
50695038 else
50705039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
50885057
50895058 endif
50905059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
50915061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
50925062 # This file is part of erlang.mk and subject to the terms of the ISC License.
50935063
51055075 doc-deps:
51065076 else
51075077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5108 $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5109 endif
5110
5111 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51125082 # This file is part of erlang.mk and subject to the terms of the ISC License.
51135083
51145084 .PHONY: rel-deps
51255095 rel-deps:
51265096 else
51275097 rel-deps: $(ALL_REL_DEPS_DIRS)
5128 $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5129 endif
5130
5131 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51325102 # This file is part of erlang.mk and subject to the terms of the ISC License.
51335103
51345104 .PHONY: test-deps test-dir test-build clean-test-dir
51505120 test-deps:
51515121 else
51525122 test-deps: $(ALL_TEST_DEPS_DIRS)
5153 $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
51545124 endif
51555125
51565126 ifneq ($(wildcard $(TEST_DIR)),)
51835153 endif
51845154 endif
51855155
5186 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51875157 # This file is part of erlang.mk and subject to the terms of the ISC License.
51885158
51895159 .PHONY: rebar.config
52195189 rebar.config:
52205190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
52215191
5222 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
52235193 # This file is part of erlang.mk and subject to the terms of the ISC License.
52245194
5225 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
5226
5227 MAN_INSTALL_PATH ?= /usr/local/share/man
5228 MAN_SECTIONS ?= 3 7
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
52295200
52305201 docs:: asciidoc
52315202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
52325207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
52335210
52345211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
52355212 asciidoc-guide:
52365213 else
5237 asciidoc-guide: distclean-asciidoc doc-deps
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
52385215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
52395216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5240 endif
5241
5242 ifeq ($(wildcard doc/src/manual/*.asciidoc),)
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
52435227 asciidoc-manual:
52445228 else
5245 asciidoc-manual: distclean-asciidoc doc-deps
5246 for f in doc/src/manual/*.asciidoc ; do \
5247 a2x -v -f manpage $$f ; \
5248 done
5249 for s in $(MAN_SECTIONS); do \
5250 mkdir -p doc/man$$s/ ; \
5251 mv doc/src/manual/*.$$s doc/man$$s/ ; \
5252 gzip doc/man$$s/*.$$s ; \
5253 done
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
52545262
52555263 install-docs:: install-asciidoc
52565264
52575265 install-asciidoc: asciidoc-manual
5258 for s in $(MAN_SECTIONS); do \
5259 mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
5260 install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
5261 done
5262 endif
5263
5264 distclean:: distclean-asciidoc
5265
5266 distclean-asciidoc:
5267 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
5268
5269 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
52705276 # This file is part of erlang.mk and subject to the terms of the ISC License.
52715277
52725278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
53235329 define bs_Makefile
53245330 PROJECT = $p
53255331 PROJECT_DESCRIPTION = New project
5326 PROJECT_VERSION = 0.0.1
5332 PROJECT_VERSION = 0.1.0
53275333
53285334 # Whitespace to be used when creating files from templates.
53295335 SP = $(SP)
53335339 define bs_Makefile
53345340 PROJECT = $p
53355341 PROJECT_DESCRIPTION = New project
5336 PROJECT_VERSION = 0.0.1
5342 PROJECT_VERSION = 0.1.0
53375343
53385344 endef
53395345 endif
53415347 define bs_apps_Makefile
53425348 PROJECT = $p
53435349 PROJECT_DESCRIPTION = New project
5344 PROJECT_VERSION = 0.0.1
5350 PROJECT_VERSION = 0.1.0
53455351
53465352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
53475353 endef
53615367 endef
53625368
53635369 define bs_relx_config
5364 {release, {$p_release, "1"}, [$p]}.
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
53655371 {extended_start_script, true}.
53665372 {sys_config, "rel/sys.config"}.
53675373 {vm_args, "rel/vm.args"}.
57125718 ifndef t
57135719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57145720 endif
5715 ifndef tpl_$(t)
5716 $(error Unknown template)
5717 endif
57185721 ifndef n
57195722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57205723 endif
57275730 list-templates:
57285731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
57295732
5730 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
57315734 # This file is part of erlang.mk and subject to the terms of the ISC License.
57325735
57335736 .PHONY: clean-c_src distclean-c_src-env
59625965 $(call render_template,bs_erl_nif,src/$n.erl)
59635966 endif
59645967
5965 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
59665969 # This file is part of erlang.mk and subject to the terms of the ISC License.
59675970
5968 .PHONY: ci ci-setup distclean-kerl
5969
5970 KERL ?= $(CURDIR)/kerl
5971 export KERL
5972
5973 KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
5974
5975 OTP_GIT ?= https://github.com/erlang/otp
5976
5977 CI_INSTALL_DIR ?= $(HOME)/erlang
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
59785973 CI_OTP ?=
5979
5980 ifeq ($(strip $(CI_OTP)),)
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
59815986 ci::
59825987 else
5983 ci:: $(addprefix ci-,$(CI_OTP))
5984
5985 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
59866007
59876008 ci-setup::
6009
6010 ci-extra::
59886011
59896012 ci_verbose_0 = @echo " CI " $(1);
59906013 ci_verbose = $(ci_verbose_$(V))
59916014
59926015 define ci_target
5993 ci-$(1): $(CI_INSTALL_DIR)/$(1)
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
59946018 $(ci_verbose) \
5995 PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
5996 CI_OTP_RELEASE="$(1)" \
5997 CT_OPTS="-label $(1)" \
5998 $(MAKE) clean ci-setup tests
5999 endef
6000
6001 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
60026030
60036031 define ci_otp_target
60046032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
60056033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6006 $(KERL) build git $(OTP_GIT) $(1) $(1)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
60076035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
60086036 endif
60096037 endef
60106038
60116039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
60126040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
60136052 $(KERL):
6014 $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
60156056 $(verbose) chmod +x $(KERL)
60166057
60176058 help::
60286069 $(gen_verbose) rm -rf $(KERL)
60296070 endif
60306071
6031 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
60326073 # This file is part of erlang.mk and subject to the terms of the ISC License.
60336074
60346075 .PHONY: ct apps-ct distclean-ct
60366077 # Configuration.
60376078
60386079 CT_OPTS ?=
6080
60396081 ifneq ($(wildcard $(TEST_DIR)),)
6040 CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6041 else
6042 CT_SUITES ?=
6043 endif
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
60446088
60456089 # Core targets.
60466090
60636107 -noinput \
60646108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
60656109 -dir $(TEST_DIR) \
6066 -logdir $(CURDIR)/logs
6110 -logdir $(CT_LOGS_DIR)
60676111
60686112 ifeq ($(CT_SUITES),)
60696113 ct: $(if $(IS_APP),,apps-ct)
60706114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
60716117 ct: test-build $(if $(IS_APP),,apps-ct)
6072 $(verbose) mkdir -p $(CURDIR)/logs/
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
60736119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
60746121 endif
60756122
60766123 ifneq ($(ALL_APPS_DIRS),)
60976144
60986145 define ct_suite_target
60996146 ct-$(1): test-build
6100 $(verbose) mkdir -p $(CURDIR)/logs/
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
61016148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
61026149 endef
61036150
61046151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
61056152
61066153 distclean-ct:
6107 $(gen_verbose) rm -rf $(CURDIR)/logs/
6108
6109 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61106157 # This file is part of erlang.mk and subject to the terms of the ISC License.
61116158
61126159 .PHONY: plt distclean-plt dialyze
61506197 endef
61516198
61526199 $(DIALYZER_PLT): deps app
6153 $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
61546204
61556205 plt: $(DIALYZER_PLT)
61566206
61646214 endif
61656215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
61666216
6167 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61686218 # This file is part of erlang.mk and subject to the terms of the ISC License.
61696219
61706220 .PHONY: distclean-edoc edoc
61726222 # Configuration.
61736223
61746224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
61756235
61766236 # Core targets.
61776237
6178 ifneq ($(wildcard doc/overview.edoc),)
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
61796239 docs:: edoc
61806240 endif
61816241
61846244 # Plugin-specific targets.
61856245
61866246 edoc: distclean-edoc doc-deps
6187 $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
61886248
61896249 distclean-edoc:
61906250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
61916251
6192 # Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61936253 # This file is part of erlang.mk and subject to the terms of the ISC License.
61946254
6195 .PHONY: distclean-escript escript
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
61966320
61976321 # Configuration.
61986322
61996323 ESCRIPT_NAME ?= $(PROJECT)
62006324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
62016325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
62026327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6203
6204 ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
6205 ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
6206 ESCRIPT_EMU_ARGS ?= -pa . \
6207 -sasl errlog_type error \
6208 -escript main $(ESCRIPT_NAME)
6209 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6210 ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
62116332
62126333 # Core targets.
62136334
62206341
62216342 # Plugin-specific targets.
62226343
6223 # Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
6224 # Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
6225 # Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
6226 # Software may only be used for the great good and the true happiness of all
6227 # sentient beings.
6228
6229 define ESCRIPT_RAW
6230 'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
6231 'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
6232 ' [F || F <- A, not filelib:is_dir(F) ] end,'\
6233 'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
6234 'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
6235 'Ez = fun(Escript) ->'\
6236 ' Static = Files([$(ESCRIPT_STATIC)]),'\
6237 ' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
6238 ' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
6239 ' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
6240 ' {archive, Archive, [memory]},'\
6241 ' {shebang, "$(ESCRIPT_SHEBANG)"},'\
6242 ' {comment, "$(ESCRIPT_COMMENT)"},'\
6243 ' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
6244 ' ]),'\
6245 ' file:change_mode(Escript, 8#755)'\
6246 'end,'\
6247 'Ez("$(ESCRIPT_FILE)"),'\
6248 'halt().'
6249 endef
6250
6251 ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
6252
6253 escript:: distclean-escript deps app
6254 $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
62556360
62566361 distclean-escript:
6257 $(gen_verbose) rm -f $(ESCRIPT_NAME)
6258
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
62596365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6260 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
62616366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
62626367
62636368 .PHONY: eunit apps-eunit
63216426
63226427 ifneq ($(ALL_APPS_DIRS),)
63236428 apps-eunit:
6324 $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done
6325 endif
6326 endif
6327
6328 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
63296436 # This file is part of erlang.mk and subject to the terms of the ISC License.
63306437
6331 .PHONY: relx-rel distclean-relx-rel distclean-relx run
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
63326439
63336440 # Configuration.
63346441
6335 RELX ?= $(CURDIR)/relx
6442 RELX ?= $(ERLANG_MK_TMP)/relx
63366443 RELX_CONFIG ?= $(CURDIR)/relx.config
63376444
63386445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
63396446 RELX_OPTS ?=
63406447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
63416454
63426455 ifeq ($(firstword $(RELX_OPTS)),-o)
63436456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
63506463 ifeq ($(IS_DEP),)
63516464 ifneq ($(wildcard $(RELX_CONFIG)),)
63526465 rel:: relx-rel
6353 endif
6354 endif
6355
6356 distclean:: distclean-relx-rel distclean-relx
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
63576472
63586473 # Plugin-specific targets.
63596474
63626477 $(verbose) chmod +x $(RELX)
63636478
63646479 relx-rel: $(RELX) rel-deps app
6365 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
63666484
63676485 distclean-relx-rel:
63686486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6369
6370 distclean-relx:
6371 $(gen_verbose) rm -rf $(RELX)
63726487
63736488 # Run target.
63746489
63776492 else
63786493
63796494 define get_relx_release.erl
6380 {ok, Config} = file:consult("$(RELX_CONFIG)"),
6381 {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
6382 io:format("~s", [Name]),
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
63836504 halt(0).
63846505 endef
63856506
6386 RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
63876514
63886515 run: all
6389 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
63906517
63916518 help::
63926519 $(verbose) printf "%s\n" "" \
63956522
63966523 endif
63976524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
63986526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6399 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
64006527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
64016528
64026529 .PHONY: shell
64216548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
64226549
64236550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6424 $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
64256552
64266553 shell: build-shell-deps
64276554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
64286555
6429 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
64306571 # This file is part of erlang.mk and subject to the terms of the ISC License.
64316572
64326573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
64376578 tests:: triq
64386579
64396580 define triq_check.erl
6440 code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
64416582 try
64426583 case $(1) of
64436584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
64696610 endif
64706611 endif
64716612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
64726614 # Copyright (c) 2015, Erlang Solutions Ltd.
64736615 # This file is part of erlang.mk and subject to the terms of the ISC License.
64746616
64856627 XREFR ?= $(CURDIR)/xrefr
64866628 export XREFR
64876629
6488 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
64896631
64906632 # Core targets.
64916633
64926634 help::
6493 $(verbose) printf "%s\n" "" \
6494 "Xref targets:" \
6495 " xref Run Xrefr using $XREF_CONFIG as config file if defined"
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
64966638
64976639 distclean:: distclean-xref
64986640
65086650 distclean-xref:
65096651 $(gen_verbose) rm -rf $(XREFR)
65106652
6511 # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
65126655 # This file is part of erlang.mk and subject to the terms of the ISC License.
65136656
65146657 COVER_REPORT_DIR = cover
65176660
65186661 ifdef COVER
65196662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
65206664 # All modules in 'ebin'
65216665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
65226666
65316675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
65326676 endif
65336677 endif
6678 endif
65346679
65356680 # Core targets
65366681
65526697 "Cover targets:" \
65536698 " cover-report Generate a HTML coverage report from previously collected" \
65546699 " cover data." \
6555 " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
65566701 "" \
65576702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
65586703 "target tests additionally generates a HTML coverage report from the combined" \
65656710
65666711 .PHONY: coverdata-clean
65676712 coverdata-clean:
6568 $(gen_verbose) rm -f *.coverdata ct.cover.spec
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
65696714
65706715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
65716721 all.coverdata: $(COVERDATA)
6572 $(gen_verbose) $(ERL) -eval ' \
6573 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
6574 cover:export("$@"), halt(0).'
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
65756723
65766724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
65776725 # empty if you want the coverdata files but not the HTML report.
65896737 # Modules which include eunit.hrl always contain one line without coverage
65906738 # because eunit defines test/0 which is never called. We compensate for this.
65916739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6592 grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
65936741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
65946742
65956743 define cover_report.erl
66246772 endef
66256773
66266774 cover-report:
6627 $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
66286776 $(gen_verbose) $(call erlang,$(cover_report.erl))
66296777
66306778 endif
66316779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
66326839
66336840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
66346841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
66976904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
66986905 endif
66996906 ifndef IS_APP
6700 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
67016908 $(MAKE) -C $$dep $@ \
67026909 IS_APP=1 \
6703 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6704 || exit $$?; \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67056911 done
67066912 endif
6707 $(verbose) for dep in $^ ; do \
6913 $(verbose) set -e; for dep in $^ ; do \
67086914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
67096915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6710 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
67116917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
67126918 $(MAKE) -C $$dep fetch-deps \
67136919 IS_DEP=1 \
6714 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6715 || exit $$?; \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67166921 fi \
67176922 fi \
67186923 done
22 # before the inclusion of erlang.mk leading to the wrong target becoming
33 # the default.
44 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
526 endif
627
728 # --------------------------------------------------------------------
2041 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
2142 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
2243 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
2345 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
2446 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
2547 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
2648 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
2749 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
2851 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
2952 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
3054 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
3155 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
3256 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
3559 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
3660 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
3761 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
3863 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
3964 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
4065 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
7095
7196 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
7297
73 # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
74 # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
75 # needs to add "ranch" as a BUILD_DEPS. The list of projects needing
76 # this workaround are:
77 # o rabbitmq-web-stomp
78 dep_ranch = git https://github.com/ninenines/ranch 1.2.1
98 # Third-party dependencies version pinning.
99 #
100 # We do that in this file, which is copied in all projects, to ensure
101 # all projects use the same versions. It avoids conflicts and makes it
102 # possible to work with rabbitmq-public-umbrella.
103
104 dep_cowboy_commit = 1.0.4
105 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
106 dep_ranch_commit = 1.3.1
107 dep_webmachine_commit = 1.10.8p2
79108
80109 RABBITMQ_COMPONENTS = amqp_client \
81110 rabbit \
82111 rabbit_common \
83112 rabbitmq_amqp1_0 \
84113 rabbitmq_auth_backend_amqp \
114 rabbitmq_auth_backend_cache \
85115 rabbitmq_auth_backend_http \
86116 rabbitmq_auth_backend_ldap \
87117 rabbitmq_auth_mechanism_ssl \
88118 rabbitmq_boot_steps_visualiser \
89119 rabbitmq_clusterer \
120 rabbitmq_cli \
90121 rabbitmq_codegen \
91122 rabbitmq_consistent_hash_exchange \
123 rabbitmq_ct_client_helpers \
92124 rabbitmq_ct_helpers \
93125 rabbitmq_delayed_message_exchange \
94126 rabbitmq_dotnet_client \
97129 rabbitmq_federation_management \
98130 rabbitmq_java_client \
99131 rabbitmq_jms_client \
132 rabbitmq_jms_cts \
100133 rabbitmq_jms_topic_exchange \
101134 rabbitmq_lvc \
102135 rabbitmq_management \
+0
-8
deps/rabbitmq_jms_topic_exchange/src/rabbitmq_jms_topic_exchange.app.src less more
0 { application, rabbitmq_jms_topic_exchange
1 , [ {description, "RabbitMQ JMS topic selector exchange plugin"}
2 , {vsn, "3.6.6"}
3 , {modules, []}
4 , {registered, []}
5 , {applications, [kernel, stdlib, rabbit_common, rabbit, mnesia]}
6 ]
7 }.
44 jQuery - http://jquery.com/ - MIT license, see LICENSE-MIT-jQuery164
55 EJS - http://embeddedjs.com/ - MIT license, see LICENSE-MIT-EJS10
66 Sammy - http://code.quirkey.com/sammy/ - MIT license, see LICENSE-MIT-Sammy060
7 webmachine - http://webmachine.basho.com/ - Apache license, 2.0
8 mochiweb - http://github.com/mochi/mochiweb/ - MIT license
7 Cowboy - http://ninenines.eu/ - ISC license
98 base64.js - http://code.google.com/p/stringencoders/ - BSD license, see LICENSE-BSD-base64js
109 If you have any questions regarding licensing, please contact us at
1110 info@rabbitmq.com.
00 PROJECT = rabbitmq_management
1 PROJECT_DESCRIPTION = RabbitMQ Management Console
2 PROJECT_MOD = rabbit_mgmt_app
13
2 DEPS = rabbit_common rabbit amqp_client webmachine rabbitmq_web_dispatch rabbitmq_management_agent
3 TEST_DEPS = rabbitmq_ct_helpers
4 define PROJECT_ENV
5 [
6 {listener, [{port, 15672}]},
7 {http_log_dir, none},
8 {load_definitions, none},
9 {management_db_cache_multiplier, 5},
10 {process_stats_gc_timeout, 300000},
11 {stats_event_max_backlog, 250},
12 {cors_allow_origins, []},
13 {cors_max_age, 1800}
14 ]
15 endef
416
5 dep_webmachine = git https://github.com/rabbitmq/webmachine.git 6b5210c0ed07159f43222255e05a90bbef6c8cbe
6 dep_rabbitmq_web_dispatch = git https://github.com/rabbitmq/rabbitmq-web-dispatch.git stable
17 DEPS = rabbit_common rabbit amqp_client cowboy cowlib rabbitmq_web_dispatch rabbitmq_management_agent
18 TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers proper
19 dep_cowboy_commit = 1.0.3
20 LOCAL_DEPS += xmerl mnesia ranch ssl crypto public_key
721
8 DEP_PLUGINS = rabbit_common/mk/rabbitmq-dist.mk \
9 rabbit_common/mk/rabbitmq-run.mk \
10 rabbit_common/mk/rabbitmq-tools.mk
22 # FIXME: Add Ranch as a BUILD_DEPS to be sure the correct version is picked.
23 # See rabbitmq-components.mk.
24 BUILD_DEPS += ranch
25
26 DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
27 DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk
1128
1229 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
1330 # reviewed and merged.
2643 @echo bin/rabbitmqadmin
2744
2845 prepare-dist::
29 $(verbose) sed 's/%%VSN%%/$(VSN)/' bin/rabbitmqadmin \
46 $(verbose) sed 's/%%VSN%%/$(PROJECT_VERSION)/' bin/rabbitmqadmin \
3047 > $(EZ_DIR)/priv/www/cli/rabbitmqadmin
00 # RabbitMQ Management Plugin
11
22 This plugin provides a management UI and HTTP API for RabbitMQ.
3
4 ## Installation
5
36 This plugin is included in the RabbitMQ distribution. To enable
4 it, use <href="http://www.rabbitmq.com/man/rabbitmq-plugins.1.man.html">rabbitmq-plugins</a>.
7 it, use <a href="http://www.rabbitmq.com/man/rabbitmq-plugins.1.man.html">rabbitmq-plugins</a>.
58
69 ## Documentation
710
8 [RabbitMQ management UI documentation](http://www.rabbitmq.com/management.html).
11 * [RabbitMQ management UI documentation](http://www.rabbitmq.com/management.html).
12 * [HTTP API documentation](http://www.rabbitmq.com/management.html#http-api) and [reference](https://raw.githack.com/rabbitmq/rabbitmq-management/rabbitmq_v3_6_9/priv/www/api/index.html)
913
10 ## Continuous Integration
14 ## Copyright
1115
12 [![Build Status](https://travis-ci.org/rabbitmq/rabbitmq-management.svg?branch=master)](https://travis-ci.org/rabbitmq/rabbitmq-management)
16 (c) Pivotal Software Inc., 2007-2017.
412412 method()
413413
414414 def output(s):
415 print(maybe_utf8(s, sys.stdout))
415 print(s)
416416
417417 def die(s):
418 sys.stderr.write(maybe_utf8("*** {0}\n".format(s), sys.stderr))
418 sys.stderr.write("*** {0}\n".format(s))
419419 exit(1)
420420
421 def maybe_utf8(s, stream):
422 if sys.version_info[0] == 3 or stream.isatty():
421 def maybe_utf8(s):
422 if isinstance(s, int):
423 # s can be also an int for ex messages count
424 return str(s)
425 if sys.version_info[0] == 3:
423426 # It will have an encoding, which Python will respect
424427 return s
425428 else:
426429 # It won't have an encoding, and Python will pick ASCII by default
427430 return s.encode('utf-8')
431
428432
429433 class Management:
430434 def __init__(self, options, args):
458462
459463 def __initialize_tls_context(self):
460464 # Python 2.7.9+ only
461 ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
465 ssl_ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
462466 ssl_ctx.options &= ~ssl.OP_NO_SSLv3
463467 ssl_ctx.verify_mode = ssl.CERT_REQUIRED
464468 ssl_ctx.check_hostname = not self.options.ssl_disable_hostname_verification
465 ssl_ctx.load_cert_chain(self.options.ssl_cert_file,
466 self.options.ssl_key_file)
469 if self.options.ssl_key_file:
470 ssl_ctx.load_cert_chain(self.options.ssl_cert_file,
471 self.options.ssl_key_file)
467472 if self.options.ssl_ca_cert_file:
468473 ssl_ctx.load_verify_locations(self.options.ssl_ca_cert_file)
469474 return ssl_ctx
752757
753758 def add_to_row(col, val):
754759 if col in column_ix:
755 row[column_ix[col]] = str(val)
760 row[column_ix[col]] = maybe_utf8(val)
756761
757762 if len(self.columns) == 0:
758763 for item in items:
0 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
11 #
22 # Permission to use, copy, modify, and/or distribute this software for any
33 # purpose with or without fee is hereby granted, provided that the above
1111 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1212 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1313
14 .PHONY: all app apps deps search rel docs install-docs check tests clean distclean help erlang-mk
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
1515
1616 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17
18 ERLANG_MK_VERSION = 2.0.0-pre.2-144-g647ffd1
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
1931
2032 # Core configuration.
2133
2436
2537 PROJECT_VERSION ?= rolling
2638 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
2740
2841 # Verbosity.
2942
8497 rel::
8598 $(verbose) :
8699
100 relup:: deps app
101
87102 check:: tests
88103
89104 clean:: clean-crashdump
101116 help::
102117 $(verbose) printf "%s\n" \
103118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
104 "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
105120 "" \
106121 "Usage: [V=1] $(MAKE) [target]..." \
107122 "" \
149164 core_native_path = $1
150165 endif
151166
152 ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
153 define core_http_get
154 wget --no-check-certificate -O $(1) $(2)|| rm $(1)
155 endef
156 else
157 define core_http_get.erl
158 ssl:start(),
159 inets:start(),
160 case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
161 {ok, {{_, 200, _}, _, Body}} ->
162 case file:write_file("$(1)", Body) of
163 ok -> ok;
164 {error, R1} -> halt(R1)
165 end;
166 {error, R2} ->
167 halt(R2)
168 end,
169 halt(0).
170 endef
171
172 define core_http_get
173 $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
174 endef
175 endif
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
176168
177169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
178170
192184 ERLANG_MK_BUILD_CONFIG ?= build.config
193185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
194186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
195188 erlang-mk:
196189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
197190 ifdef ERLANG_MK_COMMIT
198191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
199192 endif
200193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
201 $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
202195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
203196 rm -rf $(ERLANG_MK_BUILD_DIR)
204197
285278 pkg_apns_repo = https://github.com/inaka/apns4erl
286279 pkg_apns_commit = master
287280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
288289 PACKAGES += azdht
289290 pkg_azdht_name = azdht
290291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
405406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
406407 pkg_bootstrap_commit = master
407408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
408417 PACKAGES += boss_db
409418 pkg_boss_db_name = boss_db
410419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
413422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
414423 pkg_boss_db_commit = master
415424
416 PACKAGES += boss
417 pkg_boss_name = boss
418 pkg_boss_description = Erlang web MVC, now featuring Comet
419 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
420 pkg_boss_fetch = git
421 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
422 pkg_boss_commit = master
423
424425 PACKAGES += brod
425426 pkg_brod_name = brod
426427 pkg_brod_description = Kafka client in Erlang
565566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
566567 pkg_cloudi_service_api_requests_commit = master
567568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
568585 PACKAGES += cloudi_service_db_cassandra_cql
569586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
570587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
573590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
574591 pkg_cloudi_service_db_cassandra_cql_commit = master
575592
576 PACKAGES += cloudi_service_db_cassandra
577 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
579 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
580 pkg_cloudi_service_db_cassandra_fetch = git
581 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
582 pkg_cloudi_service_db_cassandra_commit = master
583
584593 PACKAGES += cloudi_service_db_couchdb
585594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
586595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
637646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
638647 pkg_cloudi_service_db_tokyotyrant_commit = master
639648
640 PACKAGES += cloudi_service_db
641 pkg_cloudi_service_db_name = cloudi_service_db
642 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
643 pkg_cloudi_service_db_homepage = http://cloudi.org/
644 pkg_cloudi_service_db_fetch = git
645 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
646 pkg_cloudi_service_db_commit = master
647
648649 PACKAGES += cloudi_service_filesystem
649650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
650651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
10371038 pkg_edown_repo = https://github.com/uwiger/edown
10381039 pkg_edown_commit = master
10391040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
10401049 PACKAGES += eep_app
10411050 pkg_eep_app_name = eep_app
10421051 pkg_eep_app_description = Embedded Event Processing
10451054 pkg_eep_app_repo = https://github.com/darach/eep-erl
10461055 pkg_eep_app_commit = master
10471056
1048 PACKAGES += eep
1049 pkg_eep_name = eep
1050 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1051 pkg_eep_homepage = https://github.com/virtan/eep
1052 pkg_eep_fetch = git
1053 pkg_eep_repo = https://github.com/virtan/eep
1054 pkg_eep_commit = master
1055
10561057 PACKAGES += efene
10571058 pkg_efene_name = efene
10581059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
12371238 pkg_eqm_repo = https://github.com/loucash/eqm
12381239 pkg_eqm_commit = master
12391240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
12401249 PACKAGES += eredis_pool
12411250 pkg_eredis_pool_name = eredis_pool
12421251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
12451254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
12461255 pkg_eredis_pool_commit = master
12471256
1248 PACKAGES += eredis
1249 pkg_eredis_name = eredis
1250 pkg_eredis_description = Erlang Redis client
1251 pkg_eredis_homepage = https://github.com/wooga/eredis
1252 pkg_eredis_fetch = git
1253 pkg_eredis_repo = https://github.com/wooga/eredis
1254 pkg_eredis_commit = master
1255
12561257 PACKAGES += erl_streams
12571258 pkg_erl_streams_name = erl_streams
12581259 pkg_erl_streams_description = Streams in Erlang
15411542 pkg_etap_repo = https://github.com/ngerakines/etap
15421543 pkg_etap_commit = master
15431544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
15441553 PACKAGES += etest_http
15451554 pkg_etest_http_name = etest_http
15461555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
15491558 pkg_etest_http_repo = https://github.com/wooga/etest_http
15501559 pkg_etest_http_commit = master
15511560
1552 PACKAGES += etest
1553 pkg_etest_name = etest
1554 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1555 pkg_etest_homepage = https://github.com/wooga/etest
1556 pkg_etest_fetch = git
1557 pkg_etest_repo = https://github.com/wooga/etest
1558 pkg_etest_commit = master
1559
15601561 PACKAGES += etoml
15611562 pkg_etoml_name = etoml
15621563 pkg_etoml_description = TOML language erlang parser
15651566 pkg_etoml_repo = https://github.com/kalta/etoml
15661567 pkg_etoml_commit = master
15671568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
15681577 PACKAGES += eunit_formatters
15691578 pkg_eunit_formatters_name = eunit_formatters
15701579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
15731582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
15741583 pkg_eunit_formatters_commit = master
15751584
1576 PACKAGES += eunit
1577 pkg_eunit_name = eunit
1578 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1579 pkg_eunit_homepage = https://github.com/richcarl/eunit
1580 pkg_eunit_fetch = git
1581 pkg_eunit_repo = https://github.com/richcarl/eunit
1582 pkg_eunit_commit = master
1583
15841585 PACKAGES += euthanasia
15851586 pkg_euthanasia_name = euthanasia
15861587 pkg_euthanasia_description = Merciful killer for your Erlang processes
15981599 pkg_evum_commit = master
15991600
16001601 PACKAGES += exec
1601 pkg_exec_name = exec
1602 pkg_exec_name = erlexec
16021603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
16031604 pkg_exec_homepage = http://saleyn.github.com/erlexec
16041605 pkg_exec_fetch = git
17171718 pkg_fn_repo = https://github.com/reiddraper/fn
17181719 pkg_fn_commit = master
17191720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
17201729 PACKAGES += folsom_cowboy
17211730 pkg_folsom_cowboy_name = folsom_cowboy
17221731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
17251734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
17261735 pkg_folsom_cowboy_commit = master
17271736
1728 PACKAGES += folsom
1729 pkg_folsom_name = folsom
1730 pkg_folsom_description = Expose Erlang Events and Metrics
1731 pkg_folsom_homepage = https://github.com/boundary/folsom
1732 pkg_folsom_fetch = git
1733 pkg_folsom_repo = https://github.com/boundary/folsom
1734 pkg_folsom_commit = master
1735
17361737 PACKAGES += folsomite
17371738 pkg_folsomite_name = folsomite
17381739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
20932094 pkg_jesse_repo = https://github.com/for-GET/jesse
20942095 pkg_jesse_commit = master
20952096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
20962105 PACKAGES += jiffy_v
20972106 pkg_jiffy_v_name = jiffy_v
20982107 pkg_jiffy_v_description = JSON validation utility
21012110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
21022111 pkg_jiffy_v_commit = master
21032112
2104 PACKAGES += jiffy
2105 pkg_jiffy_name = jiffy
2106 pkg_jiffy_description = JSON NIFs for Erlang.
2107 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2108 pkg_jiffy_fetch = git
2109 pkg_jiffy_repo = https://github.com/davisp/jiffy
2110 pkg_jiffy_commit = master
2111
21122113 PACKAGES += jobs
21132114 pkg_jobs_name = jobs
21142115 pkg_jobs_description = a Job scheduler for load regulation
21252126 pkg_joxa_repo = https://github.com/joxa/joxa
21262127 pkg_joxa_commit = master
21272128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
21282137 PACKAGES += json_rec
21292138 pkg_json_rec_name = json_rec
21302139 pkg_json_rec_description = JSON to erlang record
21332142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
21342143 pkg_json_rec_commit = master
21352144
2136 PACKAGES += json
2137 pkg_json_name = json
2138 pkg_json_description = a high level json library for erlang (17.0+)
2139 pkg_json_homepage = https://github.com/talentdeficit/json
2140 pkg_json_fetch = git
2141 pkg_json_repo = https://github.com/talentdeficit/json
2142 pkg_json_commit = master
2143
21442145 PACKAGES += jsone
21452146 pkg_jsone_name = jsone
21462147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
21812182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
21822183 pkg_jsx_commit = master
21832184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
21842193 PACKAGES += kafka_protocol
21852194 pkg_kafka_protocol_name = kafka_protocol
21862195 pkg_kafka_protocol_description = Kafka protocol Erlang library
21892198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
21902199 pkg_kafka_protocol_commit = master
21912200
2192 PACKAGES += kafka
2193 pkg_kafka_name = kafka
2194 pkg_kafka_description = Kafka consumer and producer in Erlang
2195 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2196 pkg_kafka_fetch = git
2197 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2198 pkg_kafka_commit = master
2199
22002201 PACKAGES += kai
22012202 pkg_kai_name = kai
22022203 pkg_kai_description = DHT storage by Takeshi Inoue
22932294 pkg_kvs_repo = https://github.com/synrc/kvs
22942295 pkg_kvs_commit = master
22952296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
22962305 PACKAGES += lager_amqp_backend
22972306 pkg_lager_amqp_backend_name = lager_amqp_backend
22982307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
23092318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
23102319 pkg_lager_syslog_commit = master
23112320
2312 PACKAGES += lager
2313 pkg_lager_name = lager
2314 pkg_lager_description = A logging framework for Erlang/OTP.
2315 pkg_lager_homepage = https://github.com/basho/lager
2316 pkg_lager_fetch = git
2317 pkg_lager_repo = https://github.com/basho/lager
2318 pkg_lager_commit = master
2319
23202321 PACKAGES += lambdapad
23212322 pkg_lambdapad_name = lambdapad
23222323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
25732574 pkg_mixer_repo = https://github.com/chef/mixer
25742575 pkg_mixer_commit = master
25752576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
25762585 PACKAGES += mochiweb_xpath
25772586 pkg_mochiweb_xpath_name = mochiweb_xpath
25782587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
25812590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
25822591 pkg_mochiweb_xpath_commit = master
25832592
2584 PACKAGES += mochiweb
2585 pkg_mochiweb_name = mochiweb
2586 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2587 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2588 pkg_mochiweb_fetch = git
2589 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2590 pkg_mochiweb_commit = master
2591
25922593 PACKAGES += mockgyver
25932594 pkg_mockgyver_name = mockgyver
25942595 pkg_mockgyver_description = A mocking library for Erlang
30613062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
30623063 pkg_quickrand_commit = master
30633064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
30643073 PACKAGES += rabbit_exchange_type_riak
30653074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
30663075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
30693078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
30703079 pkg_rabbit_exchange_type_riak_commit = master
30713080
3072 PACKAGES += rabbit
3073 pkg_rabbit_name = rabbit
3074 pkg_rabbit_description = RabbitMQ Server
3075 pkg_rabbit_homepage = https://www.rabbitmq.com/
3076 pkg_rabbit_fetch = git
3077 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3078 pkg_rabbit_commit = master
3079
30803081 PACKAGES += rack
30813082 pkg_rack_name = rack
30823083 pkg_rack_description = Rack handler for erlang
34933494 pkg_smother_repo = https://github.com/ramsay-t/Smother
34943495 pkg_smother_commit = master
34953496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
34963505 PACKAGES += social
34973506 pkg_social_name = social
34983507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
35413550 pkg_stable_repo = https://github.com/dvv/stable
35423551 pkg_stable_commit = master
35433552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
35443561 PACKAGES += statebox_riak
35453562 pkg_statebox_riak_name = statebox_riak
35463563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
35493566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
35503567 pkg_statebox_riak_commit = master
35513568
3552 PACKAGES += statebox
3553 pkg_statebox_name = statebox
3554 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3555 pkg_statebox_homepage = https://github.com/mochi/statebox
3556 pkg_statebox_fetch = git
3557 pkg_statebox_repo = https://github.com/mochi/statebox
3558 pkg_statebox_commit = master
3559
35603569 PACKAGES += statman
35613570 pkg_statman_name = statman
35623571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
37253734 pkg_tirerl_repo = https://github.com/inaka/tirerl
37263735 pkg_tirerl_commit = master
37273736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
37283745 PACKAGES += traffic_tools
37293746 pkg_traffic_tools_name = traffic_tools
37303747 pkg_traffic_tools_description = Simple traffic limiting library
40614078 pkg_zucchini_repo = https://github.com/devinus/zucchini
40624079 pkg_zucchini_commit = master
40634080
4064 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
40654082 # This file is part of erlang.mk and subject to the terms of the ISC License.
40664083
40674084 .PHONY: search
40884105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
40894106 endif
40904107
4091 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
40924109 # This file is part of erlang.mk and subject to the terms of the ISC License.
40934110
4094 .PHONY: distclean-deps
4111 .PHONY: distclean-deps clean-tmp-deps.log
40954112
40964113 # Configuration.
40974114
41104127
41114128 REBAR_DEPS_DIR = $(DEPS_DIR)
41124129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
41134150
41144151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
41154152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
41164153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
41174154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
41184155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
41194157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
41204158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
41214159
41384176
41394177 # Core targets.
41404178
4141 ifdef IS_APP
4142 apps::
4143 else
4144 apps:: $(ALL_APPS_DIRS)
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
41454180 ifeq ($(IS_APP)$(IS_DEP),)
41464181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
41474182 endif
41494184 # Create ebin directory for all apps to make sure Erlang recognizes them
41504185 # as proper OTP applications when using -include_lib. This is a temporary
41514186 # fix, a proper fix would be to compile apps/* in the right order.
4152 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4153 mkdir -p $$dep/ebin || exit $$?; \
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
41544190 done
4155 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
41564196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
41574197 :; \
41584198 else \
41594199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4160 $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
4200 $(MAKE) -C $$dep IS_APP=1; \
41614201 fi \
41624202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
41634207 endif
41644208
41654209 ifneq ($(SKIP_DEPS),)
41664210 deps::
41674211 else
4168 deps:: $(ALL_DEPS_DIRS) apps
4169 ifeq ($(IS_APP)$(IS_DEP),)
4170 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4171 endif
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
41724213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4173 $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
41744215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
41754216 :; \
41764217 else \
41774218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
41784219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4179 $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
4220 $(MAKE) -C $$dep IS_DEP=1; \
41804221 else \
4181 echo "Error: No Makefile to build dependency $$dep."; \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
41824223 exit 2; \
41834224 fi \
41844225 fi \
41924233 # in practice only Makefile is needed so far.
41934234 define dep_autopatch
41944235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
41954237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
41964238 $(call dep_autopatch_erlang_mk,$(1)); \
41974239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4198 if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
41994243 $(call dep_autopatch2,$(1)); \
4200 elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
42014245 $(call dep_autopatch2,$(1)); \
4202 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
42034247 $(call dep_autopatch2,$(1)); \
4204 else \
4205 $(call erlang,$(call dep_autopatch_app.erl,$(1))); \
42064248 fi \
42074249 else \
42084250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
42144256 endef
42154257
42164258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
42174262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
42184263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
42194264 fi; \
42204265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4221 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
42224267 $(call dep_autopatch_fetch_rebar); \
42234268 $(call dep_autopatch_rebar,$(1)); \
42244269 else \
42304275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
42314276 endef
42324277
4233 # Overwrite erlang.mk with the current file by default.
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
42344280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
42354281 define dep_autopatch_erlang_mk
4236 echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \
4237 > $(DEPS_DIR)/$1/erlang.mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
42384287 endef
42394288 else
42404289 define dep_autopatch_erlang_mk
42534302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
42544303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
42554304 cd $(ERLANG_MK_TMP)/rebar; \
4256 git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
42574306 $(MAKE); \
42584307 cd -; \
42594308 fi
42704319 define dep_autopatch_rebar.erl
42714320 application:load(rebar),
42724321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
42734323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
42744324 {ok, Conf0} -> Conf0;
42754325 _ -> []
44234473 [] -> ok;
44244474 _ ->
44254475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4426 PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
44274477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4428 PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L \\"~s\\" -lerl_interface -lei\n",
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
44294479 [code:lib_dir(erl_interface, lib)])),
44304480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
44314481 FilterEnv = fun(Env) ->
44644514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44654515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44664516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4467 [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
44684518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
44694519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
44704520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
44764526 end,
44774527 [PortSpec(S) || S <- PortSpecs]
44784528 end,
4479 Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"),
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
44804530 RunPlugin = fun(Plugin, Step) ->
44814531 case erlang:function_exported(Plugin, Step, 2) of
44824532 false -> ok;
45244574 halt()
45254575 endef
45264576
4527 define dep_autopatch_app.erl
4528 UpdateModules = fun(App) ->
4529 case filelib:is_regular(App) of
4530 false -> ok;
4531 true ->
4532 {ok, [{application, '$(1)', L0}]} = file:consult(App),
4533 Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true,
4534 fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
4535 L = lists:keystore(modules, 1, L0, {modules, Mods}),
4536 ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}]))
4537 end
4538 end,
4539 UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"),
4540 halt()
4541 endef
4542
45434577 define dep_autopatch_appsrc_script.erl
45444578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
45454579 AppSrcScript = AppSrc ++ ".script",
45874621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
45884622 endef
45894623
4590 define dep_fetch_hex.erl
4591 ssl:start(),
4592 inets:start(),
4593 {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
4594 {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
4595 [], [{body_format, binary}]),
4596 {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
4597 {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
4598 ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
4599 halt()
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
46004626 endef
46014627
46024628 # Hex only has a package version. No need to look in the Erlang.mk packages.
46034629 define dep_fetch_hex
4604 $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
46054634 endef
46064635
46074636 define dep_fetch_fail
46314660 $(eval DEP_NAME := $(call dep_name,$1))
46324661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
46334662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4634 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
46354664 exit 17; \
46364665 fi
46374666 $(verbose) mkdir -p $(DEPS_DIR)
46734702 clean:: clean-apps
46744703
46754704 clean-apps:
4676 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4677 $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
46784707 done
46794708
46804709 distclean:: distclean-apps
46814710
46824711 distclean-apps:
4683 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4684 $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
46854714 done
46864715 endif
46874716
47014730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
47024731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
47034732
4704 # External plugins.
4705
4706 DEP_PLUGINS ?=
4707
4708 define core_dep_plugin
4709 -include $(DEPS_DIR)/$(1)
4710
4711 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4712 endef
4713
4714 $(foreach p,$(DEP_PLUGINS),\
4715 $(eval $(if $(findstring /,$p),\
4716 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4717 $(call core_dep_plugin,$p/plugins.mk,$p))))
4718
4719 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4720 # This file is part of erlang.mk and subject to the terms of the ISC License.
4721
4722 # Configuration.
4723
4724 DTL_FULL_PATH ?=
4725 DTL_PATH ?= templates/
4726 DTL_SUFFIX ?= _dtl
4727 DTL_OPTS ?=
4728
4729 # Verbosity.
4730
4731 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
4732 dtl_verbose = $(dtl_verbose_$(V))
4733
4734 # Core targets.
4735
4736 DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl))
4737
4738 ifneq ($(DTL_FILES),)
4739
4740 ifdef DTL_FULL_PATH
4741 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
4742 else
4743 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
4744 endif
4745
4746 ifneq ($(words $(DTL_FILES)),0)
4747 # Rebuild templates when the Makefile changes.
4748 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
4749 @mkdir -p $(ERLANG_MK_TMP)
4750 @if test -f $@; then \
4751 touch $(DTL_FILES); \
4752 fi
4753 @touch $@
4754
4755 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
4756 endif
4757
4758 define erlydtl_compile.erl
4759 [begin
4760 Module0 = case "$(strip $(DTL_FULL_PATH))" of
4761 "" ->
4762 filename:basename(F, ".dtl");
4763 _ ->
4764 "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
4765 re:replace(F2, "/", "_", [{return, list}, global])
4766 end,
4767 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
4768 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
4769 ok -> ok;
4770 {ok, _} -> ok
4771 end
4772 end || F <- string:tokens("$(1)", " ")],
4773 halt().
4774 endef
4775
4776 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
4777 $(if $(strip $?),\
4778 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
4779
4780 endif
4781
4782 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
47834734 # This file is part of erlang.mk and subject to the terms of the ISC License.
47844735
47854736 # Verbosity.
47984749
47994750 define compile_proto.erl
48004751 [begin
4801 Dir = filename:dirname(filename:dirname(F)),
48024752 protobuffs_compile:generate_source(F,
4803 [{output_include_dir, Dir ++ "/include"},
4804 {output_src_dir, Dir ++ "/ebin"}])
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
48054755 end || F <- string:tokens("$(1)", " ")],
48064756 halt().
48074757 endef
48114761 $(if $(strip $?),$(call compile_proto,$?))
48124762 endif
48134763
4814 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
48154765 # This file is part of erlang.mk and subject to the terms of the ISC License.
48164766
48174767 .PHONY: clean-app
48254775 ERLC_EXCLUDE ?=
48264776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
48274777
4778 ERLC_ASN1_OPTS ?=
4779
48284780 ERLC_MIB_OPTS ?=
48294781 COMPILE_MIB_FIRST ?=
48304782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
48744826
48754827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
48764828 define app_file
4877 {application, $(PROJECT), [
4829 {application, '$(PROJECT)', [
48784830 {description, "$(PROJECT_DESCRIPTION)"},
48794831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48804832 {id$(comma)$(space)"$(1)"}$(comma))
48814833 {modules, [$(call comma_list,$(2))]},
48824834 {registered, []},
4883 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48844837 ]}.
48854838 endef
48864839 else
48874840 define app_file
4888 {application, $(PROJECT), [
4841 {application, '$(PROJECT)', [
48894842 {description, "$(PROJECT_DESCRIPTION)"},
48904843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48914844 {id$(comma)$(space)"$(1)"}$(comma))
48924845 {modules, [$(call comma_list,$(2))]},
48934846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
48944847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4895 {mod, {$(PROJECT_MOD), []}}
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48964850 ]}.
48974851 endef
48984852 endif
49024856
49034857 # Source files.
49044858
4905 ERL_FILES = $(sort $(call core_find,src/,*.erl))
4906 CORE_FILES = $(sort $(call core_find,src/,*.core))
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
49074863
49084864 # ASN.1 files.
49094865
49134869
49144870 define compile_asn1
49154871 $(verbose) mkdir -p include/
4916 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
49174873 $(verbose) mv asn1/*.erl src/
49184874 $(verbose) mv asn1/*.hrl include/
49194875 $(verbose) mv asn1/*.asn1db include/
49364892
49374893 # Leex and Yecc files.
49384894
4939 XRL_FILES = $(sort $(call core_find,src/,*.xrl))
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
49404896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
49414897 ERL_FILES += $(XRL_ERL_FILES)
49424898
4943 YRL_FILES = $(sort $(call core_find,src/,*.yrl))
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
49444900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
49454901 ERL_FILES += $(YRL_ERL_FILES)
49464902
49474903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4948 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
49494905
49504906 # Erlang and Core Erlang files.
49514907
49954951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49964952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49974953 (F, Mod, import, {Imp, _}) ->
4998 case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
49994960 false -> ok;
50004961 true -> Add(Mod, Imp)
50014962 end;
50194980 end || F <- ErlFiles],
50204981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
50214982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
50224991 ok = file:write_file("$(1)", [
50234992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
5024 "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n"
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
50254994 ]),
50264995 halt()
50274996 endef
50345003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
50355004 # Rebuild everything when the Makefile changes.
50365005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5037 @mkdir -p $(ERLANG_MK_TMP)
5038 @if test -f $@; then \
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
50395008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
50405009 touch -c $(PROJECT).d; \
50415010 fi
5042 @touch $@
5011 $(verbose) touch $@
50435012
50445013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
50455014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
50465015 endif
50475016
5048 -include $(PROJECT).d
5017 include $(wildcard $(PROJECT).d)
50495018
50505019 ebin/$(PROJECT).app:: ebin/
50515020
50645033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
50655034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
50665035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5067 $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
50685037 > ebin/$(PROJECT).app
50695038 else
50705039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
50885057
50895058 endif
50905059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
50915061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
50925062 # This file is part of erlang.mk and subject to the terms of the ISC License.
50935063
51055075 doc-deps:
51065076 else
51075077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5108 $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5109 endif
5110
5111 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51125082 # This file is part of erlang.mk and subject to the terms of the ISC License.
51135083
51145084 .PHONY: rel-deps
51255095 rel-deps:
51265096 else
51275097 rel-deps: $(ALL_REL_DEPS_DIRS)
5128 $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5129 endif
5130
5131 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51325102 # This file is part of erlang.mk and subject to the terms of the ISC License.
51335103
51345104 .PHONY: test-deps test-dir test-build clean-test-dir
51505120 test-deps:
51515121 else
51525122 test-deps: $(ALL_TEST_DEPS_DIRS)
5153 $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
51545124 endif
51555125
51565126 ifneq ($(wildcard $(TEST_DIR)),)
51835153 endif
51845154 endif
51855155
5186 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51875157 # This file is part of erlang.mk and subject to the terms of the ISC License.
51885158
51895159 .PHONY: rebar.config
52195189 rebar.config:
52205190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
52215191
5222 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
52235193 # This file is part of erlang.mk and subject to the terms of the ISC License.
52245194
5225 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
5226
5227 MAN_INSTALL_PATH ?= /usr/local/share/man
5228 MAN_SECTIONS ?= 3 7
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
52295200
52305201 docs:: asciidoc
52315202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
52325207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
52335210
52345211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
52355212 asciidoc-guide:
52365213 else
5237 asciidoc-guide: distclean-asciidoc doc-deps
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
52385215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
52395216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5240 endif
5241
5242 ifeq ($(wildcard doc/src/manual/*.asciidoc),)
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
52435227 asciidoc-manual:
52445228 else
5245 asciidoc-manual: distclean-asciidoc doc-deps
5246 for f in doc/src/manual/*.asciidoc ; do \
5247 a2x -v -f manpage $$f ; \
5248 done
5249 for s in $(MAN_SECTIONS); do \
5250 mkdir -p doc/man$$s/ ; \
5251 mv doc/src/manual/*.$$s doc/man$$s/ ; \
5252 gzip doc/man$$s/*.$$s ; \
5253 done
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
52545262
52555263 install-docs:: install-asciidoc
52565264
52575265 install-asciidoc: asciidoc-manual
5258 for s in $(MAN_SECTIONS); do \
5259 mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
5260 install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
5261 done
5262 endif
5263
5264 distclean:: distclean-asciidoc
5265
5266 distclean-asciidoc:
5267 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
5268
5269 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
52705276 # This file is part of erlang.mk and subject to the terms of the ISC License.
52715277
52725278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
53235329 define bs_Makefile
53245330 PROJECT = $p
53255331 PROJECT_DESCRIPTION = New project
5326 PROJECT_VERSION = 0.0.1
5332 PROJECT_VERSION = 0.1.0
53275333
53285334 # Whitespace to be used when creating files from templates.
53295335 SP = $(SP)
53335339 define bs_Makefile
53345340 PROJECT = $p
53355341 PROJECT_DESCRIPTION = New project
5336 PROJECT_VERSION = 0.0.1
5342 PROJECT_VERSION = 0.1.0
53375343
53385344 endef
53395345 endif
53415347 define bs_apps_Makefile
53425348 PROJECT = $p
53435349 PROJECT_DESCRIPTION = New project
5344 PROJECT_VERSION = 0.0.1
5350 PROJECT_VERSION = 0.1.0
53455351
53465352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
53475353 endef
53615367 endef
53625368
53635369 define bs_relx_config
5364 {release, {$p_release, "1"}, [$p]}.
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
53655371 {extended_start_script, true}.
53665372 {sys_config, "rel/sys.config"}.
53675373 {vm_args, "rel/vm.args"}.
57125718 ifndef t
57135719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57145720 endif
5715 ifndef tpl_$(t)
5716 $(error Unknown template)
5717 endif
57185721 ifndef n
57195722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57205723 endif
57275730 list-templates:
57285731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
57295732
5730 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
57315734 # This file is part of erlang.mk and subject to the terms of the ISC License.
57325735
57335736 .PHONY: clean-c_src distclean-c_src-env
59625965 $(call render_template,bs_erl_nif,src/$n.erl)
59635966 endif
59645967
5965 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
59665969 # This file is part of erlang.mk and subject to the terms of the ISC License.
59675970
5968 .PHONY: ci ci-setup distclean-kerl
5969
5970 KERL ?= $(CURDIR)/kerl
5971 export KERL
5972
5973 KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
5974
5975 OTP_GIT ?= https://github.com/erlang/otp
5976
5977 CI_INSTALL_DIR ?= $(HOME)/erlang
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
59785973 CI_OTP ?=
5979
5980 ifeq ($(strip $(CI_OTP)),)
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
59815986 ci::
59825987 else
5983 ci:: $(addprefix ci-,$(CI_OTP))
5984
5985 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
59866007
59876008 ci-setup::
6009
6010 ci-extra::
59886011
59896012 ci_verbose_0 = @echo " CI " $(1);
59906013 ci_verbose = $(ci_verbose_$(V))
59916014
59926015 define ci_target
5993 ci-$(1): $(CI_INSTALL_DIR)/$(1)
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
59946018 $(ci_verbose) \
5995 PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
5996 CI_OTP_RELEASE="$(1)" \
5997 CT_OPTS="-label $(1)" \
5998 $(MAKE) clean ci-setup tests
5999 endef
6000
6001 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
60026030
60036031 define ci_otp_target
60046032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
60056033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6006 $(KERL) build git $(OTP_GIT) $(1) $(1)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
60076035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
60086036 endif
60096037 endef
60106038
60116039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
60126040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
60136052 $(KERL):
6014 $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
60156056 $(verbose) chmod +x $(KERL)
60166057
60176058 help::
60286069 $(gen_verbose) rm -rf $(KERL)
60296070 endif
60306071
6031 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
60326073 # This file is part of erlang.mk and subject to the terms of the ISC License.
60336074
60346075 .PHONY: ct apps-ct distclean-ct
60366077 # Configuration.
60376078
60386079 CT_OPTS ?=
6080
60396081 ifneq ($(wildcard $(TEST_DIR)),)
6040 CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6041 else
6042 CT_SUITES ?=
6043 endif
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
60446088
60456089 # Core targets.
60466090
60636107 -noinput \
60646108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
60656109 -dir $(TEST_DIR) \
6066 -logdir $(CURDIR)/logs
6110 -logdir $(CT_LOGS_DIR)
60676111
60686112 ifeq ($(CT_SUITES),)
60696113 ct: $(if $(IS_APP),,apps-ct)
60706114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
60716117 ct: test-build $(if $(IS_APP),,apps-ct)
6072 $(verbose) mkdir -p $(CURDIR)/logs/
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
60736119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
60746121 endif
60756122
60766123 ifneq ($(ALL_APPS_DIRS),)
60976144
60986145 define ct_suite_target
60996146 ct-$(1): test-build
6100 $(verbose) mkdir -p $(CURDIR)/logs/
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
61016148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
61026149 endef
61036150
61046151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
61056152
61066153 distclean-ct:
6107 $(gen_verbose) rm -rf $(CURDIR)/logs/
6108
6109 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61106157 # This file is part of erlang.mk and subject to the terms of the ISC License.
61116158
61126159 .PHONY: plt distclean-plt dialyze
61506197 endef
61516198
61526199 $(DIALYZER_PLT): deps app
6153 $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
61546204
61556205 plt: $(DIALYZER_PLT)
61566206
61646214 endif
61656215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
61666216
6167 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61686218 # This file is part of erlang.mk and subject to the terms of the ISC License.
61696219
61706220 .PHONY: distclean-edoc edoc
61726222 # Configuration.
61736223
61746224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
61756235
61766236 # Core targets.
61776237
6178 ifneq ($(wildcard doc/overview.edoc),)
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
61796239 docs:: edoc
61806240 endif
61816241
61846244 # Plugin-specific targets.
61856245
61866246 edoc: distclean-edoc doc-deps
6187 $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
61886248
61896249 distclean-edoc:
61906250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
61916251
6192 # Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61936253 # This file is part of erlang.mk and subject to the terms of the ISC License.
61946254
6195 .PHONY: distclean-escript escript
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
61966320
61976321 # Configuration.
61986322
61996323 ESCRIPT_NAME ?= $(PROJECT)
62006324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
62016325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
62026327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6203
6204 ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
6205 ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
6206 ESCRIPT_EMU_ARGS ?= -pa . \
6207 -sasl errlog_type error \
6208 -escript main $(ESCRIPT_NAME)
6209 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6210 ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
62116332
62126333 # Core targets.
62136334
62206341
62216342 # Plugin-specific targets.
62226343
6223 # Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
6224 # Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
6225 # Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
6226 # Software may only be used for the great good and the true happiness of all
6227 # sentient beings.
6228
6229 define ESCRIPT_RAW
6230 'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
6231 'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
6232 ' [F || F <- A, not filelib:is_dir(F) ] end,'\
6233 'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
6234 'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
6235 'Ez = fun(Escript) ->'\
6236 ' Static = Files([$(ESCRIPT_STATIC)]),'\
6237 ' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
6238 ' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
6239 ' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
6240 ' {archive, Archive, [memory]},'\
6241 ' {shebang, "$(ESCRIPT_SHEBANG)"},'\
6242 ' {comment, "$(ESCRIPT_COMMENT)"},'\
6243 ' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
6244 ' ]),'\
6245 ' file:change_mode(Escript, 8#755)'\
6246 'end,'\
6247 'Ez("$(ESCRIPT_FILE)"),'\
6248 'halt().'
6249 endef
6250
6251 ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
6252
6253 escript:: distclean-escript deps app
6254 $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
62556360
62566361 distclean-escript:
6257 $(gen_verbose) rm -f $(ESCRIPT_NAME)
6258
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
62596365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6260 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
62616366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
62626367
62636368 .PHONY: eunit apps-eunit
63216426
63226427 ifneq ($(ALL_APPS_DIRS),)
63236428 apps-eunit:
6324 $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done
6325 endif
6326 endif
6327
6328 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
63296436 # This file is part of erlang.mk and subject to the terms of the ISC License.
63306437
6331 .PHONY: relx-rel distclean-relx-rel distclean-relx run
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
63326439
63336440 # Configuration.
63346441
6335 RELX ?= $(CURDIR)/relx
6442 RELX ?= $(ERLANG_MK_TMP)/relx
63366443 RELX_CONFIG ?= $(CURDIR)/relx.config
63376444
63386445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
63396446 RELX_OPTS ?=
63406447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
63416454
63426455 ifeq ($(firstword $(RELX_OPTS)),-o)
63436456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
63506463 ifeq ($(IS_DEP),)
63516464 ifneq ($(wildcard $(RELX_CONFIG)),)
63526465 rel:: relx-rel
6353 endif
6354 endif
6355
6356 distclean:: distclean-relx-rel distclean-relx
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
63576472
63586473 # Plugin-specific targets.
63596474
63626477 $(verbose) chmod +x $(RELX)
63636478
63646479 relx-rel: $(RELX) rel-deps app
6365 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
63666484
63676485 distclean-relx-rel:
63686486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6369
6370 distclean-relx:
6371 $(gen_verbose) rm -rf $(RELX)
63726487
63736488 # Run target.
63746489
63776492 else
63786493
63796494 define get_relx_release.erl
6380 {ok, Config} = file:consult("$(RELX_CONFIG)"),
6381 {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
6382 io:format("~s", [Name]),
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
63836504 halt(0).
63846505 endef
63856506
6386 RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
63876514
63886515 run: all
6389 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
63906517
63916518 help::
63926519 $(verbose) printf "%s\n" "" \
63956522
63966523 endif
63976524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
63986526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6399 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
64006527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
64016528
64026529 .PHONY: shell
64216548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
64226549
64236550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6424 $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
64256552
64266553 shell: build-shell-deps
64276554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
64286555
6429 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
64306571 # This file is part of erlang.mk and subject to the terms of the ISC License.
64316572
64326573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
64376578 tests:: triq
64386579
64396580 define triq_check.erl
6440 code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
64416582 try
64426583 case $(1) of
64436584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
64696610 endif
64706611 endif
64716612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
64726614 # Copyright (c) 2015, Erlang Solutions Ltd.
64736615 # This file is part of erlang.mk and subject to the terms of the ISC License.
64746616
64856627 XREFR ?= $(CURDIR)/xrefr
64866628 export XREFR
64876629
6488 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
64896631
64906632 # Core targets.
64916633
64926634 help::
6493 $(verbose) printf "%s\n" "" \
6494 "Xref targets:" \
6495 " xref Run Xrefr using $XREF_CONFIG as config file if defined"
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
64966638
64976639 distclean:: distclean-xref
64986640
65086650 distclean-xref:
65096651 $(gen_verbose) rm -rf $(XREFR)
65106652
6511 # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
65126655 # This file is part of erlang.mk and subject to the terms of the ISC License.
65136656
65146657 COVER_REPORT_DIR = cover
65176660
65186661 ifdef COVER
65196662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
65206664 # All modules in 'ebin'
65216665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
65226666
65316675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
65326676 endif
65336677 endif
6678 endif
65346679
65356680 # Core targets
65366681
65526697 "Cover targets:" \
65536698 " cover-report Generate a HTML coverage report from previously collected" \
65546699 " cover data." \
6555 " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
65566701 "" \
65576702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
65586703 "target tests additionally generates a HTML coverage report from the combined" \
65656710
65666711 .PHONY: coverdata-clean
65676712 coverdata-clean:
6568 $(gen_verbose) rm -f *.coverdata ct.cover.spec
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
65696714
65706715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
65716721 all.coverdata: $(COVERDATA)
6572 $(gen_verbose) $(ERL) -eval ' \
6573 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
6574 cover:export("$@"), halt(0).'
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
65756723
65766724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
65776725 # empty if you want the coverdata files but not the HTML report.
65896737 # Modules which include eunit.hrl always contain one line without coverage
65906738 # because eunit defines test/0 which is never called. We compensate for this.
65916739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6592 grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
65936741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
65946742
65956743 define cover_report.erl
66246772 endef
66256773
66266774 cover-report:
6627 $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
66286776 $(gen_verbose) $(call erlang,$(cover_report.erl))
66296777
66306778 endif
66316779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
66326839
66336840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
66346841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
66976904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
66986905 endif
66996906 ifndef IS_APP
6700 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
67016908 $(MAKE) -C $$dep $@ \
67026909 IS_APP=1 \
6703 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6704 || exit $$?; \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67056911 done
67066912 endif
6707 $(verbose) for dep in $^ ; do \
6913 $(verbose) set -e; for dep in $^ ; do \
67086914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
67096915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6710 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
67116917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
67126918 $(MAKE) -C $$dep fetch-deps \
67136919 IS_DEP=1 \
6714 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6715 || exit $$?; \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67166921 fi \
67176922 fi \
67186923 done
1313 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
16 -record(context, {user,
17 password = none,
18 impl}). % storage for a context of the resource handler
19 -record(range, {first, last, incr}).
20
2116 -define(AUTH_REALM, "Basic realm=\"RabbitMQ Management\"").
+0
-32
deps/rabbitmq_management/include/rabbit_mgmt_event_collector.hrl less more
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License at
3 %% http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
7 %% License for the specific language governing rights and limitations
8 %% under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is Pivotal Software, Inc.
13 %% Copyright (c) 2010-2016 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -record(state, {
17 lookups,
18 interval,
19 event_refresh_ref,
20 rates_mode,
21 max_backlog}).
22
23 -define(FINE_STATS_TYPES, [channel_queue_stats, channel_exchange_stats,
24 channel_queue_exchange_stats]).
25
26 -define(TABLES, [queue_stats, connection_stats, channel_stats,
27 consumers_by_queue, consumers_by_channel,
28 node_stats, node_node_stats,
29 %% What the previous info item was for any given
30 %% {queue/channel/connection}
31 old_stats]).
+0
-211
deps/rabbitmq_management/include/rabbit_mgmt_metrics.hrl less more
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License at
3 %% http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
7 %% License for the specific language governing rights and limitations
8 %% under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is Pivotal Software, Inc.
13 %% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -define(DELIVER_GET, [deliver, deliver_no_ack, get, get_no_ack]).
17 -define(FINE_STATS, [publish, publish_in, publish_out,
18 ack, deliver_get, confirm, return_unroutable, redeliver] ++
19 ?DELIVER_GET).
20
21 %% Most come from channels as fine stats, but queues emit these directly.
22 -define(QUEUE_MSG_RATES, [disk_reads, disk_writes]).
23
24 -define(MSG_RATES, ?FINE_STATS ++ ?QUEUE_MSG_RATES).
25
26 -define(MSG_RATES_DETAILS, [publish_details, publish_in_details,
27 publish_out_details, ack_details,
28 deliver_get_details, confirm_details,
29 return_unroutable_details, redeliver_details,
30 deliver_details, deliver_no_ack_details,
31 get_details, get_no_ack_details,
32 disk_reads_details, disk_writes_details] ++ ?MSG_RATES).
33
34 -define(QUEUE_MSG_COUNTS, [messages, messages_ready, messages_unacknowledged]).
35
36 -define(COARSE_NODE_STATS,
37 [mem_used, fd_used, sockets_used, proc_used, disk_free,
38 io_read_count, io_read_bytes, io_read_time,
39 io_write_count, io_write_bytes, io_write_time,
40 io_sync_count, io_sync_time,
41 io_seek_count, io_seek_time,
42 io_reopen_count, mnesia_ram_tx_count, mnesia_disk_tx_count,
43 msg_store_read_count, msg_store_write_count,
44 queue_index_journal_write_count,
45 queue_index_write_count, queue_index_read_count,
46 gc_num, gc_bytes_reclaimed, context_switches,
47 io_file_handle_open_attempt_count, io_file_handle_open_attempt_time]).
48
49 -define(COARSE_NODE_NODE_STATS, [send_bytes, recv_bytes]).
50
51 %% Normally 0 and no history means "has never happened, don't
52 %% report". But for these things we do want to report even at 0 with
53 %% no history.
54 -define(ALWAYS_REPORT_STATS,
55 [io_read_time, io_write_time,
56 io_sync_time, sockets_used | ?QUEUE_MSG_COUNTS]).
57
58 -define(COARSE_CONN_STATS, [recv_oct, send_oct]).
59
60 -define(PROCESS_STATS, [reductions]).
61
62 -type(event_type() :: queue_stats | queue_exchange_stats | vhost_stats
63 | channel_queue_stats | channel_stats
64 | channel_exchange_stats | exchange_stats
65 | node_stats | node_node_stats | connection_stats).
66 -type(type() :: deliver_get | fine_stats | queue_msg_rates | queue_msg_counts
67 | coarse_node_stats | coarse_node_node_stats | coarse_conn_stats
68 | process_stats).
69
70 -type(table_name() :: atom()).
71
72 %% TODO remove unused tables
73 %% Not all events generate all metrics, so some of the tables may be deleted
74 -define(AGGR_TABLES, [aggr_queue_stats_fine_stats,
75 aggr_queue_stats_deliver_get,
76 aggr_queue_stats_queue_msg_counts,
77 aggr_queue_stats_queue_msg_rates,
78 aggr_queue_stats_process_stats,
79 aggr_queue_exchange_stats_fine_stats,
80 aggr_vhost_stats_deliver_get,
81 aggr_vhost_stats_fine_stats,
82 aggr_vhost_stats_queue_msg_rates,
83 aggr_vhost_stats_queue_msg_counts,
84 aggr_vhost_stats_coarse_conn_stats,
85 aggr_channel_queue_stats_deliver_get,
86 aggr_channel_queue_stats_fine_stats,
87 aggr_channel_queue_stats_queue_msg_counts,
88 aggr_channel_stats_deliver_get,
89 aggr_channel_stats_fine_stats,
90 aggr_channel_stats_queue_msg_counts,
91 aggr_channel_stats_process_stats,
92 aggr_channel_exchange_stats_deliver_get,
93 aggr_channel_exchange_stats_fine_stats,
94 aggr_exchange_stats_fine_stats,
95 aggr_node_stats_coarse_node_stats,
96 aggr_node_node_stats_coarse_node_node_stats,
97 aggr_connection_stats_coarse_conn_stats,
98 aggr_connection_stats_process_stats
99 ]).
100
101 -define(INDEX_TABLES, [aggr_queue_stats_fine_stats_index,
102 aggr_queue_stats_deliver_get_index,
103 aggr_queue_stats_queue_msg_counts_index,
104 aggr_queue_stats_queue_msg_rates_index,
105 aggr_queue_stats_process_stats_index,
106 aggr_queue_exchange_stats_fine_stats_index,
107 aggr_vhost_stats_deliver_get_index,
108 aggr_vhost_stats_fine_stats_index,
109 aggr_vhost_stats_queue_msg_rates_index,
110 aggr_vhost_stats_queue_msg_counts_index,
111 aggr_vhost_stats_coarse_conn_stats_index,
112 aggr_channel_queue_stats_deliver_get_index,
113 aggr_channel_queue_stats_fine_stats_index,
114 aggr_channel_queue_stats_queue_msg_counts_index,
115 aggr_channel_stats_deliver_get_index,
116 aggr_channel_stats_fine_stats_index,
117 aggr_channel_stats_queue_msg_counts_index,
118 aggr_channel_stats_process_stats_index,
119 aggr_channel_exchange_stats_deliver_get_index,
120 aggr_channel_exchange_stats_fine_stats_index,
121 aggr_exchange_stats_fine_stats_index,
122 aggr_node_stats_coarse_node_stats_index,
123 aggr_node_node_stats_coarse_node_node_stats_index,
124 aggr_connection_stats_coarse_conn_stats_index,
125 aggr_connection_stats_process_stats_index
126 ]).
127
128 -define(KEY_INDEX_TABLES,
129 [aggr_queue_stats_fine_stats_key_index,
130 aggr_queue_stats_deliver_get_key_index,
131 aggr_queue_stats_queue_msg_counts_key_index,
132 aggr_queue_stats_queue_msg_rates_key_index,
133 aggr_queue_stats_process_stats_key_index,
134 aggr_queue_exchange_stats_fine_stats_key_index,
135 aggr_vhost_stats_deliver_get_key_index,
136 aggr_vhost_stats_fine_stats_key_index,
137 aggr_vhost_stats_queue_msg_rates_key_index,
138 aggr_vhost_stats_queue_msg_counts_key_index,
139 aggr_vhost_stats_coarse_conn_stats_key_index,
140 aggr_channel_queue_stats_deliver_get_key_index,
141 aggr_channel_queue_stats_fine_stats_key_index,
142 aggr_channel_queue_stats_queue_msg_counts_key_index,
143 aggr_channel_stats_deliver_get_key_index,
144 aggr_channel_stats_fine_stats_key_index,
145 aggr_channel_stats_queue_msg_counts_key_index,
146 aggr_channel_stats_process_stats_key_index,
147 aggr_channel_exchange_stats_deliver_get_key_index,
148 aggr_channel_exchange_stats_fine_stats_key_index,
149 aggr_exchange_stats_fine_stats_key_index,
150 aggr_node_stats_coarse_node_stats_key_index,
151 aggr_node_node_stats_coarse_node_node_stats_key_index,
152 aggr_connection_stats_coarse_conn_stats_key_index,
153 aggr_connection_stats_process_stats_key_index
154 ]).
155
156 -define(PROC_STATS_TABLES,
157 [channel_stats, connection_stats]).
158
159 %% Records are only used to retrieve the field position and to facilitate
160 %% keeping track of the data
161 -record(deliver_get, {deliver,
162 deliver_no_ack,
163 get,
164 get_no_ack}).
165 -record(fine_stats, {publish,
166 publish_in,
167 publish_out,
168 ack,
169 deliver_get,
170 confirm,
171 return_unroutable,
172 redeliver}).
173 -record(queue_msg_rates, {disk_reads,
174 disk_writes}).
175 -record(queue_msg_counts, {messages,
176 messages_ready,
177 messages_unacknowledged}).
178 -record(coarse_node_stats, {mem_used,
179 fd_used,
180 sockets_used,
181 proc_used,
182 disk_free,
183 io_read_count,
184 io_read_bytes,
185 io_read_time,
186 io_write_count,
187 io_write_bytes,
188 io_write_time,
189 io_sync_count,
190 io_sync_time,
191 io_seek_count,
192 io_seek_time,
193 io_reopen_count,
194 mnesia_ram_tx_count,
195 mnesia_disk_tx_count,
196 msg_store_read_count,
197 msg_store_write_count,
198 queue_index_journal_write_count,
199 queue_index_write_count,
200 queue_index_read_count,
201 gc_num,
202 gc_bytes_reclaimed,
203 context_switches,
204 io_file_handle_open_attempt_count,
205 io_file_handle_open_attempt_time}).
206 -record(coarse_node_node_stats, {send_bytes,
207 recv_bytes}).
208 -record(coarse_conn_stats, {recv_oct,
209 send_oct}).
210 -record(process_stats, {reductions}).
+0
-11
deps/rabbitmq_management/include/rabbit_mgmt_test.hrl less more
0 -include_lib("eunit/include/eunit.hrl").
1 -include_lib("amqp_client/include/amqp_client.hrl").
2
3 -define(OK, 200).
4 -define(CREATED, 201).
5 -define(NO_CONTENT, 204).
6 -define(BAD_REQUEST, 400).
7 -define(NOT_AUTHORISED, 401).
8 %%-define(NOT_FOUND, 404). Defined for AMQP by amqp_client.hrl (as 404)
9 %% httpc seems to get racy when using HTTP 1.1
10 -define(HTTPC_OPTS, [{version, "HTTP/1.0"}]).
5353 list of subfields separated by dots. See the example below.</p>
5454
5555 <p>Most of the GET queries return many fields per
56 object. See <a href="/doc/stats.html">the separate stats
56 object. See <a href="../doc/stats.html">the separate stats
5757 documentation</a>.</p>
5858
5959 <h2>Examples</h2>
587587 <td class="path">/api/bindings/<i>vhost</i>/e/<i>exchange</i>/q/<i>queue</i>/<i>props</i></td>
588588 <td>An individual binding between an exchange and a queue.
589589 The <i>props</i> part of the URI is a "name" for the binding
590 composed of its routing key and a hash of its arguments.</td>
590 composed of its routing key and a hash of its
591 arguments. <i>props</i> is the field named "properties_key"
592 from a bindings listing response.</td>
591593 </tr>
592594 <tr>
593595 <td>X</td>
660662 <pre>{"password_hash":"2lmoth8l4H0DViLaK9Fxi6l9ds8=", "tags":"administrator"}</pre>
661663 The <code>tags</code> key is mandatory. Either
662664 <code>password</code> or <code>password_hash</code>
663 must be set. Setting <code>password_hash</code> to "" will ensure the
665 must be set. Setting <code>password_hash</code> to <code>""</code> will ensure the
664666 user cannot use a password to log in. <code>tags</code> is a
665667 comma-separated list of tags for the user. Currently recognised tags
666 are "administrator", "monitoring" and "management".
668 are <code>administrator</code>, <code>monitoring</code> and <code>management</code>.
669 <code>password_hash</code> must be generated using the algorithm described
670 <a href="http://rabbitmq.com/passwords.html#password-generation">here</a>.
667671 </td>
668672 </tr>
669673 <tr>
706710 <td></td>
707711 <td></td>
708712 <td class="path">/api/parameters</td>
709 <td>A list of all parameters.</td>
713 <td>A list of all vhost-scoped parameters.</td>
710714 </tr>
711715 <tr>
712716 <td>X</td>
714718 <td></td>
715719 <td></td>
716720 <td class="path">/api/parameters/<i>component</i></td>
717 <td>A list of all parameters for a given component.</td>
721 <td>A list of all vhost-scoped parameters for a given component.</td>
718722 </tr>
719723 <tr>
720724 <td>X</td>
722726 <td></td>
723727 <td></td>
724728 <td class="path">/api/parameters/<i>component</i>/<i>vhost</i></td>
725 <td>A list of all parameters for a given component and virtual host.</td>
729 <td>A list of all vhost-scoped parameters for a given component and virtual host.</td>
726730 </tr>
727731 <tr>
728732 <td>X</td>
730734 <td>X</td>
731735 <td></td>
732736 <td class="path">/api/parameters/<i>component</i>/<i>vhost</i>/<i>name</i></td>
733 <td>An individual parameter. To PUT a parameter, you will need a body looking something like this:
737 <td>An individual vhost-scoped parameter. To PUT a parameter, you will need a body looking something like this:
734738 <pre>{"vhost": "/","component":"federation","name":"local_username","value":"guest"}</pre>
735739 </td>
736740 </tr>
741 <tr>
742 <td>X</td>
743 <td></td>
744 <td></td>
745 <td></td>
746 <td class="path">/api/global-parameters</td>
747 <td>A list of all global parameters.</td>
748 </tr>
749 <tr>
750 <td>X</td>
751 <td>X</td>
752 <td>X</td>
753 <td></td>
754 <td class="path">/api/global-parameters/<i>name</i></td>
755 <td>An individual global parameter. To PUT a parameter, you will need a body looking something like this:
756 <pre>{"name":"user_vhost_mapping","value":{"guest":"/","rabbit":"warren"}}</pre>
757 </td>
737758 <tr>
738759 <td>X</td>
739760 <td></td>
22 //
33
44 function message_rates(id, stats) {
5 var items = [['Publish', 'publish'], ['Confirm', 'confirm'],
5 var items = [['Publish', 'publish'],
6 ['Publisher confirm', 'confirm'],
67 ['Publish (In)', 'publish_in'],
78 ['Publish (Out)', 'publish_out'],
8 ['Deliver', 'deliver'],
9 ['Deliver (manual ack)', 'deliver'],
10 ['Deliver (auto ack)', 'deliver_no_ack'],
11 ['Consumer ack', 'ack'],
912 ['Redelivered', 'redeliver'],
10 ['Acknowledge', 'ack'],
11 ['Get', 'get'], ['Deliver (noack)', 'deliver_no_ack'],
12 ['Get (noack)', 'get_no_ack'],
13 ['Get (manual ack)', 'get'],
14 ['Get (auto ack)', 'get_no_ack'],
1315 ['Return', 'return_unroutable'],
1416 ['Disk read', 'disk_reads'],
1517 ['Disk write', 'disk_writes']];
6666
6767
6868 sammy.get('#/exchanges', function() {
69 renderExchanges()
69 renderExchanges();
7070 });
7171
7272
225225 });
226226
227227 sammy.put('#/logout', function() {
228 // clear a local storage value used by earlier versions
228229 clear_pref('auth');
230 clear_cookie_value('auth');
229231 location.reload();
230232 });
231233
238240 sammy.put('#/column-options', function() {
239241 update_column_options(this);
240242 });
243 sammy.del("#/reset", function(){
244 if(sync_delete(this, '/reset')){
245 update();
246 }
247 });
248 sammy.del("#/reset_node", function(){
249 if(sync_delete(this, '/reset/:node')){
250 update();
251 }
252 });
241253 });
125125 var res = {};
126126 for (var k in obj.arguments) {
127127 if (k in KNOWN_ARGS) {
128 res[k] = obj.arguments[k];
128 res[k] = fmt_escape_html(obj.arguments[k]);
129129 }
130130 else {
131131 if (res.arguments == undefined) res.arguments = {};
132 res.arguments[k] = obj.arguments[k];
132 res.arguments[fmt_escape_html(k)] = fmt_escape_html(obj.arguments[k]);
133133 }
134134 }
135135 if (obj.durable) {
149149 var unsynced = queue.slave_nodes || [];
150150 unsynced = jQuery.grep(unsynced,
151151 function (node, i) {
152 return jQuery.inArray(node, synced) == -1
152 return jQuery.inArray(node, synced) == -1;
153153 });
154154 var res = '';
155155 if (synced.length > 0) {
439439 return 'unknown';
440440 }
441441
442 function fmt_strip_tags(txt) {
443 if(txt === null) {
444 return "";
445 }
446 if(txt === undefined) {
447 return "";
448 }
449 return ("" + txt).replace(/<(?:.|\n)*?>/gm, '');
450 }
451
442452 function fmt_escape_html(txt) {
443453 return fmt_escape_html0(txt).replace(/\n/g, '<br/>');
444454 }
448458 }
449459
450460 function fmt_escape_html0(txt) {
451 return txt.replace(/&/g, '&amp;')
461 if(txt === null) {
462 return "";
463 }
464 if(txt === undefined) {
465 return "";
466 }
467
468 return ("" + txt).replace(/&/g, '&amp;')
452469 .replace(/</g, '&lt;')
453470 .replace(/>/g, '&gt;')
454471 .replace(/\"/g, '&quot;');
695712 var selected = current_filter == '' ? (items_desc(items.length)) :
696713 (items.length + ' of ' + items_desc(total) + ' selected');
697714
698
715
699716 selected += appendselect;
700717
701718 res += '<p id="filter-truncate"><span class="updatable">' + selected +
711728 parseInt(get_pref('truncate')) : current_truncate;
712729 var truncate_input = '<input type="text" id="truncate" value="' +
713730 current_truncate + '">';
714 var selected = '';
731 var selected = '';
715732 if (items.length > current_truncate) {
716733 selected += '<span id="filter-warning-show"> ' +
717734 '(only showing first</span> ';
758775 res += '</input></th></span>' ;
759776
760777 res += '<th> <input type="checkbox" data-page-start="1" class="pagination_class pagination_class_checkbox" id="'+ context +'-filter-regex-mode"' ;
761
762 res += fmt_regex_request(context, "") + '></input> <label for="filter-regex-mode">Regex</label> <span class="help" id="filter-regex">(?)</span></th>' ;
763
778
779 res += fmt_regex_request(context, "") + '></input> <label for="filter-regex-mode">Regex</label> <span class="help" id="filter-regex">(?)</span></th>' ;
780
764781 res +=' </table>' ;
765782 res += '<p id="filter-truncate"><span class="updatable">';
766783 res += '<span><label for="'+ context +'-pagesize"> Displaying ' + pages.item_count + ' item'+ ((pages.item_count > 1) ? 's' : '' ) + ' , page size up to: </label> ';
855872 if (obj.hasOwnProperty(k)) count++;
856873 }
857874 return count;
858 }
875 }
859876
860877 function frm_default_value(template, defaultValue){
861878 var store_value = get_pref(template);
862 var result = (((store_value == null)
863 || (store_value == undefined)
864 || (store_value == '')) ? defaultValue :
879 var result = (((store_value == null)
880 || (store_value == undefined)
881 || (store_value == '')) ? defaultValue :
865882 store_value);
866883
867884 return ((result == undefined) ? defaultValue : result);
868885 }
869886
870887 function fmt_page_number_request(template, defaultPage){
871 if ((defaultPage == undefined) || (defaultPage <= 0))
888 if ((defaultPage == undefined) || (defaultPage <= 0))
872889 defaultPage = 1;
873890 return frm_default_value(template + '_current_page_number', defaultPage);
874891 }
3030
3131 'queue-max-priority':
3232 'Maximum number of priority levels for the queue to support; if not set, the queue will not support message priorities.<br/>(Sets the "<a target="_blank" href="http://rabbitmq.com/priority.html">x-max-priority</a>" argument.)',
33
34 'queue-lazy':
35 'Set the queue into lazy mode, keeping as many messages as possible on disk to reduce RAM usage; if not set, the queue will keep an in-memory cache to deliver messages as fast as possible.<br/>(Sets the "<a target="_blank" href="https://www.rabbitmq.com/lazy-queues.html">x-queue-mode</a>" argument.)',
3336
3437 'queue-messages':
3538 '<p>Message counts.</p><p>Note that "in memory" and "persistent" are not mutually exclusive; persistent messages can be in memory as well as on disc, and transient messages can be paged out if memory is tight. Non-durable queues will consider all messages to be transient.</p>',
209212 <dl>\
210213 <dt>Publish</dt>\
211214 <dd>Rate at which messages are entering the server.</dd>\
212 <dt>Confirm</dt>\
215 <dt>Publisher confirm</dt>\
213216 <dd>Rate at which the server is confirming publishes.</dd>\
214 <dt>Deliver</dt>\
215 <dd>Rate at which messages requiring acknowledgement are being delivered in response to basic.consume.</dd>\
216 <dt>Deliver (noack)</dt>\
217 <dd>Rate at which messages not requiring acknowledgement are being delivered in response to basic.consume.</dd>\
218 <dt>Get</dt>\
219 <dd>Rate at which messages requiring acknowledgement are being delivered in response to basic.get.</dd>\
220 <dt>Get (noack)</dt>\
221 <dd>Rate at which messages not requiring acknowledgement are being delivered in response to basic.get.</dd>\
222 <dt>Acknowledge</dt>\
223 <dd>Rate at which messages are being acknowledged.</dd>\
217 <dt>Deliver (manual ack)</dt>\
218 <dd>Rate at which messages are delivered to consumers that use manual acknowledgements.</dd>\
219 <dt>Deliver (auto ack)</dt>\
220 <dd>Rate at which messages are delivered to consumers that use automatic acknowledgements.</dd>\
221 <dt>Consumer ack</dt>\
222 <dd>Rate at which messages are being acknowledged by consumers.</dd>\
224223 <dt>Redelivered</dt>\
225224 <dd>Rate at which messages with the \'redelivered\' flag set are being delivered. Note that these messages will <b>also</b> be counted in one of the delivery rates above.</dd>\
225 <dt>Get (manual ack)</dt>\
226 <dd>Rate at which messages requiring acknowledgement are being delivered in response to basic.get.</dd>\
227 <dt>Get (auto ack)</dt>\
228 <dd>Rate at which messages not requiring acknowledgement are being delivered in response to basic.get.</dd>\
226229 <dt>Return</dt>\
227230 <dd>Rate at which basic.return is sent to publishers for unroutable messages published with the \'mandatory\' flag set.</dd>\
228231 <dt>Disk read</dt>\
1616 }
1717
1818 function set_auth_pref(userinfo) {
19 // clear a local storage value used by earlier versions
20 clear_local_pref('auth');
21
1922 var b64 = b64_encode_utf8(userinfo);
20 store_pref('auth', encodeURIComponent(b64));
23 var date = new Date();
24 // 8 hours from now
25 date.setHours(date.getHours() + 8);
26 store_cookie_value_with_expiration('auth', encodeURIComponent(b64), date);
2127 }
2228
2329 function login_route () {
5763 this.get(/\#\/login\/(.*)/, login_route_with_path);
5864 });
5965 app.run();
60 if (get_pref('auth') != null) {
66 if (get_cookie_value('auth') != null) {
6167 check_login();
6268 }
6369 }
6571 function check_login() {
6672 user = JSON.parse(sync_get('/whoami'));
6773 if (user == false) {
74 // clear a local storage value used by earlier versions
6875 clear_pref('auth');
76 clear_cookie_value('auth');
6977 replace_content('login-status', '<p>Login failed</p>');
7078 }
7179 else {
259267 var befores = $('#main .updatable');
260268 var afters = $('#scratch .updatable');
261269 if (befores.length != afters.length) {
262 throw("before/after mismatch");
270 console.log("before/after mismatch! Doing a full reload...");
271 full_refresh();
263272 }
264273 for (var i = 0; i < befores.length; i++) {
265274 $(befores[i]).empty().append($(afters[i]).contents());
394403 }
395404
396405 function with_update(fun) {
406 if(outstanding_reqs.length > 0){
407 return false;
408 }
397409 with_reqs(apply_state(current_reqs), [], function(json) {
398410 var html = format(current_template, json);
399411 fun(html);
400412 update_status('ok');
401413 });
414 return true;
402415 }
403416
404417 function apply_state(reqs) {
507520
508521
509522
510 function submit_import(form) {
511 var idx = $("select[name='vhost-upload'] option:selected").index()
512 var vhost = ((idx <=0 ) ? "" : "/" + esc($("select[name='vhost-upload'] option:selected").val()));
513 form.action ="api/definitions" + vhost + '?auth=' + get_pref('auth');
514 form.submit();
515 };
523 function submit_import(form) {
524 if (form.file.value) {
525 var confirm_upload = confirm('Are you sure you want to import a definitions file? Some entities (vhosts, users, queues, etc) may be overwritten!');
526 if (confirm_upload === true) {
527 var idx = $("select[name='vhost-upload'] option:selected").index();
528 var vhost = ((idx <= 0) ? "" : "/" + esc($("select[name='vhost-upload'] option:selected").val()));
529 form.action ="api/definitions" + vhost + '?auth=' + get_cookie_value('auth');
530 form.submit();
531 window.location.replace("../../#/import-succeeded");
532 } else {
533 return false;
534 }
535 } else {
536 return false;
537 }
538 };
516539
517540
518541 function postprocess() {
542 $('form.confirm-queue').submit(function() {
543 return confirm("Are you sure? The queue is going to be deleted. " +
544 "Messages cannot be recovered after deletion.");
545 });
546 $('form.confirm-purge-queue').submit(function() {
547 return confirm("Are you sure? Messages cannot be recovered after purging.");
548 });
519549 $('form.confirm').submit(function() {
520550 return confirm("Are you sure? This object cannot be recovered " +
521551 "after deletion.");
534564 }
535565 });
536566 $('#download-definitions').click(function() {
537 var idx = $("select[name='vhost-download'] option:selected").index()
567 var idx = $("select[name='vhost-download'] option:selected").index();
538568 var vhost = ((idx <=0 ) ? "" : "/" + esc($("select[name='vhost-download'] option:selected").val()));
539569 var path = 'api/definitions' + vhost + '?download=' +
540570 esc($('#download-filename').val()) +
541 '&auth=' + get_pref('auth');
571 '&auth=' + get_cookie_value('auth');
542572 window.location = path;
543573 setTimeout('app.run()');
544574 return false;
603633 var field = $(this).attr('field');
604634 var row = $('#' + field).find('.mf').last();
605635 var key = row.find('input').first();
636 var value = row.find('input').last();
606637 var type = row.find('select').last();
607638 key.val($(this).attr('key'));
639 value.val($(this).attr('value'));
608640 type.val($(this).attr('type'));
609641 update_multifields();
610642 });
632664
633665
634666 function stored_page_info(template, page_start){
635 var pageSize = $('#' + template+'-pagesize').val();
636 var filterName = $('#' + template+'-name').val();
667 var pageSize = fmt_strip_tags($('#' + template+'-pagesize').val());
668 var filterName = fmt_strip_tags($('#' + template+'-name').val());
637669
638670 store_pref(template + '_current_page_number', page_start);
639671 if (filterName != null && filterName != undefined) {
689721
690722
691723 function update_pages_from_ui(sender) {
692 update_pages(current_template, !!$(sender).attr('data-page-start') ? $(sender).attr('data-page-start') : $(sender).val());
724 var val = $(sender).val();
725 var raw = !!$(sender).attr('data-page-start') ? $(sender).attr('data-page-start') : val;
726 var s = fmt_strip_tags(raw);
727 update_pages(current_template, s);
693728 }
694729
695730 function postprocess_partial() {
820855 current_filter_regex = new RegExp(current_filter,'i');
821856 } catch (e) {
822857 jElem.parents('.filter').append('<p class="status-error">' +
823 e.message + '</p>');
858 fmt_escape_html(e.message) + '</p>');
824859 }
825860 }
826861 }
903938 }
904939
905940 function publish_msg(params0) {
906 var params = params_magic(params0);
941 try {
942 var params = params_magic(params0);
943 publish_msg0(params);
944 } catch (e) {
945 show_popup('warn', fmt_escape_html(e));
946 return false;
947 }
948 }
949
950 function publish_msg0(params) {
907951 var path = fill_path_template('/exchanges/:vhost/:name/publish', params);
908952 params['payload_encoding'] = 'string';
909953 params['properties'] = {};
9891033 return tmpl.render(json);
9901034 } catch (err) {
9911035 clearInterval(timer);
992 debug(err['name'] + ": " + err['message']);
1036 console.log("Uncaught error: " + err);
1037 console.log("Stack: " + err['stack']);
1038 debug(err['name'] + ": " + err['message'] + "\n" + err['stack'] + "\n");
9931039 }
9941040 }
9951041
10101056 replace_content('status', html);
10111057 }
10121058
1059 function has_auth_cookie_value() {
1060 return get_cookie_value('auth') != null;
1061 }
1062
10131063 function auth_header() {
1014 return "Basic " + decodeURIComponent(get_pref('auth'));
1064 if(has_auth_cookie_value()) {
1065 return "Basic " + decodeURIComponent(get_cookie_value('auth'));
1066 } else {
1067 return null;
1068 }
10151069 }
10161070
10171071 function with_req(method, path, body, fun) {
1072 if(!has_auth_cookie_value()) {
1073 // navigate to the login form
1074 location.reload();
1075 return;
1076 }
1077
10181078 var json;
10191079 var req = xmlHttpRequest();
10201080 req.open(method, 'api' + path, true );
10591119 params = params_magic(params0);
10601120 path = fill_path_template(path_template, params);
10611121 } catch (e) {
1062 show_popup('warn', e);
1122 show_popup('warn', fmt_escape_html(e));
10631123 return false;
10641124 }
10651125 var req = xmlHttpRequest();
11171177 var error = JSON.parse(req.responseText).error;
11181178 if (typeof(error) != 'string') error = JSON.stringify(error);
11191179
1120 if (error == 'bad_request' || error == 'not_found') {
1121 show_popup('warn', reason);
1180 if (error == 'bad_request' || error == 'not_found' || error == 'not_authorised') {
1181 show_popup('warn', fmt_escape_html(reason));
11221182 } else if (error == 'page_out_of_range') {
11231183 var seconds = 60;
11241184 if (last_page_out_of_range_error > 0)
55 } catch (e) {
66 return false;
77 }
8 }
9
10 function store_cookie_value(k, v) {
11 var d = parse_cookie();
12 d[short_key(k)] = v;
13 store_cookie(d);
14 }
15
16 function store_cookie_value_with_expiration(k, v, expiration_date) {
17 var d = parse_cookie();
18 d[short_key(k)] = v;
19 store_cookie_with_expiration(d, expiration_date);
20 }
21
22 function clear_cookie_value(k) {
23 var d = parse_cookie();
24 delete d[short_key(k)];
25 store_cookie(d);
26 }
27
28 function get_cookie_value(k) {
29 var r;
30 r = parse_cookie()[short_key(k)];
31 return r == undefined ? default_pref(k) : r;
832 }
933
1034 function store_pref(k, v) {
2751 delete d[short_key(k)];
2852 store_cookie(d);
2953 }
54 }
3055
56 function clear_local_pref(k) {
57 if (local_storage_available()) {
58 window.localStorage.removeItem('rabbitmq.' + k);
59 }
3160 }
3261
3362 function get_pref(k) {
94123 }
95124
96125 function store_cookie(dict) {
126 var date = new Date();
127 date.setFullYear(date.getFullYear() + 1);
128 store_cookie_with_expiration(dict, date);
129 }
130
131 function store_cookie_with_expiration(dict, expiration_date) {
97132 var enc = [];
98133 for (var k in dict) {
99134 enc.push(k + ':' + escape(dict[k]));
100135 }
101 var date = new Date();
102 date.setFullYear(date.getFullYear() + 1);
103 document.cookie = 'm=' + enc.join('|') + '; expires=' + date.toUTCString();
136 document.cookie = 'm=' + enc.join('|') + '; expires=' + expiration_date.toUTCString();
104137 }
105138
106139 function get_cookie(key) {
9090 <td>
9191 <select name="vhost">
9292 <% for (var i = 0; i < vhosts.length; i++) { %>
93 <option value="<%= fmt_string(vhosts[i].name) %>"><%= fmt_string(vhosts[i].name) %></option>
93 <option value="<%= fmt_string(vhosts[i].name) %>" <%= (vhosts[i].name === current_vhost) ? 'selected="selected"' : '' %>><%= fmt_string(vhosts[i].name) %></option>
9494 <% } %>
9595 </select>
9696 </td>
297297 [['Context switches', 'context_switches']],
298298 fmt_rate, fmt_rate_axis, true, 'Context switch operations', 'context-switches-operations') %>
299299
300 <div class="box">
301 <h3>Management GC queue length</h3>
302 <table class="facts">
303 <% for(var k in node.metrics_gc_queue_length) {
304 if(node.metrics_gc_queue_length.hasOwnProperty(k)) { %>
305 <tr>
306 <th><%= k %></th>
307 <td><%= node.metrics_gc_queue_length[k] %></td>
308 </tr>
309 <% } } %>
310 </table>
311 </div>
312
300313 <h3>All applications</h3>
301314 <table class="list">
302315 <tr>
2121 <div class="section">
2222 <h2>Totals</h2>
2323 <div class="hider updatable">
24 <% if (overview.statistics_db_node != 'not_running') { %>
25 <%= queue_lengths('lengths-over', overview.queue_totals) %>
24 <%= queue_lengths('lengths-over', overview.queue_totals) %>
2625 <% if (rates_mode != 'none') { %>
2726 <%= message_rates('msg-rates-over', overview.message_stats) %>
28 <% } %>
29 <% } else { %>
30 Totals not available
3127 <% } %>
3228
3329 <% if (overview.object_totals) { %>
107103 <% } %>
108104 <% if (show_column('overview', 'info')) { %>
109105 <th>Info</th>
106 <% } %>
107 <% if (user_administrator) { %>
108 <th>Reset stats DB</th>
110109 <% } %>
111110 <th class="plus-minus"><span class="popup-options-link" title="Click to change columns" type="columns" for="overview">+/-</span></th>
112111 </tr>
198197 <acronym title="Broker definitions are held in RAM. Messages will still be written to disc if necessary.">RAM</acronym>
199198 <% } %>
200199 <%= fmt_plugins_small(node) %>
201 <% if (overview.statistics_db_node == node.name) { %>
202 <acronym title="This node contains the management statistics database">Stats</acronym>
203 <% } %>
204 </td>
200 </td>
201 <% } %>
202 <% if(user_administrator) { %>
203 <td>
204 <form action="#/reset_node" method="delete" class="confirm">
205 <input type="hidden" name="node" value="<%= node.name %>"/>
206 <input type="submit" value="Reset"/>
207 </form>
205208 <% } %>
206209 <% } %>
207210 </tr>
208211 <% } %>
209212 </table>
210213
211 <% if (overview.statistics_db_node == 'not_running') { %>
212 <p class="status-error">Statistics database could not be contacted. Message rates and queue lengths will not be shown.</p>
213 <% } %>
214 <p>
215 <form action="#/reset" method="delete" class="confirm">
216 <input type="submit" value="Reset stats on all nodes"/>
217 </form>
218 </p>
214219
215220 <% if (nodes.length == 1 && nodes[0].os_pid != undefined) { %>
216221 <h3>Paths</h3>
279284 </div>
280285
281286 <div class="section-hidden administrator-only">
282 <h2>Import / export definitions</h2>
287 <h2>Export definitions</h2>
283288 <div class="hider">
284 <form method="post" enctype="multipart/form-data">
285289 <table class="two-col-layout">
286290 <tr>
287291 <td>
288 <h3>Export</h3>
289292 <p>
290293 <label for="download-filename">Filename for download:</label><br/>
291294 <input type="text" id="download-filename" value="<%= fmt_download_filename(overview.node) %>" class="wide" />
292295 </p>
293296 </td>
294297 <td>
295 <h3>Import</h3>
296298 <p>
297 <label>Definitions file:</label><br/>
298 <input type="file" name="file"/>
299 <button id="download-definitions">Download broker definitions</button>
300 <span class="help" id="export-definitions"></span>
299301 </p>
300302 </td>
301303 </tr>
313315 <input type="hidden" name="vhost" value="all"/>
314316 <% } %>
315317 </td>
318 </tr>
319 </table>
320 </div>
321 </div>
322
323 <div class="section-hidden administrator-only">
324 <h2>Import definitions</h2>
325 <div class="hider">
326 <form method="post" enctype="multipart/form-data">
327 <table class="two-col-layout">
328 <tr>
329 <td>
330 <p>
331 <label>Definitions file:</label><br/>
332 <input type="file" name="file"/>
333 </p>
334 </td>
335 <td>
336 <p>
337 <input type="submit" value="Upload broker definitions" onclick="submit_import($(this).closest('form')[0]); return false"/>
338 <span class="help" id="import-definitions"></span>
339 </p>
340 </td>
341 </tr>
342 <tr>
316343 <td>
317344 <% if (vhosts_interesting) { %>
318345 <label>Virtual host:</label>
326353 <% } else { %>
327354 <input type="hidden" name="vhost" value="all"/>
328355 <% } %>
329 </td>
330 </tr>
331 <tr>
332 <td>
333 <p>
334 <button id="download-definitions">Download broker definitions</button>
335 <span class="help" id="export-definitions"></span>
336 </p>
337 </td>
338 <td>
339 <p>
340 <input type="hidden" name="redirect" value="../../#/import-succeeded"/>
341 <input type="submit" value="Upload broker definitions" onclick="submit_import($(this).closest('form')[0])" />
342 <span class="help" id="import-definitions"></span>
343 </p>
344356 </td>
345357 </tr>
346358 </table>
5858 <td>
5959 <select name="vhost">
6060 <% for (var i = 0; i < vhosts.length; i++) { %>
61 <option value="<%= fmt_string(vhosts[i].name) %>"><%= fmt_string(vhosts[i].name) %></option>
61 <option value="<%= fmt_string(vhosts[i].name) %>" <%= (vhosts[i].name === current_vhost) ? 'selected="selected"' : '' %>><%= fmt_string(vhosts[i].name) %></option>
6262 <% } %>
6363 </select>
6464 </td>
116116 <span class="argument-link" field="definition" key="max-length" type="number">Max length</span> |
117117 <span class="argument-link" field="definition" key="max-length-bytes" type="number">Max length bytes</span><br/>
118118 <span class="argument-link" field="definition" key="dead-letter-exchange" type="string">Dead letter exchange</span> |
119 <span class="argument-link" field="definition" key="dead-letter-routing-key" type="string">Dead letter routing key</span>
119 <span class="argument-link" field="definition" key="dead-letter-routing-key" type="string">Dead letter routing key</span> |
120 <span class="argument-link" field="definition" key="queue-mode" type="string" value="lazy">Lazy mode</span>
120121 </td>
121122 </tr>
122123 <tr>
100100 <th class="horizontal">Unacked</th>
101101 <th class="horizontal">In memory</th>
102102 <th class="horizontal">Persistent</th>
103 <th class="horizontal">Transient, Paged Out</th>
103104 </tr>
104105 <tr>
105106 <th>
121122 <td class="r">
122123 <%= fmt_num_thousands(queue.messages_persistent) %>
123124 </td>
125 <td class="r">
126 <%= fmt_num_thousands(queue.messages_paged_out) %>
127 </td>
124128 </tr>
125129 <tr>
126130 <th>
141145 </td>
142146 <td class="r">
143147 <%= fmt_bytes(queue.message_bytes_persistent) %>
148 </td>
149 <td class="r">
150 <%= fmt_bytes(queue.message_bytes_paged_out) %>
144151 </td>
145152 </tr>
146153 <tr>
288295 <% } %>
289296
290297 <div class="section-hidden">
291 <h2>Delete / purge</h2>
292 <div class="hider">
293 <form action="#/queues" method="delete" class="confirm inline-form">
298 <h2>Delete</h2>
299 <div class="hider">
300 <form action="#/queues" method="delete" class="confirm-queue inline-form">
294301 <input type="hidden" name="vhost" value="<%= fmt_string(queue.vhost) %>"/>
295302 <input type="hidden" name="name" value="<%= fmt_string(queue.name) %>"/>
296303 <input type="hidden" name="mode" value="delete"/>
297304 <input type="submit" value="Delete" />
298305 </form>
299
300 <form action="#/queues" method="delete" class="inline-form-right">
306 </div>
307 </div>
308
309 <div class="section-hidden">
310 <h2>Purge</h2>
311 <div class="hider">
312 <form action="#/queues" method="delete" class="confirm-purge-queue inline-form">
301313 <input type="hidden" name="vhost" value="<%= fmt_string(queue.vhost) %>"/>
302314 <input type="hidden" name="name" value="<%= fmt_string(queue.name) %>"/>
303315 <input type="hidden" name="mode" value="purge"/>
192192 <td>
193193 <select name="vhost">
194194 <% for (var i = 0; i < vhosts.length; i++) { %>
195 <option value="<%= fmt_string(vhosts[i].name) %>"><%= fmt_string(vhosts[i].name) %></option>
195 <option value="<%= fmt_string(vhosts[i].name) %>" <%= (vhosts[i].name === current_vhost) ? 'selected="selected"' : '' %>><%= fmt_string(vhosts[i].name) %></option>
196196 <% } %>
197197 </select>
198198 </td>
251251 <span class="argument-link" field="arguments" key="x-max-length-bytes" type="number">Max length bytes</span> <span class="help" id="queue-max-length-bytes"></span><br/>
252252 <span class="argument-link" field="arguments" key="x-dead-letter-exchange" type="string">Dead letter exchange</span> <span class="help" id="queue-dead-letter-exchange"></span> |
253253 <span class="argument-link" field="arguments" key="x-dead-letter-routing-key" type="string">Dead letter routing key</span> <span class="help" id="queue-dead-letter-routing-key"></span> |
254 <span class="argument-link" field="arguments" key="x-max-priority" type="number">Maximum priority</span> <span class="help" id="queue-max-priority"></span>
254 <span class="argument-link" field="arguments" key="x-max-priority" type="number">Maximum priority</span> <span class="help" id="queue-max-priority"></span><br/>
255 <span class="argument-link" field="arguments" key="x-queue-mode" type="string" value="lazy">Lazy mode</span> <span class="help" id="queue-lazy"></span>
255256 </td>
256257 </tr>
257258 </table>
6262 [<span class="tag-link" tag="monitoring">Monitoring</span>]
6363 [<span class="tag-link" tag="policymaker">Policymaker</span>]
6464 [<span class="tag-link" tag="management">Management</span>]
65 [<span class="tag-link" tag="impersonator">Impersonator</span>]
6566 [<span class="tag-link" tag="">None</span>]
6667 </sub>
6768 </td>
2929 </tbody>
3030 </table>
3131 <% } else { %>
32 <p>... no vhosts ...</p>
32 <p>... no users ...</p>
3333 <% } %>
3434 <p><span class="help" id="internal-users-only"></span></p>
3535 </div>
8383 <span class="tag-link" tag="monitoring">Monitoring</span> |
8484 <span class="tag-link" tag="policymaker">Policymaker</span> |
8585 <span class="tag-link" tag="management">Management</span> |
86 <span class="tag-link" tag="impersonator">Impersonator</span> |
8687 <span class="tag-link" tag="">None</span>
8788 </td>
8889 </tr>
22 # before the inclusion of erlang.mk leading to the wrong target becoming
33 # the default.
44 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
526 endif
627
728 # --------------------------------------------------------------------
2041 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
2142 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
2243 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
2345 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
2446 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
2547 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
2648 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
2749 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
2851 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
2952 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
3054 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
3155 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
3256 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
3559 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
3660 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
3761 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
3863 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
3964 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
4065 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
7095
7196 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
7297
73 # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
74 # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
75 # needs to add "ranch" as a BUILD_DEPS. The list of projects needing
76 # this workaround are:
77 # o rabbitmq-web-stomp
78 dep_ranch = git https://github.com/ninenines/ranch 1.2.1
98 # Third-party dependencies version pinning.
99 #
100 # We do that in this file, which is copied in all projects, to ensure
101 # all projects use the same versions. It avoids conflicts and makes it
102 # possible to work with rabbitmq-public-umbrella.
103
104 dep_cowboy_commit = 1.0.4
105 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
106 # Last commit of PropEr supporting Erlang R16B03.
107 dep_proper_commit = 735d972758d8bd85b12483626fe1b66450d6a6fe
108 dep_ranch_commit = 1.3.1
109 dep_webmachine_commit = 1.10.8p2
79110
80111 RABBITMQ_COMPONENTS = amqp_client \
81112 rabbit \
82113 rabbit_common \
83114 rabbitmq_amqp1_0 \
84115 rabbitmq_auth_backend_amqp \
116 rabbitmq_auth_backend_cache \
85117 rabbitmq_auth_backend_http \
86118 rabbitmq_auth_backend_ldap \
87119 rabbitmq_auth_mechanism_ssl \
88120 rabbitmq_boot_steps_visualiser \
89121 rabbitmq_clusterer \
122 rabbitmq_cli \
90123 rabbitmq_codegen \
91124 rabbitmq_consistent_hash_exchange \
125 rabbitmq_ct_client_helpers \
92126 rabbitmq_ct_helpers \
93127 rabbitmq_delayed_message_exchange \
94128 rabbitmq_dotnet_client \
97131 rabbitmq_federation_management \
98132 rabbitmq_java_client \
99133 rabbitmq_jms_client \
134 rabbitmq_jms_cts \
100135 rabbitmq_jms_topic_exchange \
101136 rabbitmq_lvc \
102137 rabbitmq_management \
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_app).
1818 -behaviour(application).
1919 -export([start/2, stop/1, reset_dispatcher/1]).
2020
21 -include("rabbit_mgmt.hrl").
21 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2222 -include_lib("amqp_client/include/amqp_client.hrl").
2323
2424 -define(CONTEXT, rabbit_mgmt).
25 -define(STATIC_PATH, "priv/www").
2625
2726 start(_Type, _StartArgs) ->
2827 {ok, Listener} = application:get_env(rabbitmq_management, listener),
29 setup_wm_logging(),
30 register_context(Listener, []),
28 {ok, _} = register_context(Listener, []),
3129 log_startup(Listener),
3230 rabbit_mgmt_sup_sup:start_link().
3331
4745
4846 register_context(Listener, IgnoreApps) ->
4947 rabbit_web_dispatch:register_context_handler(
50 ?CONTEXT, Listener, "", make_loop(IgnoreApps), "RabbitMQ Management").
48 ?CONTEXT, Listener, "",
49 rabbit_mgmt_dispatcher:build_dispatcher(IgnoreApps),
50 "RabbitMQ Management").
5151
5252 unregister_context() ->
5353 rabbit_web_dispatch:unregister_context(?CONTEXT).
54
55 make_loop(IgnoreApps) ->
56 Dispatch = rabbit_mgmt_dispatcher:build_dispatcher(IgnoreApps),
57 WMLoop = rabbit_webmachine:makeloop(Dispatch),
58 LocalPaths = [filename:join(module_path(M), ?STATIC_PATH) ||
59 M <- rabbit_mgmt_dispatcher:modules(IgnoreApps)],
60 fun(Req) -> respond(Req, LocalPaths, WMLoop) end.
61
62 module_path(Module) ->
63 {file, Here} = code:is_loaded(Module),
64 filename:dirname(filename:dirname(Here)).
65
66 respond(Req, LocalPaths, WMLoop) ->
67 Path = Req:get(path),
68 Redirect = fun(L) -> {301, [{"Location", L}], ""} end,
69 case Path of
70 "/api/" ++ Rest when length(Rest) > 0 ->
71 WMLoop(Req);
72 "" ->
73 Req:respond(Redirect("/"));
74 "/mgmt/" ->
75 Req:respond(Redirect("/"));
76 "/mgmt" ->
77 Req:respond(Redirect("/"));
78 "/" ++ Stripped ->
79 serve_file(Req, Stripped, LocalPaths, Redirect)
80 end.
81
82 serve_file(Req, Path, [LocalPath], _Redirect) ->
83 Req:serve_file(Path, LocalPath);
84 serve_file(Req, Path, [LocalPath | Others], Redirect) ->
85 Path1 = filename:join([LocalPath, Path]),
86 case filelib:is_regular(Path1) of
87 true -> Req:serve_file(Path, LocalPath);
88 false -> case filelib:is_regular(Path1 ++ "/index.html") of
89 true -> index(Req, Path, LocalPath, Redirect);
90 false -> serve_file(Req, Path, Others, Redirect)
91 end
92 end.
93
94 index(Req, Path, LocalPath, Redirect) ->
95 case lists:reverse(Path) of
96 "" -> Req:serve_file("index.html", LocalPath);
97 "/" ++ _ -> Req:serve_file(Path ++ "index.html", LocalPath);
98 _ -> Req:respond(Redirect(Path ++ "/"))
99 end.
100
101 setup_wm_logging() ->
102 rabbit_webmachine:setup(),
103 {ok, LogDir} = application:get_env(rabbitmq_management, http_log_dir),
104 case LogDir of
105 none -> ok;
106 _ -> webmachine_log:add_handler(webmachine_log_handler, [LogDir])
107 end.
10854
10955 log_startup(Listener) ->
11056 rabbit_log:info("Management plugin started. Port: ~w~n", [port(Listener)]).
+0
-125
deps/rabbitmq_management/src/rabbit_mgmt_channel_stats_collector.erl less more
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License at
3 %% http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
7 %% License for the specific language governing rights and limitations
8 %% under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is Pivotal Software, Inc.
13 %% Copyright (c) 2010-2016 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_mgmt_channel_stats_collector).
17
18 -include("rabbit_mgmt.hrl").
19 -include("rabbit_mgmt_metrics.hrl").
20 -include("rabbit_mgmt_event_collector.hrl").
21 -include_lib("rabbit_common/include/rabbit.hrl").
22
23 -behaviour(gen_server2).
24
25 -export([start_link/0]).
26
27 -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
28 code_change/3, handle_pre_hibernate/1]).
29
30 -export([prioritise_cast/3]).
31
32 -import(rabbit_misc, [pget/3]).
33 -import(rabbit_mgmt_db, [pget/2, id_name/1, id/2, lookup_element/2]).
34
35 prioritise_cast({event, #event{type = channel_stats}}, Len,
36 #state{max_backlog = MaxBacklog} = _State)
37 when Len > MaxBacklog ->
38 drop;
39 prioritise_cast(_Msg, _Len, _State) ->
40 0.
41
42 %% See the comment on rabbit_mgmt_db for the explanation of
43 %% events and stats.
44
45 %% Although this gen_server could process all types of events through the
46 %% handle_cast, rabbit_mgmt_db_handler (in the management agent) forwards
47 %% only the non-prioritiy events channel_stats
48 %%----------------------------------------------------------------------------
49 %% API
50 %%----------------------------------------------------------------------------
51
52 start_link() ->
53 case gen_server2:start_link({global, ?MODULE}, ?MODULE, [], []) of
54 {ok, Pid} -> register(?MODULE, Pid), %% [1]
55 {ok, Pid};
56 Else -> Else
57 end.
58 %% [1] For debugging it's helpful to locally register the name too
59 %% since that shows up in places global names don't.
60
61 %%----------------------------------------------------------------------------
62 %% Internal, gen_server2 callbacks
63 %%----------------------------------------------------------------------------
64
65 init([]) ->
66 {ok, Interval} = application:get_env(rabbit, collect_statistics_interval),
67 {ok, RatesMode} = application:get_env(rabbitmq_management, rates_mode),
68 {ok, MaxBacklog} = application:get_env(rabbitmq_management,
69 stats_event_max_backlog),
70 process_flag(priority, high),
71 rabbit_log:info("Statistics channel stats collector started.~n"),
72 {ok, reset_lookups(
73 #state{interval = Interval,
74 rates_mode = RatesMode,
75 max_backlog = MaxBacklog}), hibernate,
76 {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}.
77
78 %% Used in rabbit_mgmt_test_db where we need guarantees events have
79 %% been handled before querying
80 handle_call({event, Event = #event{reference = none}}, _From, State) ->
81 rabbit_mgmt_event_collector_utils:handle_event(Event, State),
82 reply(ok, State);
83
84 handle_call(_Request, _From, State) ->
85 reply(not_understood, State).
86
87 %% Only handle events that are real.
88 handle_cast({event, Event = #event{reference = none}}, State) ->
89 rabbit_mgmt_event_collector_utils:handle_event(Event, State),
90 noreply(State);
91
92 handle_cast({event, Event = #event{reference = Ref}},
93 State = #state{event_refresh_ref = Ref}) ->
94 rabbit_mgmt_event_collector_utils:handle_event(Event, State),
95 noreply(State);
96
97 handle_cast(_Request, State) ->
98 noreply(State).
99
100 handle_info(_Info, State) ->
101 noreply(State).
102
103 terminate(_Arg, _State) ->
104 ok.
105
106 code_change(_OldVsn, State, _Extra) ->
107 {ok, State}.
108
109 reply(Reply, NewState) -> {reply, Reply, NewState, hibernate}.
110 noreply(NewState) -> {noreply, NewState, hibernate}.
111
112 reset_lookups(State) ->
113 State#state{lookups = [{exchange, fun rabbit_exchange:lookup/1},
114 {queue, fun rabbit_amqqueue:lookup/1}]}.
115
116 handle_pre_hibernate(State) ->
117 %% rabbit_event can end up holding on to some memory after a busy
118 %% workout, but it's not a gen_server so we can't make it
119 %% hibernate. The best we can do is forcibly GC it here (if
120 %% rabbit_mgmt_db is hibernating the odds are rabbit_event is
121 %% quiescing in some way too).
122 rpc:multicall(
123 rabbit_mnesia:cluster_nodes(running), rabbit_mgmt_db_handler, gc, []),
124 {hibernate, State}.
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% Useful documentation about CORS:
2121
2222 -export([set_headers/2]).
2323
24 %% We don't set access-control-max-age because we currently have
25 %% no way to know which headers apply to the whole resource. We
26 %% only know for the next request.
2724 set_headers(ReqData, Module) ->
28 ReqData1 = case wrq:get_resp_header("vary", ReqData) of
29 undefined -> wrq:set_resp_header("vary", "origin", ReqData);
30 VaryValue -> wrq:set_resp_header("vary", VaryValue ++ ", origin", ReqData)
31 end,
25 %% Send vary: origin by default if nothing else was set.
26 ReqData1 = cowboy_req:set_resp_header(<<"vary">>, <<"origin">>, ReqData),
3227 case match_origin(ReqData1) of
3328 false ->
3429 ReqData1;
3530 Origin ->
36 ReqData2 = case wrq:method(ReqData1) of
37 'OPTIONS' -> handle_options(ReqData1, Module);
38 _ -> ReqData1
31 ReqData2 = case cowboy_req:method(ReqData1) of
32 {<<"OPTIONS">>, _} -> handle_options(ReqData1, Module);
33 _ -> ReqData1
3934 end,
40 wrq:set_resp_headers([
41 {"access-control-allow-origin", Origin},
42 {"access-control-allow-credentials", "true"}
43 ], ReqData2)
35 ReqData3 = cowboy_req:set_resp_header(<<"access-control-allow-origin">>,
36 Origin,
37 ReqData2),
38 cowboy_req:set_resp_header(<<"access-control-allow-credentials">>,
39 "true",
40 ReqData3)
4441 end.
4542
4643 %% Set max-age from configuration (default: 30 minutes).
4744 %% Set allow-methods from what is defined in Module:allowed_methods/2.
4845 %% Set allow-headers to the same as the request (accept all headers).
49 handle_options(ReqData, Module) ->
46 handle_options(ReqData0, Module) ->
5047 MaxAge = application:get_env(rabbitmq_management, cors_max_age, 1800),
51 {Methods, _, _} = Module:allowed_methods(undefined, undefined),
52 AllowMethods = string:join([atom_to_list(M) || M <- Methods], ", "),
53 ReqHeaders = wrq:get_req_header("access-control-request-headers", ReqData),
54 MaxAgeHd = case MaxAge of
55 undefined -> [];
56 _ -> {"access-control-max-age", integer_to_list(MaxAge)}
48 Methods = case erlang:function_exported(Module, allowed_methods, 2) of
49 false -> [<<"HEAD">>, <<"GET">>, <<"OPTIONS">>];
50 true -> element(1, Module:allowed_methods(undefined, undefined))
5751 end,
58 MaybeAllowHeaders = case ReqHeaders of
59 undefined -> [];
60 _ -> [{"access-control-allow-headers", ReqHeaders}]
52 AllowMethods = string:join([binary_to_list(M) || M <- Methods], ", "),
53 {ReqHeaders, _} = cowboy_req:header(<<"access-control-request-headers">>, ReqData0),
54
55 ReqData1 = case MaxAge of
56 undefined -> ReqData0;
57 _ -> cowboy_req:set_resp_header(<<"access-control-max-age">>,
58 integer_to_list(MaxAge),
59 ReqData0)
6160 end,
62 wrq:set_resp_headers([MaxAgeHd,
63 {"access-control-allow-methods", AllowMethods}
64 |MaybeAllowHeaders], ReqData).
61 ReqData2 = case ReqHeaders of
62 undefined -> ReqData1;
63 _ -> cowboy_req:set_resp_header(<<"access-control-allow-headers">>,
64 ReqHeaders,
65 ReqData0)
66 end,
67 cowboy_req:set_resp_header(<<"access-control-allow-methods">>,
68 AllowMethods,
69 ReqData2).
6570
6671 %% If the origin header is missing or "null", we disable CORS.
6772 %% Otherwise, we only enable it if the origin is found in the
6873 %% cors_allow_origins configuration variable, or if "*" is (it
6974 %% allows all origins).
7075 match_origin(ReqData) ->
71 case wrq:get_req_header("origin", ReqData) of
72 undefined -> false;
73 "null" -> false;
74 Origin ->
76 case cowboy_req:header(<<"origin">>, ReqData) of
77 {undefined, _} -> false;
78 {<<"null">>, _} -> false;
79 {Origin, _} ->
7580 AllowedOrigins = application:get_env(rabbitmq_management,
7681 cors_allow_origins, []),
77 case lists:member(Origin, AllowedOrigins) of
82 case lists:member(binary_to_list(Origin), AllowedOrigins) of
7883 true ->
7984 Origin;
8085 false ->
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_db).
1717
18 -include("rabbit_mgmt.hrl").
19 -include("rabbit_mgmt_metrics.hrl").
18 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
19 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_metrics.hrl").
2020 -include_lib("rabbit_common/include/rabbit.hrl").
21 -include_lib("rabbit_common/include/rabbit_core_metrics.hrl").
2122
2223 -behaviour(gen_server2).
2324
2425 -export([start_link/0]).
25 -export([pget/2, id_name/1, id/2, lookup_element/2]).
2626
2727 -export([augment_exchanges/3, augment_queues/3,
2828 augment_nodes/2, augment_vhosts/2,
3737
3838 -import(rabbit_misc, [pget/3]).
3939
40 %% The management database listens to events broadcast via the
41 %% rabbit_event mechanism, and responds to queries from the various
42 %% rabbit_mgmt_wm_* modules. It handles several kinds of events, and
43 %% slices and dices them in various ways.
44 %%
45 %% There are three types of events coming in: created (when an object
46 %% is created, containing immutable facts about it), stats (emitted on
47 %% a timer, with mutable facts about the object), and deleted (just
48 %% containing the object's ID). In this context "objects" means
49 %% connections, channels, exchanges, queues, consumers, vhosts and
50 %% nodes. Note that we do not care about users, permissions, bindings,
51 %% parameters or policies.
40 -type maybe_slide() :: exometer_slide:slide() | not_found.
41 -type slide_data() :: dict:dict(atom(), {maybe_slide(), maybe_slide()}).
42 -type maybe_range() :: rabbit_mgmt_stats:maybe_range().
43 -type ranges() :: {maybe_range(), maybe_range(), maybe_range(), maybe_range()}.
44 -type mfargs() :: {module(), atom(), [any()]}.
45 -type lookup_key() :: atom() | {atom(), any()}.
46
47 -define(NO_RANGES, {no_range, no_range, no_range, no_range}).
48
49 -define(DEFAULT_TIMEOUT, 30000).
50
51 %% The management database responds to queries from the various
52 %% rabbit_mgmt_wm_* modules. It calls out to all rabbit nodes to fetch
53 %% node-local data and aggregates it before returning it. It uses a worker-
54 %% pool to provide a degree of parallelism.
55 %%
56 %% The management database reads metrics and stats written by the
57 %% rabbit_mgmt_metrics_collector(s).
58 %%
59 %% The metrics collectors (there is one for each stats table - see ?TABLES
60 %% in rabbit_mgmt_metrics.hrl) periodically read their corresponding core
61 %% metrics ETS tables and aggregate the data into the management specific ETS
62 %% tables.
63 %%
64 %% The metric collectors consume two types of core metrics: created (when an
65 %% object is created, containing immutable facts about it) and stats (emitted on
66 %% a timer, with mutable facts about the object). Deleted events are handled
67 %% by the rabbit_mgmt_metrics_gc process.
68 %% In this context "objects" means connections, channels, exchanges, queues,
69 %% consumers, vhosts and nodes. Note that we do not care about users,
70 %% permissions, bindings, parameters or policies.
5271 %%
5372 %% Connections and channels are identified by pids. Queues and
5473 %% exchanges are identified by names (which are #resource{}s). VHosts
5574 %% and nodes are identified by names which are binaries. And consumers
5675 %% are identified by {ChPid, QName, CTag}.
5776 %%
58 %% The management database records the "created" events for
77 %% The management collectors records the "created" metrics for
5978 %% connections, channels and consumers, and can thus be authoritative
6079 %% about those objects. For queues, exchanges and nodes we go to
6180 %% Mnesia to find out the immutable details of the objects.
6281 %%
63 %% For everything other than consumers, the database can then augment
82 %% For everything other than consumers, the collectors can then augment
6483 %% these immutable details with stats, as the object changes. (We
6584 %% never emit anything very interesting about consumers).
6685 %%
67 %% Stats on the inbound side are referred to as coarse- and
86 %% Stats on the inbound side are referred to as coarse and
6887 %% fine-grained. Fine grained statistics are the message rates
6988 %% maintained by channels and associated with tuples: {publishing
7089 %% channel, exchange}, {publishing channel, exchange, queue} and
90109 %% have to have originated as fine grained stats, but can still have
91110 %% been aggregated.
92111 %%
93 %% Created events and basic stats are stored in ETS tables by object.
112 %% Created metrics and basic stats are stored in ETS tables by object.
94113 %% Simple and detailed stats (which only differ depending on how
95114 %% they're keyed) are stored in aggregated stats tables
96115 %% (see rabbit_mgmt_stats.erl and include/rabbit_mgmt_metrics.hrl)
100119 %% for everything that happened before the samples we have kept,
101120 %% and a series of records which add the timestamp as part of the key.
102121 %%
103 %% Each ETS aggregated table has a GC process with a timer to periodically
104 %% aggregate old samples in the base.
105 %%
106 %% We also have old_stats to let us calculate instantaneous
122 %% There is also a GC process to handle the deleted/closed
123 %% rabbit events to remove the corresponding objects from the aggregated
124 %% stats ETS tables.
125 %%
126 %% We also have an old_aggr_stats table to let us calculate instantaneous
107127 %% rates, in order to apportion simple / detailed stats into time
108128 %% slices as they come in. These instantaneous rates are not returned
109129 %% in response to any query, the rates shown in the API are calculated
110 %% at query time. old_stats contains both coarse and fine
130 %% at query time. old_aggr_stats contains both coarse and fine
111131 %% entries. Coarse entries are pruned when the corresponding object is
112132 %% deleted, and fine entries are pruned when the emitting channel is
113133 %% closed, and whenever we receive new fine stats from a channel. So
114134 %% it's quite close to being a cache of "the previous stats we
115135 %% received".
116136 %%
117 %% Overall the object is to do all the aggregation when events come
118 %% in, and make queries be simple lookups as much as possible. One
119 %% area where this does not happen is the global overview - which is
120 %% aggregated from vhost stats at query time since we do not want to
121 %% reveal anything about other vhosts to unprivileged users.
137 %% Overall the object is to do some aggregation when metrics are read
138 %% and only aggregate metrics between nodes at query time.
122139
123140 %%----------------------------------------------------------------------------
124141 %% API
125142 %%----------------------------------------------------------------------------
126143
127144 start_link() ->
128 case gen_server2:start_link({global, ?MODULE}, ?MODULE, [], []) of
129 {ok, Pid} -> register(?MODULE, Pid), %% [1]
130 {ok, Pid};
131 Else -> Else
132 end.
133 %% [1] For debugging it's helpful to locally register the name too
134 %% since that shows up in places global names don't.
135
136 %% R = Ranges, M = Mode
137 augment_exchanges(Xs, R, M) -> safe_call({augment_exchanges, Xs, R, M}, Xs).
138 augment_queues(Qs, R, M) -> safe_call({augment_queues, Qs, R, M}, Qs).
139 augment_vhosts(VHosts, R) -> safe_call({augment_vhosts, VHosts, R}, VHosts).
140 augment_nodes(Nodes, R) -> safe_call({augment_nodes, Nodes, R}, Nodes).
141
142 get_channel(Name, R) -> safe_call({get_channel, Name, R}, not_found).
143 get_connection(Name, R) -> safe_call({get_connection, Name, R}, not_found).
144
145 get_all_channels(R) -> safe_call({get_all_channels, R}).
146 get_all_connections(R) -> safe_call({get_all_connections, R}).
147
148 get_all_consumers() -> safe_call({get_all_consumers, all}).
149 get_all_consumers(V) -> safe_call({get_all_consumers, V}).
150
151 get_overview(User, R) -> safe_call({get_overview, User, R}).
152 get_overview(R) -> safe_call({get_overview, all, R}).
153
154 safe_call(Term) -> safe_call(Term, []).
155 safe_call(Term, Default) -> safe_call(Term, Default, 1).
156
157 %% See rabbit_mgmt_sup_sup for a discussion of the retry logic.
158 safe_call(Term, Default, Retries) ->
159 rabbit_misc:with_exit_handler(
160 fun () ->
161 case Retries of
162 0 -> Default;
163 _ -> rabbit_mgmt_sup_sup:start_child(),
164 safe_call(Term, Default, Retries - 1)
165 end
166 end,
167 fun () -> gen_server2:call({global, ?MODULE}, Term, infinity) end).
145 gen_server2:start_link({local, ?MODULE}, ?MODULE, [], []).
146
147 augment_exchanges(Xs, Ranges, basic) ->
148 submit(fun(Interval) -> list_exchange_stats(Ranges, Xs, Interval) end);
149 augment_exchanges(Xs, Ranges, _) ->
150 submit(fun(Interval) -> detail_exchange_stats(Ranges, Xs, Interval) end).
151
152 %% we can only cache if no ranges are requested.
153 %% The mgmt ui doesn't use ranges for queue listings
154 -spec augment_queues([proplists:proplist()], ranges(), basic | full) -> any().
155 augment_queues(Qs, ?NO_RANGES = Ranges, basic) ->
156 submit_cached(queues,
157 fun(Interval, Queues) ->
158 list_queue_stats(Ranges, Queues, Interval)
159 end, Qs, max(60000, length(Qs) * 2));
160 augment_queues(Qs, Ranges, basic) ->
161 submit(fun(Interval) -> list_queue_stats(Ranges, Qs, Interval) end);
162 augment_queues(Qs, Ranges, _) ->
163 submit(fun(Interval) -> detail_queue_stats(Ranges, Qs, Interval) end).
164
165 augment_vhosts(VHosts, Ranges) ->
166 submit(fun(Interval) -> vhost_stats(Ranges, VHosts, Interval) end).
167
168 augment_nodes(Nodes, Ranges) ->
169 submit(fun(Interval) -> node_stats(Ranges, Nodes, Interval) end).
170
171 get_channel(Name, Ranges) ->
172 submit(fun(Interval) ->
173 case created_stats_delegated(Name, channel_created_stats) of
174 not_found -> not_found;
175 Ch -> [Result] =
176 detail_channel_stats(Ranges, [Ch], Interval),
177 Result
178 end
179 end).
180
181 get_connection(Name, Ranges) ->
182 submit(fun(Interval) ->
183 case created_stats_delegated(Name, connection_created_stats) of
184 not_found -> not_found;
185 C -> [Result] = connection_stats(Ranges, [C], Interval),
186 Result
187 end
188 end).
189
190 get_all_channels(?NO_RANGES = Ranges) ->
191 submit_cached(channels,
192 fun(Interval) ->
193
194 Chans = created_stats_delegated(channel_created_stats),
195 list_channel_stats(Ranges, Chans, Interval)
196 end);
197 get_all_channels(Ranges) ->
198 submit(fun(Interval) ->
199 Chans = created_stats_delegated(channel_created_stats),
200 list_channel_stats(Ranges, Chans, Interval)
201 end).
202
203 get_all_connections(?NO_RANGES = Ranges) ->
204 submit_cached(connections,
205 fun(Interval) ->
206 Chans = created_stats_delegated(connection_created_stats),
207 connection_stats(Ranges, Chans, Interval)
208 end);
209 get_all_connections(Ranges) ->
210 submit(fun(Interval) ->
211 Chans = created_stats_delegated(connection_created_stats),
212 connection_stats(Ranges, Chans, Interval)
213 end).
214
215 get_all_consumers() -> get_all_consumers(all).
216 get_all_consumers(VHosts) ->
217 submit(fun(_Interval) -> consumers_stats(VHosts) end).
218
219 get_overview(Ranges) -> get_overview(all, Ranges).
220 get_overview(User, Ranges) ->
221 submit(fun(Interval) -> overview(User, Ranges, Interval) end).
168222
169223 %%----------------------------------------------------------------------------
170224 %% Internal, gen_server2 callbacks
173227 -record(state, {interval}).
174228
175229 init([]) ->
176 %% When Rabbit is overloaded, it's usually especially important
177 %% that the management plugin work.
178 process_flag(priority, high),
179230 {ok, Interval} = application:get_env(rabbit, collect_statistics_interval),
180231 rabbit_log:info("Statistics database started.~n"),
181232 {ok, #state{interval = Interval}, hibernate,
182233 {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}.
183
184 handle_call({augment_exchanges, Xs, Ranges, basic}, _From,
185 #state{interval = Interval} = State) ->
186 reply(list_exchange_stats(Ranges, Xs, Interval), State);
187
188 handle_call({augment_exchanges, Xs, Ranges, full}, _From,
189 #state{interval = Interval} = State) ->
190 reply(detail_exchange_stats(Ranges, Xs, Interval), State);
191
192 handle_call({augment_queues, Qs, Ranges, basic}, _From,
193 #state{interval = Interval} = State) ->
194 reply(list_queue_stats(Ranges, Qs, Interval), State);
195
196 handle_call({augment_queues, Qs, Ranges, full}, _From,
197 #state{interval = Interval} = State) ->
198 reply(detail_queue_stats(Ranges, Qs, Interval), State);
199
200 handle_call({augment_vhosts, VHosts, Ranges}, _From,
201 #state{interval = Interval} = State) ->
202 reply(vhost_stats(Ranges, VHosts, Interval), State);
203
204 handle_call({augment_nodes, Nodes, Ranges}, _From,
205 #state{interval = Interval} = State) ->
206 {reply, node_stats(Ranges, Nodes, Interval), State};
207
208 handle_call({get_channel, Name, Ranges}, _From,
209 #state{interval = Interval} = State) ->
210 case created_event(Name, channel_stats) of
211 not_found -> reply(not_found, State);
212 Ch -> [Result] = detail_channel_stats(Ranges, [Ch], Interval),
213 reply(Result, State)
214 end;
215
216 handle_call({get_connection, Name, Ranges}, _From,
217 #state{interval = Interval} = State) ->
218 case created_event(Name, connection_stats) of
219 not_found -> reply(not_found, State);
220 Conn -> [Result] = connection_stats(Ranges, [Conn], Interval),
221 reply(Result, State)
222 end;
223
224 handle_call({get_all_channels, Ranges}, _From,
225 #state{interval = Interval} = State) ->
226 Chans = created_events(channel_stats),
227 reply(list_channel_stats(Ranges, Chans, Interval), State);
228
229 handle_call({get_all_connections, Ranges}, _From,
230 #state{interval = Interval} = State) ->
231 Conns = created_events(connection_stats),
232 reply(connection_stats(Ranges, Conns, Interval), State);
233
234 handle_call({get_all_consumers, VHost}, _From, State) ->
235 {reply, [augment_msg_stats(augment_consumer(Obj)) ||
236 Obj <- consumers_by_queue_and_vhost(VHost)], State};
237
238 handle_call({get_overview, User, Ranges}, _From,
239 #state{interval = Interval} = State) ->
240 VHosts = case User of
241 all -> rabbit_vhost:list();
242 _ -> rabbit_mgmt_util:list_visible_vhosts(User)
243 end,
244 %% TODO: there's no reason we can't do an overview of send_oct and
245 %% recv_oct now!
246 MessageStats = [overview_sum(Type, VHosts) ||
247 Type <- [fine_stats, deliver_get, queue_msg_rates]],
248 QueueStats = [overview_sum(queue_msg_counts, VHosts)],
249 F = case User of
250 all -> fun (L) -> length(L) end;
251 _ -> fun (L) -> length(rabbit_mgmt_util:filter_user(L, User)) end
252 end,
253 %% Filtering out the user's consumers would be rather expensive so let's
254 %% just not show it
255 Consumers = case User of
256 all -> [{consumers, ets:info(consumers_by_queue, size)}];
257 _ -> []
258 end,
259 ObjectTotals = Consumers ++
260 [{queues, length([Q || V <- VHosts,
261 Q <- rabbit_amqqueue:list(V)])},
262 {exchanges, length([X || V <- VHosts,
263 X <- rabbit_exchange:list(V)])},
264 {connections, F(created_events(connection_stats))},
265 {channels, F(created_events(channel_stats))}],
266 FormatMessage = format_samples(Ranges, MessageStats, Interval),
267 FormatQueue = format_samples(Ranges, QueueStats, Interval),
268 [rabbit_mgmt_stats:free(S) || {S, _, _} <- MessageStats],
269 [rabbit_mgmt_stats:free(S) || {S, _, _} <- QueueStats],
270 reply([{message_stats, FormatMessage},
271 {queue_totals, FormatQueue},
272 {object_totals, ObjectTotals},
273 {statistics_db_event_queue, event_queue()}],
274 State);
275234
276235 handle_call(_Request, _From, State) ->
277236 reply(not_understood, State).
297256 %% hibernate. The best we can do is forcibly GC it here (if
298257 %% rabbit_mgmt_db is hibernating the odds are rabbit_event is
299258 %% quiescing in some way too).
300 rpc:multicall(
259 _ = rpc:multicall(
301260 rabbit_mnesia:cluster_nodes(running), rabbit_mgmt_db_handler, gc, []),
302261 {hibernate, State}.
303262
331290 id_lookup(Type, List) ->
332291 id(Type, List).
333292
334 lookup_element(Table, Key) -> lookup_element(Table, Key, 2).
335
336 lookup_element(Table, Key, Pos) ->
337 try ets:lookup_element(Table, Key, Pos)
338 catch error:badarg -> []
293
294 %%----------------------------------------------------------------------------
295 %% Internal, querying side api
296 %%----------------------------------------------------------------------------
297
298 overview(User, Ranges, Interval) ->
299 VHosts = case User of
300 all -> rabbit_vhost:list();
301 _ -> rabbit_mgmt_util:list_visible_vhosts(User)
302 end,
303
304 DataLookup = get_data_from_nodes({rabbit_mgmt_data, overview_data,
305 [User, Ranges, VHosts]}),
306
307 MessageStats = lists:append(
308 [format_range(DataLookup, vhost_stats_fine_stats,
309 pick_range(fine_stats, Ranges), Interval),
310 format_range(DataLookup, vhost_msg_rates,
311 pick_range(queue_msg_rates, Ranges), Interval),
312 format_range(DataLookup, vhost_stats_deliver_stats,
313 pick_range(deliver_get, Ranges), Interval)]),
314
315 QueueStats = format_range(DataLookup, vhost_msg_stats,
316 pick_range(queue_msg_counts, Ranges), Interval),
317 %% Filtering out the user's consumers would be rather expensive so let's
318 %% just not show it
319 Consumers = case User of
320 all -> [{consumers, dict:fetch(consumers_count, DataLookup)}];
321 _ -> []
322 end,
323 ObjectTotals = Consumers ++
324 [{queues, length([Q || V <- VHosts, Q <- rabbit_amqqueue:list(V)])},
325 {exchanges, length([X || V <- VHosts, X <- rabbit_exchange:list(V)])},
326 {connections, dict:fetch(connections_count, DataLookup)},
327 {channels, dict:fetch(channels_count, DataLookup)}],
328
329 [{message_stats, MessageStats},
330 {queue_totals, QueueStats},
331 {object_totals, ObjectTotals},
332 {statistics_db_event_queue, event_queue()}]. % TODO: event queue?
333
334 event_queue() ->
335 lists:foldl(fun ({T, _}, Sum) ->
336 case whereis(rabbit_mgmt_metrics_collector:name(T)) of
337 P when is_pid(P) ->
338 {message_queue_len, Len} =
339 erlang:process_info(P, message_queue_len),
340 Sum + Len;
341 _ -> Sum
342 end
343 end, 0, ?CORE_TABLES).
344
345 consumers_stats(VHost) ->
346 Data = get_data_from_nodes({rabbit_mgmt_data, consumer_data, [VHost]}),
347 Consumers = [V || {_,V} <- dict:to_list(Data)],
348 ChPids = [ pget(channel_pid, Con)
349 || Con <- Consumers, [] =:= pget(channel_details, Con)],
350 ChDets = get_channel_detail_lookup(ChPids),
351 [merge_channel_into_obj(Con, ChDets) || Con <- Consumers].
352
353 -spec list_queue_stats(ranges(), [proplists:proplist()], integer()) ->
354 [proplists:proplist()].
355 list_queue_stats(Ranges, Objs, Interval) ->
356 Ids = [id_lookup(queue_stats, Obj) || Obj <- Objs],
357 DataLookup = get_data_from_nodes({rabbit_mgmt_data, all_list_queue_data, [Ids, Ranges]}),
358 adjust_hibernated_memory_use(
359 [begin
360 Id = id_lookup(queue_stats, Obj),
361 Pid = pget(pid, Obj),
362 QueueData = dict:fetch(Id, DataLookup),
363 Props = dict:fetch(queue_stats, QueueData),
364 Stats = queue_stats(QueueData, Ranges, Interval),
365 {Pid, combine(Props, Obj) ++ Stats}
366 end || Obj <- Objs]).
367
368 detail_queue_stats(Ranges, Objs, Interval) ->
369 Ids = [id_lookup(queue_stats, Obj) || Obj <- Objs],
370 DataLookup = get_data_from_nodes({rabbit_mgmt_data, all_detail_queue_data,
371 [Ids, Ranges]}),
372
373 QueueStats = adjust_hibernated_memory_use(
374 [begin
375 Id = id_lookup(queue_stats, Obj),
376 Pid = pget(pid, Obj),
377 QueueData = dict:fetch(Id, DataLookup),
378 Props = dict:fetch(queue_stats, QueueData),
379 Stats = queue_stats(QueueData, Ranges, Interval),
380 Consumers = [{consumer_details, dict:fetch(consumer_stats, QueueData)}],
381 StatsD = [{deliveries,
382 detail_stats(QueueData, channel_queue_stats_deliver_stats,
383 deliver_get, second(Id), Ranges, Interval)},
384 {incoming,
385 detail_stats(QueueData, queue_exchange_stats_publish,
386 fine_stats, first(Id), Ranges, Interval)}],
387 {Pid, combine(Props, Obj) ++ Stats ++ StatsD ++ Consumers}
388 end || Obj <- Objs]),
389
390 % patch up missing channel details
391 ChPids = lists:usort(get_pids_for_missing_channel_details(QueueStats)),
392 ChDets = get_channel_detail_lookup(ChPids),
393 Merged = merge_channel_details(QueueStats, ChDets),
394 Merged.
395
396 detail_stats(Lookup, Table, Type, Id, Ranges, Interval) ->
397 [begin
398 S = format_range(Lookup, {Table, Key}, pick_range(Type, Ranges), Interval),
399 [{stats, S} | format_detail_id(revert(Id, Key))] %TODO: not actually delegated
400 end || {{T, Key}, _} <- dict:to_list(Lookup), T =:= Table].
401
402 queue_stats(QueueData, Ranges, Interval) ->
403 message_stats(format_range(QueueData, queue_stats_publish,
404 pick_range(fine_stats, Ranges), Interval) ++
405 format_range(QueueData, queue_stats_deliver_stats,
406 pick_range(deliver_get, Ranges), Interval)) ++
407 format_range(QueueData, queue_process_stats,
408 pick_range(process_stats, Ranges), Interval) ++
409 format_range(QueueData, queue_msg_stats,
410 pick_range(queue_msg_counts, Ranges), Interval).
411
412 channel_stats(ChannelData, Ranges, Interval) ->
413 message_stats(format_range(ChannelData, channel_stats_fine_stats,
414 pick_range(fine_stats, Ranges), Interval) ++
415 format_range(ChannelData, channel_stats_deliver_stats,
416 pick_range(deliver_get, Ranges), Interval)) ++
417 format_range(ChannelData, channel_process_stats,
418 pick_range(process_stats, Ranges), Interval).
419
420 -spec format_range(slide_data(), lookup_key(), maybe_range(), non_neg_integer()) ->
421 proplists:proplist().
422 format_range(Data, Key, Range0, Interval) ->
423 Table = case Key of
424 {T, _} -> T;
425 T -> T
426 end,
427 InstantRateFun = fun() -> fetch_slides(1, Key, Data) end,
428 SamplesFun = fun() -> fetch_slides(2, Key, Data) end,
429 Now = exometer_slide:timestamp(),
430 rabbit_mgmt_stats:format_range(Range0, Now, Table, Interval, InstantRateFun,
431 SamplesFun).
432
433 fetch_slides(Ele, Key, Data) ->
434 case element(Ele, dict:fetch(Key, Data)) of
435 not_found -> [];
436 Slides when is_list(Slides) ->
437 [S || S <- Slides, not_found =/= S];
438 Slide ->
439 [Slide]
339440 end.
340441
341 %%----------------------------------------------------------------------------
342 %% Internal, querying side
343 %%----------------------------------------------------------------------------
344
345 -define(QUEUE_DETAILS,
346 {queue_stats, [{incoming, queue_exchange_stats, fun first/1},
347 {deliveries, channel_queue_stats, fun second/1}]}).
348
349 -define(EXCHANGE_DETAILS,
350 {exchange_stats, [{incoming, channel_exchange_stats, fun second/1},
351 {outgoing, queue_exchange_stats, fun second/1}]}).
352
353 -define(CHANNEL_DETAILS,
354 {channel_stats, [{publishes, channel_exchange_stats, fun first/1},
355 {deliveries, channel_queue_stats, fun first/1}]}).
356
357 -define(NODE_DETAILS,
358 {node_stats, [{cluster_links, node_node_stats, fun first/1}]}).
359
360 first(Id) ->
361 {Id, '_'}.
362 second(Id) ->
363 {'_', Id}.
364
365 list_queue_stats(Ranges, Objs, Interval) ->
366 adjust_hibernated_memory_use(
367 merge_queue_stats(Objs, queue_funs(Ranges, Interval))).
368
369 detail_queue_stats(Ranges, Objs, Interval) ->
370 adjust_hibernated_memory_use(
371 merge_queue_stats(Objs,
372 [consumer_details_fun(
373 fun (Props) -> id_lookup(queue_stats, Props) end,
374 consumers_by_queue),
375 detail_stats_fun(Ranges, ?QUEUE_DETAILS, Interval)
376 | queue_funs(Ranges, Interval)])).
377
378 queue_funs(Ranges, Interval) ->
379 [basic_stats_fun(queue_stats),
380 simple_stats_fun(Ranges, queue_stats, Interval),
381 augment_queue_msg_stats_fun()].
442 get_channel_detail_lookup(ChPids) ->
443 ChDets = delegate_invoke({rabbit_mgmt_data, augment_channel_pids, [ChPids]}),
444 dict:from_list([{pget(pid, C), C} || [_|_] = C <- lists:append(ChDets)]).
445
446 merge_channel_details(QueueStats, Lookup) ->
447 [begin
448 Cons = pget(consumer_details, QueueStat),
449 Cons1 = [merge_channel_into_obj(Con, Lookup) || Con <- Cons],
450 rabbit_misc:pset(consumer_details, Cons1, QueueStat)
451 end || QueueStat <- QueueStats].
452
453 merge_channel_into_obj(Obj, ChDet) ->
454 case pget(channel_details, Obj) of
455 [] -> case dict:find(pget(channel_pid, Obj), ChDet) of
456 {ok, CHd} ->
457 rabbit_misc:pset(channel_details, CHd, Obj);
458 error ->
459 Obj
460 end;
461 _ -> Obj
462 end.
463
464 get_pids_for_missing_channel_details(QueueStats) ->
465 CDs = lists:append([pget(consumer_details, QueueStat) || QueueStat <- QueueStats]),
466 [ pget(channel_pid, CD) || CD <- CDs, [] =:= pget(channel_details, CD)].
467
382468
383469 list_exchange_stats(Ranges, Objs, Interval) ->
384 merge_stats(Objs, [simple_stats_fun(Ranges, exchange_stats, Interval),
385 augment_msg_stats_fun()]).
470 Ids = [id_lookup(exchange_stats, Obj) || Obj <- Objs],
471 DataLookup = get_data_from_nodes({rabbit_mgmt_data, all_exchange_data, [Ids, Ranges]}),
472 [begin
473 Id = id_lookup(exchange_stats, Obj),
474 ExData = dict:fetch(Id, DataLookup),
475 Stats = message_stats(format_range(ExData, exchange_stats_publish_out,
476 pick_range(fine_stats, Ranges), Interval) ++
477 format_range(ExData, exchange_stats_publish_in,
478 pick_range(deliver_get, Ranges), Interval)),
479 Obj ++ Stats
480 end || Obj <- Objs].
386481
387482 detail_exchange_stats(Ranges, Objs, Interval) ->
388 merge_stats(Objs, [simple_stats_fun(Ranges, exchange_stats, Interval),
389 detail_stats_fun(Ranges, ?EXCHANGE_DETAILS, Interval),
390 augment_msg_stats_fun()]).
483 Ids = [id_lookup(exchange_stats, Obj) || Obj <- Objs],
484 DataLookup = get_data_from_nodes({rabbit_mgmt_data, all_exchange_data, [Ids, Ranges]}),
485 [begin
486 Id = id_lookup(exchange_stats, Obj),
487 ExData = dict:fetch(Id, DataLookup),
488 Stats = message_stats(format_range(ExData, exchange_stats_publish_out,
489 pick_range(fine_stats, Ranges), Interval) ++
490 format_range(ExData, exchange_stats_publish_in,
491 pick_range(deliver_get, Ranges), Interval)),
492 StatsD = [{incoming,
493 detail_stats(ExData, channel_exchange_stats_fine_stats,
494 fine_stats, second(Id), Ranges, Interval)},
495 {outgoing,
496 detail_stats(ExData, queue_exchange_stats_publish,
497 fine_stats, second(Id), Ranges, Interval)}],
498 %% remove live state? not sure it has!
499 Obj ++ StatsD ++ Stats
500 end || Obj <- Objs].
391501
392502 connection_stats(Ranges, Objs, Interval) ->
393 merge_stats(Objs, [basic_stats_fun(connection_stats),
394 simple_stats_fun(Ranges, connection_stats, Interval),
395 augment_msg_stats_fun()]).
503 Ids = [id_lookup(connection_stats, Obj) || Obj <- Objs],
504 DataLookup = get_data_from_nodes({rabbit_mgmt_data, all_connection_data, [Ids, Ranges]}),
505 [begin
506 Id = id_lookup(connection_stats, Obj),
507 ConnData = dict:fetch(Id, DataLookup),
508 Props = dict:fetch(connection_stats, ConnData),
509 Stats = format_range(ConnData, connection_stats_coarse_conn_stats,
510 pick_range(coarse_conn_stats, Ranges), Interval),
511 Details = augment_details(Obj, []), % TODO: not delegated
512 combine(Props, Obj) ++ Details ++ Stats
513 end || Obj <- Objs].
396514
397515 list_channel_stats(Ranges, Objs, Interval) ->
398 merge_stats(Objs, [basic_stats_fun(channel_stats),
399 simple_stats_fun(Ranges, channel_stats, Interval),
400 augment_msg_stats_fun()]).
516 Ids = [id_lookup(channel_stats, Obj) || Obj <- Objs],
517 DataLookup = get_data_from_nodes({rabbit_mgmt_data, all_list_channel_data, [Ids, Ranges]}),
518 ChannelStats =
519 [begin
520 Id = id_lookup(channel_stats, Obj),
521 ChannelData = dict:fetch(Id, DataLookup),
522 Props = dict:fetch(channel_stats, ChannelData),
523 Stats = channel_stats(ChannelData, Ranges, Interval),
524 combine(Props, Obj) ++ Stats
525 end || Obj <- Objs],
526 ChannelStats.
401527
402528 detail_channel_stats(Ranges, Objs, Interval) ->
403 merge_stats(Objs, [basic_stats_fun(channel_stats),
404 simple_stats_fun(Ranges, channel_stats, Interval),
405 consumer_details_fun(
406 fun (Props) -> pget(pid, Props) end,
407 consumers_by_channel),
408 detail_stats_fun(Ranges, ?CHANNEL_DETAILS, Interval),
409 augment_msg_stats_fun()]).
529 Ids = [id_lookup(channel_stats, Obj) || Obj <- Objs],
530 DataLookup = get_data_from_nodes({rabbit_mgmt_data, all_detail_channel_data,
531 [Ids, Ranges]}),
532 ChannelStats =
533 [begin
534 Id = id_lookup(channel_stats, Obj),
535 ChannelData = dict:fetch(Id, DataLookup),
536 Props = dict:fetch(channel_stats, ChannelData),
537 Stats = channel_stats(ChannelData, Ranges, Interval),
538 Consumers = [{consumer_details, dict:fetch(consumer_stats, ChannelData)}],
539 StatsD = [{publishes,
540 detail_stats(ChannelData, channel_exchange_stats_fine_stats,
541 fine_stats, first(Id), Ranges, Interval)},
542 {deliveries,
543 detail_stats(ChannelData, channel_queue_stats_deliver_stats,
544 fine_stats, first(Id), Ranges, Interval)}],
545 combine(Props, Obj) ++ Consumers ++ Stats ++ StatsD
546 end || Obj <- Objs],
547 rabbit_mgmt_format:strip_pids(ChannelStats).
410548
411549 vhost_stats(Ranges, Objs, Interval) ->
412 merge_stats(Objs, [simple_stats_fun(Ranges, vhost_stats, Interval)]).
550 Ids = [id_lookup(vhost_stats, Obj) || Obj <- Objs],
551 DataLookup = get_data_from_nodes({rabbit_mgmt_data, all_vhost_data, [Ids, Ranges]}),
552 [begin
553 Id = id_lookup(vhost_stats, Obj),
554 VData = dict:fetch(Id, DataLookup),
555 Stats = format_range(VData, vhost_stats_coarse_conn_stats,
556 pick_range(coarse_conn_stats, Ranges), Interval) ++
557 format_range(VData, vhost_msg_stats,
558 pick_range(queue_msg_rates, Ranges), Interval),
559 StatsD = message_stats(format_range(VData, vhost_stats_fine_stats,
560 pick_range(fine_stats, Ranges), Interval) ++
561 format_range(VData, vhost_stats_deliver_stats,
562 pick_range(deliver_get, Ranges), Interval)),
563 Details = augment_details(Obj, []),
564 Obj ++ Details ++ Stats ++ StatsD
565 end || Obj <- Objs].
413566
414567 node_stats(Ranges, Objs, Interval) ->
415 merge_stats(Objs, [basic_stats_fun(node_stats),
416 simple_stats_fun(Ranges, node_stats, Interval),
417 detail_and_basic_stats_fun(
418 node_node_stats, Ranges, ?NODE_DETAILS, Interval)]).
419
420 merge_stats(Objs, Funs) ->
421 %% Don't pass the props to the Fun in combine, as it contains the results
422 %% from previous funs and:
423 %% * augment_msg_stats_fun() only needs the original object. Otherwise,
424 %% must fold over a very longs list
425 %% * All other funs only require the Type that is in the original Obj
426 [combine_all_funs(Funs, Obj, Obj) || Obj <- Objs].
427
428 combine_all_funs([Fun | Funs], Obj, Props) ->
429 combine_all_funs(Funs, Obj, combine(Fun(Obj), Props));
430 combine_all_funs([], _Obj, Props) ->
431 Props.
432
433 merge_queue_stats(Objs, Funs) ->
434 %% Don't pass the props to the Fun in combine, as it contains the results
435 %% from previous funs and:
436 %% * augment_msg_stats_fun() only needs the original object. Otherwise,
437 %% must fold over a very longs list
438 %% * All other funs only require the Type that is in the original Obj
568 Ids = [id_lookup(node_stats, Obj) || Obj <- Objs],
569 DataLookup = get_data_from_nodes({rabbit_mgmt_data, all_node_data, [Ids, Ranges]}),
439570 [begin
440 Pid = pget(pid, Obj),
441 {Pid, combine_all_funs(Funs, Obj, rabbit_mgmt_format:strip_queue_pids(Obj))}
571 Id = id_lookup(node_stats, Obj),
572 NData = dict:fetch(Id, DataLookup),
573 Props = dict:fetch(node_stats, NData),
574 Stats = format_range(NData, node_coarse_stats,
575 pick_range(coarse_node_stats, Ranges), Interval) ++
576 format_range(NData, node_persister_stats,
577 pick_range(coarse_node_stats, Ranges), Interval),
578 StatsD = [{cluster_links,
579 detail_stats(NData, node_node_coarse_stats,
580 coarse_node_node_stats, first(Id),
581 Ranges, Interval)}],
582 MgmtStats = dict:fetch(mgmt_stats, NData),
583 Details = augment_details(Obj, []), % augmentation needs to be node local
584 combine(Props, Obj) ++ Details ++ Stats ++ StatsD ++ MgmtStats
442585 end || Obj <- Objs].
443586
444587 combine(New, Old) ->
448591 _ -> lists:keydelete(state, 1, New) ++ Old
449592 end.
450593
451 %% i.e. the non-calculated stats
452 basic_stats_fun(Type) ->
453 fun (Props) ->
454 Id = id_lookup(Type, Props),
455 lookup_element(Type, {Id, stats})
456 end.
457
458 %% i.e. coarse stats, and fine stats aggregated up to a single number per thing
459 simple_stats_fun(Ranges, Type, Interval) ->
460 {Msg, Other} = read_simple_stats(Type),
461 fun (Props) ->
462 Id = id_lookup(Type, Props),
463 OtherStats = format_samples(Ranges, {Id, Other}, Interval),
464 case format_samples(Ranges, {Id, Msg}, Interval) of
465 [] ->
466 OtherStats;
467 MsgStats ->
468 [{message_stats, MsgStats} | OtherStats]
469 end
470 end.
471
472 %% i.e. fine stats that are broken out per sub-thing
473 detail_stats_fun(Ranges, {IdType, FineSpecs}, Interval) ->
474 fun (Props) ->
475 Id = id_lookup(IdType, Props),
476 [detail_stats(Ranges, Name, AggregatedStatsType, IdFun(Id), Interval)
477 || {Name, AggregatedStatsType, IdFun} <- FineSpecs]
478 end.
479
480 %% This does not quite do the same as detail_stats_fun +
481 %% basic_stats_fun; the basic part here assumes compound keys (like
482 %% detail stats) but non-calculated (like basic stats). Currently the
483 %% only user of that is node-node stats.
484 %%
485 %% We also assume that FineSpecs is single length here (at [1]).
486 detail_and_basic_stats_fun(Type, Ranges, {IdType, FineSpecs}, Interval) ->
487 F = detail_stats_fun(Ranges, {IdType, FineSpecs}, Interval),
488 fun (Props) ->
489 Id = id_lookup(IdType, Props),
490 BasicStats = ets:select(Type, [{{{{'$1', '$2'}, '$3'}, '$4', '_'},
491 [{'==', '$1', Id},
492 {'==', '$3', stats}],
493 [{{'$2', '$4'}}]}]),
494 [{K, Items}] = F(Props), %% [1]
495 Items2 = [case lists:keyfind(id_lookup(IdType, Item), 1, BasicStats) of
496 false -> Item;
497 {_, BS} -> BS ++ Item
498 end || Item <- Items],
499 [{K, Items2}]
500 end.
501
502 read_simple_stats(EventType) ->
503 lists:partition(
504 fun({_, Type}) ->
505 lists:member(Type, [fine_stats, deliver_get, queue_msg_rates])
506 end, rabbit_mgmt_stats_tables:aggr_tables(EventType)).
507
508 read_detail_stats(EventType, Id) ->
509 Tables = rabbit_mgmt_stats_tables:aggr_tables(EventType),
510 Keys = [{Table, Type, Key} || {Table, Type} <- Tables,
511 Key <- rabbit_mgmt_stats:get_keys(Table, Id)],
512 lists:foldl(
513 fun ({_Table, _Type, Id0} = Entry, L) ->
514 NewId = revert(Id, Id0),
515 case lists:keyfind(NewId, 1, L) of
516 false ->
517 [{NewId, [Entry]} | L];
518 {NewId, KVs} ->
519 lists:keyreplace(NewId, 1, L, {NewId, [Entry | KVs]})
520 end
521 end, [], Keys).
522
523594 revert({'_', _}, {Id, _}) ->
524595 Id;
525596 revert({_, '_'}, {_, Id}) ->
526597 Id.
527598
528 detail_stats(Ranges, Name, AggregatedStatsType, Id, Interval) ->
529 {Name,
530 [[{stats, format_samples(Ranges, KVs, Interval)} | format_detail_id(G)]
531 || {G, KVs} <- read_detail_stats(AggregatedStatsType, Id)]}.
532
533 format_detail_id(ChPid) when is_pid(ChPid) ->
534 augment_msg_stats([{channel, ChPid}]);
535 format_detail_id(#resource{name = Name, virtual_host = Vhost, kind = Kind}) ->
536 [{Kind, [{name, Name}, {vhost, Vhost}]}];
537 format_detail_id(Node) when is_atom(Node) ->
538 [{name, Node}].
539
540 format_samples(Ranges, {Id, ManyStats}, Interval) ->
541 lists:append(foldl_stats_format(ManyStats, Id, Ranges, Interval, []));
542 format_samples(Ranges, ManyStats, Interval) ->
543 lists:append(foldl_stats_format(ManyStats, Ranges, Interval, [])).
544
545 foldl_stats_format([{Table, Record} | T], Id, Ranges, Interval, Acc) ->
546 foldl_stats_format(T, Id, Ranges, Interval,
547 stats_format(Table, Id, Record, Ranges, Interval, Acc));
548 foldl_stats_format([], _Id, _Ranges, _Interval, Acc) ->
549 Acc.
550
551 foldl_stats_format([{Table, Record, Id} | T], Ranges, Interval, Acc) ->
552 foldl_stats_format(T, Ranges, Interval,
553 stats_format(Table, Id, Record, Ranges, Interval, Acc));
554 foldl_stats_format([], _Ranges, _Interval, Acc) ->
555 Acc.
556
557 stats_format(Table, Id, Record, Ranges, Interval, Acc) ->
558 case rabbit_mgmt_stats:is_blank(Table, Id, Record) of
559 true ->
560 Acc;
561 false ->
562 [rabbit_mgmt_stats:format(pick_range(Record, Ranges),
563 Table, Id, Interval, Record) | Acc]
599 %%----------------------------------------------------------------------------
600 %% Internal, delegated operations
601 %%----------------------------------------------------------------------------
602
603 -spec get_data_from_nodes(mfargs()) -> dict:dict(atom(), any()).
604 get_data_from_nodes(MFA) ->
605 Data = delegate_invoke(MFA),
606 lists:foldl(fun(D, Agg) ->
607 dict:merge(fun merge_data/3, D, Agg)
608 end, dict:new(), Data).
609
610 -spec merge_data(atom(), any(), any()) -> any().
611 merge_data(_, A, B) when is_integer(A), is_integer(B) -> A + B;
612 merge_data(_, [], [_|_] = B) -> B;
613 merge_data(_, [_|_] = A, []) -> A;
614 merge_data(_, [], []) -> [];
615 merge_data(_, {A1, B1}, {[_|_] = A2, [_|_] = B2}) ->
616 {[A1 | A2], [B1 | B2]};
617 merge_data(_, {A1, B1}, {A2, B2}) -> % first slide
618 {[A1, A2], [B1, B2]};
619 merge_data(_, D1, D2) -> % we assume if we get here both values a dicts
620 try
621 dict:merge(fun merge_data/3, D1, D2)
622 catch
623 error:Err ->
624 rabbit_log:debug("merge_data err ~p got: ~p ~p ~n", [Err, D1, D2]),
625 case is_dict(D1) of
626 true -> D1;
627 false -> D2
628 end
629 end.
630
631 %% crummy is_dict function
632 is_dict(D) ->
633 try
634 _ = dict:size(D),
635 true
636 catch
637 error:_ -> false
564638 end.
565
566 pick_range(queue_msg_counts, {RangeL, _RangeM, _RangeD, _RangeN}) ->
567 RangeL;
568 pick_range(K, {_RangeL, RangeM, _RangeD, _RangeN}) when K == fine_stats;
569 K == deliver_get;
570 K == queue_msg_rates ->
571 RangeM;
572 pick_range(K, {_RangeL, _RangeM, RangeD, _RangeN}) when K == coarse_conn_stats;
573 K == process_stats ->
574 RangeD;
575 pick_range(K, {_RangeL, _RangeM, _RangeD, RangeN})
576 when K == coarse_node_stats;
577 K == coarse_node_node_stats ->
578 RangeN.
579639
580640 %% We do this when retrieving the queue record rather than when
581641 %% storing it since the memory use will drop *after* we find out about
590650 MemDict = dict:from_list([{P, M} || {P, M = {memory, _}} <- Mem]),
591651 [case dict:find(Pid, MemDict) of
592652 error -> Q;
593 {ok, Memory} -> [Memory|proplists:delete(memory, Q)]
653 {ok, Memory} -> [Memory | proplists:delete(memory, Q)]
594654 end || {Pid, Q} <- Qs].
595655
596 created_event(Name, Type) ->
597 case ets:select(Type, [{{{'_', '$1'}, '$2', '$3'}, [{'==', 'create', '$1'},
598 {'==', Name, '$3'}],
599 ['$2']}]) of
656 -spec created_stats_delegated(any(), fun((any()) -> any()) | atom()) -> not_found | any().
657 created_stats_delegated(Key, Type) ->
658 Data = delegate_invoke({rabbit_mgmt_data, augmented_created_stats, [Key, Type]}),
659 case [X || X <- Data, X =/= not_found] of
600660 [] -> not_found;
601 [Elem] -> Elem
661 [X] -> X
602662 end.
603663
604 created_events(Type) ->
605 ets:select(Type, [{{{'_', '$1'}, '$2', '_'}, [{'==', 'create', '$1'}],
606 ['$2']}]).
607
608 consumers_by_queue_and_vhost(VHost) ->
609 ets:select(consumers_by_queue,
610 [{{{#resource{virtual_host = '$1', _ = '_'}, '_', '_'}, '$2'},
611 [{'orelse', {'==', 'all', VHost}, {'==', VHost, '$1'}}],
612 ['$2']}]).
613
614 consumer_details_fun(KeyFun, TableName) ->
615 fun ([]) -> [];
616 (Props) -> Pattern = {KeyFun(Props), '_', '_'},
617 [{consumer_details,
618 [augment_msg_stats(augment_consumer(Obj))
619 || Obj <- lists:append(
620 ets:match(TableName, {Pattern, '$1'}))]}]
621 end.
622
623 augment_consumer(Obj) ->
624 [{queue, rabbit_mgmt_format:resource(pget(queue, Obj))} |
625 lists:keydelete(queue, 1, Obj)].
626
627 %%----------------------------------------------------------------------------
628 %% Internal, query-time summing for overview
629 %%----------------------------------------------------------------------------
630
631 overview_sum(Type, VHosts) ->
632 Stats = [{rabbit_mgmt_stats_tables:aggr_table(vhost_stats, Type), VHost}
633 || VHost <- VHosts],
634 {rabbit_mgmt_stats:sum(Stats), Type, all}.
635
636 %%----------------------------------------------------------------------------
637 %% Internal, query-time augmentation
638 %%----------------------------------------------------------------------------
639
640 augment_msg_stats(Props) ->
641 rabbit_mgmt_format:strip_pids(
642 (augment_msg_stats_fun())(Props) ++ Props).
643
644 augment_msg_stats_fun() ->
645 fun(Props) ->
646 augment_details(Props, [])
647 end.
648
649 augment_details([{_, none} | T], Acc) ->
650 augment_details(T, Acc);
651 augment_details([{_, unknown} | T], Acc) ->
652 augment_details(T, Acc);
653 augment_details([{connection, Value} | T], Acc) ->
654 augment_details(T, [{connection_details, augment_connection_pid(Value)} | Acc]);
655 augment_details([{channel, Value} | T], Acc) ->
656 augment_details(T, [{channel_details, augment_channel_pid(Value)} | Acc]);
657 augment_details([{owner_pid, Value} | T], Acc) ->
658 augment_details(T, [{owner_pid_details, augment_connection_pid(Value)} | Acc]);
659 augment_details([_ | T], Acc) ->
660 augment_details(T, Acc);
661 augment_details([], Acc) ->
662 Acc.
663
664 augment_queue_msg_stats_fun() ->
665 fun(Props) ->
666 case lists:keyfind(owner_pid, 1, Props) of
667 {owner_pid, Value} when is_pid(Value) ->
668 [{owner_pid_details, augment_connection_pid(Value)}];
669 _ ->
670 []
671 end
672 end.
673
674 augment_channel_pid(Pid) ->
675 Ch = lookup_element(channel_stats, {Pid, create}),
676 Conn = lookup_element(connection_stats,
677 {pget(connection, Ch), create}),
678 [{name, pget(name, Ch)},
679 {number, pget(number, Ch)},
680 {user, pget(user, Ch)},
681 {connection_name, pget(name, Conn)},
682 {peer_port, pget(peer_port, Conn)},
683 {peer_host, pget(peer_host, Conn)}].
684
685 augment_connection_pid(Pid) ->
686 Conn = lookup_element(connection_stats, {Pid, create}),
687 [{name, pget(name, Conn)},
688 {peer_port, pget(peer_port, Conn)},
689 {peer_host, pget(peer_host, Conn)}].
690
691 event_queue() ->
692 {message_queue_len, Q0} =
693 erlang:process_info(whereis(rabbit_mgmt_event_collector),
694 message_queue_len),
695 {message_queue_len, Q1} =
696 erlang:process_info(whereis(rabbit_mgmt_queue_stats_collector),
697 message_queue_len),
698 {message_queue_len, Q2} =
699 erlang:process_info(whereis(rabbit_mgmt_channel_stats_collector),
700 message_queue_len),
701 Q0 + Q1 + Q2.
664 created_stats_delegated(Type) ->
665 lists:append(
666 delegate_invoke({rabbit_mgmt_data, augmented_created_stats, [Type]})).
667
668 -spec delegate_invoke(mfargs()) -> [any()].
669 delegate_invoke(FunOrMFA) ->
670 MemberPids = [P || P <- pg2:get_members(management_db)],
671 {Results, Errors} = delegate:invoke(MemberPids, ?DELEGATE_PREFIX, FunOrMFA),
672 case Errors of
673 [] -> ok;
674 _ -> rabbit_log:warning("Management delegate query returned errors:~n~p", [Errors])
675 end,
676 [R || {_, R} <- Results].
677
678 submit(Fun) ->
679 {ok, Interval} = application:get_env(rabbit, collect_statistics_interval),
680 worker_pool:submit(management_worker_pool, fun() -> Fun(Interval) end, reuse).
681
682 submit_cached(Key, Fun) ->
683 {ok, Interval} = application:get_env(rabbit, collect_statistics_interval),
684 {ok, Res} = rabbit_mgmt_db_cache:fetch(Key, fun() -> Fun(Interval) end),
685 Res.
686
687 submit_cached(Key, Fun, Arg, Timeout) ->
688 {ok, Interval} = application:get_env(rabbit, collect_statistics_interval),
689 {ok, Res} = rabbit_mgmt_db_cache:fetch(Key,
690 fun(A) -> Fun(Interval, A) end,
691 [Arg], Timeout),
692 Res.
693
694
695 message_stats([]) ->
696 [];
697 message_stats(Stats) ->
698 [{message_stats, Stats}].
699
700 pick_range(Table, Ranges) ->
701 rabbit_mgmt_data:pick_range(Table, Ranges).
702
703 first(Id) ->
704 {Id, '_'}.
705
706 second(Id) ->
707 {'_', Id}.
708
709 augment_details(Obj, Acc) ->
710 rabbit_mgmt_data:augment_details(Obj, Acc).
711
712 format_detail_id(ChPid) when is_pid(ChPid) ->
713 augment_details([{channel, ChPid}], []);
714 format_detail_id(#resource{name = Name, virtual_host = Vhost, kind = Kind}) ->
715 [{Kind, [{name, Name}, {vhost, Vhost}]}];
716 format_detail_id(Node) when is_atom(Node) ->
717 [{name, Node}].
0 %% the contents of this file are subject to the mozilla public license
1 %% version 1.1 (the "license"); you may not use this file except in
2 %% compliance with the license. you may obtain a copy of the license at
3 %% http://www.mozilla.org/mpl/
4 %%
5 %% software distributed under the license is distributed on an "as is"
6 %% basis, without warranty of any kind, either express or implied. see the
7 %% license for the specific language governing rights and limitations
8 %% under the license.
9 %%
10 %% copyright (c) 2016 pivotal software, inc. all rights reserved.
11
12 -module(rabbit_mgmt_db_cache).
13
14 -behaviour(gen_server).
15
16 %% API functions
17 -export([start_link/1]).
18 -export([process_name/1,
19 fetch/2,
20 fetch/3,
21 fetch/4]).
22
23 %% gen_server callbacks
24 -export([init/1,
25 handle_call/3,
26 handle_cast/2,
27 handle_info/2,
28 terminate/2,
29 code_change/3]).
30
31 -record(state, {data :: any() | none,
32 args :: [any()],
33 timer_ref :: undefined | timer:tref(),
34 multiplier :: integer()}).
35
36 -type error_desc() :: key_not_found | timeout | {throw, atom()}.
37 -type fetch_fun() :: fun((_) -> any()) | fun(() -> any()).
38 -type fetch_ret() :: {ok, any()} | {error, error_desc()}.
39
40 -define(DEFAULT_MULT, 5).
41 -define(DEFAULT_TIMEOUT, 60000).
42 -define(CHILD(Key), {rabbit_mgmt_db_cache:process_name(Key),
43 {rabbit_mgmt_db_cache, start_link, [Key]},
44 permanent, 5000, worker,
45 [rabbit_mgmt_db_cache]}).
46 -define(RESET_STATE(State), State#state{data = none, args = []}).
47
48 %% Implements an adaptive cache that times the value generating fun
49 %% and uses the return value as the cached value for the time it took
50 %% to produce multiplied by some factor (defaults to 5).
51 %% There is one cache process per key. New processes are started as
52 %% required. The cache is invalidated if the arguments to the fetch
53 %% fun have changed.
54
55
56 %%%===================================================================
57 %%% API functions
58 %%%===================================================================
59
60 -spec fetch(atom(), fetch_fun()) -> fetch_ret().
61 fetch(Key, FetchFun) ->
62 fetch(Key, FetchFun, []).
63
64 -spec fetch(atom(), fetch_fun(), [any()]) -> fetch_ret().
65 fetch(Key, FetchFun, Args) when is_list(Args) ->
66 fetch(Key, FetchFun, Args, ?DEFAULT_TIMEOUT).
67
68 -spec fetch(atom(), fetch_fun(), [any()], integer()) -> fetch_ret().
69 fetch(Key, FetchFun, FunArgs, Timeout) ->
70 ProcName = process_name(Key),
71 Pid = case whereis(ProcName) of
72 undefined ->
73 {ok, P} = supervisor:start_child(rabbit_mgmt_db_cache_sup,
74 ?CHILD(Key)),
75 P;
76 P -> P
77 end,
78 gen_server:call(Pid, {fetch, FetchFun, FunArgs}, Timeout).
79
80
81 -spec process_name(atom()) -> atom().
82 process_name(Key) ->
83 list_to_atom(atom_to_list(?MODULE) ++ "_" ++ atom_to_list(Key)).
84
85 -spec start_link(atom()) -> {ok, pid()} | ignore | {error, any()}.
86 start_link(Key) ->
87 gen_server:start_link({local, process_name(Key)}, ?MODULE, [], []).
88
89 %%%===================================================================
90 %%% gen_server callbacks
91 %%%===================================================================
92
93 init([]) ->
94 Mult = application:get_env(rabbitmg_management, management_db_cache_multiplier,
95 ?DEFAULT_MULT),
96 {ok, #state{data = none,
97 args = [],
98 multiplier = Mult}}.
99
100 handle_call({fetch, FetchFun, FunArgs}, _From,
101 #state{data = CachedData, args = Args,
102 multiplier = Mult, timer_ref = Ref} = State) when
103 CachedData =:= none orelse Args =/= FunArgs ->
104 _ = timer:cancel(Ref),
105
106 try timer:tc(FetchFun, FunArgs) of
107 {Time, Data} ->
108 case trunc(Time / 1000 * Mult) of
109 0 -> {reply, {ok, Data}, ?RESET_STATE(State)}; % no need to cache that
110 T ->
111 {ok, TimerRef} = timer:send_after(T, self(), purge_cache),
112 {reply, {ok, Data}, State#state{data = Data,
113 timer_ref = TimerRef,
114 args = FunArgs}}
115 end
116 catch
117 Throw -> {reply, {error, {throw, Throw}}, State}
118 end;
119 handle_call({fetch, _FetchFun, _}, _From, #state{data = Data} = State) ->
120 Reply = {ok, Data},
121 {reply, Reply, State};
122 handle_call(purge_cache, _From, State) ->
123 {reply, ok, ?RESET_STATE(State)}.
124
125
126 handle_cast(_Msg, State) ->
127 {noreply, State}.
128
129 handle_info(purge_cache, State) ->
130 {noreply, ?RESET_STATE(State)};
131 handle_info(_Info, State) ->
132 {noreply, State}.
133
134 terminate(_Reason, _State) ->
135 ok.
136
137 code_change(_OldVsn, State, _Extra) ->
138 {ok, State}.
0 %% the contents of this file are subject to the mozilla public license
1 %% version 1.1 (the "license"); you may not use this file except in
2 %% compliance with the license. you may obtain a copy of the license at
3 %% http://www.mozilla.org/mpl/
4 %%
5 %% software distributed under the license is distributed on an "as is"
6 %% basis, without warranty of any kind, either express or implied. see the
7 %% license for the specific language governing rights and limitations
8 %% under the license.
9 %%
10 %% copyright (c) 2016 pivotal software, inc. all rights reserved.
11
12 -module(rabbit_mgmt_db_cache_sup).
13
14 -behaviour(supervisor).
15
16 %% API functions
17 -export([start_link/0]).
18
19 %% Supervisor callbacks
20 -export([init/1]).
21
22 %%%===================================================================
23 %%% API functions
24 %%%===================================================================
25
26 start_link() ->
27 supervisor:start_link({local, ?MODULE}, ?MODULE, []).
28
29 %%%===================================================================
30 %%% Supervisor callbacks
31 %%%===================================================================
32
33 init([]) ->
34 {ok, {{one_for_one, 5, 10}, []}}.
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_dispatcher).
2020 -behaviour(rabbit_mgmt_extension).
2121 -export([dispatcher/0, web_ui/0]).
2222
23 static_path(M) ->
24 filename:join(module_path(M), "priv/www").
25
2326 build_dispatcher(Ignore) ->
24 [{["api" | Path], Mod, Args} ||
25 {Path, Mod, Args} <-
26 lists:append([Module:dispatcher() || Module <- modules(Ignore)])].
27 ThisLocalPath = static_path(?MODULE),
28 LocalPaths = [static_path(M) || M <- modules(Ignore)],
29 cowboy_router:compile([{'_',
30 [{"/api" ++ Path, Mod, Args}
31 || {Path, Mod, Args} <- lists:append([Module:dispatcher()
32 || Module <- modules(Ignore)])]
33 ++ [{"/", cowboy_static, {file, ThisLocalPath ++ "/index.html"}},
34 {"/api", cowboy_static, {file, ThisLocalPath ++ "/api/index.html"}},
35 {"/cli", cowboy_static, {file, ThisLocalPath ++ "/cli/index.html"}},
36 {"/mgmt", rabbit_mgmt_wm_redirect, "/"}]
37 ++ [{"/[...]", rabbit_mgmt_wm_static, LocalPaths}]
38 }]).
2739
2840 modules(IgnoreApps) ->
2941 [Module || {App, Module, Behaviours} <-
3143 not lists:member(App, IgnoreApps),
3244 lists:member(rabbit_mgmt_extension, Behaviours)].
3345
46 module_path(Module) ->
47 {file, Here} = code:is_loaded(Module),
48 filename:dirname(filename:dirname(Here)).
49
3450 %%----------------------------------------------------------------------------
3551
3652 web_ui() -> [{javascript, <<"dispatcher.js">>}].
3753
3854 dispatcher() ->
39 [{["overview"], rabbit_mgmt_wm_overview, []},
40 {["cluster-name"], rabbit_mgmt_wm_cluster_name, []},
41 {["nodes"], rabbit_mgmt_wm_nodes, []},
42 {["nodes", node], rabbit_mgmt_wm_node, []},
43 {["nodes", node, "memory"], rabbit_mgmt_wm_node_memory, [absolute]},
44 {["nodes", node, "memory", "relative"], rabbit_mgmt_wm_node_memory, [relative]},
45 {["nodes", node, "memory", "ets"], rabbit_mgmt_wm_node_memory_ets, [absolute]},
46 {["nodes", node, "memory", "ets", "relative"], rabbit_mgmt_wm_node_memory_ets, [relative]},
47 {["nodes", node, "memory", "ets", filter], rabbit_mgmt_wm_node_memory_ets, [absolute]},
48 {["nodes", node, "memory", "ets", filter, "relative"], rabbit_mgmt_wm_node_memory_ets, [relative]},
49 {["extensions"], rabbit_mgmt_wm_extensions, []},
50 {["all-configuration"], rabbit_mgmt_wm_definitions, []}, %% This was the old name, let's not break things gratuitously.
51 {["definitions"], rabbit_mgmt_wm_definitions, []},
52 {["definitions", vhost], rabbit_mgmt_wm_definitions, []},
53 {["parameters"], rabbit_mgmt_wm_parameters, []},
54 {["parameters", component], rabbit_mgmt_wm_parameters, []},
55 {["parameters", component, vhost], rabbit_mgmt_wm_parameters, []},
56 {["parameters", component, vhost, name], rabbit_mgmt_wm_parameter, []},
57 {["policies"], rabbit_mgmt_wm_policies, []},
58 {["policies", vhost], rabbit_mgmt_wm_policies, []},
59 {["policies", vhost, name], rabbit_mgmt_wm_policy, []},
60 {["connections"], rabbit_mgmt_wm_connections, []},
61 {["connections", connection], rabbit_mgmt_wm_connection, []},
62 {["connections", connection, "channels"], rabbit_mgmt_wm_connection_channels, []},
63 {["channels"], rabbit_mgmt_wm_channels, []},
64 {["channels", channel], rabbit_mgmt_wm_channel, []},
65 {["consumers"], rabbit_mgmt_wm_consumers, []},
66 {["consumers", vhost], rabbit_mgmt_wm_consumers, []},
67 {["exchanges"], rabbit_mgmt_wm_exchanges, []},
68 {["exchanges", vhost], rabbit_mgmt_wm_exchanges, []},
69 {["exchanges", vhost, exchange], rabbit_mgmt_wm_exchange, []},
70 {["exchanges", vhost, exchange, "publish"], rabbit_mgmt_wm_exchange_publish, []},
71 {["exchanges", vhost, exchange, "bindings", "source"], rabbit_mgmt_wm_bindings, [exchange_source]},
72 {["exchanges", vhost, exchange, "bindings", "destination"], rabbit_mgmt_wm_bindings, [exchange_destination]},
73 {["queues"], rabbit_mgmt_wm_queues, []},
74 {["queues", vhost], rabbit_mgmt_wm_queues, []},
75 {["queues", vhost, queue], rabbit_mgmt_wm_queue, []},
76 {["queues", vhost, destination, "bindings"], rabbit_mgmt_wm_bindings, [queue]},
77 {["queues", vhost, queue, "contents"], rabbit_mgmt_wm_queue_purge, []},
78 {["queues", vhost, queue, "get"], rabbit_mgmt_wm_queue_get, []},
79 {["queues", vhost, queue, "actions"], rabbit_mgmt_wm_queue_actions, []},
80 {["bindings"], rabbit_mgmt_wm_bindings, [all]},
81 {["bindings", vhost], rabbit_mgmt_wm_bindings, [all]},
82 {["bindings", vhost, "e", source, dtype, destination], rabbit_mgmt_wm_bindings, [source_destination]},
83 {["bindings", vhost, "e", source, dtype, destination, props], rabbit_mgmt_wm_binding, []},
84 {["vhosts"], rabbit_mgmt_wm_vhosts, []},
85 {["vhosts", vhost], rabbit_mgmt_wm_vhost, []},
86 {["vhosts", vhost, "permissions"], rabbit_mgmt_wm_permissions_vhost, []},
55 [{"/overview", rabbit_mgmt_wm_overview, []},
56 {"/cluster-name", rabbit_mgmt_wm_cluster_name, []},
57 {"/nodes", rabbit_mgmt_wm_nodes, []},
58 {"/nodes/:node", rabbit_mgmt_wm_node, []},
59 {"/nodes/:node/memory", rabbit_mgmt_wm_node_memory, [absolute]},
60 {"/nodes/:node/memory/relative", rabbit_mgmt_wm_node_memory, [relative]},
61 {"/nodes/:node/memory/ets", rabbit_mgmt_wm_node_memory_ets, [absolute]},
62 {"/nodes/:node/memory/ets/relative", rabbit_mgmt_wm_node_memory_ets, [relative]},
63 {"/nodes/:node/memory/ets/:filter", rabbit_mgmt_wm_node_memory_ets, [absolute]},
64 {"/nodes/:node/memory/ets/:filter/relative", rabbit_mgmt_wm_node_memory_ets, [relative]},
65 {"/extensions", rabbit_mgmt_wm_extensions, []},
66 {"/all-configuration", rabbit_mgmt_wm_definitions, []}, %% This was the old name, let's not break things gratuitously.
67 {"/definitions", rabbit_mgmt_wm_definitions, []},
68 {"/definitions/:vhost", rabbit_mgmt_wm_definitions, []},
69 {"/parameters", rabbit_mgmt_wm_parameters, []},
70 {"/parameters/:component", rabbit_mgmt_wm_parameters, []},
71 {"/parameters/:component/:vhost", rabbit_mgmt_wm_parameters, []},
72 {"/parameters/:component/:vhost/:name", rabbit_mgmt_wm_parameter, []},
73 {"/global-parameters", rabbit_mgmt_wm_global_parameters, []},
74 {"/global-parameters/:name", rabbit_mgmt_wm_global_parameter, []},
75 {"/policies", rabbit_mgmt_wm_policies, []},
76 {"/policies/:vhost", rabbit_mgmt_wm_policies, []},
77 {"/policies/:vhost/:name", rabbit_mgmt_wm_policy, []},
78 {"/connections", rabbit_mgmt_wm_connections, []},
79 {"/connections/:connection", rabbit_mgmt_wm_connection, []},
80 {"/connections/:connection/channels", rabbit_mgmt_wm_connection_channels, []},
81 {"/channels", rabbit_mgmt_wm_channels, []},
82 {"/channels/:channel", rabbit_mgmt_wm_channel, []},
83 {"/consumers", rabbit_mgmt_wm_consumers, []},
84 {"/consumers/:vhost", rabbit_mgmt_wm_consumers, []},
85 {"/exchanges", rabbit_mgmt_wm_exchanges, []},
86 {"/exchanges/:vhost", rabbit_mgmt_wm_exchanges, []},
87 {"/exchanges/:vhost/:exchange", rabbit_mgmt_wm_exchange, []},
88 {"/exchanges/:vhost/:exchange/publish", rabbit_mgmt_wm_exchange_publish, []},
89 {"/exchanges/:vhost/:exchange/bindings/source", rabbit_mgmt_wm_bindings, [exchange_source]},
90 {"/exchanges/:vhost/:exchange/bindings/destination", rabbit_mgmt_wm_bindings, [exchange_destination]},
91 {"/queues", rabbit_mgmt_wm_queues, []},
92 {"/queues/:vhost", rabbit_mgmt_wm_queues, []},
93 {"/queues/:vhost/:queue", rabbit_mgmt_wm_queue, []},
94 {"/queues/:vhost/:destination/bindings", rabbit_mgmt_wm_bindings, [queue]},
95 {"/queues/:vhost/:queue/contents", rabbit_mgmt_wm_queue_purge, []},
96 {"/queues/:vhost/:queue/get", rabbit_mgmt_wm_queue_get, []},
97 {"/queues/:vhost/:queue/actions", rabbit_mgmt_wm_queue_actions, []},
98 {"/bindings", rabbit_mgmt_wm_bindings, [all]},
99 {"/bindings/:vhost", rabbit_mgmt_wm_bindings, [all]},
100 {"/bindings/:vhost/e/:source/:dtype/:destination", rabbit_mgmt_wm_bindings, [source_destination]},
101 {"/bindings/:vhost/e/:source/:dtype/:destination/:props", rabbit_mgmt_wm_binding, []},
102 {"/vhosts", rabbit_mgmt_wm_vhosts, []},
103 {"/vhosts/:vhost", rabbit_mgmt_wm_vhost, []},
104 {"/vhosts/:vhost/permissions", rabbit_mgmt_wm_permissions_vhost, []},
87105 %% /connections/:connection is already taken, we cannot use our standard scheme here
88 {["vhosts", vhost, "connections"], rabbit_mgmt_wm_connections_vhost, []},
106 {"/vhosts/:vhost/connections", rabbit_mgmt_wm_connections_vhost, []},
89107 %% /channels/:channel is already taken, we cannot use our standard scheme here
90 {["vhosts", vhost, "channels"], rabbit_mgmt_wm_channels_vhost, []},
91 {["users"], rabbit_mgmt_wm_users, []},
92 {["users", user], rabbit_mgmt_wm_user, []},
93 {["users", user, "permissions"], rabbit_mgmt_wm_permissions_user, []},
94 {["whoami"], rabbit_mgmt_wm_whoami, []},
95 {["permissions"], rabbit_mgmt_wm_permissions, []},
96 {["permissions", vhost, user], rabbit_mgmt_wm_permission, []},
97 {["aliveness-test", vhost], rabbit_mgmt_wm_aliveness_test, []},
98 {["healthchecks", "node"], rabbit_mgmt_wm_healthchecks, []},
99 {["healthchecks", "node", node], rabbit_mgmt_wm_healthchecks, []}
108 {"/vhosts/:vhost/channels", rabbit_mgmt_wm_channels_vhost, []},
109 {"/users", rabbit_mgmt_wm_users, []},
110 {"/users/:user", rabbit_mgmt_wm_user, []},
111 {"/users/:user/permissions", rabbit_mgmt_wm_permissions_user, []},
112 {"/whoami", rabbit_mgmt_wm_whoami, []},
113 {"/permissions", rabbit_mgmt_wm_permissions, []},
114 {"/permissions/:vhost/:user", rabbit_mgmt_wm_permission, []},
115 {"/aliveness-test/:vhost", rabbit_mgmt_wm_aliveness_test, []},
116 {"/healthchecks/node", rabbit_mgmt_wm_healthchecks, []},
117 {"/healthchecks/node/:node", rabbit_mgmt_wm_healthchecks, []},
118 {"/reset", rabbit_mgmt_wm_reset, []},
119 {"/reset/:node", rabbit_mgmt_wm_reset, []}
100120 ].
+0
-165
deps/rabbitmq_management/src/rabbit_mgmt_event_collector.erl less more
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License at
3 %% http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
7 %% License for the specific language governing rights and limitations
8 %% under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is Pivotal Software, Inc.
13 %% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_mgmt_event_collector).
17
18 -include("rabbit_mgmt.hrl").
19 -include("rabbit_mgmt_metrics.hrl").
20 -include("rabbit_mgmt_event_collector.hrl").
21 -include_lib("rabbit_common/include/rabbit.hrl").
22
23 -behaviour(gen_server2).
24
25 -export([start_link/0]).
26
27 -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
28 code_change/3, handle_pre_hibernate/1]).
29
30 %% For testing
31 -export([override_lookups/1, reset_lookups/0]).
32
33 -import(rabbit_mgmt_db, [pget/2]).
34
35 %% See the comment on rabbit_mgmt_db for the explanation of
36 %% events and stats.
37
38 %% Although this gen_server could process all types of events through the
39 %% handle_cast, rabbit_mgmt_db_handler (in the management agent) forwards
40 %% the non-prioritiy events channel_stats and queue_stats to their own gen_servers
41
42 %%----------------------------------------------------------------------------
43 %% API
44 %%----------------------------------------------------------------------------
45
46 start_link() ->
47 Ref = make_ref(),
48 case gen_server2:start_link({global, ?MODULE}, ?MODULE, [Ref], []) of
49 {ok, Pid} -> register(?MODULE, Pid), %% [1]
50 rabbit:force_event_refresh(Ref),
51 {ok, Pid};
52 Else -> Else
53 end.
54 %% [1] For debugging it's helpful to locally register the name too
55 %% since that shows up in places global names don't.
56
57 override_lookups(Lookups) ->
58 gen_server2:call({global, ?MODULE}, {override_lookups, Lookups}, infinity).
59 reset_lookups() ->
60 gen_server2:call({global, ?MODULE}, reset_lookups, infinity).
61
62 %%----------------------------------------------------------------------------
63 %% Internal, gen_server2 callbacks
64 %%----------------------------------------------------------------------------
65
66 init([Ref]) ->
67 %% When Rabbit is overloaded, it's usually especially important
68 %% that the management plugin work.
69 process_flag(priority, high),
70 {ok, Interval} = application:get_env(rabbit, collect_statistics_interval),
71 {ok, RatesMode} = application:get_env(rabbitmq_management, rates_mode),
72 rabbit_node_monitor:subscribe(self()),
73 rabbit_log:info("Statistics event collector started.~n"),
74 ?TABLES = [ets:new(Key, [public, set, named_table]) || Key <- ?TABLES],
75 %% Index for cleaning up stats of abnormally terminated processes.
76 [ets:new(rabbit_mgmt_stats_tables:key_index(Table),
77 [ordered_set, public, named_table]) || Table <- ?PROC_STATS_TABLES],
78 %% Index for the deleting of fine stats, reduces the number of reductions
79 %% to 1/8 under heavy load.
80 ets:new(old_stats_fine_index, [bag, public, named_table]),
81 ?AGGR_TABLES = [rabbit_mgmt_stats:blank(Name) || Name <- ?AGGR_TABLES],
82 {ok, reset_lookups(
83 #state{interval = Interval,
84 event_refresh_ref = Ref,
85 rates_mode = RatesMode}), hibernate,
86 {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}.
87
88 %% Used in rabbit_mgmt_test_db where we need guarantees events have
89 %% been handled before querying
90 handle_call({event, Event = #event{reference = none}}, _From, State) ->
91 rabbit_mgmt_event_collector_utils:handle_event(Event, State),
92 reply(ok, State);
93
94 handle_call({override_lookups, Lookups}, _From, State) ->
95 reply(ok, State#state{lookups = Lookups});
96
97 handle_call(reset_lookups, _From, State) ->
98 reply(ok, reset_lookups(State));
99
100 handle_call(_Request, _From, State) ->
101 reply(not_understood, State).
102
103 %% Only handle events that are real, or pertain to a force-refresh
104 %% that we instigated.
105 handle_cast({event, Event = #event{reference = none}}, State) ->
106 rabbit_mgmt_event_collector_utils:handle_event(Event, State),
107 noreply(State);
108
109 handle_cast({event, Event = #event{reference = Ref}},
110 State = #state{event_refresh_ref = Ref}) ->
111 rabbit_mgmt_event_collector_utils:handle_event(Event, State),
112 noreply(State);
113
114 handle_cast(_Request, State) ->
115 noreply(State).
116
117 handle_info({node_down, Node}, State) ->
118 Conns = created_events(connection_stats),
119 Chs = created_events(channel_stats),
120 delete_all_from_node(connection_closed, Node, Conns, State),
121 delete_all_from_node(channel_closed, Node, Chs, State),
122 noreply(State);
123
124 handle_info(_Info, State) ->
125 noreply(State).
126
127 terminate(_Arg, _State) ->
128 ok.
129
130 code_change(_OldVsn, State, _Extra) ->
131 {ok, State}.
132
133 reply(Reply, NewState) -> {reply, Reply, NewState, hibernate}.
134 noreply(NewState) -> {noreply, NewState, hibernate}.
135
136 reset_lookups(State) ->
137 State#state{lookups = [{exchange, fun rabbit_exchange:lookup/1},
138 {queue, fun rabbit_amqqueue:lookup/1}]}.
139
140 handle_pre_hibernate(State) ->
141 %% rabbit_event can end up holding on to some memory after a busy
142 %% workout, but it's not a gen_server so we can't make it
143 %% hibernate. The best we can do is forcibly GC it here (if
144 %% rabbit_mgmt_db is hibernating the odds are rabbit_event is
145 %% quiescing in some way too).
146 rpc:multicall(
147 rabbit_mnesia:cluster_nodes(running), rabbit_mgmt_db_handler, gc, []),
148 {hibernate, State}.
149
150 delete_all_from_node(Type, Node, [Item | Items], State) ->
151 Pid = pget(pid, Item),
152 case node(Pid) of
153 Node ->
154 rabbit_mgmt_event_collector_utils:handle_event(
155 #event{type = Type, props = [{pid, Pid}]}, State);
156 _ -> ok
157 end,
158 delete_all_from_node(Type, Node, Items, State);
159 delete_all_from_node(_Type, _Node, [], _State) ->
160 ok.
161
162 created_events(Table) ->
163 ets:select(Table, [{{{'_', '$1'}, '$2', '_'}, [{'==', 'create', '$1'}],
164 ['$2']}]).
+0
-551
deps/rabbitmq_management/src/rabbit_mgmt_event_collector_utils.erl less more
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License at
3 %% http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
7 %% License for the specific language governing rights and limitations
8 %% under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is Pivotal Software, Inc.
13 %% Copyright (c) 2010-2016 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_mgmt_event_collector_utils).
17
18 -include("rabbit_mgmt_metrics.hrl").
19 -include("rabbit_mgmt_event_collector.hrl").
20 -include_lib("rabbit_common/include/rabbit.hrl").
21
22 -export([handle_event/2]).
23
24 -import(rabbit_misc, [pget/3]).
25 -import(rabbit_mgmt_db, [pget/2, id_name/1, id/2, lookup_element/2]).
26
27 %%----------------------------------------------------------------------------
28 %% External functions
29 %%----------------------------------------------------------------------------
30
31 %%------------------------------------------------------------------------------ %% @doc Handles events from any collector.
32 %%
33 %% All the gen_server of the collectors have the same internal state record,
34 %% which contains the interval, lookups and rate_mode required
35 %% by this function. Apart from the lookups that can be modified by the
36 %% tests, the rest of the state doesn't change after startup.
37 %%
38 %% Ideally, the gen_server should pass only the required parameters and not the
39 %% full state. However, this simplified the refactor and avoided changing all
40 %% internal functions.
41 %%
42 %% @end
43 %%------------------------------------------------------------------------------
44 -spec handle_event(#event{}, #state{}) -> ok.
45 handle_event(#event{type = queue_stats, props = Stats, timestamp = Timestamp},
46 State) ->
47 handle_stats(queue_stats, Stats, Timestamp,
48 {fun rabbit_mgmt_format:format_queue_stats/1, false},
49 ?QUEUE_MSG_COUNTS, ?QUEUE_MSG_RATES ++ ?PROCESS_STATS, State);
50
51 handle_event(Event = #event{type = queue_deleted,
52 props = [{name, Name}],
53 timestamp = Timestamp},
54 State) ->
55 delete_consumers(Name, consumers_by_queue, consumers_by_channel),
56 %% This is fiddly. Unlike for connections and channels, we need to
57 %% decrease any amalgamated coarse stats for [messages,
58 %% messages_ready, messages_unacknowledged] for this queue - since
59 %% the queue's deletion means we have really got rid of messages!
60 Id = {coarse, {queue_stats, Name}},
61 %% This ceil must correspond to the ceil in append_samples/5
62 TS = ceil(Timestamp, State),
63 OldStats = lookup_element(old_stats, Id),
64 record_sample_list(Id, OldStats, TS, State, ?QUEUE_MSG_COUNTS),
65 delete_samples(channel_queue_stats, {'_', Name}),
66 delete_samples(queue_exchange_stats, {Name, '_'}),
67 delete_samples(queue_stats, Name),
68 handle_deleted(queue_stats, Event);
69
70 handle_event(Event = #event{type = exchange_deleted,
71 props = [{name, Name}]}, _State) ->
72 delete_samples(channel_exchange_stats, {'_', Name}),
73 delete_samples(queue_exchange_stats, {'_', Name}),
74 delete_samples(exchange_stats, Name),
75 handle_deleted(exchange_stats, Event);
76
77 handle_event(#event{type = vhost_deleted,
78 props = [{name, Name}]}, _State) ->
79 delete_samples(vhost_stats, Name);
80
81 handle_event(#event{type = connection_created, props = Stats}, _State) ->
82 handle_created(
83 connection_stats, Stats,
84 {fun rabbit_mgmt_format:format_connection_created/1, true});
85
86 handle_event(#event{type = connection_stats, props = Stats,
87 timestamp = Timestamp},
88 State) ->
89 handle_stats(connection_stats, Stats, Timestamp, {[], false},
90 ?COARSE_CONN_STATS, ?PROCESS_STATS, State);
91
92 handle_event(Event = #event{type = connection_closed,
93 props = [{pid, Pid}]}, _State) ->
94 delete_samples(connection_stats, Pid),
95 handle_deleted(connection_stats, Event);
96
97 handle_event(#event{type = channel_created, props = Stats}, _State) ->
98 handle_created(channel_stats, Stats, {[], false});
99
100 handle_event(#event{type = channel_stats, props = Stats, timestamp = Timestamp},
101 State) ->
102 handle_stats(channel_stats, Stats, Timestamp,
103 {fun rabbit_mgmt_format:format_channel_stats/1, true},
104 [], ?PROCESS_STATS, State),
105 ChPid = id(channel_stats, Stats),
106 AllStats = [old_fine_stats(ChPid, Type, Stats)
107 || Type <- ?FINE_STATS_TYPES],
108 Objs = ets:lookup(old_stats_fine_index, ChPid),
109 ets:delete(old_stats_fine_index, ChPid),
110 [ets:delete(old_stats, Key) || {_, Key} <- Objs],
111 %% This ceil must correspond to the ceil in handle_event
112 %% queue_deleted
113 handle_fine_stats_list(ChPid, ceil(Timestamp, State), State, AllStats);
114
115 handle_event(Event = #event{type = channel_closed,
116 props = [{pid, Pid}]},
117 _State) ->
118 delete_consumers(Pid, consumers_by_channel, consumers_by_queue),
119 delete_samples(channel_queue_stats, {Pid, '_'}),
120 delete_samples(channel_exchange_stats, {Pid, '_'}),
121 delete_samples(channel_stats, Pid),
122 handle_deleted(channel_stats, Event),
123 Objs = ets:lookup(old_stats_fine_index, Pid),
124 ets:delete(old_stats_fine_index, Pid),
125 [ets:delete(old_stats, Key) || {_, Key} <- Objs];
126
127 handle_event(#event{type = consumer_created, props = Props}, _State) ->
128 Fmt = {fun rabbit_mgmt_format:format_arguments/1, true},
129 handle_consumer(fun(Table, Id, P0) ->
130 P = rabbit_mgmt_format:format(P0, Fmt),
131 ets:insert(Table, {Id, P})
132 end,
133 Props);
134
135 handle_event(#event{type = consumer_deleted, props = Props}, _State) ->
136 handle_consumer(fun(Table, Id, _P) -> ets:delete(Table, Id) end,
137 Props);
138
139 %% TODO: we don't clear up after dead nodes here - this is a very tiny
140 %% leak every time a node is permanently removed from the cluster. Do
141 %% we care?
142 handle_event(#event{type = node_stats, props = Stats0, timestamp = Timestamp},
143 State) ->
144 Stats = proplists:delete(persister_stats, Stats0) ++
145 pget(persister_stats, Stats0),
146 handle_stats(node_stats, Stats, Timestamp, {[], false}, ?COARSE_NODE_STATS, State);
147
148 handle_event(#event{type = node_node_stats, props = Stats,
149 timestamp = Timestamp}, State) ->
150 handle_stats(node_node_stats, Stats, Timestamp, {[], false}, ?COARSE_NODE_NODE_STATS,
151 State);
152
153 handle_event(Event = #event{type = node_node_deleted,
154 props = [{route, Route}]}, _State) ->
155 delete_samples(node_node_stats, Route),
156 handle_deleted(node_node_stats, Event);
157
158 handle_event(_Event, _State) ->
159 ok.
160
161 %%----------------------------------------------------------------------------
162 %% Internal functions
163 %%----------------------------------------------------------------------------
164 handle_stats(TName, Stats, Timestamp, Funs, RatesKeys, State) ->
165 handle_stats(TName, Stats, Timestamp, Funs, RatesKeys, [], State).
166
167 handle_stats(TName, Stats, Timestamp, Funs, RatesKeys, NoAggRatesKeys,
168 State) ->
169 Id = id(TName, Stats),
170 IdSamples = {coarse, {TName, Id}},
171 OldStats = lookup_element(old_stats, IdSamples),
172 append_set_of_samples(
173 Stats, Timestamp, OldStats, IdSamples, RatesKeys, NoAggRatesKeys, State),
174 StripKeys = [id_name(TName)] ++ RatesKeys ++ ?FINE_STATS_TYPES,
175 Stats1 = [{K, V} || {K, V} <- Stats, not lists:member(K, StripKeys),
176 V =/= unknown],
177 Stats2 = rabbit_mgmt_format:format(Stats1, Funs),
178 ets:insert(TName, {{Id, stats}, Stats2, Timestamp}),
179 ok.
180
181 fine_stats_id(ChPid, {Q, X}) -> {ChPid, Q, X};
182 fine_stats_id(ChPid, QorX) -> {ChPid, QorX}.
183
184 ceil(TS, #state{interval = Interval}) ->
185 rabbit_mgmt_util:ceil(TS, Interval).
186
187 handle_created(TName, Stats, Funs) ->
188 Formatted = rabbit_mgmt_format:format(Stats, Funs),
189 Id = id(TName, Stats),
190 ets:insert(TName, {{Id, create}, Formatted, pget(name, Stats)}),
191 case lists:member(TName, ?PROC_STATS_TABLES) of
192 true -> ets:insert(rabbit_mgmt_stats_tables:key_index(TName), {Id});
193 false -> true
194 end.
195
196 handle_deleted(TName, #event{props = Props}) ->
197 Id = id(TName, Props),
198 case lists:member(TName, ?TABLES) of
199 true -> ets:delete(TName, {Id, create}),
200 ets:delete(TName, {Id, stats});
201 false -> ok
202 end,
203 ets:delete(old_stats, {coarse, {TName, Id}}),
204 case lists:member(TName, ?PROC_STATS_TABLES) of
205 true -> ets:delete(rabbit_mgmt_stats_tables:key_index(TName), Id);
206 false -> true
207 end.
208
209 handle_consumer(Fun, Props) ->
210 P = rabbit_mgmt_format:format(Props, {[], false}),
211 CTag = pget(consumer_tag, P),
212 Q = pget(queue, P),
213 Ch = pget(channel, P),
214 Fun(consumers_by_queue, {Q, Ch, CTag}, P),
215 Fun(consumers_by_channel, {Ch, Q, CTag}, P).
216
217 %% The consumer_deleted event is emitted by queues themselves -
218 %% therefore in the event that a queue dies suddenly we may not get
219 %% it. The best way to handle this is to make sure we also clean up
220 %% consumers when we hear about any queue going down.
221 delete_consumers(PrimId, PrimTableName, SecTableName) ->
222 SecIdCTags = ets:match(PrimTableName, {{PrimId, '$1', '$2'}, '_'}),
223 ets:match_delete(PrimTableName, {{PrimId, '_', '_'}, '_'}),
224 delete_consumers_entry(PrimId, SecTableName, SecIdCTags).
225
226 delete_consumers_entry(PrimId, SecTableName, [[SecId, CTag] | SecIdTags]) ->
227 ets:delete(SecTableName, {SecId, PrimId, CTag}),
228 delete_consumers_entry(PrimId, SecTableName, SecIdTags);
229 delete_consumers_entry(_PrimId, _SecTableName, []) ->
230 ok.
231
232 old_fine_stats(ChPid, Type, Props) ->
233 case pget(Type, Props) of
234 unknown -> ignore;
235 AllFineStats0 -> [begin
236 Id = fine_stats_id(ChPid, Ids),
237 {{fine, Id}, Stats, lookup_element(old_stats, {fine, Id})}
238 end || {Ids, Stats} <- AllFineStats0]
239 end.
240
241 handle_fine_stats_list(ChPid, Timestamp, State, [AllStatsElem | AllStats]) ->
242 handle_fine_stats(ChPid, Timestamp, AllStatsElem, State),
243 handle_fine_stats_list(ChPid, Timestamp, State, AllStats);
244 handle_fine_stats_list(_ChPid, _Timestamp, _State, []) ->
245 ok.
246
247 handle_fine_stats(_ChPid, _Timestamp, ignore, _State) ->
248 ok;
249 handle_fine_stats(ChPid, Timestamp, [{Id, Stats, OldStats} | AllStats], State) ->
250 Total = lists:sum([V || {K, V} <- Stats, lists:member(K, ?DELIVER_GET)]),
251 Stats1 = case Total of
252 0 -> Stats;
253 _ -> [{deliver_get, Total}|Stats]
254 end,
255 append_all_samples(Timestamp, OldStats, Id, true, State, Stats1),
256 ets:insert(old_stats, {Id, Stats1}),
257 ets:insert(old_stats_fine_index, {ChPid, Id}),
258 handle_fine_stats(ChPid, Timestamp, AllStats, State);
259 handle_fine_stats(_ChPid, _Timestamp, [], _State) ->
260 ok.
261
262 delete_samples(Type, Id0) ->
263 [rabbit_mgmt_stats:delete_stats(Table, Id0)
264 || {Table, _} <- rabbit_mgmt_stats_tables:aggr_tables(Type)].
265
266 append_set_of_samples(Stats, TS, OldStats, Id, Keys, NoAggKeys, State) ->
267 %% Refactored to avoid duplicated calls to ignore_coarse_sample, ceil and
268 %% ets:insert(old_stats ...)
269 case ignore_coarse_sample(Id, State) of
270 false ->
271 %% This ceil must correspond to the ceil in handle_event
272 %% queue_deleted
273 NewMS = ceil(TS, State),
274 append_samples_by_keys(
275 Stats, NewMS, OldStats, Id, Keys, true, State),
276 append_samples_by_keys(
277 Stats, NewMS, OldStats, Id, NoAggKeys, false, State),
278 ets:insert(old_stats, {Id, Stats});
279 true ->
280 ok
281 end.
282
283 append_samples_by_keys(Stats, TS, OldStats, Id, Keys, Agg, State) ->
284 case Keys of
285 all ->
286 append_all_samples(TS, OldStats, Id, Agg, State, Stats);
287 _ ->
288 append_some_samples(TS, OldStats, Id, Agg, State, Stats, Keys)
289 end.
290
291 append_some_samples(NewMS, OldStats, Id, Agg, State, Stats, [K | Keys]) ->
292 V = pget(K, Stats),
293 case V =/= 0 orelse lists:member(K, ?ALWAYS_REPORT_STATS) of
294 true ->
295 append_sample(K, V, NewMS, OldStats, Id, Agg, State);
296 false ->
297 ok
298 end,
299 append_some_samples(NewMS, OldStats, Id, Agg, State, Stats, Keys);
300 append_some_samples(_NewMS, _OldStats, _Id, _Agg, _State, _Stats, []) ->
301 ok.
302
303 append_all_samples(NewMS, OldStats, Id, Agg, State, [{K, 0} | Stats]) ->
304 case lists:member(K, ?ALWAYS_REPORT_STATS) of
305 true ->
306 append_sample(K, 0, NewMS, OldStats, Id, Agg, State);
307 false ->
308 ok
309 end,
310 append_all_samples(NewMS, OldStats, Id, Agg, State, Stats);
311 append_all_samples(NewMS, OldStats, Id, Agg, State, [{K, V} | Stats]) ->
312 append_sample(K, V, NewMS, OldStats, Id, Agg, State),
313 append_all_samples(NewMS, OldStats, Id, Agg, State, Stats);
314 append_all_samples(_NewMS, _OldStats, _Id, _Agg, _State, []) ->
315 ok.
316
317 append_sample(Key, Val, NewMS, OldStats, Id, Agg, State) when is_number(Val) ->
318 OldVal = case pget(Key, OldStats, 0) of
319 N when is_number(N) -> N;
320 _ -> 0
321 end,
322 record_sample(Id, {Key, Val - OldVal, NewMS, State}, Agg, State),
323 ok;
324 append_sample(_Key, _Value, _NewMS, _OldStats, _Id, _Agg, _State) ->
325 ok.
326
327 ignore_coarse_sample({coarse, {queue_stats, Q}}, State) ->
328 not object_exists(Q, State);
329 ignore_coarse_sample(_, _) ->
330 false.
331
332
333 record_sample_list(Id, OldStats, TS, State, [Key | Keys]) ->
334 record_sample(Id, {Key, -pget(Key, OldStats, 0), TS, State}, true, State),
335 record_sample_list(Id, OldStats, TS, State, Keys);
336 record_sample_list(_Id, _OldStats, _TS, _State, []) ->
337 ok.
338
339 %% Node stats do not have a vhost of course
340 record_sample({coarse, {node_stats, _Node} = Id}, Args, true, _State) ->
341 record_sample0(Id, Args);
342
343 record_sample({coarse, {node_node_stats, _Names} = Id}, Args, true, _State) ->
344 record_sample0(Id, Args);
345
346 record_sample({coarse, Id}, Args, false, _State) ->
347 record_sample0(Id, Args);
348
349 record_sample({coarse, Id}, Args, true, _State) ->
350 record_sample0(Id, Args),
351 record_sample0({vhost_stats, vhost(Id)}, Args);
352
353 %% Deliveries / acks (Q -> Ch)
354 record_sample({fine, {Ch, Q = #resource{kind = queue}}}, Args, true, State) ->
355 case object_exists(Q, State) of
356 true -> record_sample0({channel_queue_stats, {Ch, Q}}, Args),
357 record_sample0({queue_stats, Q}, Args);
358 false -> ok
359 end,
360 record_sample0({channel_stats, Ch}, Args),
361 record_sample0({vhost_stats, vhost(Q)}, Args);
362
363 %% Publishes / confirms (Ch -> X)
364 record_sample({fine, {Ch, X = #resource{kind = exchange}}}, Args, true,State) ->
365 case object_exists(X, State) of
366 true -> record_sample0({channel_exchange_stats, {Ch, X}}, Args),
367 record_sampleX(publish_in, X, Args);
368 false -> ok
369 end,
370 record_sample0({channel_stats, Ch}, Args),
371 record_sample0({vhost_stats, vhost(X)}, Args);
372
373 %% Publishes (but not confirms) (Ch -> X -> Q)
374 record_sample({fine, {_Ch,
375 Q = #resource{kind = queue},
376 X = #resource{kind = exchange}}}, Args, true, State) ->
377 %% TODO This one logically feels like it should be here. It would
378 %% correspond to "publishing channel message rates to queue" -
379 %% which would be nice to handle - except we don't. And just
380 %% uncommenting this means it gets merged in with "consuming
381 %% channel delivery from queue" - which is not very helpful.
382 %% record_sample0({channel_queue_stats, {Ch, Q}}, Args),
383 QExists = object_exists(Q, State),
384 XExists = object_exists(X, State),
385 case QExists of
386 true -> record_sample0({queue_stats, Q}, Args);
387 false -> ok
388 end,
389 case QExists andalso XExists of
390 true -> record_sample0({queue_exchange_stats, {Q, X}}, Args);
391 false -> ok
392 end,
393 case XExists of
394 true -> record_sampleX(publish_out, X, Args);
395 false -> ok
396 end.
397
398 %% We have to check the queue and exchange objects still exist since
399 %% their deleted event could be overtaken by a channel stats event
400 %% which contains fine stats referencing them. That's also why we
401 %% don't need to check the channels exist - their deleted event can't
402 %% be overtaken by their own last stats event.
403 %%
404 %% Also, sometimes the queue_deleted event is not emitted by the queue
405 %% (in the nodedown case) - so it can overtake the final queue_stats
406 %% event (which is not *guaranteed* to be lost). So we make a similar
407 %% check for coarse queue stats.
408 %%
409 %% We can be sure that mnesia will be up to date by the time we receive
410 %% the event (even though we dirty read) since the deletions are
411 %% synchronous and we do not emit the deleted event until after the
412 %% deletion has occurred.
413 object_exists(Name = #resource{kind = Kind}, #state{lookups = Lookups}) ->
414 case (pget(Kind, Lookups))(Name) of
415 {ok, _} -> true;
416 _ -> false
417 end.
418
419 vhost(#resource{virtual_host = VHost}) ->
420 VHost;
421 vhost({queue_stats, #resource{virtual_host = VHost}}) ->
422 VHost;
423 vhost({TName, Pid}) ->
424 pget(vhost, lookup_element(TName, {Pid, create})).
425
426 %% exchanges have two sets of "publish" stats, so rearrange things a touch
427 record_sampleX(RenamePublishTo, X, {publish, Diff, TS, State}) ->
428 record_sample0({exchange_stats, X}, {RenamePublishTo, Diff, TS, State});
429 record_sampleX(_RenamePublishTo, X, {Type, Diff, TS, State}) ->
430 record_sample0({exchange_stats, X}, {Type, Diff, TS, State}).
431
432 %% Ignore case where ID1 and ID2 are in a tuple, i.e. detailed stats,
433 %% when in basic mode
434 record_sample0({Type, {_ID1, _ID2}}, {_, _, _, #state{rates_mode = basic}})
435 when Type =/= node_node_stats ->
436 ok;
437 record_sample0({Type, Id0}, {Key0, Diff, TS, #state{}}) ->
438 {Key, Pos} = stat_type(Key0),
439 Id = {Id0, TS},
440 rabbit_mgmt_stats:record(Id, Pos, Diff, Key,
441 rabbit_mgmt_stats_tables:aggr_table(Type, Key)).
442
443 %%------------------------------------------------------------------------------
444 %% @hidden
445 %% @doc Returns the type of the stat and the position in the tuple
446 %%
447 %% Uses the record definitions for simplicity, keeping track of the positions in
448 %% the tuple.
449 %% @end
450 %%------------------------------------------------------------------------------
451 stat_type(deliver) ->
452 {deliver_get, #deliver_get.deliver};
453 stat_type(deliver_no_ack) ->
454 {deliver_get, #deliver_get.deliver_no_ack};
455 stat_type(get) ->
456 {deliver_get, #deliver_get.get};
457 stat_type(get_no_ack) ->
458 {deliver_get, #deliver_get.get_no_ack};
459 stat_type(publish) ->
460 {fine_stats, #fine_stats.publish};
461 stat_type(publish_in) ->
462 {fine_stats, #fine_stats.publish_in};
463 stat_type(publish_out) ->
464 {fine_stats, #fine_stats.publish_out};
465 stat_type(ack) ->
466 {fine_stats, #fine_stats.ack};
467 stat_type(deliver_get) ->
468 {fine_stats, #fine_stats.deliver_get};
469 stat_type(confirm) ->
470 {fine_stats, #fine_stats.confirm};
471 stat_type(return_unroutable) ->
472 {fine_stats, #fine_stats.return_unroutable};
473 stat_type(redeliver) ->
474 {fine_stats, #fine_stats.redeliver};
475 stat_type(disk_reads) ->
476 {queue_msg_rates, #queue_msg_rates.disk_reads};
477 stat_type(disk_writes) ->
478 {queue_msg_rates, #queue_msg_rates.disk_writes};
479 stat_type(messages) ->
480 {queue_msg_counts, #queue_msg_counts.messages};
481 stat_type(messages_ready) ->
482 {queue_msg_counts, #queue_msg_counts.messages_ready};
483 stat_type(messages_unacknowledged) ->
484 {queue_msg_counts, #queue_msg_counts.messages_unacknowledged};
485 stat_type(mem_used) ->
486 {coarse_node_stats, #coarse_node_stats.mem_used};
487 stat_type(fd_used) ->
488 {coarse_node_stats, #coarse_node_stats.fd_used};
489 stat_type(sockets_used) ->
490 {coarse_node_stats, #coarse_node_stats.sockets_used};
491 stat_type(proc_used) ->
492 {coarse_node_stats, #coarse_node_stats.proc_used};
493 stat_type(disk_free) ->
494 {coarse_node_stats, #coarse_node_stats.disk_free};
495 stat_type(io_read_count) ->
496 {coarse_node_stats, #coarse_node_stats.io_read_count};
497 stat_type(io_read_bytes) ->
498 {coarse_node_stats, #coarse_node_stats.io_read_bytes};
499 stat_type(io_read_time) ->
500 {coarse_node_stats, #coarse_node_stats.io_read_time};
501 stat_type(io_write_count) ->
502 {coarse_node_stats, #coarse_node_stats.io_write_count};
503 stat_type(io_write_bytes) ->
504 {coarse_node_stats, #coarse_node_stats.io_write_bytes};
505 stat_type(io_write_time) ->
506 {coarse_node_stats, #coarse_node_stats.io_write_time};
507 stat_type(io_sync_count) ->
508 {coarse_node_stats, #coarse_node_stats.io_sync_count};
509 stat_type(io_sync_time) ->
510 {coarse_node_stats, #coarse_node_stats.io_sync_time};
511 stat_type(io_seek_count) ->
512 {coarse_node_stats, #coarse_node_stats.io_seek_count};
513 stat_type(io_seek_time) ->
514 {coarse_node_stats, #coarse_node_stats.io_seek_time};
515 stat_type(io_reopen_count) ->
516 {coarse_node_stats, #coarse_node_stats.io_reopen_count};
517 stat_type(mnesia_ram_tx_count) ->
518 {coarse_node_stats, #coarse_node_stats.mnesia_ram_tx_count};
519 stat_type(mnesia_disk_tx_count) ->
520 {coarse_node_stats, #coarse_node_stats.mnesia_disk_tx_count};
521 stat_type(msg_store_read_count) ->
522 {coarse_node_stats, #coarse_node_stats.msg_store_read_count};
523 stat_type(msg_store_write_count) ->
524 {coarse_node_stats, #coarse_node_stats.msg_store_write_count};
525 stat_type(queue_index_journal_write_count) ->
526 {coarse_node_stats, #coarse_node_stats.queue_index_journal_write_count};
527 stat_type(queue_index_write_count) ->
528 {coarse_node_stats, #coarse_node_stats.queue_index_write_count};
529 stat_type(queue_index_read_count) ->
530 {coarse_node_stats, #coarse_node_stats.queue_index_read_count};
531 stat_type(gc_num) ->
532 {coarse_node_stats, #coarse_node_stats.gc_num};
533 stat_type(gc_bytes_reclaimed) ->
534 {coarse_node_stats, #coarse_node_stats.gc_bytes_reclaimed};
535 stat_type(context_switches) ->
536 {coarse_node_stats, #coarse_node_stats.context_switches};
537 stat_type(send_bytes) ->
538 {coarse_node_node_stats, #coarse_node_node_stats.send_bytes};
539 stat_type(recv_bytes) ->
540 {coarse_node_node_stats, #coarse_node_node_stats.recv_bytes};
541 stat_type(recv_oct) ->
542 {coarse_conn_stats, #coarse_conn_stats.recv_oct};
543 stat_type(send_oct) ->
544 {coarse_conn_stats, #coarse_conn_stats.send_oct};
545 stat_type(reductions) ->
546 {process_stats, #process_stats.reductions};
547 stat_type(io_file_handle_open_attempt_count) ->
548 {coarse_node_stats, #coarse_node_stats.io_file_handle_open_attempt_count};
549 stat_type(io_file_handle_open_attempt_time) ->
550 {coarse_node_stats, #coarse_node_stats.io_file_handle_open_attempt_time}.
1515
1616 -module(rabbit_mgmt_extension).
1717
18 -export([behaviour_info/1]).
18 %% Return a Cowboy dispatcher table to integrate
19 -callback dispatcher() -> [{string(), atom(), [atom()]}].
1920
20 behaviour_info(callbacks) ->
21 [
22 %% Return a webmachine dispatcher table to integrate
23 {dispatcher, 0},
24
25 %% Return a proplist of information for the web UI to integrate
26 %% this extension. Currently the proplist should have one key,
27 %% 'javascript', the name of a javascript file to load and run.
28 {web_ui, 0}
29 ];
30 behaviour_info(_Other) ->
31 undefined.
21 %% Return a proplist of information for the web UI to integrate
22 %% this extension. Currently the proplist should have one key,
23 %% 'javascript', the name of a javascript file to load and run.
24 -callback web_ui() -> proplists:proplist().
+0
-453
deps/rabbitmq_management/src/rabbit_mgmt_format.erl less more
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License at
3 %% http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
7 %% License for the specific language governing rights and limitations
8 %% under the License.
9 %%
10 %% The Original Code is RabbitMQ Management Plugin.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_mgmt_format).
17
18 -export([format/2, ip/1, ipb/1, amqp_table/1, tuple/1]).
19 -export([parameter/1, now_to_str/1, now_to_str_ms/1, strip_pids/1]).
20 -export([protocol/1, resource/1, queue/1, queue_state/1]).
21 -export([exchange/1, user/1, internal_user/1, binding/1, url/2]).
22 -export([pack_binding_props/2, tokenise/1]).
23 -export([to_amqp_table/1, listener/1, properties/1, basic_properties/1]).
24 -export([record/2, to_basic_properties/1]).
25 -export([addr/1, port/1]).
26 -export([format_nulls/1]).
27 -export([print/2, print/1]).
28
29 -export([format_queue_stats/1, format_channel_stats/1,
30 format_arguments/1, format_connection_created/1,
31 format_accept_content/1, format_args/1]).
32
33 -export([strip_queue_pids/1]).
34
35 -import(rabbit_misc, [pget/2, pset/3]).
36
37 -include_lib("rabbit_common/include/rabbit.hrl").
38 -include_lib("rabbit_common/include/rabbit_framing.hrl").
39
40 %%--------------------------------------------------------------------
41
42 format(Stats, {[], _}) ->
43 [Stat || {_Name, Value} = Stat <- Stats, Value =/= unknown];
44 format(Stats, {Fs, true}) ->
45 [Fs(Stat) || {_Name, Value} = Stat <- Stats, Value =/= unknown];
46 format(Stats, {Fs, false}) ->
47 lists:concat([Fs(Stat) || {_Name, Value} = Stat <- Stats,
48 Value =/= unknown]).
49
50 format_queue_stats({reductions, _}) ->
51 [];
52 format_queue_stats({exclusive_consumer_pid, _}) ->
53 [];
54 format_queue_stats({slave_pids, ''}) ->
55 [];
56 format_queue_stats({slave_pids, Pids}) ->
57 [{slave_nodes, [node(Pid) || Pid <- Pids]}];
58 format_queue_stats({synchronised_slave_pids, ''}) ->
59 [];
60 format_queue_stats({synchronised_slave_pids, Pids}) ->
61 [{synchronised_slave_nodes, [node(Pid) || Pid <- Pids]}];
62 format_queue_stats({backing_queue_status, Value}) ->
63 [{backing_queue_status, properties(Value)}];
64 format_queue_stats({idle_since, Value}) ->
65 [{idle_since, now_to_str(Value)}];
66 format_queue_stats({state, Value}) ->
67 queue_state(Value);
68 format_queue_stats(Stat) ->
69 [Stat].
70
71 format_channel_stats({idle_since, Value}) ->
72 {idle_since, now_to_str(Value)};
73 format_channel_stats(Stat) ->
74 Stat.
75
76 format_arguments({arguments, Value}) ->
77 {arguments, amqp_table(Value)};
78 format_arguments(Stat) ->
79 Stat.
80
81 format_args({arguments, Value}) ->
82 {arguments, rabbit_mgmt_util:args(Value)};
83 format_args(Stat) ->
84 Stat.
85
86 format_connection_created({host, Value}) ->
87 {host, addr(Value)};
88 format_connection_created({peer_host, Value}) ->
89 {peer_host, addr(Value)};
90 format_connection_created({port, Value}) ->
91 {port, port(Value)};
92 format_connection_created({peer_port, Value}) ->
93 {peer_port, port(Value)};
94 format_connection_created({protocol, Value}) ->
95 {protocol, protocol(Value)};
96 format_connection_created({client_properties, Value}) ->
97 {client_properties, amqp_table(Value)};
98 format_connection_created(Stat) ->
99 Stat.
100
101 format_exchange_and_queue({policy, Value}) ->
102 policy(Value);
103 format_exchange_and_queue({arguments, Value}) ->
104 [{arguments, amqp_table(Value)}];
105 format_exchange_and_queue({name, Value}) ->
106 resource(Value);
107 format_exchange_and_queue(Stat) ->
108 [Stat].
109
110 format_binding({source, Value}) ->
111 resource(source, Value);
112 format_binding({arguments, Value}) ->
113 [{arguments, amqp_table(Value)}];
114 format_binding(Stat) ->
115 [Stat].
116
117 format_basic_properties({headers, Value}) ->
118 {headers, amqp_table(Value)};
119 format_basic_properties(Stat) ->
120 Stat.
121
122 format_accept_content({durable, Value}) ->
123 {durable, rabbit_mgmt_util:parse_bool(Value)};
124 format_accept_content({auto_delete, Value}) ->
125 {auto_delete, rabbit_mgmt_util:parse_bool(Value)};
126 format_accept_content({internal, Value}) ->
127 {internal, rabbit_mgmt_util:parse_bool(Value)};
128 format_accept_content(Stat) ->
129 Stat.
130
131 print(Fmt, Val) when is_list(Val) ->
132 list_to_binary(lists:flatten(io_lib:format(Fmt, Val)));
133 print(Fmt, Val) ->
134 print(Fmt, [Val]).
135
136 print(Val) when is_list(Val) ->
137 list_to_binary(lists:flatten(Val));
138 print(Val) ->
139 Val.
140
141 %% TODO - can we remove all these "unknown" cases? Coverage never hits them.
142
143 ip(unknown) -> unknown;
144 ip(IP) -> list_to_binary(rabbit_misc:ntoa(IP)).
145
146 ipb(unknown) -> unknown;
147 ipb(IP) -> list_to_binary(rabbit_misc:ntoab(IP)).
148
149 addr(S) when is_list(S); is_atom(S); is_binary(S) -> print("~s", S);
150 addr(Addr) when is_tuple(Addr) -> ip(Addr).
151
152 port(Port) when is_number(Port) -> Port;
153 port(Port) -> print("~w", Port).
154
155 properties(unknown) -> unknown;
156 properties(Table) -> {struct, [{Name, tuple(Value)} ||
157 {Name, Value} <- Table]}.
158
159 amqp_table(unknown) -> unknown;
160 amqp_table(undefined) -> amqp_table([]);
161 amqp_table(Table) -> {struct, [{Name, amqp_value(Type, Value)} ||
162 {Name, Type, Value} <- Table]}.
163
164 amqp_value(array, Vs) -> [amqp_value(T, V) || {T, V} <- Vs];
165 amqp_value(table, V) -> amqp_table(V);
166 amqp_value(_Type, V) when is_binary(V) -> utf8_safe(V);
167 amqp_value(_Type, V) -> V.
168
169 utf8_safe(V) ->
170 try
171 xmerl_ucs:from_utf8(V),
172 V
173 catch exit:{ucs, _} ->
174 Enc = split_lines(base64:encode(V)),
175 <<"Not UTF-8, base64 is: ", Enc/binary>>
176 end.
177
178 % MIME enforces a limit on line length of base 64-encoded data to 76 characters.
179 split_lines(<<Text:76/binary, Rest/binary>>) ->
180 <<Text/binary, $\n, (split_lines(Rest))/binary>>;
181 split_lines(Text) ->
182 Text.
183
184 parameter(P) -> pset(value, rabbit_misc:term_to_json(pget(value, P)), P).
185
186 tuple(unknown) -> unknown;
187 tuple(Tuple) when is_tuple(Tuple) -> [tuple(E) || E <- tuple_to_list(Tuple)];
188 tuple(Term) -> Term.
189
190 protocol(unknown) ->
191 unknown;
192 protocol(Version = {_Major, _Minor, _Revision}) ->
193 protocol({'AMQP', Version});
194 protocol({Family, Version}) ->
195 print("~s ~s", [Family, protocol_version(Version)]).
196
197 protocol_version(Arbitrary)
198 when is_list(Arbitrary) -> Arbitrary;
199 protocol_version({Major, Minor}) -> io_lib:format("~B-~B", [Major, Minor]);
200 protocol_version({Major, Minor, 0}) -> protocol_version({Major, Minor});
201 protocol_version({Major, Minor, Revision}) -> io_lib:format("~B-~B-~B",
202 [Major, Minor, Revision]).
203
204 now_to_str(unknown) ->
205 unknown;
206 now_to_str(MilliSeconds) ->
207 BaseDate = calendar:datetime_to_gregorian_seconds({{1970, 1, 1},
208 {0, 0, 0}}),
209 Seconds = BaseDate + (MilliSeconds div 1000),
210 {{Y, M, D}, {H, Min, S}} = calendar:gregorian_seconds_to_datetime(Seconds),
211 print("~w-~2.2.0w-~2.2.0w ~w:~2.2.0w:~2.2.0w", [Y, M, D, H, Min, S]).
212
213 now_to_str_ms(unknown) ->
214 unknown;
215 now_to_str_ms(MilliSeconds) ->
216 print("~s:~3.3.0w", [now_to_str(MilliSeconds), MilliSeconds rem 1000]).
217
218 resource(unknown) -> unknown;
219 resource(Res) -> resource(name, Res).
220
221 resource(_, unknown) ->
222 unknown;
223 resource(NameAs, #resource{name = Name, virtual_host = VHost}) ->
224 [{NameAs, Name}, {vhost, VHost}].
225
226 policy('') -> [];
227 policy(Policy) -> [{policy, Policy}].
228
229 internal_user(User) ->
230 [{name, User#internal_user.username},
231 {password_hash, base64:encode(User#internal_user.password_hash)},
232 {hashing_algorithm, rabbit_auth_backend_internal:hashing_module_for_user(
233 User)},
234 {tags, tags(User#internal_user.tags)}].
235
236 user(User) ->
237 [{name, User#user.username},
238 {tags, tags(User#user.tags)}].
239
240 tags(Tags) ->
241 list_to_binary(string:join([atom_to_list(T) || T <- Tags], ",")).
242
243 listener(#listener{node = Node, protocol = Protocol,
244 ip_address = IPAddress, port = Port}) ->
245 [{node, Node},
246 {protocol, Protocol},
247 {ip_address, ip(IPAddress)},
248 {port, Port}].
249
250 pack_binding_props(<<"">>, []) ->
251 <<"~">>;
252 pack_binding_props(Key, []) ->
253 list_to_binary(quote_binding(Key));
254 pack_binding_props(Key, Args) ->
255 ArgsEnc = rabbit_mgmt_wm_binding:args_hash(Args),
256 list_to_binary(quote_binding(Key) ++ "~" ++ quote_binding(ArgsEnc)).
257
258 quote_binding(Name) ->
259 re:replace(mochiweb_util:quote_plus(Name), "~", "%7E", [global]).
260
261 %% Unfortunately string:tokens("foo~~bar", "~"). -> ["foo","bar"], we lose
262 %% the fact that there's a double ~.
263 tokenise("") ->
264 [];
265 tokenise(Str) ->
266 Count = string:cspan(Str, "~"),
267 case length(Str) of
268 Count -> [Str];
269 _ -> [string:sub_string(Str, 1, Count) |
270 tokenise(string:sub_string(Str, Count + 2))]
271 end.
272
273 to_amqp_table({struct, T}) ->
274 to_amqp_table(T);
275 to_amqp_table(T) ->
276 [to_amqp_table_row(K, V) || {K, V} <- T].
277
278 to_amqp_table_row(K, V) ->
279 {T, V2} = type_val(V),
280 {K, T, V2}.
281
282 to_amqp_array(L) ->
283 [type_val(I) || I <- L].
284
285 type_val({struct, M}) -> {table, to_amqp_table(M)};
286 type_val(L) when is_list(L) -> {array, to_amqp_array(L)};
287 type_val(X) when is_binary(X) -> {longstr, X};
288 type_val(X) when is_integer(X) -> {long, X};
289 type_val(X) when is_number(X) -> {double, X};
290 type_val(true) -> {bool, true};
291 type_val(false) -> {bool, false};
292 type_val(null) -> throw({error, null_not_allowed});
293 type_val(X) -> throw({error, {unhandled_type, X}}).
294
295 url(Fmt, Vals) ->
296 print(Fmt, [mochiweb_util:quote_plus(V) || V <- Vals]).
297
298 exchange(X) ->
299 format(X, {fun format_exchange_and_queue/1, false}).
300
301 %% We get queues using rabbit_amqqueue:list/1 rather than :info_all/1 since
302 %% the latter wakes up each queue. Therefore we have a record rather than a
303 %% proplist to deal with.
304 queue(#amqqueue{name = Name,
305 durable = Durable,
306 auto_delete = AutoDelete,
307 exclusive_owner = ExclusiveOwner,
308 arguments = Arguments,
309 pid = Pid,
310 state = State}) ->
311 format(
312 [{name, Name},
313 {durable, Durable},
314 {auto_delete, AutoDelete},
315 {exclusive, is_pid(ExclusiveOwner)},
316 {owner_pid, ExclusiveOwner},
317 {arguments, Arguments},
318 {pid, Pid},
319 {state, State}],
320 {fun format_exchange_and_queue/1, false}).
321
322 queue_state({syncing, Msgs}) -> [{state, syncing},
323 {sync_messages, Msgs}];
324 queue_state(Status) -> [{state, Status}].
325
326 %% We get bindings using rabbit_binding:list_*/1 rather than :info_all/1 since
327 %% there are no per-exchange / queue / etc variants for the latter. Therefore
328 %% we have a record rather than a proplist to deal with.
329 binding(#binding{source = S,
330 key = Key,
331 destination = D,
332 args = Args}) ->
333 format(
334 [{source, S},
335 {destination, D#resource.name},
336 {destination_type, D#resource.kind},
337 {routing_key, Key},
338 {arguments, Args},
339 {properties_key, pack_binding_props(Key, Args)}],
340 {fun format_binding/1, false}).
341
342 basic_properties(Props = #'P_basic'{}) ->
343 Res = record(Props, record_info(fields, 'P_basic')),
344 format(Res, {fun format_basic_properties/1, true}).
345
346 record(Record, Fields) ->
347 {Res, _Ix} = lists:foldl(fun (K, {L, Ix}) ->
348 {case element(Ix, Record) of
349 undefined -> L;
350 V -> [{K, V}|L]
351 end, Ix + 1}
352 end, {[], 2}, Fields),
353 Res.
354
355 to_basic_properties({struct, P}) ->
356 to_basic_properties(P);
357
358 to_basic_properties(Props) ->
359 E = fun (A, B) -> throw({error, {A, B}}) end,
360 Fmt = fun (headers, H) -> to_amqp_table(H);
361 (delivery_mode, V) when is_integer(V) -> V;
362 (delivery_mode, _V) -> E(not_int,delivery_mode);
363 (priority, V) when is_integer(V) -> V;
364 (priority, _V) -> E(not_int, priority);
365 (timestamp, V) when is_integer(V) -> V;
366 (timestamp, _V) -> E(not_int, timestamp);
367 (_, V) when is_binary(V) -> V;
368 (K, _V) -> E(not_string, K)
369 end,
370 {Res, _Ix} = lists:foldl(
371 fun (K, {P, Ix}) ->
372 {case proplists:get_value(a2b(K), Props) of
373 undefined -> P;
374 V -> setelement(Ix, P, Fmt(K, V))
375 end, Ix + 1}
376 end, {#'P_basic'{}, 2},
377 record_info(fields, 'P_basic')),
378 Res.
379
380 a2b(A) ->
381 list_to_binary(atom_to_list(A)).
382
383 strip_queue_pids(Item) ->
384 strip_queue_pids(Item, []).
385
386 strip_queue_pids([{_, unknown} | T], Acc) ->
387 strip_queue_pids(T, Acc);
388 strip_queue_pids([{pid, Pid} | T], Acc) when is_pid(Pid) ->
389 strip_queue_pids(T, [{node, node(Pid)} | Acc]);
390 strip_queue_pids([{pid, _} | T], Acc) ->
391 strip_queue_pids(T, Acc);
392 strip_queue_pids([{owner_pid, _} | T], Acc) ->
393 strip_queue_pids(T, Acc);
394 strip_queue_pids([Any | T], Acc) ->
395 strip_queue_pids(T, [Any | Acc]);
396 strip_queue_pids([], Acc) ->
397 Acc.
398
399 %% Items can be connections, channels, consumers or queues, hence remove takes
400 %% various items.
401 strip_pids(Item = [T | _]) when is_tuple(T) ->
402 strip_pids(Item, []);
403
404 strip_pids(Items) -> [strip_pids(I) || I <- Items].
405
406 strip_pids([{_, unknown} | T], Acc) ->
407 strip_pids(T, Acc);
408 strip_pids([{pid, Pid} | T], Acc) when is_pid(Pid) ->
409 strip_pids(T, [{node, node(Pid)} | Acc]);
410 strip_pids([{pid, _} | T], Acc) ->
411 strip_pids(T, Acc);
412 strip_pids([{connection, _} | T], Acc) ->
413 strip_pids(T, Acc);
414 strip_pids([{owner_pid, _} | T], Acc) ->
415 strip_pids(T, Acc);
416 strip_pids([{channel, _} | T], Acc) ->
417 strip_pids(T, Acc);
418 strip_pids([{exclusive_consumer_pid, _} | T], Acc) ->
419 strip_pids(T, Acc);
420 strip_pids([{slave_pids, ''} | T], Acc) ->
421 strip_pids(T, Acc);
422 strip_pids([{slave_pids, Pids} | T], Acc) ->
423 strip_pids(T, [{slave_nodes, [node(Pid) || Pid <- Pids]} | Acc]);
424 strip_pids([{synchronised_slave_pids, ''} | T], Acc) ->
425 strip_pids(T, Acc);
426 strip_pids([{synchronised_slave_pids, Pids} | T], Acc) ->
427 strip_pids(T, [{synchronised_slave_nodes, [node(Pid) || Pid <- Pids]} | Acc]);
428 strip_pids([Any | T], Acc) ->
429 strip_pids(T, [Any | Acc]);
430 strip_pids([], Acc) ->
431 Acc.
432
433 %% Format for JSON replies. Transforms '' into null
434 format_nulls(Items) when is_list(Items) ->
435 [format_null_item(Pair) || Pair <- Items];
436 format_nulls(Item) ->
437 format_null_item(Item).
438
439 format_null_item({Key, ''}) ->
440 {Key, null};
441 format_null_item({Key, Value}) when is_list(Value) ->
442 {Key, format_nulls(Value)};
443 format_null_item({Key, {struct, Struct}}) ->
444 {Key, {struct, format_nulls(Struct)}};
445 format_null_item({Key, {array, Struct}}) ->
446 {Key, {array, format_nulls(Struct)}};
447 format_null_item({Key, Value}) ->
448 {Key, Value};
449 format_null_item([{_K, _V} | _T] = L) ->
450 format_nulls(L);
451 format_null_item(Value) ->
452 Value.
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_load_definitions).
+0
-120
deps/rabbitmq_management/src/rabbit_mgmt_queue_stats_collector.erl less more
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License at
3 %% http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
7 %% License for the specific language governing rights and limitations
8 %% under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is Pivotal Software, Inc.
13 %% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_mgmt_queue_stats_collector).
17
18 -include("rabbit_mgmt.hrl").
19 -include("rabbit_mgmt_metrics.hrl").
20 -include("rabbit_mgmt_event_collector.hrl").
21 -include_lib("rabbit_common/include/rabbit.hrl").
22
23 -behaviour(gen_server2).
24
25 -export([start_link/0]).
26
27 -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
28 code_change/3, handle_pre_hibernate/1]).
29
30 -export([prioritise_cast/3]).
31
32 -import(rabbit_misc, [pget/3]).
33 -import(rabbit_mgmt_db, [pget/2, id_name/1, id/2, lookup_element/2]).
34
35 prioritise_cast({event, #event{type = queue_stats}}, Len,
36 #state{max_backlog = MaxBacklog} = _State)
37 when Len > MaxBacklog ->
38 drop;
39 prioritise_cast(_Msg, _Len, _State) ->
40 0.
41
42 %% See the comment on rabbit_mgmt_db for the explanation of
43 %% events and stats.
44
45 %% Although this gen_server could process all types of events through the
46 %% handle_cast, rabbit_mgmt_db_handler (in the management agent) forwards
47 %% only the non-prioritiy events channel_stats
48 %%----------------------------------------------------------------------------
49 %% API
50 %%----------------------------------------------------------------------------
51
52 start_link() ->
53 case gen_server2:start_link({global, ?MODULE}, ?MODULE, [], []) of
54 {ok, Pid} -> register(?MODULE, Pid), %% [1]
55 {ok, Pid};
56 Else -> Else
57 end.
58 %% [1] For debugging it's helpful to locally register the name too
59 %% since that shows up in places global names don't.
60
61 %%----------------------------------------------------------------------------
62 %% Internal, gen_server2 callbacks
63 %%----------------------------------------------------------------------------
64
65 init([]) ->
66 {ok, Interval} = application:get_env(rabbit, collect_statistics_interval),
67 {ok, RatesMode} = application:get_env(rabbitmq_management, rates_mode),
68 {ok, MaxBacklog} = application:get_env(rabbitmq_management,
69 stats_event_max_backlog),
70 process_flag(priority, high),
71 rabbit_log:info("Statistics queue stats collector started.~n"),
72 {ok, reset_lookups(
73 #state{interval = Interval,
74 rates_mode = RatesMode,
75 max_backlog = MaxBacklog}), hibernate,
76 {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}.
77
78 %% Used in rabbit_mgmt_test_db where we need guarantees events have
79 %% been handled before querying
80 handle_call({event, Event = #event{reference = none}}, _From, State) ->
81 rabbit_mgmt_event_collector_utils:handle_event(Event, State),
82 reply(ok, State);
83
84 handle_call(_Request, _From, State) ->
85 reply(not_understood, State).
86
87 %% Only handle events that are real.
88 handle_cast({event, Event = #event{reference = none}}, State) ->
89 rabbit_mgmt_event_collector_utils:handle_event(Event, State),
90 noreply(State);
91
92 handle_cast(_Request, State) ->
93 noreply(State).
94
95 handle_info(_Info, State) ->
96 noreply(State).
97
98 terminate(_Arg, _State) ->
99 ok.
100
101 code_change(_OldVsn, State, _Extra) ->
102 {ok, State}.
103
104 reply(Reply, NewState) -> {reply, Reply, NewState, hibernate}.
105 noreply(NewState) -> {noreply, NewState, hibernate}.
106
107 reset_lookups(State) ->
108 State#state{lookups = [{exchange, fun rabbit_exchange:lookup/1},
109 {queue, fun rabbit_amqqueue:lookup/1}]}.
110
111 handle_pre_hibernate(State) ->
112 %% rabbit_event can end up holding on to some memory after a busy
113 %% workout, but it's not a gen_server so we can't make it
114 %% hibernate. The best we can do is forcibly GC it here (if
115 %% rabbit_mgmt_db is hibernating the odds are rabbit_event is
116 %% quiescing in some way too).
117 rpc:multicall(
118 rabbit_mnesia:cluster_nodes(running), rabbit_mgmt_db_handler, gc, []),
119 {hibernate, State}.
1010 %% The Original Code is RabbitMQ Management Console.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% When management extensions are enabled and/or disabled at runtime, the
5050 Enabled = pget(enabled, Details),
5151 Disabled = pget(disabled, Details),
5252 case extensions_changed(Enabled ++ Disabled) of
53 true -> rabbit_mgmt_app:reset_dispatcher(Disabled);
53 true ->
54 _ = rabbit_mgmt_app:reset_dispatcher(Disabled),
55 ok;
5456 false -> ok
5557 end,
5658 {ok, State};
1515
1616 -module(rabbit_mgmt_stats).
1717
18 -include("rabbit_mgmt.hrl").
19 -include("rabbit_mgmt_metrics.hrl").
20
21 -export([blank/1, is_blank/3, record/5, format/5, sum/1, gc/3,
22 free/1, delete_stats/2, get_keys/2]).
23
24 -import(rabbit_misc, [pget/2]).
25
26 -define(ALWAYS_REPORT, [queue_msg_counts, coarse_node_stats]).
18 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
19 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_metrics.hrl").
20
21 -export([format_range/6]).
22
2723 -define(MICRO_TO_MILLI, 1000).
2824
29 %% Data is stored in ETS tables:
30 %% * one set of ETS tables per event (queue_stats, queue_exchange_stats...)
31 %% * each set contains one table per group of events (queue_msg_rates,
32 %% deliver_get, fine_stats...) such as aggr_queue_stats_deliver_get
33 %% (see ?AGGR_TABLES in rabbit_mgmt_metrics.hrl)
34 %% * data is then stored as a tuple (not a record) to take advantage of the
35 %% atomic call ets:update_counter/3. The equivalent records are noted in
36 %% rabbit_mgmt_metrics.hrl to get the position and as reference for developers
37 %% * Records are of the shape:
38 %% {{Id, base}, Field1, Field2, ....}
39 %% {{Id, total}, Field1, Field2, ....}
40 %% {{Id, Timestamp}, Field1, Field2, ....}
41 %% where Id can be a simple key or a tuple {Id0, Id1}
42 %%
43 %% This module is not generic any longer, any new event or field needs to be
44 %% manually added, but it increases the performance and allows concurrent
45 %% GC, event collection and querying
46 %%
47
48 %%----------------------------------------------------------------------------
49 %% External functions
50 %%----------------------------------------------------------------------------
51
52 blank(Name) ->
53 ets:new(rabbit_mgmt_stats_tables:index(Name),
54 [bag, public, named_table]),
55 ets:new(rabbit_mgmt_stats_tables:key_index(Name),
56 [ordered_set, public, named_table]),
57 ets:new(Name, [set, public, named_table]).
58
59 is_blank({Table, _, _}, Id, Record) ->
60 is_blank(Table, Id, Record);
61 is_blank(Table, Id, Record) ->
62 case ets:lookup(Table, {Id, total}) of
63 [] ->
64 true;
65 [Total] ->
66 case lists:member(Record, ?ALWAYS_REPORT) of
67 true -> false;
68 false -> is_blank(Total)
69 end
70 end.
71
72 %%----------------------------------------------------------------------------
73 free({Table, IndexTable, KeyIndexTable}) ->
74 ets:delete(Table),
75 ets:delete(IndexTable),
76 ets:delete(KeyIndexTable).
77
78 delete_stats(Table, {'_', _} = Id) ->
79 delete_complex_stats(Table, Id);
80 delete_stats(Table, {_, '_'} = Id) ->
81 delete_complex_stats(Table, Id);
82 delete_stats(Table, Id) ->
83 Keys = full_indexes(Table, Id),
84 ets:delete(rabbit_mgmt_stats_tables:index(Table), Id),
85 ets:delete(rabbit_mgmt_stats_tables:key_index(Table), Id),
86 [ets:delete(Table, Key) || Key <- Keys].
87
88 delete_complex_stats(Table, Id) ->
89 Ids = ets:select(rabbit_mgmt_stats_tables:key_index(Table),
90 match_spec_key_index(Id)),
91 delete_complex_stats_loop(Table, Ids).
92
93 delete_complex_stats_loop(_Table, []) ->
94 ok;
95 delete_complex_stats_loop(Table, [{Id} | Ids]) ->
96 delete_stats(Table, Id),
97 delete_complex_stats_loop(Table, Ids).
98
99 %%----------------------------------------------------------------------------
100 get_keys(Table, Id0) ->
101 ets:select(rabbit_mgmt_stats_tables:key_index(Table), match_spec_keys(Id0)).
102
103 %%----------------------------------------------------------------------------
104 %% Event-time
105 %%----------------------------------------------------------------------------
106
107 record({Id, _TS} = Key, Pos, Diff, Record, Table) ->
108 ets_update(Table, Key, Record, Pos, Diff),
109 ets_update(Table, {Id, total}, Record, Pos, Diff).
25 -type maybe_range() :: no_range | #range{}.
26 -type maybe_slide() :: exometer_slide:slide() | not_found.
27 -type maybe_slide_fun() :: fun(() -> [maybe_slide()]).
28
29 -export_type([maybe_range/0, maybe_slide/0, maybe_slide_fun/0]).
30
11031
11132 %%----------------------------------------------------------------------------
11233 %% Query-time
11334 %%----------------------------------------------------------------------------
11435
115 format(no_range, Table, Id, Interval, Type) ->
116 Now = time_compat:os_system_time(milli_seconds),
117 Counts = get_value(Table, Id, total, Type),
36 -spec format_range(maybe_range(), exometer_slide:timestamp(), atom(),
37 non_neg_integer(), maybe_slide_fun(), maybe_slide_fun()) ->
38 proplists:proplist().
39 format_range(no_range, Now, Table, Interval, InstantRateFun, _SamplesFun) ->
40 format_no_range(Table, Now, Interval, InstantRateFun);
41 format_range(#range{last = Last, first = First, incr = Incr}, _Now, Table,
42 Interval, _InstantRateFun, SamplesFun) ->
43 case SamplesFun() of
44 [] ->
45 [];
46 Slides ->
47 Empty = empty(Table, 0),
48 Slide = exometer_slide:sum(Last, First, Incr, Slides, Empty),
49 List = exometer_slide:to_list(Last, First, Slide),
50 Length = length(List),
51 RangePoint = Last - Interval,
52 LastTwo = get_last_two(List, Length),
53 {Total, Rate} = calculate_rate(LastTwo, Table, RangePoint),
54 {Samples, SampleTotals} = format_samples(List, empty(Table, []),
55 Empty),
56 format_rate(Table, Total, Rate, Samples, SampleTotals, Length)
57 end.
58
59 -spec format_no_range(atom(), exometer_slide:timestamp(), non_neg_integer(),
60 maybe_slide_fun()) -> proplists:proplist().
61 format_no_range(Table, Now, Interval, InstantRateFun) ->
11862 RangePoint = ((Now div Interval) * Interval) - Interval,
119 {Record, Factor} = format_rate_with(
120 Table, Id, RangePoint, Interval, Interval, Type),
121 format_rate(Type, Record, Counts, Factor);
122
123 format(Range, Table, Id, Interval, Type) ->
124 Base = get_value(Table, Id, base, Type),
125 RangePoint = Range#range.last - Interval,
126 {Samples, Counts} = extract_samples(Range, Base, Table, Id, Type),
127 {Record, Factor} = format_rate_with(
128 Table, Id, RangePoint, Range#range.incr, Interval, Type),
129 format_rate(Type, Record, Counts, Samples, Factor).
130
131 sum([]) -> blank();
132
133 sum([{T1, Id} | StatsN]) ->
134 {Table, IndexTable, KeyIndexTable} = T = blank(),
135 AllIds = full_indexes(T1, Id),
136 lists:foreach(fun(Index) ->
137 case ets:lookup(T1, Index) of
138 [V] ->
139 {_, TS} = element(1, V),
140 ets:insert(Table, setelement(1, V, {all, TS})),
141 insert_index(IndexTable, KeyIndexTable, {all, TS});
142 [] -> %% base
143 ok
144 end
145 end, AllIds),
146 sum(StatsN, T).
147
148 sum(StatsN, T) ->
149 lists:foreach(
150 fun ({T1, Id}) ->
151 AllIds = full_indexes(T1, Id),
152 lists:foreach(fun(Index) ->
153 case ets:lookup(T1, Index) of
154 [V] ->
155 {_, TS} = element(1, V),
156 ets_update(T, {all, TS}, V);
157 [] -> %% base
158 ok
159 end
160 end, AllIds)
161 end, StatsN),
162 T.
163
164 gc(Cutoff, Table, Id) ->
165 gc(Cutoff, lists:reverse(indexes(Table, Id)), Table, undefined).
166
167 %%----------------------------------------------------------------------------
168 %% Internal functions
169 %%----------------------------------------------------------------------------
170 format_rate_with({Table, IndexTable, _KeyIndexTable}, Id, RangePoint, Incr,
171 Interval, Type) ->
172 format_rate_with(Table, IndexTable, Id, RangePoint, Incr, Interval, Type);
173 format_rate_with(Table, Id, RangePoint, Incr, Interval, Type) ->
174 format_rate_with(Table, rabbit_mgmt_stats_tables:index(Table), Id,
175 RangePoint, Incr, Interval, Type).
176
177 format_rate_with(Table, IndexTable, Id, RangePoint, Incr, Interval, Type) ->
178 case second_largest(Table, IndexTable, Id) of
179 [S] ->
180 {_, TS} = element(1, S),
181 case TS - RangePoint of %% [0]
182 D when D =< Incr andalso D >= 0 -> {S, Interval};
183 _ -> {S, 0.0}
184 end;
185 _ ->
186 {empty(Id, Type), 0.0}
63 case calculate_instant_rate(InstantRateFun, Table, RangePoint) of
64 {Total, Rate} ->
65 format_rate(Table, Total, Rate);
66 not_found ->
67 []
18768 end.
18869
189 %% [0] Only display the rate if it's live - i.e. ((the end of the
190 %% range) - interval) corresponds to the second to last data point we
191 %% have. If the end of the range is earlier we have gone silent, if
192 %% it's later we have been asked for a range back in time (in which
193 %% case showing the correct instantaneous rate would be quite a faff,
194 %% and probably unwanted). Why the second to last? Because data is
195 %% still arriving for the last...
196 second_largest(Table, IndexTable, Id) ->
197 case ets:lookup(IndexTable, Id) of
198 [_, _ | _] = List ->
199 ets:lookup(Table, sl(List, {none, 0}, {none, 0}));
200 _ ->
201 unknown
70 -spec calculate_instant_rate(maybe_slide_fun(), atom(), integer()) ->
71 not_found | {integer(), number()}.
72 calculate_instant_rate(Fun, Table, RangePoint) ->
73 case Fun() of
74 [] ->
75 not_found;
76 Slides ->
77 Slide = exometer_slide:sum(Slides),
78 case exometer_slide:last_two(Slide) of
79 [] -> {empty(Table, 0), empty(Table, 0.0)};
80 [Last | T] ->
81 Total = get_total(Slide, Table),
82 Rate = rate_from_last_increment(Table, Last, T, RangePoint),
83 {Total, Rate}
84 end
85 end.
86
87 calculate_rate([], Table, _RangePoint) ->
88 {empty(Table, 0), empty(Table, 0.0)};
89 calculate_rate([{_, Total} = Last | T], Table, RangePoint) ->
90 Rate = rate_from_last_increment(Table, Last, T, RangePoint),
91 {Total, Rate}.
92
93 get_last_two(List, Length) when Length =< 2 ->
94 lists:reverse(List);
95 get_last_two(List, Length) ->
96 lists:reverse(lists:nthtail(Length - 2, List)).
97
98 get_total(Slide, Table) ->
99 case exometer_slide:last(Slide) of
100 undefined -> empty(Table, 0);
101 Other -> Other
202102 end.
203103
204 sl([{_, TS} = H | T], {_, T1} = L1, _L2) when TS > T1 ->
205 sl(T, H, L1);
206 sl([{_, TS} = H | T], L1, {_, T2}) when TS > T2 ->
207 sl(T, L1, H);
208 sl([_ | T], L1, L2) ->
209 sl(T, L1, L2);
210 sl([], _L1, L2) ->
211 L2.
212
213 %% What we want to do here is: given the #range{}, provide a set of
214 %% samples such that we definitely provide a set of samples which
215 %% covers the exact range requested, despite the fact that we might
216 %% not have it. We need to spin up over the entire range of the
217 %% samples we *do* have since they are diff-based (and we convert to
218 %% absolute values here).
219 extract_samples(Range, Base, Table, Id, Type) ->
220 %% In order to calculate the average operation time for some of the node
221 %% metrics, it needs to carry around the last raw sample taken (before
222 %% calculations). This is the first element of the 'Samples' tuple.
223 %% It is initialised to the base, which is updated with the latest value until
224 %% it finds the first valid sample. Thus, generating an instant rate for it.
225 %% Afterwards, it will store the last raw sample.
226 extract_samples0(Range, Base, indexes(Table, Id), Table, Type,
227 {Base, empty_list(Type)}).
228
229 extract_samples0(Range = #range{first = Next}, Base, [], Table, Type, Samples) ->
230 %% [3] Empty or finished table
231 extract_samples1(Range, Base, empty({unused_id, Next}, Type), [], Table, Type,
232 Samples);
233 extract_samples0(Range, Base, [Index | List], Tab, Type, Samples) ->
234 Table = case Tab of
235 {T, _, _} ->
236 T;
237 T ->
238 T
239 end,
240 case ets:lookup(Table, Index) of
241 [S] ->
242 extract_samples1(Range, Base, S, List, Table, Type, Samples);
243 [] ->
244 extract_samples0(Range, Base, List, Table, Type, Samples)
245 end.
246
247 extract_samples1(Range = #range{first = Next, last = Last, incr = Incr},
248 Base, S, List, Table, Type, {LastRawSample, Samples}) ->
249 {_, TS} = element(1, S),
250 if
251 %% We've gone over the range. Terminate.
252 Next > Last ->
253 %% Drop the raw sample
254 {Samples, Base};
255 %% We've hit bang on a sample. Record it and move to the next.
256 Next =:= TS ->
257 %% The new base is the last sample used to generate instant rates
258 %% in the node stats
259 NewBase = add_record(Base, S),
260 extract_samples0(Range#range{first = Next + Incr}, NewBase, List,
261 Table, Type, {NewBase, append(NewBase, Samples, Next,
262 LastRawSample)});
263 %% We haven't yet hit the beginning of our range.
264 Next > TS ->
265 NewBase = add_record(Base, S),
266 %% Roll the latest value until we find the first sample
267 RawSample = case element(2, Samples) of
268 [] -> NewBase;
269 _ -> LastRawSample
270 end,
271 extract_samples0(Range, NewBase, List, Table, Type,
272 {RawSample, Samples});
273 %% We have a valid sample, but we haven't used it up
274 %% yet. Append it and loop around.
275 Next < TS ->
276 %% Pass the last raw sample to calculate instant node stats
277 extract_samples1(Range#range{first = Next + Incr}, Base, S,
278 List, Table, Type,
279 {Base, append(Base, Samples, Next, LastRawSample)})
280 end.
281
282 append({_Key, V1}, {samples, V1s}, TiS, _LastRawSample) ->
283 {samples, append_sample(V1, TiS, V1s)};
284 append({_Key, V1, V2}, {samples, V1s, V2s}, TiS, _LastRawSample) ->
285 {samples, append_sample(V1, TiS, V1s), append_sample(V2, TiS, V2s)};
286 append({_Key, V1, V2, V3}, {samples, V1s, V2s, V3s}, TiS, _LastRawSample) ->
287 {samples, append_sample(V1, TiS, V1s), append_sample(V2, TiS, V2s),
288 append_sample(V3, TiS, V3s)};
289 append({_Key, V1, V2, V3, V4}, {samples, V1s, V2s, V3s, V4s}, TiS, _LastRawSample) ->
290 {samples, append_sample(V1, TiS, V1s), append_sample(V2, TiS, V2s),
291 append_sample(V3, TiS, V3s), append_sample(V4, TiS, V4s)};
292 append({_Key, V1, V2, V3, V4, V5, V6, V7, V8},
293 {samples, V1s, V2s, V3s, V4s, V5s, V6s, V7s, V8s}, TiS, _LastRawSample) ->
294 {samples, append_sample(V1, TiS, V1s), append_sample(V2, TiS, V2s),
295 append_sample(V3, TiS, V3s), append_sample(V4, TiS, V4s),
296 append_sample(V5, TiS, V5s), append_sample(V6, TiS, V6s),
297 append_sample(V7, TiS, V7s), append_sample(V8, TiS, V8s)};
298 append({_Key, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15,
299 V16, V17, V18, V19, V20, V21, V22, V23, V24, V25, V26, V27, V28},
300 {samples, V1s, V2s, V3s, V4s, V5s, V6s, V7s, V8s, V9s, V10s, V11s, V12s,
301 V13s, V14s, V15s, V16s, V17s, V18s, V19s, V20s, V21s, V22s, V23s, V24s,
302 V25s, V26s, V27s, V28s},
303 TiS,
304 {_, _V1r, _V2r, _V3r, _V4r, _V5r, V6r, _V7r, V8r, V9r, _V10r, V11r,
305 V12r, V13r, V14r, V15r, _V16r, _V17r, _V18r, _V19r, _V20r, _V21r,
306 _V22r, _V23r, _V24r, _V25r, _V26r, V27r, V28r}) ->
307 %% This clause covers the coarse node stats, which must calculate the average
308 %% operation times for read, write, sync and seek. These differ from any other
309 %% statistic and must be caculated using the total time and counter of operations.
310 %% By calculating the new sample against the last sampled point, we provide
311 %% instant averages that truly reflect the behaviour of the system
312 %% during that space of time.
313 {samples, append_sample(V1, TiS, V1s), append_sample(V2, TiS, V2s),
314 append_sample(V3, TiS, V3s), append_sample(V4, TiS, V4s),
315 append_sample(V5, TiS, V5s), append_sample(V6, TiS, V6s),
316 append_sample(V7, TiS, V7s),
317 append_sample(avg_time(V8, V6, V8r, V6r), TiS, V8s),
318 append_sample(V9, TiS, V9s), append_sample(V10, TiS, V10s),
319 append_sample(avg_time(V11, V9, V11r, V9r), TiS, V11s),
320 append_sample(V12, TiS, V12s),
321 append_sample(avg_time(V13, V12, V13r, V12r), TiS, V13s),
322 append_sample(V14, TiS, V14s),
323 append_sample(avg_time(V15, V14, V15r, V14r), TiS, V15s),
324 append_sample(V16, TiS, V16s),
325 append_sample(V17, TiS, V17s), append_sample(V18, TiS, V18s),
326 append_sample(V19, TiS, V19s), append_sample(V20, TiS, V20s),
327 append_sample(V21, TiS, V21s), append_sample(V22, TiS, V22s),
328 append_sample(V23, TiS, V23s), append_sample(V24, TiS, V24s),
329 append_sample(V25, TiS, V25s), append_sample(V26, TiS, V26s),
330 append_sample(V27, TiS, V27s),
331 append_sample(avg_time(V28, V27, V28r, V27r), TiS, V28s)}.
332
333 append_sample(S, TS, List) ->
334 [[{sample, S}, {timestamp, TS}] | List].
335
336 blank() ->
337 Table = ets:new(rabbit_mgmt_stats, [ordered_set, public]),
338 Index = ets:new(rabbit_mgmt_stats, [bag, public]),
339 KeyIndex = ets:new(rabbit_mgmt_stats, [ordered_set, public]),
340 {Table, Index, KeyIndex}.
341
342 %%----------------------------------------------------------------------------
343 %% Event-GCing
344 %%----------------------------------------------------------------------------
345 %% Go through the list, amalgamating all too-old samples with the next
346 %% newest keepable one [0] (we move samples forward in time since the
347 %% semantics of a sample is "we had this many x by this time"). If the
348 %% sample is too old, but would not be too old if moved to a rounder
349 %% timestamp which does not exist then invent one and move it there
350 %% [1]. But if it's just outright too old, move it to the base [2].
351 gc(_Cutoff, [], _Table, _Keep) ->
352 ok;
353 gc(Cutoff, [Index | T], Table, Keep) ->
354 case ets:lookup(Table, Index) of
355 [S] ->
356 {Id, TS} = Key = element(1, S),
357 Keep1 = case keep(Cutoff, TS) of
358 keep ->
359 TS;
360 drop -> %% [2]
361 ets_update(Table, {Id, base}, S),
362 ets_delete_value(Table, Key),
363 Keep;
364 {move, D} when Keep =:= undefined -> %% [1]
365 ets_update(Table, {Id, TS + D}, S),
366 ets_delete_value(Table, Key),
367 TS + D;
368 {move, _} -> %% [0]
369 ets_update(Table, {Id, Keep}, S),
370 ets_delete_value(Table, Key),
371 Keep
372 end,
373 gc(Cutoff, T, Table, Keep1);
374 _ ->
375 gc(Cutoff, T, Table, Keep)
376 end.
377
378 keep({Policy, Now}, TS) ->
379 lists:foldl(fun ({AgeSec, DivisorSec}, Action) ->
380 prefer_action(
381 Action,
382 case (Now - TS) =< (AgeSec * 1000) of
383 true -> DivisorMillis = DivisorSec * 1000,
384 case TS rem DivisorMillis of
385 0 -> keep;
386 Rem -> {move, DivisorMillis - Rem}
387 end;
388 false -> drop
389 end)
390 end, drop, Policy).
391
392 prefer_action(keep, _) -> keep;
393 prefer_action(_, keep) -> keep;
394 prefer_action({move, A}, {move, B}) -> {move, lists:min([A, B])};
395 prefer_action({move, A}, drop) -> {move, A};
396 prefer_action(drop, {move, A}) -> {move, A};
397 prefer_action(drop, drop) -> drop.
398
399 %%----------------------------------------------------------------------------
400 %% ETS update
401 %%----------------------------------------------------------------------------
402 ets_update(Table, K, R, P, V) ->
403 try
404 ets:update_counter(Table, K, {P, V})
405 catch
406 _:_ ->
407 ets:insert(Table, new_record(K, R, P, V)),
408 insert_index(Table, K)
409 end.
410
411 insert_index(Table, Key) ->
412 insert_index(rabbit_mgmt_stats_tables:index(Table),
413 rabbit_mgmt_stats_tables:key_index(Table),
414 Key).
415
416 insert_index(_, _, {_, V}) when is_atom(V) ->
417 ok;
418 insert_index(Index, KeyIndex, {Id, _TS} = Key) ->
419 ets:insert(Index, Key),
420 ets:insert(KeyIndex, {Id}).
421
422 ets_update({Table, IndexTable, KeyIndexTable}, Key, Record) ->
423 try
424 ets:update_counter(Table, Key, record_to_list(Record))
425 catch
426 _:_ ->
427 ets:insert(Table, setelement(1, Record, Key)),
428 insert_index(IndexTable, KeyIndexTable, Key)
429 end;
430 ets_update(Table, Key, Record) ->
431 try
432 ets:update_counter(Table, Key, record_to_list(Record))
433 catch
434 _:_ ->
435 ets:insert(Table, setelement(1, Record, Key)),
436 insert_index(Table, Key)
437 end.
438
439 new_record(K, deliver_get, P, V) ->
440 setelement(P, {K, 0, 0, 0, 0}, V);
441 new_record(K, fine_stats, P, V) ->
442 setelement(P, {K, 0, 0, 0, 0, 0, 0, 0, 0}, V);
443 new_record(K, queue_msg_rates, P, V) ->
444 setelement(P, {K, 0, 0}, V);
445 new_record(K, queue_msg_counts, P, V) ->
446 setelement(P, {K, 0, 0, 0}, V);
447 new_record(K, coarse_node_stats, P, V) ->
448 setelement(P, {K, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
449 0, 0, 0, 0, 0, 0, 0, 0, 0}, V);
450 new_record(K, coarse_node_node_stats, P, V) ->
451 setelement(P, {K, 0, 0}, V);
452 new_record(K, coarse_conn_stats, P, V) ->
453 setelement(P, {K, 0, 0}, V);
454 new_record(K, process_stats, P, V) ->
455 setelement(P, {K, 0}, V).
456
457 %% Returns a list of {Position, Increment} to update the current record
458 record_to_list({_Key, V1}) ->
459 [{2, V1}];
460 record_to_list({_Key, V1, V2}) ->
461 [{2, V1}, {3, V2}];
462 record_to_list({_Key, V1, V2, V3}) ->
463 [{2, V1}, {3, V2}, {4, V3}];
464 record_to_list({_Key, V1, V2, V3, V4}) ->
465 [{2, V1}, {3, V2}, {4, V3}, {5, V4}];
466 record_to_list({_Key, V1, V2, V3, V4, V5, V6, V7, V8}) ->
467 [{2, V1}, {3, V2}, {4, V3}, {5, V4}, {6, V5}, {7, V6}, {8, V7}, {9, V8}];
468 record_to_list({_Key, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12,
469 V13, V14, V15, V16, V17, V18, V19, V20, V21, V22, V23, V24, V25,
470 V26, V27, V28}) ->
471 [{2, V1}, {3, V2}, {4, V3}, {5, V4}, {6, V5}, {7, V6}, {8, V7}, {9, V8},
472 {10, V9}, {11, V10}, {12, V11}, {13, V12}, {14, V13}, {15, V14},
473 {16, V15}, {17, V16}, {18, V17}, {19, V18}, {20, V19}, {21, V20},
474 {22, V21}, {23, V22}, {24, V23}, {25, V24}, {26, V25}, {27, V26},
475 {28, V27}, {29, V28}].
476
477 %%----------------------------------------------------------------------------
478
479 get_value({Table, _, _}, Id, Tag, Type) ->
480 get_value(Table, Id, Tag, Type);
481 get_value(Table, Id, Tag, Type) ->
482 Key = {Id, Tag},
483 case ets:lookup(Table, Key) of
484 [] -> empty(Key, Type);
485 [Elem] -> Elem
486 end.
487
488 ets_delete_value(Table, Key) ->
489 ets:delete_object(rabbit_mgmt_stats_tables:index(Table), Key),
490 ets:delete(Table, Key).
491
492 indexes({_, Index, _}, Id) ->
493 lists:sort(ets:lookup(Index, Id));
494 indexes(Table, Id) ->
495 lists:sort(ets:lookup(rabbit_mgmt_stats_tables:index(Table), Id)).
496
497 full_indexes(Table, Id) ->
498 full_indexes(Table, rabbit_mgmt_stats_tables:index(Table), Id).
499
500 full_indexes(_Table, Index, Id) ->
501 Indexes = ets:lookup(Index, Id),
502 [{Id, base}, {Id, total} | Indexes].
503
504 %%----------------------------------------------------------------------------
505 %% Match specs to select or delete from the ETS tables
506 %%----------------------------------------------------------------------------
507 match_spec_key_index(Id) ->
508 MatchHead = {partial_match(Id)},
509 Id0 = to_simple_match_spec(Id),
510 [{MatchHead, [{'==', Id0, '$1'}],['$_']}].
511
512 partial_match({_Id0, '_'}) ->
513 {'$1', '_'};
514 partial_match({'_', _Id1}) ->
515 {'_', '$1'}.
516
517 to_simple_match_spec({Id0, '_'}) when is_tuple(Id0) ->
518 {Id0};
519 to_simple_match_spec({'_', Id1}) when is_tuple(Id1) ->
520 {Id1};
521 to_simple_match_spec({Id0, '_'}) ->
522 Id0;
523 to_simple_match_spec({'_', Id1}) ->
524 Id1;
525 to_simple_match_spec(Id) when is_tuple(Id) ->
526 {Id};
527 to_simple_match_spec(Id) ->
528 Id.
529
530 to_match_condition({'_', Id1}) when is_tuple(Id1) ->
531 {'==', {Id1}, '$2'};
532 to_match_condition({'_', Id1}) ->
533 {'==', Id1, '$2'};
534 to_match_condition({Id0, '_'}) when is_tuple(Id0) ->
535 {'==', {Id0}, '$1'};
536 to_match_condition({Id0, '_'}) ->
537 {'==', Id0, '$1'}.
538
539 match_spec_keys(Id) ->
540 MatchCondition = to_match_condition(Id),
541 MatchHead = {{'$1', '$2'}},
542 [{MatchHead, [MatchCondition], [{{'$1', '$2'}}]}].
543
544 %%----------------------------------------------------------------------------
545 %% Format output
546 %%----------------------------------------------------------------------------
547 format_rate(deliver_get, {_, D, DN, G, GN}, {_, TD, TDN, TG, TGN}, Factor) ->
548 [
549 {deliver, TD}, {deliver_details, [{rate, apply_factor(D, Factor)}]},
104 format_samples(Samples, ESamples, ETotal) ->
105 lists:foldl(fun({TS, Sample}, {SamplesAcc, TotalsAcc}) ->
106 append_full_sample(TS, Sample, SamplesAcc, TotalsAcc)
107 end, {ESamples, ETotal}, Samples).
108
109 %% connection_stats_coarse_conn_stats, channel_stats_fine_stats,
110 %% vhost_stats_fine_stats, channel_exchange_stats_fine_stats,
111 %% queue_msg_stats, vhost_msg_stats
112 append_full_sample(TS, {V1, V2, V3}, {S1, S2, S3}, {T1, T2, T3}) ->
113 {{append_sample(V1, TS, S1), append_sample(V2, TS, S2), append_sample(V3, TS, S3)},
114 {V1 + T1, V2 + T2, V3 + T3}};
115 %% channel_queue_stats_deliver_stats, queue_stats_deliver_stats,
116 %% vhost_stats_deliver_stats, channel_stats_deliver_stats
117 append_full_sample(TS, {V1, V2, V3, V4, V5, V6, V7},
118 {S1, S2, S3, S4, S5, S6, S7},
119 {T1, T2, T3, T4, T5, T6, T7}) ->
120 {{append_sample(V1, TS, S1), append_sample(V2, TS, S2),
121 append_sample(V3, TS, S3), append_sample(V4, TS, S4),
122 append_sample(V5, TS, S5), append_sample(V6, TS, S6),
123 append_sample(V7, TS, S7)},
124 {V1 + T1, V2 + T2, V3 + T3, V4 + T4, V5 + T5, V6 + T6, V7 + T7}};
125 %% channel_process_stats, queue_stats_publish, queue_exchange_stats_publish,
126 %% exchange_stats_publish_out, exchange_stats_publish_in, queue_process_stats
127 append_full_sample(TS, {V1}, {S1}, {T1}) ->
128 {{append_sample(V1, TS, S1)}, {V1 + T1}};
129 %% node_coarse_stats
130 append_full_sample(TS, {V1, V2, V3, V4, V5, V6, V7, V8},
131 {S1, S2, S3, S4, S5, S6, S7, S8},
132 {T1, T2, T3, T4, T5, T6, T7, T8}) ->
133 {{append_sample(V1, TS, S1), append_sample(V2, TS, S2),
134 append_sample(V3, TS, S3), append_sample(V4, TS, S4),
135 append_sample(V5, TS, S5), append_sample(V6, TS, S6),
136 append_sample(V7, TS, S7), append_sample(V8, TS, S8)},
137 {V1 + T1, V2 + T2, V3 + T3, V4 + T4, V5 + T5, V6 + T6, V7 + T7, V8 + T8}};
138 %% node_persister_stats
139 append_full_sample(TS,
140 {V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14,
141 V15, V16, V17, V18, V19, V20},
142 {S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13, S14,
143 S15, S16, S17, S18, S19, S20},
144 {T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
145 T15, T16, T17, T18, T19, T20}
146 ) ->
147 {{append_sample(V1, TS, S1), append_sample(V2, TS, S2),
148 append_sample(V3, TS, S3), append_sample(V4, TS, S4),
149 append_sample(V5, TS, S5), append_sample(V6, TS, S6),
150 append_sample(V7, TS, S7), append_sample(V8, TS, S8),
151 append_sample(V9, TS, S9), append_sample(V10, TS, S10),
152 append_sample(V11, TS, S11), append_sample(V12, TS, S12),
153 append_sample(V13, TS, S13), append_sample(V14, TS, S14),
154 append_sample(V15, TS, S15), append_sample(V16, TS, S16),
155 append_sample(V17, TS, S17), append_sample(V18, TS, S18),
156 append_sample(V19, TS, S19), append_sample(V20, TS, S20)},
157 {V1 + T1, V2 + T2, V3 + T3, V4 + T4, V5 + T5, V6 + T6, V7 + T7, V8 + T8,
158 V9 + T9, V10 + T10, V11 + T11, V12 + T12, V13 + T13, V14 + T14, V15 + T15,
159 V16 + T16, V17 + T17, V18 + T18, V19 + T19, V20 + T20}};
160 %% node_node_coarse_stats, vhost_stats_coarse_connection_stats, queue_msg_rates,
161 %% vhost_msg_rates
162 append_full_sample(TS, {V1, V2}, {S1, S2}, {T1, T2}) ->
163 {{append_sample(V1, TS, S1), append_sample(V2, TS, S2)}, {V1 + T1, V2 + T2}}.
164
165
166 format_rate(connection_stats_coarse_conn_stats, {TR, TS, TRe}, {RR, RS, RRe}) ->
167 [
168 {send_oct, TS},
169 {send_oct_details, [{rate, RS}]},
170 {recv_oct, TR},
171 {recv_oct_details, [{rate, RR}]},
172 {reductions, TRe},
173 {reductions_details, [{rate, RRe}]}
174 ];
175 format_rate(vhost_stats_coarse_conn_stats, {TR, TS}, {RR, RS}) ->
176 [
177 {send_oct, TS},
178 {send_oct_details, [{rate, RS}]},
179 {recv_oct, TR},
180 {recv_oct_details, [{rate, RR}]}
181 ];
182 format_rate(Type, {TR, TW}, {RR, RW}) when Type =:= vhost_msg_rates;
183 Type =:= queue_msg_rates ->
184 [
185 {disk_reads, TR},
186 {disk_reads_details, [{rate, RR}]},
187 {disk_writes, TW},
188 {disk_writes_details, [{rate, RW}]}
189 ];
190 format_rate(Type, {TP, TC, TRe}, {RP, RC, RRe})
191 when Type =:= channel_stats_fine_stats;
192 Type =:= vhost_stats_fine_stats;
193 Type =:= channel_exchange_stats_fine_stats ->
194 [
195 {publish, TP},
196 {publish_details, [{rate, RP}]},
197 {confirm, TC},
198 {confirm_details, [{rate, RC}]},
199 {return_unroutable, TRe},
200 {return_unroutable_details, [{rate, RRe}]}
201 ];
202 format_rate(Type, {TG, TGN, TD, TDN, TR, TA, TDG},
203 {RG, RGN, RD, RDN, RR, RA, RDG})
204 when Type =:= channel_queue_stats_deliver_stats;
205 Type =:= channel_stats_deliver_stats;
206 Type =:= vhost_stats_deliver_stats;
207 Type =:= queue_stats_deliver_stats ->
208 [
209 {get, TG},
210 {get_details, [{rate, RG}]},
211 {get_no_ack, TGN},
212 {get_no_ack_details, [{rate, RGN}]},
213 {deliver, TD},
214 {deliver_details, [{rate, RD}]},
550215 {deliver_no_ack, TDN},
551 {deliver_no_ack_details, [{rate, apply_factor(DN, Factor)}]},
552 {get, TG}, {get_details, [{rate, apply_factor(G, Factor)}]},
553 {get_no_ack, TGN},
554 {get_no_ack_details, [{rate, apply_factor(GN, Factor)}]}
555 ];
556 format_rate(fine_stats, {_, P, PI, PO, A, D, C, RU, R},
557 {_, TP, TPI, TPO, TA, TD, TC, TRU, TR}, Factor) ->
558 [
559 {publish, TP}, {publish_details, [{rate, apply_factor(P, Factor)}]},
560 {publish_in, TPI},
561 {publish_in_details, [{rate, apply_factor(PI, Factor)}]},
562 {publish_out, TPO},
563 {publish_out_details, [{rate, apply_factor(PO, Factor)}]},
564 {ack, TA}, {ack_details, [{rate, apply_factor(A, Factor)}]},
565 {deliver_get, TD}, {deliver_get_details, [{rate, apply_factor(D, Factor)}]},
566 {confirm, TC}, {confirm_details, [{rate, apply_factor(C, Factor)}]},
567 {return_unroutable, TRU},
568 {return_unroutable_details, [{rate, apply_factor(RU, Factor)}]},
569 {redeliver, TR}, {redeliver_details, [{rate, apply_factor(R, Factor)}]}
570 ];
571 format_rate(queue_msg_rates, {_, R, W}, {_, TR, TW}, Factor) ->
572 [
573 {disk_reads, TR}, {disk_reads_details, [{rate, apply_factor(R, Factor)}]},
574 {disk_writes, TW}, {disk_writes_details, [{rate, apply_factor(W, Factor)}]}
575 ];
576 format_rate(queue_msg_counts, {_, M, MR, MU}, {_, TM, TMR, TMU}, Factor) ->
577 [
216 {deliver_no_ack_details, [{rate, RDN}]},
217 {redeliver, TR},
218 {redeliver_details, [{rate, RR}]},
219 {ack, TA},
220 {ack_details, [{rate, RA}]},
221 {deliver_get, TDG},
222 {deliver_get_details, [{rate, RDG}]}
223 ];
224 format_rate(Type, {TR}, {RR}) when Type =:= channel_process_stats;
225 Type =:= queue_process_stats ->
226 [
227 {reductions, TR},
228 {reductions_details, [{rate, RR}]}
229 ];
230 format_rate(exchange_stats_publish_out, {TP}, {RP}) ->
231 [
232 {publish_out, TP},
233 {publish_out_details, [{rate, RP}]}
234 ];
235 format_rate(exchange_stats_publish_in, {TP}, {RP}) ->
236 [
237 {publish_in, TP},
238 {publish_in_details, [{rate, RP}]}
239 ];
240 format_rate(Type, {TP}, {RP}) when Type =:= queue_stats_publish;
241 Type =:= queue_exchange_stats_publish ->
242 [
243 {publish, TP},
244 {publish_details, [{rate, RP}]}
245 ];
246 format_rate(Type, {TR, TU, TM}, {RR, RU, RM}) when Type =:= queue_msg_stats;
247 Type =:= vhost_msg_stats ->
248 [
249 {messages_ready, TR},
250 {messages_ready_details, [{rate, RR}]},
251 {messages_unacknowledged, TU},
252 {messages_unacknowledged_details, [{rate, RU}]},
578253 {messages, TM},
579 {messages_details, [{rate, apply_factor(M, Factor)}]},
580 {messages_ready, TMR},
581 {messages_ready_details, [{rate, apply_factor(MR, Factor)}]},
582 {messages_unacknowledged, TMU},
583 {messages_unacknowledged_details, [{rate, apply_factor(MU, Factor)}]}
584 ];
585 format_rate(coarse_node_stats,
586 {_, M, F, S, P, D, IR, IB, IA, IWC, IWB, IWAT, IS, ISAT, ISC,
587 ISEAT, IRC, MRTC, MDTC, MSRC, MSWC, QIJWC, QIWC, QIRC, GC, GCW, CS,
588 IO, IOAT},
589 {_, TM, TF, TS, TP, TD, TIR, TIB, TIA, TIWC, TIWB, TIWAT, TIS,
590 TISAT, TISC, TISEAT, TIRC, TMRTC, TMDTC, TMSRC, TMSWC, TQIJWC,
591 TQIWC, TQIRC, TGC, TGCW, TCS, TIO, TIOAT}, Factor) ->
254 {messages_details, [{rate, RM}]}
255 ];
256 format_rate(node_coarse_stats, {TF, TS, TM, TD, TP, TGC, TGCW, TCS},
257 {RF, RS, RM, RD, RP, RGC, RGCW, RCS}) ->
258 [
259 {mem_used, TM},
260 {mem_used_details, [{rate, RM}]},
261 {fd_used, TF},
262 {fd_used_details, [{rate, RF}]},
263 {sockets_used, TS},
264 {sockets_used_details, [{rate, RS}]},
265 {proc_used, TP},
266 {proc_used_details, [{rate, RP}]},
267 {disk_free, TD},
268 {disk_free_details, [{rate, RD}]},
269 {gc_num, TGC},
270 {gc_num_details, [{rate, RGC}]},
271 {gc_bytes_reclaimed, TGCW},
272 {gc_bytes_reclaimed_details, [{rate, RGCW}]},
273 {context_switches, TCS},
274 {context_switches_details, [{rate, RCS}]}
275 ];
276 format_rate(node_persister_stats,
277 {TIR, TIB, TIA, TIWC, TIWB, TIWAT, TIS, TISAT, TISC,
278 TISEAT, TIRC, TMRTC, TMDTC, TMSRC, TMSWC, TQIJWC, TQIWC, TQIRC,
279 TIO, TIOAT},
280 {RIR, RIB, RIA, RIWC, RIWB, RIWAT, RIS, RISAT, RISC,
281 RISEAT, RIRC, RMRTC, RMDTC, RMSRC, RMSWC, RQIJWC, RQIWC, RQIRC,
282 RIO, RIOAT}) ->
592283 %% Calculates average times for read/write/sync/seek from the
593284 %% accumulated time and count
594285 %% io_<op>_avg_time is the average operation time for the life of the node
595286 %% io_<op>_avg_time_details/rate is the average operation time during the
596287 %% last time unit calculated (thus similar to an instant rate)
597288 [
289 {io_read_count, TIR},
290 {io_read_count_details, [{rate, RIR}]},
291 {io_read_bytes, TIB},
292 {io_read_bytes_details, [{rate, RIB}]},
293 {io_read_avg_time, avg_time(TIA, TIR)},
294 {io_read_avg_time_details, [{rate, avg_time(RIA, RIR)}]},
295 {io_write_count, TIWC},
296 {io_write_count_details, [{rate, RIWC}]},
297 {io_write_bytes, TIWB},
298 {io_write_bytes_details, [{rate, RIWB}]},
299 {io_write_avg_time, avg_time(TIWAT, TIWC)},
300 {io_write_avg_time_details, [{rate, avg_time(RIWAT, RIWC)}]},
301 {io_sync_count, TIS},
302 {io_sync_count_details, [{rate, RIS}]},
303 {io_sync_avg_time, avg_time(TISAT, TIS)},
304 {io_sync_avg_time_details, [{rate, avg_time(RISAT, RIS)}]},
305 {io_seek_count, TISC},
306 {io_seek_count_details, [{rate, RISC}]},
307 {io_seek_avg_time, avg_time(TISEAT, TISC)},
308 {io_seek_avg_time_details, [{rate, avg_time(RISEAT, RISC)}]},
309 {io_reopen_count, TIRC},
310 {io_reopen_count_details, [{rate, RIRC}]},
311 {mnesia_ram_tx_count, TMRTC},
312 {mnesia_ram_tx_count_details, [{rate, RMRTC}]},
313 {mnesia_disk_tx_count, TMDTC},
314 {mnesia_disk_tx_count_details, [{rate, RMDTC}]},
315 {msg_store_read_count, TMSRC},
316 {msg_store_read_count_details, [{rate, RMSRC}]},
317 {msg_store_write_count, TMSWC},
318 {msg_store_write_count_details, [{rate, RMSWC}]},
319 {queue_index_journal_write_count, TQIJWC},
320 {queue_index_journal_write_count_details, [{rate, RQIJWC}]},
321 {queue_index_write_count, TQIWC},
322 {queue_index_write_count_details, [{rate, RQIWC}]},
323 {queue_index_read_count, TQIRC},
324 {queue_index_read_count_details, [{rate, RQIRC}]},
325 {io_file_handle_open_attempt_count, TIO},
326 {io_file_handle_open_attempt_count_details, [{rate, RIO}]},
327 {io_file_handle_open_attempt_avg_time, avg_time(TIOAT, TIO)},
328 {io_file_handle_open_attempt_avg_time_details, [{rate, avg_time(RIOAT, RIO)}]}
329 ];
330 format_rate(node_node_coarse_stats, {TS, TR}, {RS, RR}) ->
331 [
332 {send_bytes, TS},
333 {send_bytes_details, [{rate, RS}]},
334 {recv_bytes, TR},
335 {recv_bytes_details, [{rate, RR}]}
336 ].
337
338 format_rate(connection_stats_coarse_conn_stats, {TR, TS, TRe}, {RR, RS, RRe},
339 {SR, SS, SRe}, {STR, STS, STRe}, Length) ->
340 [
341 {send_oct, TS},
342 {send_oct_details, [{rate, RS},
343 {samples, SS}] ++ average(SS, STS, Length)},
344 {recv_oct, TR},
345 {recv_oct_details, [{rate, RR},
346 {samples, SR}] ++ average(SR, STR, Length)},
347 {reductions, TRe},
348 {reductions_details, [{rate, RRe},
349 {samples, SRe}] ++ average(SRe, STRe, Length)}
350 ];
351 format_rate(vhost_stats_coarse_conn_stats, {TR, TS}, {RR, RS}, {SR, SS},
352 {STR, STS}, Length) ->
353 [
354 {send_oct, TS},
355 {send_oct_details, [{rate, RS},
356 {samples, SS}] ++ average(SS, STS, Length)},
357 {recv_oct, TR},
358 {recv_oct_details, [{rate, RR},
359 {samples, SR}] ++ average(SR, STR, Length)}
360 ];
361 format_rate(Type, {TR, TW}, {RR, RW}, {SR, SW}, {STR, STW}, Length)
362 when Type =:= vhost_msg_rates;
363 Type =:= queue_msg_rates ->
364 [
365 {disk_reads, TR},
366 {disk_reads_details, [{rate, RR},
367 {samples, SR}] ++ average(SR, STR, Length)},
368 {disk_writes, TW},
369 {disk_writes_details, [{rate, RW},
370 {samples, SW}] ++ average(SW, STW, Length)}
371 ];
372 format_rate(Type, {TP, TC, TRe}, {RP, RC, RRe},
373 {SP, SC, SRe}, {STP, STC, STRe}, Length)
374 when Type =:= channel_stats_fine_stats;
375 Type =:= vhost_stats_fine_stats;
376 Type =:= channel_exchange_stats_fine_stats ->
377 [
378 {publish, TP},
379 {publish_details, [{rate, RP},
380 {samples, SP}] ++ average(SP, STP, Length)},
381 {confirm, TC},
382 {confirm_details, [{rate, RC},
383 {samples, SC}] ++ average(SC, STC, Length)},
384 {return_unroutable, TRe},
385 {return_unroutable_details, [{rate, RRe},
386 {samples, SRe}] ++ average(SRe, STRe, Length)}
387 ];
388 format_rate(Type, {TG, TGN, TD, TDN, TR, TA, TDG}, {RG, RGN, RD, RDN, RR, RA, RDG},
389 {SG, SGN, SD, SDN, SR, SA, SDG}, {STG, STGN, STD, STDN, STR, STA, STDG},
390 Length)
391 when Type =:= channel_queue_stats_deliver_stats;
392 Type =:= channel_stats_deliver_stats;
393 Type =:= vhost_stats_deliver_stats;
394 Type =:= queue_stats_deliver_stats ->
395 [
396 {get, TG},
397 {get_details, [{rate, RG},
398 {samples, SG}] ++ average(SG, STG, Length)},
399 {get_no_ack, TGN},
400 {get_no_ack_details, [{rate, RGN},
401 {samples, SGN}] ++ average(SGN, STGN, Length)},
402 {deliver, TD},
403 {deliver_details, [{rate, RD},
404 {samples, SD}] ++ average(SD, STD, Length)},
405 {deliver_no_ack, TDN},
406 {deliver_no_ack_details, [{rate, RDN},
407 {samples, SDN}] ++ average(SDN, STDN, Length)},
408 {redeliver, TR},
409 {redeliver_details, [{rate, RR},
410 {samples, SR}] ++ average(SR, STR, Length)},
411 {ack, TA},
412 {ack_details, [{rate, RA},
413 {samples, SA}] ++ average(SA, STA, Length)},
414 {deliver_get, TDG},
415 {deliver_get_details, [{rate, RDG},
416 {samples, SDG}] ++ average(SDG, STDG, Length)}
417 ];
418 format_rate(Type, {TR}, {RR}, {SR}, {STR}, Length)
419 when Type =:= channel_process_stats;
420 Type =:= queue_process_stats ->
421 [
422 {reductions, TR},
423 {reductions_details, [{rate, RR},
424 {samples, SR}] ++ average(SR, STR, Length)}
425 ];
426 format_rate(exchange_stats_publish_out, {TP}, {RP}, {SP}, {STP}, Length) ->
427 [
428 {publish_out, TP},
429 {publish_out_details, [{rate, RP},
430 {samples, SP}] ++ average(SP, STP, Length)}
431 ];
432 format_rate(exchange_stats_publish_in, {TP}, {RP}, {SP}, {STP}, Length) ->
433 [
434 {publish_in, TP},
435 {publish_in_details, [{rate, RP},
436 {samples, SP}] ++ average(SP, STP, Length)}
437 ];
438 format_rate(Type, {TP}, {RP}, {SP}, {STP}, Length)
439 when Type =:= queue_stats_publish;
440 Type =:= queue_exchange_stats_publish ->
441 [
442 {publish, TP},
443 {publish_details, [{rate, RP},
444 {samples, SP}] ++ average(SP, STP, Length)}
445 ];
446 format_rate(Type, {TR, TU, TM}, {RR, RU, RM}, {SR, SU, SM}, {STR, STU, STM},
447 Length) when Type =:= queue_msg_stats;
448 Type =:= vhost_msg_stats ->
449 [
450 {messages_ready, TR},
451 {messages_ready_details, [{rate, RR},
452 {samples, SR}] ++ average(SR, STR, Length)},
453 {messages_unacknowledged, TU},
454 {messages_unacknowledged_details, [{rate, RU},
455 {samples, SU}] ++ average(SU, STU, Length)},
456 {messages, TM},
457 {messages_details, [{rate, RM},
458 {samples, SM}] ++ average(SM, STM, Length)}
459 ];
460 format_rate(node_coarse_stats, {TF, TS, TM, TD, TP, TGC, TGCW, TCS},
461 {RF, RS, RM, RD, RP, RGC, RGCW, RCS},
462 {SF, SS, SM, SD, SP, SGC, SGCW, SCS},
463 {STF, STS, STM, STD, STP, STGC, STGCW, STCS}, Length) ->
464 [
598465 {mem_used, TM},
599 {mem_used_details, [{rate, apply_factor(M, Factor)}]},
466 {mem_used_details, [{rate, RM},
467 {samples, SM}] ++ average(SM, STM, Length)},
600468 {fd_used, TF},
601 {fd_used_details, [{rate, apply_factor(F, Factor)}]},
469 {fd_used_details, [{rate, RF},
470 {samples, SF}] ++ average(SF, STF, Length)},
602471 {sockets_used, TS},
603 {sockets_used_details, [{rate, apply_factor(S, Factor)}]},
472 {sockets_used_details, [{rate, RS},
473 {samples, SS}] ++ average(SS, STS, Length)},
604474 {proc_used, TP},
605 {proc_used_details, [{rate, apply_factor(P, Factor)}]},
475 {proc_used_details, [{rate, RP},
476 {samples, SP}] ++ average(SP, STP, Length)},
606477 {disk_free, TD},
607 {disk_free_details, [{rate, apply_factor(D, Factor)}]},
478 {disk_free_details, [{rate, RD},
479 {samples, SD}] ++ average(SD, STD, Length)},
480 {gc_num, TGC},
481 {gc_num_details, [{rate, RGC},
482 {samples, SGC}] ++ average(SGC, STGC, Length)},
483 {gc_bytes_reclaimed, TGCW},
484 {gc_bytes_reclaimed_details, [{rate, RGCW},
485 {samples, SGCW}] ++ average(SGCW, STGCW, Length)},
486 {context_switches, TCS},
487 {context_switches_details, [{rate, RCS},
488 {samples, SCS}] ++ average(SCS, STCS, Length)}
489 ];
490 format_rate(node_persister_stats,
491 {TIR, TIB, TIA, TIWC, TIWB, TIWAT, TIS, TISAT, TISC,
492 TISEAT, TIRC, TMRTC, TMDTC, TMSRC, TMSWC, TQIJWC, TQIWC, TQIRC,
493 TIO, TIOAT},
494 {RIR, RIB, _RIA, RIWC, RIWB, _RIWAT, RIS, _RISAT, RISC,
495 _RISEAT, RIRC, RMRTC, RMDTC, RMSRC, RMSWC, RQIJWC, RQIWC, RQIRC,
496 RIO, _RIOAT},
497 {SIR, SIB, SIA, SIWC, SIWB, SIWAT, SIS, SISAT, SISC,
498 SISEAT, SIRC, SMRTC, SMDTC, SMSRC, SMSWC, SQIJWC, SQIWC, SQIRC,
499 SIO, SIOAT},
500 {STIR, STIB, _STIA, STIWC, STIWB, _STIWAT, STIS, _STISAT, STISC,
501 _STISEAT, STIRC, STMRTC, STMDTC, STMSRC, STMSWC, STQIJWC, STQIWC, STQIRC,
502 STIO, _STIOAT}, Length) ->
503 %% Calculates average times for read/write/sync/seek from the
504 %% accumulated time and count
505 %% io_<op>_avg_time is the average operation time for the life of the node
506 %% io_<op>_avg_time_details/rate is the average operation time during the
507 %% last time unit calculated (thus similar to an instant rate)
508
509 [
608510 {io_read_count, TIR},
609 {io_read_count_details, [{rate, apply_factor(IR, Factor)}]},
511 {io_read_count_details, [{rate, RIR},
512 {samples, SIR}] ++ average(SIR, STIR, Length)},
610513 {io_read_bytes, TIB},
611 {io_read_bytes_details, [{rate, apply_factor(IB, Factor)}]},
514 {io_read_bytes_details, [{rate, RIB},
515 {samples, SIB}] ++ average(SIB, STIB, Length)},
612516 {io_read_avg_time, avg_time(TIA, TIR)},
613 {io_read_avg_time_details, [{rate, avg_time(IA, IR)}]},
517 {io_read_avg_time_details, [{samples, unit_samples(SIA, SIR)}] ++
518 avg_time_details(avg_time(TIA, TIR))},
614519 {io_write_count, TIWC},
615 {io_write_count_details, [{rate, apply_factor(IWC, Factor)}]},
520 {io_write_count_details, [{rate, RIWC},
521 {samples, SIWC}] ++ average(SIWC, STIWC, Length)},
616522 {io_write_bytes, TIWB},
617 {io_write_bytes_details, [{rate, apply_factor(IWB, Factor)}]},
523 {io_write_bytes_details, [{rate, RIWB},
524 {samples, SIWB}] ++ average(SIWB, STIWB, Length)},
618525 {io_write_avg_time, avg_time(TIWAT, TIWC)},
619 {io_write_avg_time_details, [{rate, avg_time(IWAT, IWC)}]},
526 {io_write_avg_time_details, [{samples, unit_samples(SIWAT, SIWC)}] ++
527 avg_time_details(avg_time(TIWAT, TIWC))},
620528 {io_sync_count, TIS},
621 {io_sync_count_details, [{rate, apply_factor(IS, Factor)}]},
529 {io_sync_count_details, [{rate, RIS},
530 {samples, SIS}] ++ average(SIS, STIS, Length)},
622531 {io_sync_avg_time, avg_time(TISAT, TIS)},
623 {io_sync_avg_time_details, [{rate, avg_time(ISAT, IS)}]},
532 {io_sync_avg_time_details, [{samples, unit_samples(SISAT, SIS)}] ++
533 avg_time_details(avg_time(TISAT, TIS))},
624534 {io_seek_count, TISC},
625 {io_seek_count_details, [{rate, apply_factor(ISC, Factor)}]},
535 {io_seek_count_details, [{rate, RISC},
536 {samples, SISC}] ++ average(SISC, STISC, Length)},
626537 {io_seek_avg_time, avg_time(TISEAT, TISC)},
627 {io_seek_avg_time_details, [{rate, avg_time(ISEAT, ISC)}]},
538 {io_seek_avg_time_details, [{samples, unit_samples(SISEAT, SISC)}] ++
539 avg_time_details(avg_time(TISEAT, TISC))},
628540 {io_reopen_count, TIRC},
629 {io_reopen_count_details, [{rate, apply_factor(IRC, Factor)}]},
541 {io_reopen_count_details, [{rate, RIRC},
542 {samples, SIRC}] ++ average(SIRC, STIRC, Length)},
630543 {mnesia_ram_tx_count, TMRTC},
631 {mnesia_ram_tx_count_details, [{rate, apply_factor(MRTC, Factor)}]},
544 {mnesia_ram_tx_count_details, [{rate, RMRTC},
545 {samples, SMRTC}] ++ average(SMRTC, STMRTC, Length)},
632546 {mnesia_disk_tx_count, TMDTC},
633 {mnesia_disk_tx_count_details, [{rate, apply_factor(MDTC, Factor)}]},
547 {mnesia_disk_tx_count_details, [{rate, RMDTC},
548 {samples, SMDTC}] ++ average(SMDTC, STMDTC, Length)},
634549 {msg_store_read_count, TMSRC},
635 {msg_store_read_count_details, [{rate, apply_factor(MSRC, Factor)}]},
550 {msg_store_read_count_details, [{rate, RMSRC},
551 {samples, SMSRC}] ++ average(SMSRC, STMSRC, Length)},
636552 {msg_store_write_count, TMSWC},
637 {msg_store_write_count_details, [{rate, apply_factor(MSWC, Factor)}]},
553 {msg_store_write_count_details, [{rate, RMSWC},
554 {samples, SMSWC}] ++ average(SMSWC, STMSWC, Length)},
638555 {queue_index_journal_write_count, TQIJWC},
639 {queue_index_journal_write_count_details, [{rate, apply_factor(QIJWC, Factor)}]},
556 {queue_index_journal_write_count_details, [{rate, RQIJWC},
557 {samples, SQIJWC}] ++ average(SQIJWC, STQIJWC, Length)},
640558 {queue_index_write_count, TQIWC},
641 {queue_index_write_count_details, [{rate, apply_factor(QIWC, Factor)}]},
559 {queue_index_write_count_details, [{rate, RQIWC},
560 {samples, SQIWC}] ++ average(SQIWC, STQIWC, Length)},
642561 {queue_index_read_count, TQIRC},
643 {queue_index_read_count_details, [{rate, apply_factor(QIRC, Factor)}]},
644 {gc_num, TGC},
645 {gc_num_details, [{rate, apply_factor(GC, Factor)}]},
646 {gc_bytes_reclaimed, TGCW},
647 {gc_bytes_reclaimed_details, [{rate, apply_factor(GCW, Factor)}]},
648 {context_switches, TCS},
649 {context_switches_details, [{rate, apply_factor(CS, Factor)}]},
562 {queue_index_read_count_details, [{rate, RQIRC},
563 {samples, SQIRC}] ++ average(SQIRC, STQIRC, Length)},
650564 {io_file_handle_open_attempt_count, TIO},
651 {io_file_handle_open_attempt_count_details, [{rate, apply_factor(IO, Factor)}]},
652 {io_file_handle_open_attempt_avg_time, avg_time(TIOAT, TIO)},
653 {io_file_handle_open_attempt_avg_time_details, [{rate, avg_time(IOAT, IO)}]}
654 ];
655 format_rate(coarse_node_node_stats, {_, S, R}, {_, TS, TR}, Factor) ->
656 [
657 {send_bytes, TS},
658 {send_bytes_details, [{rate, apply_factor(S, Factor)}]},
659 {send_bytes, TR},
660 {send_bytes_details, [{rate, apply_factor(R, Factor)}]}
661 ];
662 format_rate(coarse_conn_stats, {_, R, S}, {_, TR, TS}, Factor) ->
663 [
664 {send_oct, TS},
665 {send_oct_details, [{rate, apply_factor(S, Factor)}]},
666 {recv_oct, TR},
667 {recv_oct_details, [{rate, apply_factor(R, Factor)}]}
668 ];
669 format_rate(process_stats, {_, R}, {_, TR}, Factor) ->
670 [
671 {reductions, TR},
672 {reductions_details, [{rate, apply_factor(R, Factor)}]}
673 ].
674
675 format_rate(deliver_get, {_, D, DN, G, GN}, {_, TD, TDN, TG, TGN},
676 {_, SD, SDN, SG, SGN}, Factor) ->
677 Length = length(SD),
678 [
679 {deliver, TD}, {deliver_details, [{rate, apply_factor(D, Factor)},
680 {samples, SD}] ++ average(SD, Length)},
681 {deliver_no_ack, TDN},
682 {deliver_no_ack_details, [{rate, apply_factor(DN, Factor)},
683 {samples, SDN}] ++ average(SDN, Length)},
684 {get, TG}, {get_details, [{rate, apply_factor(G, Factor)},
685 {samples, SG}] ++ average(SG, Length)},
686 {get_no_ack, TGN},
687 {get_no_ack_details, [{rate, apply_factor(GN, Factor)},
688 {samples, SGN}] ++ average(SGN, Length)}
689 ];
690 format_rate(fine_stats, {_, P, PI, PO, A, D, C, RU, R},
691 {_, TP, TPI, TPO, TA, TD, TC, TRU, TR},
692 {_, SP, SPI, SPO, SA, SD, SC, SRU, SR}, Factor) ->
693 Length = length(SP),
694 [
695 {publish, TP},
696 {publish_details, [{rate, apply_factor(P, Factor)},
697 {samples, SP}] ++ average(SP, Length)},
698 {publish_in, TPI},
699 {publish_in_details, [{rate, apply_factor(PI, Factor)},
700 {samples, SPI}] ++ average(SPI, Length)},
701 {publish_out, TPO},
702 {publish_out_details, [{rate, apply_factor(PO, Factor)},
703 {samples, SPO}] ++ average(SPO, Length)},
704 {ack, TA}, {ack_details, [{rate, apply_factor(A, Factor)},
705 {samples, SA}] ++ average(SA, Length)},
706 {deliver_get, TD},
707 {deliver_get_details, [{rate, apply_factor(D, Factor)},
708 {samples, SD}] ++ average(SD, Length)},
709 {confirm, TC},
710 {confirm_details, [{rate, apply_factor(C, Factor)},
711 {samples, SC}] ++ average(SC, Length)},
712 {return_unroutable, TRU},
713 {return_unroutable_details, [{rate, apply_factor(RU, Factor)},
714 {samples, SRU}] ++ average(SRU, Length)},
715 {redeliver, TR},
716 {redeliver_details, [{rate, apply_factor(R, Factor)},
717 {samples, SR}] ++ average(SR, Length)}
718 ];
719 format_rate(queue_msg_rates, {_, R, W}, {_, TR, TW}, {_, SR, SW}, Factor) ->
720 Length = length(SR),
721 [
722 {disk_reads, TR},
723 {disk_reads_details, [{rate, apply_factor(R, Factor)},
724 {samples, SR}] ++ average(SR, Length)},
725 {disk_writes, TW},
726 {disk_writes_details, [{rate, apply_factor(W, Factor)},
727 {samples, SW}] ++ average(SW, Length)}
728 ];
729 format_rate(queue_msg_counts, {_, M, MR, MU}, {_, TM, TMR, TMU},
730 {_, SM, SMR, SMU}, Factor) ->
731 Length = length(SM),
732 [
733 {messages, TM},
734 {messages_details, [{rate, apply_factor(M, Factor)},
735 {samples, SM}] ++ average(SM, Length)},
736 {messages_ready, TMR},
737 {messages_ready_details, [{rate, apply_factor(MR, Factor)},
738 {samples, SMR}] ++ average(SMR, Length)},
739 {messages_unacknowledged, TMU},
740 {messages_unacknowledged_details, [{rate, apply_factor(MU, Factor)},
741 {samples, SMU}] ++ average(SMU, Length)}
742 ];
743 format_rate(coarse_node_stats,
744 {_, M, F, S, P, D, IR, IB, IA, IWC, IWB, IWAT, IS, ISAT, ISC,
745 ISEAT, IRC, MRTC, MDTC, MSRC, MSWC, QIJWC, QIWC, QIRC, GC, GCW, CS,
746 IO, IOAT},
747 {_, TM, TF, TS, TP, TD, TIR, TIB, TIA, TIWC, TIWB, TIWAT, TIS,
748 TISAT, TISC, TISEAT, TIRC, TMRTC, TMDTC, TMSRC, TMSWC, TQIJWC,
749 TQIWC, TQIRC, TGC, TGCW, TCS, TIO, TIOAT},
750 {_, SM, SF, SS, SP, SD, SIR, SIB, SIA, SIWC, SIWB, SIWAT, SIS,
751 SISAT, SISC, SISEAT, SIRC, SMRTC, SMDTC, SMSRC, SMSWC, SQIJWC,
752 SQIWC, SQIRC, SGC, SGCW, SCS, SIO, SIOAT}, Factor) ->
753 %% Calculates average times for read/write/sync/seek from the
754 %% accumulated time and count.
755 %% io_<op>_avg_time is the average operation time for the life of the node.
756 %% io_<op>_avg_time_details/rate is the average operation time during the
757 %% last time unit calculated (thus similar to an instant rate).
758 %% io_<op>_avg_time_details/samples contain the average operation time
759 %% during each time unit requested.
760 %% io_<op>_avg_time_details/avg_rate is meaningless here, but we keep it
761 %% to maintain an uniform API with all the other metrics.
762 %% io_<op>_avg_time_details/avg is the average of the samples taken over
763 %% the requested period of time.
764 Length = length(SM),
765 [
766 {mem_used, TM},
767 {mem_used_details, [{rate, apply_factor(M, Factor)},
768 {samples, SM}] ++ average(SM, Length)},
769 {fd_used, TF},
770 {fd_used_details, [{rate, apply_factor(F, Factor)},
771 {samples, SF}] ++ average(SF, Length)},
772 {sockets_used, TS},
773 {sockets_used_details, [{rate, apply_factor(S, Factor)},
774 {samples, SS}] ++ average(SS, Length)},
775 {proc_used, TP},
776 {proc_used_details, [{rate, apply_factor(P, Factor)},
777 {samples, SP}] ++ average(SP, Length)},
778 {disk_free, TD},
779 {disk_free_details, [{rate, apply_factor(D, Factor)},
780 {samples, SD}] ++ average(SD, Length)},
781 {io_read_count, TIR},
782 {io_read_count_details, [{rate, apply_factor(IR, Factor)},
783 {samples, SIR}] ++ average(SIR, Length)},
784 {io_read_bytes, TIB},
785 {io_read_bytes_details, [{rate, apply_factor(IB, Factor)},
786 {samples, SIB}] ++ average(SIB, Length)},
787 {io_read_avg_time, avg_time(TIA, TIR)},
788 {io_read_avg_time_details, [{rate, avg_time(IA, IR)},
789 {samples, SIA}] ++ average(SIA, Length)},
790 {io_write_count, TIWC},
791 {io_write_count_details, [{rate, apply_factor(IWC, Factor)},
792 {samples, SIWC}] ++ average(SIWC, Length)},
793 {io_write_bytes, TIWB},
794 {io_write_bytes_details, [{rate, apply_factor(IWB, Factor)},
795 {samples, SIWB}] ++ average(SIWB, Length)},
796 {io_write_avg_time, avg_time(TIWAT, TIWC)},
797 {io_write_avg_time_details, [{rate, avg_time(IWAT, TIWC)},
798 {samples, SIWAT}] ++ average(SIWAT, Length)},
799 {io_sync_count, TIS},
800 {io_sync_count_details, [{rate, apply_factor(IS, Factor)},
801 {samples, SIS}] ++ average(SIS, Length)},
802 {io_sync_avg_time, avg_time(TISAT, TIS)},
803 {io_sync_avg_time_details, [{rate, avg_time(ISAT, IS)},
804 {samples, SISAT}] ++ average(SISAT, Length)},
805 {io_seek_count, TISC},
806 {io_seek_count_details, [{rate, apply_factor(ISC, Factor)},
807 {samples, SISC}] ++ average(SISC, Length)},
808 {io_seek_avg_time, avg_time(TISEAT, TISC)},
809 {io_seek_avg_time_details, [{rate, avg_time(ISEAT, ISC)},
810 {samples, SISEAT}] ++ average(SISEAT, Length)},
811 {io_reopen_count, TIRC},
812 {io_reopen_count_details, [{rate, apply_factor(IRC, Factor)},
813 {samples, SIRC}] ++ average(SIRC, Length)},
814 {mnesia_ram_tx_count, TMRTC},
815 {mnesia_ram_tx_count_details, [{rate, apply_factor(MRTC, Factor)},
816 {samples, SMRTC}] ++ average(SMRTC, Length)},
817 {mnesia_disk_tx_count, TMDTC},
818 {mnesia_disk_tx_count_details, [{rate, apply_factor(MDTC, Factor)},
819 {samples, SMDTC}] ++ average(SMDTC, Length)},
820 {msg_store_read_count, TMSRC},
821 {msg_store_read_count_details, [{rate, apply_factor(MSRC, Factor)},
822 {samples, SMSRC}] ++ average(SMSRC, Length)},
823 {msg_store_write_count, TMSWC},
824 {msg_store_write_count_details, [{rate, apply_factor(MSWC, Factor)},
825 {samples, SMSWC}] ++ average(SMSWC, Length)},
826 {queue_index_journal_write_count, TQIJWC},
827 {queue_index_journal_write_count_details,
828 [{rate, apply_factor(QIJWC, Factor)},
829 {samples, SQIJWC}] ++ average(SQIJWC, Length)},
830 {queue_index_write_count, TQIWC},
831 {queue_index_write_count_details, [{rate, apply_factor(QIWC, Factor)},
832 {samples, SQIWC}] ++ average(SQIWC, Length)},
833 {queue_index_read_count, TQIRC},
834 {queue_index_read_count_details, [{rate, apply_factor(QIRC, Factor)},
835 {samples, SQIRC}] ++ average(SQIRC, Length)},
836 {gc_num, TGC},
837 {gc_num_details, [{rate, apply_factor(GC, Factor)},
838 {samples, SGC}] ++ average(SGC, Length)},
839 {gc_bytes_reclaimed, TGCW},
840 {gc_bytes_reclaimed_details, [{rate, apply_factor(GCW, Factor)},
841 {samples, SGCW}] ++ average(SGCW, Length)},
842 {context_switches, TCS},
843 {context_switches_details, [{rate, apply_factor(CS, Factor)},
844 {samples, SCS}] ++ average(SCS, Length)},
845 {io_file_handle_open_attempt_count, TIO},
846 {io_file_handle_open_attempt_count_details,
847 [{rate, apply_factor(IO, Factor)},
848 {samples, SIO}] ++ average(SIO, Length)},
565 {io_file_handle_open_attempt_count_details, [{rate, RIO},
566 {samples, SIO}] ++ average(SIO, STIO, Length)},
849567 {io_file_handle_open_attempt_avg_time, avg_time(TIOAT, TIO)},
850568 {io_file_handle_open_attempt_avg_time_details,
851 [{rate, avg_time(IOAT, IO)},
852 {samples, SIOAT}] ++ average(SIOAT, Length)}
853 ];
854 format_rate(coarse_node_node_stats, {_, S, R}, {_, TS, TR}, {_, SS, SR},
855 Factor) ->
856 Length = length(SS),
569 [{samples, unit_samples(SIOAT, SIO)}] ++ avg_time_details(avg_time(TIOAT, TIO))}
570 ];
571 format_rate(node_node_coarse_stats, {TS, TR}, {RS, RR}, {SS, SR}, {STS, STR}, Length) ->
857572 [
858573 {send_bytes, TS},
859 {send_bytes_details, [{rate, apply_factor(S, Factor)},
860 {samples, SS}] ++ average(SS, Length)},
861 {send_bytes, TR},
862 {send_bytes_details, [{rate, apply_factor(R, Factor)},
863 {samples, SR}] ++ average(SR, Length)}
864 ];
865 format_rate(coarse_conn_stats, {_, R, S}, {_, TR, TS}, {_, SR, SS},
866 Factor) ->
867 Length = length(SS),
868 [
869 {send_oct, TS},
870 {send_oct_details, [{rate, apply_factor(S, Factor)},
871 {samples, SS}] ++ average(SS, Length)},
872 {recv_oct, TR},
873 {recv_oct_details, [{rate, apply_factor(R, Factor)},
874 {samples, SR}] ++ average(SR, Length)}
875 ];
876 format_rate(process_stats, {_, R}, {_, TR}, {_, SR}, Factor) ->
877 Length = length(SR),
878 [
879 {reductions, TR},
880 {reductions_details, [{rate, apply_factor(R, Factor)},
881 {samples, SR}] ++ average(SR, Length)}
574 {send_bytes_details, [{rate, RS},
575 {samples, SS}] ++ average(SS, STS, Length)},
576 {recv_bytes, TR},
577 {recv_bytes_details, [{rate, RR},
578 {samples, SR}] ++ average(SR, STR, Length)}
882579 ].
883580
884 apply_factor(_, 0.0) ->
885 0.0;
886 apply_factor(S, Factor) ->
887 S * 1000 / Factor.
888
889 average(_Samples, Length) when Length =< 1 ->
581 avg_time_details(Avg) ->
582 %% Rates don't make sense here, populate it with the average.
583 [{rate, Avg}, {avg_rate, Avg}, {avg, Avg}].
584
585 average(_Samples, _Total, Length) when Length =< 1->
890586 [];
891 average(Samples, Length) ->
587 average(Samples, Total, Length) ->
892588 [{sample, S2}, {timestamp, T2}] = hd(Samples),
893589 [{sample, S1}, {timestamp, T1}] = lists:last(Samples),
894 Total = lists:sum([pget(sample, I) || I <- Samples]),
895590 [{avg_rate, (S2 - S1) * 1000 / (T2 - T1)},
896 {avg, Total / Length}].
591 {avg, Total / Length}].
592
593 rate_from_last_increment(Table, {TS, _} = Last, T, RangePoint) ->
594 case TS - RangePoint of % [0]
595 D when D >= 0 ->
596 case rate_from_last_increment(Last, T) of
597 unknown ->
598 empty(Table, 0.0);
599 Rate ->
600 Rate
601 end;
602 _ ->
603 empty(Table, 0.0)
604 end.
605
606 %% [0] Only display the rate if it's live - i.e. ((the end of the
607 %% range) - interval) corresponds to the last data point we have
608 rate_from_last_increment(_Total, []) ->
609 unknown;
610 rate_from_last_increment(Total, [H | _T]) ->
611 rate_from_difference(Total, H).
612
613 rate_from_difference({TS0, {A0, A1, A2}}, {TS1, {B0, B1, B2}}) ->
614 Interval = TS0 - TS1,
615 {rate(A0, B0, Interval), rate(A1, B1, Interval), rate(A2, B2, Interval)};
616 rate_from_difference({TS0, {A0, A1}}, {TS1, {B0, B1}}) ->
617 Interval = TS0 - TS1,
618 {rate(A0, B0, Interval), rate(A1, B1, Interval)};
619 rate_from_difference({TS0, {A0, A1, A2, A3, A4, A5, A6}},
620 {TS1, {B0, B1, B2, B3, B4, B5, B6}}) ->
621 Interval = TS0 - TS1,
622 {rate(A0, B0, Interval), rate(A1, B1, Interval), rate(A2, B2, Interval),
623 rate(A3, B3, Interval), rate(A4, B4, Interval), rate(A5, B5, Interval),
624 rate(A6, B6, Interval)};
625 rate_from_difference({TS0, {A0, A1, A2, A3, A4, A5, A6, A7}},
626 {TS1, {B0, B1, B2, B3, B4, B5, B6, B7}}) ->
627 Interval = TS0 - TS1,
628 {rate(A0, B0, Interval), rate(A1, B1, Interval), rate(A2, B2, Interval),
629 rate(A3, B3, Interval), rate(A4, B4, Interval), rate(A5, B5, Interval),
630 rate(A6, B6, Interval), rate(A7, B7, Interval)};
631 rate_from_difference({TS0, {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13,
632 A14, A15, A16, A17, A18, A19}},
633 {TS1, {B0, B1, B2, B3, B4, B5, B6, B7, B8, B9, B10, B11, B12, B13,
634 B14, B15, B16, B17, B18, B19}}) ->
635 Interval = TS0 - TS1,
636 {rate(A0, B0, Interval), rate(A1, B1, Interval), rate(A2, B2, Interval),
637 rate(A3, B3, Interval), rate(A4, B4, Interval), rate(A5, B5, Interval),
638 rate(A6, B6, Interval), rate(A7, B7, Interval), rate(A8, B8, Interval),
639 rate(A9, B9, Interval), rate(A10, B10, Interval), rate(A11, B11, Interval),
640 rate(A12, B12, Interval), rate(A13, B13, Interval), rate(A14, B14, Interval),
641 rate(A15, B15, Interval), rate(A16, B16, Interval), rate(A17, B17, Interval),
642 rate(A18, B18, Interval), rate(A19, B19, Interval)};
643 rate_from_difference({TS0, {A0}}, {TS1, {B0}}) ->
644 Interval = TS0 - TS1,
645 {rate(A0, B0, Interval)}.
646
647 rate(V1, V2, Interval) when is_number(V1), is_number(V2) ->
648 (V1 - V2) * 1000 / Interval;
649 rate(_, _, _) ->
650 0.
651
652 append_sample(S, TS, List) ->
653 [[{sample, S}, {timestamp, TS}] | List].
654
897655 %%----------------------------------------------------------------------------
898
899 add_record({Base, V1}, {_, V11}) ->
900 {Base, V1 + V11};
901 add_record({Base, V1, V2}, {_, V11, V21}) ->
902 {Base, V1 + V11, V2 + V21};
903 add_record({Base, V1, V2, V3}, {_, V1a, V2a, V3a}) ->
904 {Base, V1 + V1a, V2 + V2a, V3 + V3a};
905 add_record({Base, V1, V2, V3, V4}, {_, V1a, V2a, V3a, V4a}) ->
906 {Base, V1 + V1a, V2 + V2a, V3 + V3a, V4 + V4a};
907 add_record({Base, V1, V2, V3, V4, V5, V6, V7, V8},
908 {_, V1a, V2a, V3a, V4a, V5a, V6a, V7a, V8a}) ->
909 {Base, V1 + V1a, V2 + V2a, V3 + V3a, V4 + V4a, V5 + V5a, V6 + V6a, V7 + V7a,
910 V8 + V8a};
911 add_record({Base, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14,
912 V15, V16, V17, V18, V19, V20, V21, V22, V23, V24, V25, V26, V27, V28},
913 {_, V1a, V2a, V3a, V4a, V5a, V6a, V7a, V8a, V9a, V10a, V11a, V12a,
914 V13a, V14a, V15a, V16a, V17a, V18a, V19a, V20a, V21a, V22a, V23a,
915 V24a, V25a, V26a, V27a, V28a}) ->
916 {Base, V1 + V1a, V2 + V2a, V3 + V3a, V4 + V4a, V5 + V5a, V6 + V6a, V7 + V7a,
917 V8 + V8a, V9 + V9a, V10 + V10a, V11 + V11a, V12 + V12a, V13 + V13a,
918 V14 + V14a, V15 + V15a, V16 + V16a, V17 + V17a, V18 + V18a, V19 + V19a,
919 V20 + V20a, V21 + V21a, V22 + V22a, V23 + V23a, V24 + V24a, V25 + V25a,
920 V26 + V26a, V27 + V27a, V28 + V28a}.
921
922 empty(Key, process_stats) ->
923 {Key, 0};
924 empty(Key, Type) when Type == queue_msg_rates;
925 Type == coarse_node_node_stats;
926 Type == coarse_conn_stats ->
927 {Key, 0, 0};
928 empty(Key, queue_msg_counts) ->
929 {Key, 0, 0, 0};
930 empty(Key, deliver_get) ->
931 {Key, 0, 0, 0, 0};
932 empty(Key, fine_stats) ->
933 {Key, 0, 0, 0, 0, 0, 0, 0, 0};
934 empty(Key, coarse_node_stats) ->
935 {Key, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
936 0, 0, 0, 0, 0}.
937
938 empty_list(process_stats) ->
939 {samples, []};
940 empty_list(Type) when Type == queue_msg_rates;
941 Type == coarse_node_node_stats;
942 Type == coarse_conn_stats ->
943 {samples, [], []};
944 empty_list(queue_msg_counts) ->
945 {samples, [], [], []};
946 empty_list(deliver_get) ->
947 {samples, [], [], [], []};
948 empty_list(fine_stats) ->
949 {samples, [], [], [], [], [], [], [], []};
950 empty_list(coarse_node_stats) ->
951 {samples, [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [],
952 [], [], [], [], [], [], [], [], [], [], [], []}.
953
954
955 is_blank({_Key, 0}) ->
956 true;
957 is_blank({_Key, 0, 0}) ->
958 true;
959 is_blank({_Key, 0, 0, 0}) ->
960 true;
961 is_blank({_Key, 0, 0, 0, 0}) ->
962 true;
963 is_blank({_Key, 0, 0, 0, 0, 0, 0, 0, 0}) ->
964 true;
965 is_blank({_Key, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
966 0, 0, 0, 0, 0, 0, 0}) ->
967 true;
968 is_blank(_) ->
969 false.
970
971 avg_time(_Total, 0) ->
656 %% Match specs to select from the ETS tables
657 %%----------------------------------------------------------------------------
658
659 avg_time(_Total, Count) when Count == 0;
660 Count == 0.0 ->
972661 0.0;
973662 avg_time(Total, Count) ->
974663 (Total / Count) / ?MICRO_TO_MILLI.
975664
976 avg_time(Total, Count, BaseTotal, BaseCount) ->
977 avg_time(Total - BaseTotal, Count - BaseCount).
665 unit_samples(Total, Count) ->
666 lists:zipwith(fun(T, C) ->
667 TS = proplists:get_value(timestamp, T),
668 Sample = avg_time(proplists:get_value(sample, T),
669 proplists:get_value(sample, C)),
670 [{sample, Sample}, {timestamp, TS}]
671 end, Total, Count).
672
673 empty(Table, Def) ->
674 rabbit_mgmt_data:empty(Table, Def).
+0
-219
deps/rabbitmq_management/src/rabbit_mgmt_stats_gc.erl less more
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License at
3 %% http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
7 %% License for the specific language governing rights and limitations
8 %% under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is Pivotal Software, Inc.
13 %% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_mgmt_stats_gc).
17
18 -include_lib("rabbit_common/include/rabbit.hrl").
19 -include("rabbit_mgmt_metrics.hrl").
20
21 -behaviour(gen_server2).
22
23 -export([start_link/1]).
24
25 -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
26 code_change/3, handle_pre_hibernate/1]).
27
28 -export([name/1]).
29
30 -import(rabbit_misc, [pget/3]).
31 -import(rabbit_mgmt_db, [pget/2, id_name/1, id/2, lookup_element/2]).
32
33 -record(state, {
34 interval,
35 gc_timer,
36 gc_table,
37 gc_index,
38 gc_next_key
39 }).
40
41 -define(GC_INTERVAL, 5000).
42 -define(GC_MIN_ROWS, 50).
43 -define(GC_MIN_RATIO, 0.001).
44
45 -define(DROP_LENGTH, 1000).
46
47 -define(PROCESS_ALIVENESS_TIMEOUT, 15000).
48
49 %%----------------------------------------------------------------------------
50 %% API
51 %%----------------------------------------------------------------------------
52
53 start_link(Table) ->
54 case gen_server2:start_link({global, name(Table)}, ?MODULE, [Table], []) of
55 {ok, Pid} -> register(name(Table), Pid), %% [1]
56 {ok, Pid};
57 Else -> Else
58 end.
59 %% [1] For debugging it's helpful to locally register the name too
60 %% since that shows up in places global names don't.
61
62 %%----------------------------------------------------------------------------
63 %% Internal, gen_server2 callbacks
64 %%----------------------------------------------------------------------------
65
66 init([Table]) ->
67 {ok, Interval} = application:get_env(rabbit, collect_statistics_interval),
68 rabbit_log:info("Statistics garbage collector started for table ~p with interval ~p.~n", [Table, Interval]),
69 {ok, set_gc_timer(#state{interval = Interval,
70 gc_table = Table,
71 gc_index = rabbit_mgmt_stats_tables:key_index(Table)}),
72 hibernate,
73 {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}.
74
75 handle_call(_Request, _From, State) ->
76 reply(not_understood, State).
77
78 handle_cast(_Request, State) ->
79 noreply(State).
80
81 handle_info(gc, State) ->
82 noreply(set_gc_timer(gc_batch(State)));
83
84 handle_info(_Info, State) ->
85 noreply(State).
86
87 terminate(_Arg, _State) ->
88 ok.
89
90 code_change(_OldVsn, State, _Extra) ->
91 {ok, State}.
92
93 reply(Reply, NewState) -> {reply, Reply, NewState, hibernate}.
94 noreply(NewState) -> {noreply, NewState, hibernate}.
95
96 set_gc_timer(State) ->
97 TRef = erlang:send_after(?GC_INTERVAL, self(), gc),
98 State#state{gc_timer = TRef}.
99
100 handle_pre_hibernate(State) ->
101 {hibernate, State}.
102
103 %%----------------------------------------------------------------------------
104 %% Internal, utilities
105 %%----------------------------------------------------------------------------
106
107 floor(TS, #state{interval = Interval}) ->
108 rabbit_mgmt_util:floor(TS, Interval).
109
110 %%----------------------------------------------------------------------------
111 %% Internal, event-GCing
112 %%----------------------------------------------------------------------------
113
114 gc_batch(#state{gc_index = Index} = State) ->
115 {ok, Policies} = application:get_env(
116 rabbitmq_management, sample_retention_policies),
117 {ok, ProcGCTimeout} = application:get_env(
118 rabbitmq_management, process_stats_gc_timeout),
119 Config = [{policies, Policies}, {process_stats_gc_timeout, ProcGCTimeout}],
120 Total = ets:info(Index, size),
121 Rows = erlang:max(erlang:min(Total, ?GC_MIN_ROWS), round(?GC_MIN_RATIO * Total)),
122 gc_batch(Rows, Config, State).
123
124 gc_batch(0, _Config, State) ->
125 State;
126 gc_batch(Rows, Config, State = #state{gc_next_key = Cont,
127 gc_table = Table,
128 gc_index = Index}) ->
129 Select = case Cont of
130 undefined ->
131 ets:first(Index);
132 _ ->
133 ets:next(Index, Cont)
134 end,
135 NewCont = case Select of
136 '$end_of_table' ->
137 undefined;
138 Key ->
139 Now = floor(
140 time_compat:os_system_time(milli_seconds),
141 State),
142 gc(Key, Table, Config, Now),
143 Key
144 end,
145 gc_batch(Rows - 1, Config, State#state{gc_next_key = NewCont}).
146
147 gc(Key, Table, Config, Now) ->
148 case lists:member(Table, ?PROC_STATS_TABLES) of
149 true -> gc_proc(Key, Table, Config, Now);
150 false -> gc_aggr(Key, Table, Config, Now)
151 end.
152
153 gc_proc(Key, Table, Config, Now) when Table == connection_stats;
154 Table == channel_stats ->
155 Timeout = pget(process_stats_gc_timeout, Config),
156 case ets:lookup(Table, {Key, stats}) of
157 %% Key is already cleared. Skipping
158 [] -> ok;
159 [{{Key, stats}, _Stats, TS}] -> maybe_gc_process(Key, Table,
160 TS, Now, Timeout)
161 end.
162
163 gc_aggr(Key, Table, Config, Now) ->
164 Policies = pget(policies, Config),
165 Policy = pget(retention_policy(Table), Policies),
166 rabbit_mgmt_stats:gc({Policy, Now}, Table, Key).
167
168 maybe_gc_process(Pid, Table, LastStatsTS, Now, Timeout) ->
169 case Now - LastStatsTS < Timeout of
170 true -> ok;
171 false ->
172 case process_status(Pid) of
173 %% Process doesn't exist on remote node
174 undefined -> rabbit_event:notify(deleted_event(Table),
175 [{pid, Pid}]);
176 %% Remote node is unreachable or process is alive
177 _ -> ok
178 end
179 end.
180
181 process_status(Pid) when node(Pid) =:= node() ->
182 process_info(Pid, status);
183 process_status(Pid) ->
184 rpc:block_call(node(Pid), erlang, process_info, [Pid, status],
185 ?PROCESS_ALIVENESS_TIMEOUT).
186
187 deleted_event(channel_stats) -> channel_closed;
188 deleted_event(connection_stats) -> connection_closed.
189
190 retention_policy(aggr_node_stats_coarse_node_stats) -> global;
191 retention_policy(aggr_node_node_stats_coarse_node_node_stats) -> global;
192 retention_policy(aggr_vhost_stats_deliver_get) -> global;
193 retention_policy(aggr_vhost_stats_fine_stats) -> global;
194 retention_policy(aggr_vhost_stats_queue_msg_rates) -> global;
195 retention_policy(aggr_vhost_stats_msg_rates_details) -> global;
196 retention_policy(aggr_vhost_stats_queue_msg_counts) -> global;
197 retention_policy(aggr_vhost_stats_coarse_conn_stats) -> global;
198 retention_policy(aggr_queue_stats_fine_stats) -> basic;
199 retention_policy(aggr_queue_stats_deliver_get) -> basic;
200 retention_policy(aggr_queue_stats_queue_msg_counts) -> basic;
201 retention_policy(aggr_queue_stats_queue_msg_rates) -> basic;
202 retention_policy(aggr_queue_stats_process_stats) -> basic;
203 retention_policy(aggr_exchange_stats_fine_stats) -> basic;
204 retention_policy(aggr_connection_stats_coarse_conn_stats) -> basic;
205 retention_policy(aggr_connection_stats_process_stats) -> basic;
206 retention_policy(aggr_channel_stats_deliver_get) -> basic;
207 retention_policy(aggr_channel_stats_fine_stats) -> basic;
208 retention_policy(aggr_channel_stats_queue_msg_counts) -> basic;
209 retention_policy(aggr_channel_stats_process_stats) -> basic;
210 retention_policy(aggr_queue_exchange_stats_fine_stats) -> detailed;
211 retention_policy(aggr_channel_exchange_stats_deliver_get) -> detailed;
212 retention_policy(aggr_channel_exchange_stats_fine_stats) -> detailed;
213 retention_policy(aggr_channel_queue_stats_deliver_get) -> detailed;
214 retention_policy(aggr_channel_queue_stats_fine_stats) -> detailed;
215 retention_policy(aggr_channel_queue_stats_queue_msg_counts) -> detailed.
216
217 name(Atom) ->
218 list_to_atom((atom_to_list(Atom) ++ "_gc")).
+0
-271
deps/rabbitmq_management/src/rabbit_mgmt_stats_tables.erl less more
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License at
3 %% http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
7 %% License for the specific language governing rights and limitations
8 %% under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is Pivotal Software, Inc.
13 %% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_mgmt_stats_tables).
17
18 -include("rabbit_mgmt_metrics.hrl").
19
20 -export([aggr_table/2, aggr_tables/1, type_from_table/1,
21 index/1, key_index/1]).
22
23 -spec aggr_table(event_type(), type()) -> table_name().
24 aggr_table(queue_stats, deliver_get) ->
25 aggr_queue_stats_deliver_get;
26 aggr_table(queue_stats, fine_stats) ->
27 aggr_queue_stats_fine_stats;
28 aggr_table(queue_stats, queue_msg_counts) ->
29 aggr_queue_stats_queue_msg_counts;
30 aggr_table(queue_stats, queue_msg_rates) ->
31 aggr_queue_stats_queue_msg_rates;
32 aggr_table(queue_stats, process_stats) ->
33 aggr_queue_stats_process_stats;
34 aggr_table(queue_exchange_stats, fine_stats) ->
35 aggr_queue_exchange_stats_fine_stats;
36 aggr_table(vhost_stats, deliver_get) ->
37 aggr_vhost_stats_deliver_get;
38 aggr_table(vhost_stats, fine_stats) ->
39 aggr_vhost_stats_fine_stats;
40 aggr_table(vhost_stats, queue_msg_rates) ->
41 aggr_vhost_stats_queue_msg_rates;
42 aggr_table(vhost_stats, queue_msg_counts) ->
43 aggr_vhost_stats_queue_msg_counts;
44 aggr_table(vhost_stats, coarse_conn_stats) ->
45 aggr_vhost_stats_coarse_conn_stats;
46 aggr_table(channel_queue_stats, deliver_get) ->
47 aggr_channel_queue_stats_deliver_get;
48 aggr_table(channel_queue_stats, fine_stats) ->
49 aggr_channel_queue_stats_fine_stats;
50 aggr_table(channel_queue_stats, queue_msg_counts) ->
51 aggr_channel_queue_stats_queue_msg_counts;
52 aggr_table(channel_stats, deliver_get) ->
53 aggr_channel_stats_deliver_get;
54 aggr_table(channel_stats, fine_stats) ->
55 aggr_channel_stats_fine_stats;
56 aggr_table(channel_stats, queue_msg_counts) ->
57 aggr_channel_stats_queue_msg_counts;
58 aggr_table(channel_stats, process_stats) ->
59 aggr_channel_stats_process_stats;
60 aggr_table(channel_exchange_stats, deliver_get) ->
61 aggr_channel_exchange_stats_deliver_get;
62 aggr_table(channel_exchange_stats, fine_stats) ->
63 aggr_channel_exchange_stats_fine_stats;
64 aggr_table(exchange_stats, fine_stats) ->
65 aggr_exchange_stats_fine_stats;
66 aggr_table(node_stats, coarse_node_stats) ->
67 aggr_node_stats_coarse_node_stats;
68 aggr_table(node_node_stats, coarse_node_node_stats) ->
69 aggr_node_node_stats_coarse_node_node_stats;
70 aggr_table(connection_stats, coarse_conn_stats) ->
71 aggr_connection_stats_coarse_conn_stats;
72 aggr_table(connection_stats, process_stats) ->
73 aggr_connection_stats_process_stats.
74
75 -spec aggr_tables(event_type()) -> [{table_name(), type()}].
76 aggr_tables(queue_stats) ->
77 [{aggr_queue_stats_fine_stats, fine_stats},
78 {aggr_queue_stats_deliver_get, deliver_get},
79 {aggr_queue_stats_queue_msg_counts, queue_msg_counts},
80 {aggr_queue_stats_queue_msg_rates, queue_msg_rates},
81 {aggr_queue_stats_process_stats, process_stats}];
82 aggr_tables(queue_exchange_stats) ->
83 [{aggr_queue_exchange_stats_fine_stats, fine_stats}];
84 aggr_tables(vhost_stats) ->
85 [{aggr_vhost_stats_deliver_get, deliver_get},
86 {aggr_vhost_stats_fine_stats, fine_stats},
87 {aggr_vhost_stats_queue_msg_rates, queue_msg_rates},
88 {aggr_vhost_stats_queue_msg_counts, queue_msg_counts},
89 {aggr_vhost_stats_coarse_conn_stats, coarse_conn_stats}];
90 aggr_tables(channel_queue_stats) ->
91 [{aggr_channel_queue_stats_deliver_get, deliver_get},
92 {aggr_channel_queue_stats_fine_stats, fine_stats},
93 {aggr_channel_queue_stats_queue_msg_counts, queue_msg_counts}];
94 aggr_tables(channel_stats) ->
95 [{aggr_channel_stats_deliver_get, deliver_get},
96 {aggr_channel_stats_fine_stats, fine_stats},
97 {aggr_channel_stats_queue_msg_counts, queue_msg_counts},
98 {aggr_channel_stats_process_stats, process_stats}];
99 aggr_tables(channel_exchange_stats) ->
100 [{aggr_channel_exchange_stats_deliver_get, deliver_get},
101 {aggr_channel_exchange_stats_fine_stats, fine_stats}];
102 aggr_tables(exchange_stats) ->
103 [{aggr_exchange_stats_fine_stats, fine_stats}];
104 aggr_tables(node_stats) ->
105 [{aggr_node_stats_coarse_node_stats, coarse_node_stats}];
106 aggr_tables(node_node_stats) ->
107 [{aggr_node_node_stats_coarse_node_node_stats, coarse_node_node_stats}];
108 aggr_tables(connection_stats) ->
109 [{aggr_connection_stats_coarse_conn_stats, coarse_conn_stats},
110 {aggr_connection_stats_process_stats, process_stats}].
111
112 -spec type_from_table(table_name()) -> type().
113 type_from_table(aggr_queue_stats_deliver_get) ->
114 deliver_get;
115 type_from_table(aggr_queue_stats_fine_stats) ->
116 fine_stats;
117 type_from_table(aggr_queue_stats_queue_msg_counts) ->
118 queue_msg_counts;
119 type_from_table(aggr_queue_stats_queue_msg_rates) ->
120 queue_msg_rates;
121 type_from_table(aggr_queue_stats_process_stats) ->
122 process_stats;
123 type_from_table(aggr_queue_exchange_stats_fine_stats) ->
124 fine_stats;
125 type_from_table(aggr_vhost_stats_deliver_get) ->
126 deliver_get;
127 type_from_table(aggr_vhost_stats_fine_stats) ->
128 fine_stats;
129 type_from_table(aggr_vhost_stats_queue_msg_rates) ->
130 queue_msg_rates;
131 type_from_table(aggr_vhost_stats_queue_msg_counts) ->
132 queue_msg_counts;
133 type_from_table(aggr_vhost_stats_coarse_conn_stats) ->
134 coarse_conn_stats;
135 type_from_table(aggr_channel_queue_stats_deliver_get) ->
136 deliver_get;
137 type_from_table(aggr_channel_queue_stats_fine_stats) ->
138 fine_stats;
139 type_from_table(aggr_channel_queue_stats_queue_msg_counts) ->
140 queue_msg_counts;
141 type_from_table(aggr_channel_stats_deliver_get) ->
142 deliver_get;
143 type_from_table(aggr_channel_stats_fine_stats) ->
144 fine_stats;
145 type_from_table(aggr_channel_stats_queue_msg_counts) ->
146 queue_msg_counts;
147 type_from_table(aggr_channel_stats_process_stats) ->
148 process_stats;
149 type_from_table(aggr_channel_exchange_stats_deliver_get) ->
150 deliver_get;
151 type_from_table(aggr_channel_exchange_stats_fine_stats) ->
152 fine_stats;
153 type_from_table(aggr_exchange_stats_fine_stats) ->
154 fine_stats;
155 type_from_table(aggr_node_stats_coarse_node_stats) ->
156 coarse_node_stats;
157 type_from_table(aggr_node_node_stats_coarse_node_node_stats) ->
158 coarse_node_node_stats;
159 type_from_table(aggr_node_node_stats_coarse_conn_stats) ->
160 coarse_conn_stats;
161 type_from_table(aggr_connection_stats_coarse_conn_stats) ->
162 coarse_conn_stats;
163 type_from_table(aggr_connection_stats_process_stats) ->
164 process_stats.
165
166 index(aggr_queue_stats_deliver_get) ->
167 aggr_queue_stats_deliver_get_index;
168 index(aggr_queue_stats_fine_stats) ->
169 aggr_queue_stats_fine_stats_index;
170 index(aggr_queue_stats_queue_msg_counts) ->
171 aggr_queue_stats_queue_msg_counts_index;
172 index(aggr_queue_stats_queue_msg_rates) ->
173 aggr_queue_stats_queue_msg_rates_index;
174 index(aggr_queue_stats_process_stats) ->
175 aggr_queue_stats_process_stats_index;
176 index(aggr_queue_exchange_stats_fine_stats) ->
177 aggr_queue_exchange_stats_fine_stats_index;
178 index(aggr_vhost_stats_deliver_get) ->
179 aggr_vhost_stats_deliver_get_index;
180 index(aggr_vhost_stats_fine_stats) ->
181 aggr_vhost_stats_fine_stats_index;
182 index(aggr_vhost_stats_queue_msg_rates) ->
183 aggr_vhost_stats_queue_msg_rates_index;
184 index(aggr_vhost_stats_queue_msg_counts) ->
185 aggr_vhost_stats_queue_msg_counts_index;
186 index(aggr_vhost_stats_coarse_conn_stats) ->
187 aggr_vhost_stats_coarse_conn_stats_index;
188 index(aggr_channel_queue_stats_deliver_get) ->
189 aggr_channel_queue_stats_deliver_get_index;
190 index(aggr_channel_queue_stats_fine_stats) ->
191 aggr_channel_queue_stats_fine_stats_index;
192 index(aggr_channel_queue_stats_queue_msg_counts) ->
193 aggr_channel_queue_stats_queue_msg_counts_index;
194 index(aggr_channel_stats_deliver_get) ->
195 aggr_channel_stats_deliver_get_index;
196 index(aggr_channel_stats_fine_stats) ->
197 aggr_channel_stats_fine_stats_index;
198 index(aggr_channel_stats_queue_msg_counts) ->
199 aggr_channel_stats_queue_msg_counts_index;
200 index(aggr_channel_stats_process_stats) ->
201 aggr_channel_stats_process_stats_index;
202 index(aggr_channel_exchange_stats_deliver_get) ->
203 aggr_channel_exchange_stats_deliver_get_index;
204 index(aggr_channel_exchange_stats_fine_stats) ->
205 aggr_channel_exchange_stats_fine_stats_index;
206 index(aggr_exchange_stats_fine_stats) ->
207 aggr_exchange_stats_fine_stats_index;
208 index(aggr_node_stats_coarse_node_stats) ->
209 aggr_node_stats_coarse_node_stats_index;
210 index(aggr_node_node_stats_coarse_node_node_stats) ->
211 aggr_node_node_stats_coarse_node_node_stats_index;
212 index(aggr_connection_stats_coarse_conn_stats) ->
213 aggr_connection_stats_coarse_conn_stats_index;
214 index(aggr_connection_stats_process_stats) ->
215 aggr_connection_stats_process_stats_index.
216
217 key_index(connection_stats) ->
218 connection_stats_key_index;
219 key_index(channel_stats) ->
220 channel_stats_key_index;
221 key_index(aggr_queue_stats_deliver_get) ->
222 aggr_queue_stats_deliver_get_key_index;
223 key_index(aggr_queue_stats_fine_stats) ->
224 aggr_queue_stats_fine_stats_key_index;
225 key_index(aggr_queue_stats_queue_msg_counts) ->
226 aggr_queue_stats_queue_msg_counts_key_index;
227 key_index(aggr_queue_stats_queue_msg_rates) ->
228 aggr_queue_stats_queue_msg_rates_key_index;
229 key_index(aggr_queue_stats_process_stats) ->
230 aggr_queue_stats_process_stats_key_index;
231 key_index(aggr_queue_exchange_stats_fine_stats) ->
232 aggr_queue_exchange_stats_fine_stats_key_index;
233 key_index(aggr_vhost_stats_deliver_get) ->
234 aggr_vhost_stats_deliver_get_key_index;
235 key_index(aggr_vhost_stats_fine_stats) ->
236 aggr_vhost_stats_fine_stats_key_index;
237 key_index(aggr_vhost_stats_queue_msg_rates) ->
238 aggr_vhost_stats_queue_msg_rates_key_index;
239 key_index(aggr_vhost_stats_queue_msg_counts) ->
240 aggr_vhost_stats_queue_msg_counts_key_index;
241 key_index(aggr_vhost_stats_coarse_conn_stats) ->
242 aggr_vhost_stats_coarse_conn_stats_key_index;
243 key_index(aggr_channel_queue_stats_deliver_get) ->
244 aggr_channel_queue_stats_deliver_get_key_index;
245 key_index(aggr_channel_queue_stats_fine_stats) ->
246 aggr_channel_queue_stats_fine_stats_key_index;
247 key_index(aggr_channel_queue_stats_queue_msg_counts) ->
248 aggr_channel_queue_stats_queue_msg_counts_key_index;
249 key_index(aggr_channel_stats_deliver_get) ->
250 aggr_channel_stats_deliver_get_key_index;
251 key_index(aggr_channel_stats_fine_stats) ->
252 aggr_channel_stats_fine_stats_key_index;
253 key_index(aggr_channel_stats_queue_msg_counts) ->
254 aggr_channel_stats_queue_msg_counts_key_index;
255 key_index(aggr_channel_stats_process_stats) ->
256 aggr_channel_stats_process_stats_key_index;
257 key_index(aggr_channel_exchange_stats_deliver_get) ->
258 aggr_channel_exchange_stats_deliver_get_key_index;
259 key_index(aggr_channel_exchange_stats_fine_stats) ->
260 aggr_channel_exchange_stats_fine_stats_key_index;
261 key_index(aggr_exchange_stats_fine_stats) ->
262 aggr_exchange_stats_fine_stats_key_index;
263 key_index(aggr_node_stats_coarse_node_stats) ->
264 aggr_node_stats_coarse_node_stats_key_index;
265 key_index(aggr_node_node_stats_coarse_node_node_stats) ->
266 aggr_node_node_stats_coarse_node_node_stats_key_index;
267 key_index(aggr_connection_stats_coarse_conn_stats) ->
268 aggr_connection_stats_coarse_conn_stats_key_index;
269 key_index(aggr_connection_stats_process_stats) ->
270 aggr_connection_stats_process_stats_key_index.
1515
1616 -module(rabbit_mgmt_sup).
1717
18 -behaviour(mirrored_supervisor).
18 -behaviour(supervisor).
1919
2020 -export([init/1]).
2121 -export([start_link/0]).
22 -export([setup_wm_logging/0]).
2223
23 -include("rabbit_mgmt_metrics.hrl").
24 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_metrics.hrl").
2425 -include_lib("rabbit_common/include/rabbit.hrl").
26 -include_lib("rabbit_common/include/rabbit_core_metrics.hrl").
2527
2628 init([]) ->
27 COLLECTOR = {rabbit_mgmt_event_collector,
28 {rabbit_mgmt_event_collector, start_link, []},
29 permanent, ?WORKER_WAIT, worker, [rabbit_mgmt_event_collector]},
30 CCOLLECTOR = {rabbit_mgmt_channel_stats_collector,
31 {rabbit_mgmt_channel_stats_collector, start_link, []},
32 permanent, ?WORKER_WAIT, worker, [rabbit_mgmt_channel_stats_collector]},
33 QCOLLECTOR = {rabbit_mgmt_queue_stats_collector,
34 {rabbit_mgmt_queue_stats_collector, start_link, []},
35 permanent, ?WORKER_WAIT, worker, [rabbit_mgmt_queue_stats_collector]},
36 GC = [{rabbit_mgmt_stats_gc:name(Table), {rabbit_mgmt_stats_gc, start_link, [Table]},
37 permanent, ?WORKER_WAIT, worker, [rabbit_mgmt_stats_gc]}
38 || Table <- ?AGGR_TABLES],
39 ProcGC = [{rabbit_mgmt_stats_gc:name(Table), {rabbit_mgmt_stats_gc, start_link, [Table]},
40 permanent, ?WORKER_WAIT, worker, [rabbit_mgmt_stats_gc]}
41 || Table <- ?PROC_STATS_TABLES],
4229 DB = {rabbit_mgmt_db, {rabbit_mgmt_db, start_link, []},
4330 permanent, ?WORKER_WAIT, worker, [rabbit_mgmt_db]},
44 {ok, {{one_for_one, 10, 10}, [COLLECTOR, CCOLLECTOR, QCOLLECTOR, DB] ++ GC ++ ProcGC}}.
31 WP = {management_worker_pool_sup, {worker_pool_sup, start_link, [3, management_worker_pool]},
32 permanent, ?SUPERVISOR_WAIT, supervisor, [management_worker_pool_sup]},
33 DBC = {rabbit_mgmt_db_cache_sup, {rabbit_mgmt_db_cache_sup, start_link, []},
34 permanent, ?SUPERVISOR_WAIT, supervisor, [rabbit_mgmt_db_cache_sup]},
35 {ok, {{one_for_one, 100, 1}, [DB, WP, DBC]}}.
4536
4637 start_link() ->
47 mirrored_supervisor:start_link(
48 {local, ?MODULE}, ?MODULE, fun rabbit_misc:execute_mnesia_transaction/1,
49 ?MODULE, []).
38 Res = supervisor:start_link({local, ?MODULE}, ?MODULE, []),
39 setup_wm_logging(),
40 Res.
41
42 %% While the project has switched to Cowboy for HTTP handling, we still use
43 %% the logger from Webmachine; at least until RabbitMQ switches to Lager or
44 %% similar.
45 setup_wm_logging() ->
46 {ok, LogDir} = application:get_env(rabbitmq_management, http_log_dir),
47 case LogDir of
48 none -> ok;
49 _ -> webmachine_log:add_handler(webmachine_log_handler, [LogDir])
50 end.
77 %% License for the specific language governing rights and limitations
88 %% under the License.
99 %%
10 %% The Original Code is RabbitMQ Management Console.
10 %% The Original Code is RabbitMQ.
1111 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved.
12 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1413 %%
1514
1615 -module(rabbit_mgmt_sup_sup).
1716
18 %% We want there to be one management database in the cluster, with a
19 %% globally registered name. So we use mirrored_supervisor for
20 %% failover (in rabbit_mgmt_sup) and register a global name for the
21 %% database.
22 %%
23 %% Unfortunately it's more complicated than using these things
24 %% naively. The first problem is that on failover the mirrored
25 %% supervisor might move the DB to a new node before the global name
26 %% database notices and removes the old record. In that case starting
27 %% the new database will fail.
28 %%
29 %% The second problem is that after a network partition things get
30 %% worse. Since mirrored_supervisor uses Mnesia for global shared
31 %% state, we have effectively two (or more) mirrored_supervisors. But
32 %% the global name database does not do this, so at least one of them
33 %% cannot start the management database; so the mirrored supervisor
34 %% has to die. But what if the admin restarts the partition which
35 %% contains the management DB? In that case we need to start a new
36 %% management DB in the winning partition.
37 %%
38 %% Rather than try to get mirrored_supervisor to handle this
39 %% post-partition state we go for a simpler approach: allow the whole
40 %% mirrored_supervisor to die in the two edge cases above, and
41 %% whenever we want to call into the mgmt DB we will start it up if it
42 %% appears not to be there. See rabbit_mgmt_db:safe_call/3 for the
43 %% code which restarts the DB if necessary.
44
4517 -behaviour(supervisor2).
4618
19 -export([init/1]).
4720 -export([start_link/0, start_child/0]).
48 -export([init/1]).
4921
5022 -include_lib("rabbit_common/include/rabbit.hrl").
5123
52 start_link() -> supervisor2:start_link({local, ?MODULE}, ?MODULE, []).
53
54 start_child() -> supervisor2:start_child( ?MODULE, sup()).
55
56 %%----------------------------------------------------------------------------
57
58 init([]) ->
59 %% see above as well as https://github.com/rabbitmq/rabbitmq-management/pull/84.
60 %% we sent a message to ourselves so that if there's a conflict
61 %% with the mirrored supervisor already being started on another node,
62 %% we fail and let the other node win in a way that doesn't
63 %% prevent rabbitmq_management and, in turn, the entire
64 %% node fail to start.
65 timer:apply_after(0, ?MODULE, start_child, []),
66 {ok, {{one_for_one, 0, 1}, []}}.
24 start_child() ->
25 supervisor2:start_child(?MODULE, sup()).
6726
6827 sup() ->
6928 {rabbit_mgmt_sup, {rabbit_mgmt_sup, start_link, []},
7029 temporary, ?SUPERVISOR_WAIT, supervisor, [rabbit_mgmt_sup]}.
30
31 init([]) ->
32 {ok, {{one_for_one, 0, 1}, [sup()]}}.
33
34 start_link() ->
35 supervisor2:start_link({local, ?MODULE}, ?MODULE, []).
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_util).
2020 -export([is_authorized/2, is_authorized_admin/2, is_authorized_admin/4,
2121 vhost/1, vhost_from_headers/1]).
2222 -export([is_authorized_vhost/2, is_authorized_user/3,
23 is_authorized_monitor/2, is_authorized_policies/2]).
23 is_authorized_monitor/2, is_authorized_policies/2,
24 is_authorized_global_parameters/2]).
2425 -export([bad_request/3, bad_request_exception/4, id/2, parse_bool/1,
2526 parse_int/1]).
2627 -export([with_decode/4, not_found/3, amqp_request/4]).
2728 -export([with_channel/4, with_channel/5]).
2829 -export([props_to_method/2, props_to_method/4]).
29 -export([all_or_one_vhost/2, http_to_amqp/5, reply/3, filter_vhost/3]).
30 -export([all_or_one_vhost/2, http_to_amqp/5, reply/3, responder_map/1,
31 filter_vhost/3]).
3032 -export([filter_conn_ch_list/3, filter_user/2, list_login_vhosts/2]).
31 -export([with_decode/5, decode/1, decode/2, redirect/2, set_resp_header/3,
33 -export([with_decode/5, decode/1, decode/2, set_resp_header/3,
3234 args/1]).
3335 -export([reply_list/3, reply_list/5, reply_list/4,
3436 sort_list/2, destination_type/1, reply_list_or_paginate/3]).
3537 -export([post_respond/1, columns/1, is_monitor/1]).
3638 -export([list_visible_vhosts/1, b64decode_or_throw/1, no_range/0, range/1,
37 range_ceil/1, floor/2, ceil/2]).
39 range_ceil/1, floor/2, ceil/1, ceil/2]).
3840 -export([pagination_params/1, maybe_filter_by_keyword/4,
3941 get_value_param/2]).
4042
41 -import(rabbit_misc, [pget/2, pget/3]).
43 -import(rabbit_misc, [pget/2]).
4244
4345 -include("rabbit_mgmt.hrl").
46 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
4447 -include_lib("amqp_client/include/amqp_client.hrl").
45
46 -include_lib("webmachine/include/wm_reqdata.hrl").
47 -include_lib("webmachine/include/wm_reqstate.hrl").
4848
4949 -define(FRAMING, rabbit_framing_amqp_0_9_1).
5050 -define(DEFAULT_PAGE_SIZE, 100).
5151 -define(MAX_PAGE_SIZE, 500).
5252 -record(pagination, {page = undefined, page_size = undefined,
53 name = undefined, use_regex = undefined}).
53 name = undefined, use_regex = undefined}).
5454
5555 -define(MAX_RANGE, 500).
5656
8585 case vhost(ReqData) of
8686 not_found -> true;
8787 none -> true;
88 V -> lists:member(V, list_login_vhosts(User, peersock(ReqData)))
88 V -> lists:member(V, list_login_vhosts(User, cowboy_req:get(socket, ReqData)))
8989 end.
9090
9191 %% Used for connections / channels. A normal user can only see / delete
9595 is_authorized(ReqData, Context,
9696 <<"User not authorised to access object">>,
9797 fun(#user{username = Username, tags = Tags}) ->
98 case wrq:method(ReqData) of
99 'DELETE' -> is_admin(Tags);
100 _ -> is_monitor(Tags)
98 case element(1, cowboy_req:method(ReqData)) of
99 <<"DELETE">> -> is_admin(Tags);
100 _ -> is_monitor(Tags)
101101 end orelse Username == pget(user, Item)
102102 end).
103103
111111 user_matches_vhost(ReqData, User)
112112 end).
113113
114 %% For global parameters. Must be policymaker.
115 is_authorized_global_parameters(ReqData, Context) ->
116 is_authorized(ReqData, Context,
117 <<"User not authorised to access object">>,
118 fun(#user{tags = Tags}) ->
119 is_policymaker(Tags)
120 end).
121
114122 is_authorized(ReqData, Context, ErrorMsg, Fun) ->
115 case rabbit_web_dispatch_util:parse_auth_header(
116 wrq:get_req_header("authorization", ReqData)) of
117 [Username, Password] ->
118 is_authorized(ReqData, Context, Username, Password, ErrorMsg, Fun);
123 case cowboy_req:method(ReqData) of
124 {<<"OPTIONS">>, _} -> {true, ReqData, Context};
125 _ -> is_authorized1(ReqData, Context, ErrorMsg, Fun)
126 end.
127
128 is_authorized1(ReqData, Context, ErrorMsg, Fun) ->
129 case cowboy_req:parse_header(<<"authorization">>, ReqData) of
130 {ok, {<<"basic">>, {Username, Password}}, _} ->
131 is_authorized(ReqData, Context,
132 Username, Password,
133 ErrorMsg, Fun);
119134 _ ->
120 {?AUTH_REALM, ReqData, Context}
135 {{false, ?AUTH_REALM}, ReqData, Context}
121136 end.
122137
123138 is_authorized(ReqData, Context, Username, Password, ErrorMsg, Fun) ->
132147 end,
133148 case rabbit_access_control:check_user_login(Username, AuthProps) of
134149 {ok, User = #user{tags = Tags}} ->
135 IP = peer(ReqData),
150 {{IP, _}, _} = cowboy_req:peer(ReqData),
136151 case rabbit_access_control:check_user_loopback(Username, IP) of
137152 ok ->
138153 case is_mgmt_user(Tags) of
155170 not_authorised(<<"Login failed">>, ReqData, Context)
156171 end.
157172
158 peer(ReqData) ->
159 {ok, {IP,_Port}} = peername(peersock(ReqData)),
160 IP.
161
162 %% We can't use wrq:peer/1 because that trusts X-Forwarded-For.
163 peersock(ReqData) ->
164 WMState = ReqData#wm_reqdata.wm_state,
165 WMState#wm_reqstate.socket.
166
167 %% Like the one in rabbit_net, but we and webmachine have a different
168 %% way of wrapping
169 peername(Sock) when is_port(Sock) -> inet:peername(Sock);
170 peername({ssl, SSL}) -> ssl:peername(SSL).
171
172173 vhost_from_headers(ReqData) ->
173 case wrq:get_req_header(<<"x-vhost">>, ReqData) of
174 undefined -> none;
174 case cowboy_req:header(<<"x-vhost">>, ReqData) of
175 {undefined, _} -> none;
175176 %% blank x-vhost means "All hosts" is selected in the UI
176 [] -> none;
177 VHost -> list_to_binary(VHost)
177 {<<>>, _} -> none;
178 {VHost, _} -> VHost
178179 end.
179180
180181 vhost(ReqData) ->
192193 <<"q">> -> queue
193194 end.
194195
196 %% Provides a map of content type-to-responder that are supported by
197 %% reply/3. The map can be used in the content_types_provided/2 callback
198 %% used by cowboy_rest. Responder functions must be
199 %% exported from the caller module and must use reply/3
200 %% under the hood.
201 responder_map(FunctionName) ->
202 [
203 {<<"application/json">>, FunctionName}
204 , {<<"application/bert">>, FunctionName}
205 ].
206
195207 reply(Facts, ReqData, Context) ->
196208 reply0(extract_columns(Facts, ReqData), ReqData, Context).
197209
198210 reply0(Facts, ReqData, Context) ->
199 ReqData1 = set_resp_header("Cache-Control", "no-cache", ReqData),
211 ReqData1 = set_resp_header(<<"Cache-Control">>, "no-cache", ReqData),
200212 try
201 {mochijson2:encode(rabbit_mgmt_format:format_nulls(Facts)), ReqData1,
202 Context}
213 case cowboy_req:meta(media_type, ReqData1) of
214 {{<<"application">>, <<"bert">>, _}, _} ->
215 {term_to_binary(Facts), ReqData1, Context};
216 _ ->
217 {mochijson2:encode(rabbit_mgmt_format:format_nulls(Facts)),
218 ReqData1, Context}
219 end
203220 catch exit:{json_encode, E} ->
204221 Error = iolist_to_binary(
205222 io_lib:format("JSON encode error: ~p", [E])),
214231 reply_list(Facts, DefaultSorts, ReqData, Context) ->
215232 reply_list(Facts, DefaultSorts, ReqData, Context, undefined).
216233
234 get_value_param(Name, ReqData) ->
235 case cowboy_req:qs_val(Name, ReqData) of
236 {undefined, _} -> undefined;
237 {Bin, _} -> binary_to_list(Bin)
238 end.
217239
218240 reply_list(Facts, DefaultSorts, ReqData, Context, Pagination) ->
219241 SortList =
220 sort_list(
242 sort_list(
221243 extract_columns_list(Facts, ReqData),
222244 DefaultSorts,
223 wrq:get_qs_value("sort", ReqData),
224 wrq:get_qs_value("sort_reverse", ReqData), Pagination),
245 get_value_param(<<"sort">>, ReqData),
246 get_sort_reverse(ReqData), Pagination),
225247
226248 reply(SortList, ReqData, Context).
227249
250 -spec get_sort_reverse(cowboy_req:req()) -> atom().
251 get_sort_reverse(ReqData) ->
252 case get_value_param(<<"sort_reverse">>, ReqData) of
253 undefined -> false;
254 V -> list_to_atom(V)
255 end.
228256
229257 reply_list_or_paginate(Facts, ReqData, Context) ->
230258 try
231259 Pagination = pagination_params(ReqData),
232260 reply_list(Facts, ["vhost", "name"], ReqData, Context, Pagination)
233261 catch error:badarg ->
234 Reason = iolist_to_binary(
235 io_lib:format("Pagination parameters are invalid", [])),
236 invalid_pagination(bad_request, Reason, ReqData, Context);
237 {error, ErrorType, S} ->
262 Reason = iolist_to_binary(
263 io_lib:format("Pagination parameters are invalid", [])),
264 invalid_pagination(bad_request, Reason, ReqData, Context);
265 {error, ErrorType, S} ->
238266 Reason = iolist_to_binary(S),
239267 invalid_pagination(ErrorType, Reason, ReqData, Context)
240268 end.
243271 sort_list(Facts, Sorts) -> sort_list(Facts, Sorts, undefined, false,
244272 undefined).
245273
274 sort_list(Facts, _, [], _, _) ->
275 %% Do not sort when we are explicitly requsted to sort with an
276 %% empty sort columns list. Note that this clause won't match when
277 %% 'sort' parameter is not provided in a HTTP request at all.
278 Facts;
246279 sort_list(Facts, DefaultSorts, Sort, Reverse, Pagination) ->
247280 SortList = case Sort of
248 undefined -> DefaultSorts;
249 Extra -> [Extra | DefaultSorts]
250 end,
281 undefined -> DefaultSorts;
282 Extra -> [Extra | DefaultSorts]
283 end,
251284 %% lists:sort/2 is much more expensive than lists:sort/1
252285 Sorted = [V || {_K, V} <- lists:sort(
253286 [{sort_key(F, SortList), F} || F <- Facts])],
263296 maybe_filter_context(List, #pagination{name = Name, use_regex = UseRegex}) when
264297 is_list(Name) ->
265298 lists:filter(fun(ListF) ->
266 maybe_filter_by_keyword(name, Name, ListF, UseRegex)
267 end,
268 List);
299 maybe_filter_by_keyword(name, Name, ListF, UseRegex)
300 end,
301 List);
269302 %% Here it is backward with the other API(s), that don't filter the data
270303 maybe_filter_context(List, _) ->
271304 List.
291324 true.
292325
293326 check_request_param(V, ReqData) ->
294 case wrq:get_qs_value(V, ReqData) of
295 undefined -> undefined;
296 Str -> list_to_integer(Str)
297 end.
298
299 get_value_param(V, ReqData) ->
300 wrq:get_qs_value(V, ReqData).
327 case cowboy_req:qs_val(V, ReqData) of
328 {undefined, _} -> undefined;
329 {Str, _} -> list_to_integer(binary_to_list(Str))
330 end.
301331
302332 %% Validates and returns pagination parameters:
303333 %% Page is assumed to be > 0, PageSize > 0 PageSize <= ?MAX_PAGE_SIZE
304334 pagination_params(ReqData) ->
305 PageNum = check_request_param("page", ReqData),
306 PageSize = check_request_param("page_size", ReqData),
307 Name = get_value_param("name", ReqData),
308 UseRegex = get_value_param("use_regex", ReqData),
335 PageNum = check_request_param(<<"page">>, ReqData),
336 PageSize = check_request_param(<<"page_size">>, ReqData),
337 Name = get_value_param(<<"name">>, ReqData),
338 UseRegex = get_value_param(<<"use_regex">>, ReqData),
309339 case {PageNum, PageSize} of
310340 {undefined, _} ->
311341 undefined;
312 {PageNum, undefined} when is_integer(PageNum) andalso PageNum > 0 ->
342 {PageNum, undefined} when is_integer(PageNum) andalso PageNum > 0 ->
313343 #pagination{page = PageNum, page_size = ?DEFAULT_PAGE_SIZE,
314344 name = Name, use_regex = UseRegex};
315 {PageNum, PageSize} when is_integer(PageNum)
345 {PageNum, PageSize} when is_integer(PageNum)
316346 andalso is_integer(PageSize)
317347 andalso (PageNum > 0)
318348 andalso (PageSize > 0)
324354 [PageNum, PageSize])})
325355 end.
326356
357 -spec maybe_reverse([any()], string() | true | false) -> [any()].
327358 maybe_reverse([], _) ->
328359 [];
329 maybe_reverse(RangeList, "true") when is_list(RangeList) ->
330 lists:reverse(RangeList);
331360 maybe_reverse(RangeList, true) when is_list(RangeList) ->
332361 lists:reverse(RangeList);
333 maybe_reverse(RangeList, _) ->
362 maybe_reverse(RangeList, false) ->
334363 RangeList.
335364
336365 %% for backwards compatibility, does not filter the list
337366 range_filter(List, undefined, _)
338367 -> List;
339368
340 range_filter(List, RP = #pagination{page = PageNum, page_size = PageSize},
341 TotalElements) ->
369 range_filter(List, RP = #pagination{page = PageNum, page_size = PageSize},
370 TotalElements) ->
342371 Offset = (PageNum - 1) * PageSize + 1,
343372 try
344 range_response(lists:sublist(List, Offset, PageSize), RP, List,
345 TotalElements)
373 range_response(lists:sublist(List, Offset, PageSize), RP, List,
374 TotalElements)
346375 catch
347376 error:function_clause ->
348377 Reason = io_lib:format(
349 "Page out of range, page: ~p page size: ~p, len: ~p",
350 [PageNum, PageSize, length(List)]),
378 "Page out of range, page: ~p page size: ~p, len: ~p",
379 [PageNum, PageSize, length(List)]),
351380 throw({error, page_out_of_range, Reason})
352381 end.
353382
407436 [extract_column_items(Item, Cols) || Item <- Items].
408437
409438 columns(ReqData) ->
410 case wrq:get_qs_value("columns", ReqData) of
411 undefined -> all;
412 Str -> [[list_to_binary(T) || T <- string:tokens(C, ".")]
413 || C <- string:tokens(Str, ",")]
439 case cowboy_req:qs_val(<<"columns">>, ReqData) of
440 {undefined, _} -> all;
441 {Bin, _} -> [[list_to_binary(T) || T <- string:tokens(C, ".")]
442 || C <- string:tokens(binary_to_list(Bin), ",")]
414443 end.
415444
416445 extract_column_items(Item, all) ->
425454 extract_column_items(O, _Cols) ->
426455 O.
427456
428 want_column(_Col, all) -> true;
457 % want_column(_Col, all) -> true;
429458 want_column(Col, Cols) -> lists:any(fun([C|_]) -> C == Col end, Cols).
430459
431460 descend_columns(_K, []) -> [];
455484 halt_response(Code, Type, Reason, ReqData, Context) ->
456485 Json = {struct, [{error, Type},
457486 {reason, rabbit_mgmt_format:tuple(Reason)}]},
458 ReqData1 = wrq:append_to_response_body(mochijson2:encode(Json), ReqData),
459 {{halt, Code}, set_resp_header(
460 "Content-Type", "application/json", ReqData1), Context}.
487 {ok, ReqData1} = cowboy_req:reply(Code,
488 [{<<"content-type">>, <<"application/json">>}],
489 mochijson2:encode(Json), ReqData),
490 {halt, ReqData1, Context}.
461491
462492 id(Key, ReqData) when Key =:= exchange;
463493 Key =:= source;
470500 id0(Key, ReqData).
471501
472502 id0(Key, ReqData) ->
473 case orddict:find(Key, wrq:path_info(ReqData)) of
474 {ok, Id} -> list_to_binary(mochiweb_util:unquote(Id));
475 error -> none
503 case cowboy_req:binding(Key, ReqData) of
504 {undefined, _} -> none;
505 {Id, _} -> Id
476506 end.
477507
478508 with_decode(Keys, ReqData, Context, Fun) ->
479 with_decode(Keys, wrq:req_body(ReqData), ReqData, Context, Fun).
509 {ok, Body, ReqData1} = cowboy_req:body(ReqData),
510 with_decode(Keys, Body, ReqData1, Context, Fun).
480511
481512 with_decode(Keys, Body, ReqData, Context, Fun) ->
482513 case decode(Keys, Body) of
483514 {error, Reason} -> bad_request(Reason, ReqData, Context);
484515 {ok, Values, JSON} -> try
485 Fun(Values, JSON)
516 Fun(Values, JSON, ReqData)
486517 catch {error, Error} ->
487518 bad_request(Error, ReqData, Context)
488519 end
520551 not_found ->
521552 not_found(vhost_not_found, ReqData, Context);
522553 VHost ->
523 case decode(wrq:req_body(ReqData)) of
554 {ok, Body, ReqData1} = cowboy_req:body(ReqData),
555 case decode(Body) of
524556 {ok, Props} ->
525557 try
526558 Node = case pget(<<"node">>, Props) of
528560 N -> rabbit_nodes:make(
529561 binary_to_list(N))
530562 end,
531 amqp_request(VHost, ReqData, Context, Node,
563 amqp_request(VHost, ReqData1, Context, Node,
532564 props_to_method(
533565 MethodName, Props, Transformers, Extra))
534566 catch {error, Error} ->
535 bad_request(Error, ReqData, Context)
567 bad_request(Error, ReqData1, Context)
536568 end;
537569 {error, Reason} ->
538 bad_request(Reason, ReqData, Context)
570 bad_request(rabbit_mgmt_format:escape_html_tags(Reason),
571 ReqData1, Context)
539572 end
540573 end.
541574
623656 end;
624657 {error, {auth_failure, Msg}} ->
625658 not_authorised(Msg, ReqData, Context);
659 {error, not_allowed} ->
660 not_authorised(<<"Access refused.">>, ReqData, Context);
626661 {error, access_refused} ->
627662 not_authorised(<<"Access refused.">>, ReqData, Context);
628663 {error, {nodedown, N}} ->
644679 end.
645680
646681 filter_vhost(List, ReqData, Context) ->
647 VHosts = list_login_vhosts(Context#context.user, peersock(ReqData)),
682 VHosts = list_login_vhosts(Context#context.user, cowboy_req:get(socket, ReqData)),
648683 [I || I <- List, lists:member(pget(vhost, I), VHosts)].
649684
650685 filter_user(List, _ReqData, #context{user = User}) ->
664699 VHost -> [I || I <- List, pget(vhost, I) =:= VHost]
665700 end, ReqData, Context)).
666701
667 redirect(Location, ReqData) ->
668 wrq:do_redirect(true,
669 set_resp_header("Location",
670 binary_to_list(Location), ReqData)).
671
672702 set_resp_header(K, V, ReqData) ->
673 wrq:set_resp_header(K, strip_crlf(V), ReqData).
703 cowboy_req:set_resp_header(K, strip_crlf(V), ReqData).
674704
675705 strip_crlf(Str) -> lists:append(string:tokens(Str, "\r\n")).
676706
680710 %% Make replying to a post look like anything else...
681711 post_respond({true, ReqData, Context}) ->
682712 {true, ReqData, Context};
683 post_respond({{halt, Code}, ReqData, Context}) ->
684 {{halt, Code}, ReqData, Context};
713 post_respond({halt, ReqData, Context}) ->
714 {halt, ReqData, Context};
685715 post_respond({JSON, ReqData, Context}) ->
686716 {true, set_resp_header(
687 "Content-Type", "application/json",
688 wrq:append_to_response_body(JSON, ReqData)), Context}.
717 <<"Content-Type">>, "application/json",
718 cowboy_req:set_resp_body(JSON, ReqData)), Context}.
689719
690720 is_admin(T) -> intersects(T, [administrator]).
691721 is_policymaker(T) -> intersects(T, [administrator, policymaker]).
782812 Floor -> Floor + Interval
783813 end.
784814
815 ceil(X) when X < 0 ->
816 trunc(X);
817 ceil(X) ->
818 T = trunc(X),
819 case X - T == 0 of
820 true -> T;
821 false -> T + 1
822 end.
823
785824 int(Name, ReqData) ->
786 case wrq:get_qs_value(Name, ReqData) of
787 undefined -> undefined;
788 Str -> case catch list_to_integer(Str) of
825 case cowboy_req:qs_val(list_to_binary(Name), ReqData) of
826 {undefined, _} -> undefined;
827 {Bin, _} -> case catch list_to_integer(binary_to_list(Bin)) of
789828 {'EXIT', _} -> undefined;
790829 Integer -> Integer
791830 end
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_aliveness_test).
1717
18 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2]).
19 -export([finish_request/2, allowed_methods/2]).
20 -export([encodings_provided/2]).
18 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, is_authorized/2]).
2119 -export([resource_exists/2]).
20 -export([variances/2]).
2221
23 -include("rabbit_mgmt.hrl").
24 -include_lib("webmachine/include/webmachine.hrl").
22 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2523 -include_lib("amqp_client/include/amqp_client.hrl").
2624
2725 -define(QUEUE, <<"aliveness-test">>).
2826
2927 %%--------------------------------------------------------------------
3028
31 init(_Config) -> {ok, #context{}}.
29 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3230
33 finish_request(ReqData, Context) ->
34 {ok, rabbit_mgmt_cors:set_headers(ReqData, ?MODULE), Context}.
31 rest_init(Req, _Config) ->
32 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
3533
36 allowed_methods(ReqData, Context) ->
37 {['HEAD', 'GET', 'OPTIONS'], ReqData, Context}.
34 variances(Req, Context) ->
35 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3836
3937 content_types_provided(ReqData, Context) ->
40 {[{"application/json", to_json}], ReqData, Context}.
41
42 encodings_provided(ReqData, Context) ->
43 {[{"identity", fun(X) -> X end},
44 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
38 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4539
4640 resource_exists(ReqData, Context) ->
4741 {case rabbit_mgmt_util:vhost(ReqData) of
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_binding).
1717
18 -export([init/1, resource_exists/2, to_json/2,
18 -export([init/3, rest_init/2, resource_exists/2, to_json/2,
1919 content_types_provided/2, content_types_accepted/2,
20 is_authorized/2, allowed_methods/2, delete_resource/2,
21 args_hash/1]).
22 -export([finish_request/2]).
23 -export([encodings_provided/2]).
20 is_authorized/2, allowed_methods/2, delete_resource/2]).
21 -export([variances/2]).
2422
25 -include("rabbit_mgmt.hrl").
26 -include_lib("webmachine/include/webmachine.hrl").
23 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2724 -include_lib("amqp_client/include/amqp_client.hrl").
2825
2926 %%--------------------------------------------------------------------
30 init(_Config) -> {ok, #context{}}.
27 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3128
32 finish_request(ReqData, Context) ->
33 {ok, rabbit_mgmt_cors:set_headers(ReqData, ?MODULE), Context}.
29 rest_init(Req, _Config) ->
30 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
31
32 variances(Req, Context) ->
33 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3434
3535 content_types_provided(ReqData, Context) ->
36 {[{"application/json", to_json}], ReqData, Context}.
37
38 encodings_provided(ReqData, Context) ->
39 {[{"identity", fun(X) -> X end},
40 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
36 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4137
4238 content_types_accepted(ReqData, Context) ->
43 {[{"application/json", accept_content}], ReqData, Context}.
39 {[{'*', accept_content}], ReqData, Context}.
4440
4541 allowed_methods(ReqData, Context) ->
46 {['HEAD', 'GET', 'DELETE', 'OPTIONS'], ReqData, Context}.
42 {[<<"HEAD">>, <<"GET">>, <<"DELETE">>, <<"OPTIONS">>], ReqData, Context}.
4743
4844 resource_exists(ReqData, Context) ->
4945 Binding = binding(ReqData),
131127 Fun(Binding)
132128 end.
133129
130 method(MethodName, #binding{source = #resource{name = S},
131 destination = #resource{name = D},
132 key = K,
133 args = A}) ->
134 case MethodName of
135 'exchange.unbind' ->
136 #'exchange.unbind'{
137 source = S,
138 destination = D,
139 routing_key = K,
140 arguments = A};
141 'queue.unbind' ->
142 #'queue.unbind'{
143 queue = D,
144 exchange = S,
145 routing_key = K,
146 arguments = A}
147 end.
148
134149 sync_resource(MethodName, ReqData, Context) ->
135150 with_binding(
136151 ReqData, Context,
137152 fun(Binding) ->
138 Props0 = rabbit_mgmt_format:binding(Binding),
139 Props = Props0 ++
140 [{exchange, proplists:get_value(source, Props0)},
141 {queue, proplists:get_value(destination, Props0)}],
142 rabbit_mgmt_util:amqp_request(
153 rabbit_mgmt_util:amqp_request(
143154 rabbit_mgmt_util:vhost(ReqData), ReqData, Context,
144 rabbit_mgmt_util:props_to_method(MethodName, Props))
155 method(MethodName, Binding))
145156 end).
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_bindings).
1717
18 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2]).
19 -export([allowed_methods/2, post_is_create/2, create_path/2]).
18 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, is_authorized/2]).
19 -export([allowed_methods/2]).
2020 -export([content_types_accepted/2, accept_content/2, resource_exists/2]).
2121 -export([basic/1, augmented/2]).
22 -export([finish_request/2]).
23 -export([encodings_provided/2]).
22 -export([variances/2]).
2423
25 -include("rabbit_mgmt.hrl").
26 -include_lib("webmachine/include/webmachine.hrl").
24 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2725 -include_lib("amqp_client/include/amqp_client.hrl").
2826
2927 %%--------------------------------------------------------------------
3028
31 init([Mode]) ->
32 {ok, {Mode, #context{}}}.
29 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3330
34 finish_request(ReqData, Context) ->
35 {ok, rabbit_mgmt_cors:set_headers(ReqData, ?MODULE), Context}.
31 rest_init(Req, [Mode]) ->
32 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), {Mode, #context{}}}.
33
34 variances(Req, Context) ->
35 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3636
3737 content_types_provided(ReqData, Context) ->
38 {[{"application/json", to_json}], ReqData, Context}.
38 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
3939
40 encodings_provided(ReqData, Context) ->
41 {[{"identity", fun(X) -> X end},
42 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
43
40 %% The current version of Cowboy forces us to report the resource doesn't
41 %% exist here in order to get a 201 response. It seems Cowboy confuses the
42 %% resource from the request and the resource that will be created by POST.
43 %% https://github.com/ninenines/cowboy/issues/723#issuecomment-161319576
4444 resource_exists(ReqData, {Mode, Context}) ->
45 {case list_bindings(Mode, ReqData) of
46 vhost_not_found -> false;
47 _ -> true
48 end, ReqData, {Mode, Context}}.
45 case cowboy_req:method(ReqData) of
46 {<<"POST">>, _} ->
47 {false, ReqData, {Mode, Context}};
48 _ ->
49 {case list_bindings(Mode, ReqData) of
50 vhost_not_found -> false;
51 _ -> true
52 end, ReqData, {Mode, Context}}
53 end.
4954
5055 content_types_accepted(ReqData, Context) ->
51 {[{"application/json", accept_content}], ReqData, Context}.
56 {[{'*', accept_content}], ReqData, Context}.
5257
5358 allowed_methods(ReqData, {Mode, Context}) ->
5459 {case Mode of
55 source_destination -> ['HEAD', 'GET', 'POST', 'OPTIONS'];
56 _ -> ['HEAD', 'GET', 'OPTIONS']
60 source_destination -> [<<"HEAD">>, <<"GET">>, <<"POST">>, <<"OPTIONS">>];
61 _ -> [<<"HEAD">>, <<"GET">>, <<"OPTIONS">>]
5762 end, ReqData, {Mode, Context}}.
58
59 post_is_create(ReqData, Context) ->
60 {true, ReqData, Context}.
6163
6264 to_json(ReqData, {Mode, Context}) ->
6365 Bs = [rabbit_mgmt_format:binding(B) || B <- list_bindings(Mode, ReqData)],
6769 "routing_key", "properties_key"],
6870 ReqData, {Mode, Context}).
6971
70 create_path(ReqData, Context) ->
71 {"dummy", ReqData, Context}.
72
73 accept_content(ReqData, {_Mode, Context}) ->
72 accept_content(ReqData0, {_Mode, Context}) ->
73 {ok, Body, ReqData} = cowboy_req:body(ReqData0),
7474 Source = rabbit_mgmt_util:id(source, ReqData),
7575 Dest = rabbit_mgmt_util:id(destination, ReqData),
7676 DestType = rabbit_mgmt_util:id(dtype, ReqData),
7777 VHost = rabbit_mgmt_util:vhost(ReqData),
78 {ok, Props} = rabbit_mgmt_util:decode(wrq:req_body(ReqData)),
78 {ok, Props} = rabbit_mgmt_util:decode(Body),
7979 {Method, Key, Args} = method_key_args(DestType, Source, Dest, Props),
8080 Response = rabbit_mgmt_util:amqp_request(VHost, ReqData, Context, Method),
8181 case Response of
82 {{halt, _}, _, _} = Res ->
82 {halt, _, _} = Res ->
8383 Res;
8484 {true, ReqData, Context2} ->
8585 Loc = rabbit_web_dispatch_util:relativise(
86 wrq:path(ReqData),
86 binary_to_list(element(1, cowboy_req:path(ReqData))),
8787 binary_to_list(
8888 rabbit_mgmt_format:url(
8989 "/api/bindings/~s/e/~s/~s/~s/~s",
9090 [VHost, Source, DestType, Dest,
9191 rabbit_mgmt_format:pack_binding_props(Key, Args)]))),
92 {true, rabbit_mgmt_util:set_resp_header("Location", Loc, ReqData),
93 Context2}
92 {{true, Loc}, ReqData, Context2}
9493 end.
9594
9695 is_authorized(ReqData, {Mode, Context}) ->
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_channel).
1717
18 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2]).
19 -export([finish_request/2, allowed_methods/2]).
20 -export([encodings_provided/2]).
18 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, is_authorized/2]).
2119 -export([resource_exists/2]).
20 -export([variances/2]).
2221
23 -include("rabbit_mgmt.hrl").
24 -include_lib("webmachine/include/webmachine.hrl").
22 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2523 -include_lib("rabbit_common/include/rabbit.hrl").
2624
2725 %%--------------------------------------------------------------------
2826
29 init(_Config) -> {ok, #context{}}.
27 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3028
31 finish_request(ReqData, Context) ->
32 {ok, rabbit_mgmt_cors:set_headers(ReqData, ?MODULE), Context}.
29 rest_init(Req, _Config) ->
30 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
3331
34 allowed_methods(ReqData, Context) ->
35 {['HEAD', 'GET', 'OPTIONS'], ReqData, Context}.
32 variances(Req, Context) ->
33 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3634
3735 content_types_provided(ReqData, Context) ->
38 {[{"application/json", to_json}], ReqData, Context}.
39
40 encodings_provided(ReqData, Context) ->
41 {[{"identity", fun(X) -> X end},
42 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
36 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4337
4438 resource_exists(ReqData, Context) ->
4539 case channel(ReqData) of
4842 end.
4943
5044 to_json(ReqData, Context) ->
45 Payload = rabbit_mgmt_format:clean_consumer_details(
46 rabbit_mgmt_format:strip_pids(channel(ReqData))),
5147 rabbit_mgmt_util:reply(
52 {struct, rabbit_mgmt_format:strip_pids(channel(ReqData))},
48 {struct, Payload},
5349 ReqData, Context).
5450
5551 is_authorized(ReqData, Context) ->
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_channels).
1717
18 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2,
18 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, is_authorized/2,
1919 augmented/2]).
20 -export([finish_request/2, allowed_methods/2]).
21 -export([encodings_provided/2]).
20 -export([variances/2]).
2221
2322 -import(rabbit_misc, [pget/2]).
2423
25 -include("rabbit_mgmt.hrl").
26 -include_lib("webmachine/include/webmachine.hrl").
24 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2725 -include_lib("rabbit_common/include/rabbit.hrl").
2826
2927 %%--------------------------------------------------------------------
3028
31 init(_Config) -> {ok, #context{}}.
29 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3230
33 finish_request(ReqData, Context) ->
34 {ok, rabbit_mgmt_cors:set_headers(ReqData, ?MODULE), Context}.
31 rest_init(Req, _Config) ->
32 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
3533
36 allowed_methods(ReqData, Context) ->
37 {['HEAD', 'GET', 'OPTIONS'], ReqData, Context}.
34 variances(Req, Context) ->
35 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3836
3937 content_types_provided(ReqData, Context) ->
40 {[{"application/json", to_json}], ReqData, Context}.
41
42 encodings_provided(ReqData, Context) ->
43 {[{"identity", fun(X) -> X end},
44 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
38 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4539
4640 to_json(ReqData, Context) ->
4741 try
1717
1818 %% Lists channels in a vhost
1919
20 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2,
20 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, is_authorized/2,
2121 augmented/2, resource_exists/2]).
22 -export([finish_request/2, allowed_methods/2]).
23 -export([encodings_provided/2]).
22 -export([variances/2]).
2423
2524 -import(rabbit_misc, [pget/2]).
2625
27 -include("rabbit_mgmt.hrl").
28 -include_lib("webmachine/include/webmachine.hrl").
26 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2927 -include_lib("rabbit_common/include/rabbit.hrl").
3028
3129 %%--------------------------------------------------------------------
3230
33 init(_Config) -> {ok, #context{}}.
31 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3432
35 finish_request(ReqData, Context) ->
36 {ok, rabbit_mgmt_cors:set_headers(ReqData, ?MODULE), Context}.
33 rest_init(Req, _Config) ->
34 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
3735
38 allowed_methods(ReqData, Context) ->
39 {['HEAD', 'GET', 'OPTIONS'], ReqData, Context}.
36 variances(Req, Context) ->
37 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
4038
4139 content_types_provided(ReqData, Context) ->
42 {[{"application/json", to_json}], ReqData, Context}.
43
44 encodings_provided(ReqData, Context) ->
45 {[{"identity", fun(X) -> X end},
46 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
40 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4741
4842 resource_exists(ReqData, Context) ->
4943 {rabbit_vhost:exists(rabbit_mgmt_util:id(vhost, ReqData)), ReqData, Context}.
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_cluster_name).
1717
18 -export([init/1, resource_exists/2, to_json/2,
18 -export([init/3, rest_init/2, resource_exists/2, to_json/2,
1919 content_types_provided/2, content_types_accepted/2,
2020 is_authorized/2, allowed_methods/2, accept_content/2]).
21 -export([finish_request/2]).
22 -export([encodings_provided/2]).
21 -export([variances/2]).
2322
24 -include("rabbit_mgmt.hrl").
25 -include_lib("webmachine/include/webmachine.hrl").
23 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2624 -include_lib("amqp_client/include/amqp_client.hrl").
2725
2826 %%--------------------------------------------------------------------
29 init(_Config) -> {ok, #context{}}.
3027
31 finish_request(ReqData, Context) ->
32 {ok, rabbit_mgmt_cors:set_headers(ReqData, ?MODULE), Context}.
28 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
29
30 rest_init(Req, _Config) ->
31 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
32
33 variances(Req, Context) ->
34 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3335
3436 content_types_provided(ReqData, Context) ->
35 {[{"application/json", to_json}], ReqData, Context}.
36
37 encodings_provided(ReqData, Context) ->
38 {[{"identity", fun(X) -> X end},
39 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
37 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4038
4139 content_types_accepted(ReqData, Context) ->
42 {[{"application/json", accept_content}], ReqData, Context}.
40 {[{'*', accept_content}], ReqData, Context}.
4341
4442 allowed_methods(ReqData, Context) ->
45 {['HEAD', 'GET', 'PUT', 'OPTIONS'], ReqData, Context}.
43 {[<<"HEAD">>, <<"GET">>, <<"PUT">>, <<"OPTIONS">>], ReqData, Context}.
4644
4745 resource_exists(ReqData, Context) ->
4846 {true, ReqData, Context}.
5149 rabbit_mgmt_util:reply(
5250 [{name, rabbit_nodes:cluster_name()}], ReqData, Context).
5351
54 accept_content(ReqData, Context) ->
52 accept_content(ReqData0, Context) ->
5553 rabbit_mgmt_util:with_decode(
56 [name], ReqData, Context, fun([Name], _) ->
54 [name], ReqData0, Context, fun([Name], _, ReqData) ->
5755 rabbit_nodes:set_cluster_name(
5856 as_binary(Name)),
5957 {true, ReqData, Context}
6058 end).
6159
6260 is_authorized(ReqData, Context) ->
63 case wrq:method(ReqData) of
64 'PUT' -> rabbit_mgmt_util:is_authorized_admin(ReqData, Context);
65 _ -> rabbit_mgmt_util:is_authorized(ReqData, Context)
61 case cowboy_req:method(ReqData) of
62 {<<"PUT">>, _} -> rabbit_mgmt_util:is_authorized_admin(ReqData, Context);
63 _ -> rabbit_mgmt_util:is_authorized(ReqData, Context)
6664 end.
6765
6866 as_binary(Val) when is_binary(Val) ->
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_connection).
1717
18 -export([init/1, resource_exists/2, to_json/2, content_types_provided/2,
18 -export([init/3, rest_init/2, resource_exists/2, to_json/2, content_types_provided/2,
1919 is_authorized/2, allowed_methods/2, delete_resource/2, conn/1]).
20 -export([finish_request/2]).
21 -export([encodings_provided/2]).
20 -export([variances/2]).
2221
23 -include("rabbit_mgmt.hrl").
24 -include_lib("webmachine/include/webmachine.hrl").
22 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2523 -include_lib("rabbit_common/include/rabbit.hrl").
2624
2725 %%--------------------------------------------------------------------
2826
29 init(_Config) -> {ok, #context{}}.
27 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3028
31 finish_request(ReqData, Context) ->
32 {ok, rabbit_mgmt_cors:set_headers(ReqData, ?MODULE), Context}.
29 rest_init(Req, _Config) ->
30 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
31
32 variances(Req, Context) ->
33 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3334
3435 content_types_provided(ReqData, Context) ->
35 {[{"application/json", to_json}], ReqData, Context}.
36
37 encodings_provided(ReqData, Context) ->
38 {[{"identity", fun(X) -> X end},
39 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
36 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4037
4138 allowed_methods(ReqData, Context) ->
42 {['HEAD', 'GET', 'DELETE', 'OPTIONS'], ReqData, Context}.
39 {[<<"HEAD">>, <<"GET">>, <<"DELETE">>, <<"OPTIONS">>], ReqData, Context}.
4340
4441 resource_exists(ReqData, Context) ->
4542 case conn(ReqData) of
5451 delete_resource(ReqData, Context) ->
5552 Conn = conn(ReqData),
5653 Pid = proplists:get_value(pid, Conn),
57 Reason = case wrq:get_req_header(<<"X-Reason">>, ReqData) of
58 undefined -> "Closed via management plugin";
59 V -> V
54 Reason = case cowboy_req:header(<<"x-reason">>, ReqData) of
55 {undefined, _} -> "Closed via management plugin";
56 {V, _} -> binary_to_list(V)
6057 end,
6158 case proplists:get_value(type, Conn) of
6259 direct -> amqp_direct_connection:server_close(Pid, 320, Reason);
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_connection_channels).
1717
18 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2]).
19 -export([finish_request/2, allowed_methods/2]).
20 -export([encodings_provided/2]).
18 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, is_authorized/2]).
2119 -export([resource_exists/2]).
20 -export([variances/2]).
2221
23 -include("rabbit_mgmt.hrl").
24 -include_lib("webmachine/include/webmachine.hrl").
22 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2523 -include_lib("rabbit_common/include/rabbit.hrl").
2624
2725 %%--------------------------------------------------------------------
2826
29 init(_Config) -> {ok, #context{}}.
27 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3028
31 finish_request(ReqData, Context) ->
32 {ok, rabbit_mgmt_cors:set_headers(ReqData, ?MODULE), Context}.
29 rest_init(Req, _Config) ->
30 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
3331
34 allowed_methods(ReqData, Context) ->
35 {['HEAD', 'GET', 'OPTIONS'], ReqData, Context}.
32 variances(Req, Context) ->
33 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3634
3735 content_types_provided(ReqData, Context) ->
38 {[{"application/json", to_json}], ReqData, Context}.
39
40 encodings_provided(ReqData, Context) ->
41 {[{"identity", fun(X) -> X end},
42 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
36 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4337
4438 resource_exists(ReqData, Context) ->
4539 case rabbit_mgmt_wm_connection:conn(ReqData) of
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_connections).
1717
18 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2,
18 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, is_authorized/2,
1919 augmented/2]).
20 -export([finish_request/2, allowed_methods/2]).
21 -export([encodings_provided/2]).
20 -export([variances/2]).
2221
2322 -import(rabbit_misc, [pget/2]).
2423
25 -include("rabbit_mgmt.hrl").
26 -include_lib("webmachine/include/webmachine.hrl").
24 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2725 -include_lib("rabbit_common/include/rabbit.hrl").
2826
2927 %%--------------------------------------------------------------------
3028
31 init(_Config) -> {ok, #context{}}.
29 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3230
33 finish_request(ReqData, Context) ->
34 {ok, rabbit_mgmt_cors:set_headers(ReqData, ?MODULE), Context}.
31 rest_init(Req, _Config) ->
32 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
3533
36 allowed_methods(ReqData, Context) ->
37 {['HEAD', 'GET', 'OPTIONS'], ReqData, Context}.
34 variances(Req, Context) ->
35 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3836
3937 content_types_provided(ReqData, Context) ->
40 {[{"application/json", to_json}], ReqData, Context}.
41
42 encodings_provided(ReqData, Context) ->
43 {[{"identity", fun(X) -> X end},
44 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
38 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4539
4640 to_json(ReqData, Context) ->
4741 try
1717
1818 %% Lists connections in a vhost
1919
20 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2,
20 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, is_authorized/2,
2121 augmented/2, resource_exists/2]).
22 -export([finish_request/2, allowed_methods/2]).
23 -export([encodings_provided/2]).
22 -export([variances/2]).
2423
2524 -import(rabbit_misc, [pget/2]).
2625
27 -include("rabbit_mgmt.hrl").
28 -include_lib("webmachine/include/webmachine.hrl").
26 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2927 -include_lib("rabbit_common/include/rabbit.hrl").
3028
3129 %%--------------------------------------------------------------------
3230
33 init(_Config) -> {ok, #context{}}.
31 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3432
35 finish_request(ReqData, Context) ->
36 {ok, rabbit_mgmt_cors:set_headers(ReqData, ?MODULE), Context}.
33 rest_init(Req, _Config) ->
34 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
3735
38 allowed_methods(ReqData, Context) ->
39 {['HEAD', 'GET', 'OPTIONS'], ReqData, Context}.
36 variances(Req, Context) ->
37 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
4038
4139 content_types_provided(ReqData, Context) ->
42 {[{"application/json", to_json}], ReqData, Context}.
43
44 encodings_provided(ReqData, Context) ->
45 {[{"identity", fun(X) -> X end},
46 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
40 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4741
4842 resource_exists(ReqData, Context) ->
4943 {rabbit_vhost:exists(rabbit_mgmt_util:id(vhost, ReqData)), ReqData, Context}.
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414
1515 -module(rabbit_mgmt_wm_consumers).
1616
17 -export([init/1, to_json/2, content_types_provided/2, resource_exists/2,
17 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, resource_exists/2,
1818 is_authorized/2]).
19 -export([finish_request/2, allowed_methods/2]).
20 -export([encodings_provided/2]).
19 -export([variances/2]).
2120
2221 -import(rabbit_misc, [pget/2]).
2322
24 -include("rabbit_mgmt.hrl").
25 -include_lib("webmachine/include/webmachine.hrl").
23 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2624 -include_lib("rabbit_common/include/rabbit.hrl").
2725
2826 %%--------------------------------------------------------------------
2927
30 init(_Config) -> {ok, #context{}}.
28 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3129
32 finish_request(ReqData, Context) ->
33 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), Context}.
30 rest_init(Req, _Config) ->
31 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
3432
35 allowed_methods(ReqData, Context) ->
36 {['HEAD', 'GET', 'OPTIONS'], ReqData, Context}.
33 variances(Req, Context) ->
34 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3735
3836 content_types_provided(ReqData, Context) ->
39 {[{"application/json", to_json}], ReqData, Context}.
40
41 encodings_provided(ReqData, Context) ->
42 {[{"identity", fun(X) -> X end},
43 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
37 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4438
4539 resource_exists(ReqData, Context) ->
4640 {case rabbit_mgmt_util:vhost(ReqData) of
47 vhost_not_found -> false;
48 _ -> true
41 not_found -> false;
42 none -> true; % none means `all`
43 _ -> true
4944 end, ReqData, Context}.
5045
5146 to_json(ReqData, Context = #context{user = User}) ->
52 Consumers = case rabbit_mgmt_util:vhost(ReqData) of
53 none -> rabbit_mgmt_db:get_all_consumers();
54 VHost -> rabbit_mgmt_db:get_all_consumers(VHost)
55 end,
47 Arg = case rabbit_mgmt_util:vhost(ReqData) of
48 none -> all;
49 VHost -> VHost
50 end,
51
52 Consumers = rabbit_mgmt_format:strip_pids(
53 rabbit_mgmt_db:get_all_consumers(Arg)),
5654 rabbit_mgmt_util:reply_list(
5755 filter_user(Consumers, User), ReqData, Context).
5856
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_definitions).
1717
18 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2]).
18 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, is_authorized/2]).
1919 -export([content_types_accepted/2, allowed_methods/2, accept_json/2]).
20 -export([post_is_create/2, create_path/2, accept_multipart/2]).
21 -export([finish_request/2]).
22 -export([encodings_provided/2]).
20 -export([accept_multipart/2]).
21 -export([variances/2]).
2322
2423 -export([apply_defs/3]).
2524
2625 -import(rabbit_misc, [pget/2, pget/3]).
2726
2827 -include("rabbit_mgmt.hrl").
29 -include_lib("webmachine/include/webmachine.hrl").
28 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
3029 -include_lib("amqp_client/include/amqp_client.hrl").
3130
3231 %%--------------------------------------------------------------------
33 init(_Config) -> {ok, #context{}}.
34
35 finish_request(ReqData, Context) ->
36 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), Context}.
32
33 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
34
35 rest_init(Req, _Config) ->
36 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
37
38 variances(Req, Context) ->
39 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3740
3841 content_types_provided(ReqData, Context) ->
39 {[{"application/json", to_json}], ReqData, Context}.
40
41 encodings_provided(ReqData, Context) ->
42 {[{"identity", fun(X) -> X end},
43 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
42 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4443
4544 content_types_accepted(ReqData, Context) ->
46 {[{"application/json", accept_json},
47 {"multipart/form-data", accept_multipart}], ReqData, Context}.
45 {[{<<"application/json">>, accept_json},
46 {{<<"multipart">>, <<"form-data">>, '*'}, accept_multipart}], ReqData, Context}.
4847
4948 allowed_methods(ReqData, Context) ->
50 {['HEAD', 'GET', 'POST', 'OPTIONS'], ReqData, Context}.
51
52 post_is_create(ReqData, Context) ->
53 {true, ReqData, Context}.
54
55 create_path(ReqData, Context) ->
56 {"dummy", ReqData, Context}.
49 {[<<"HEAD">>, <<"GET">>, <<"POST">>, <<"OPTIONS">>], ReqData, Context}.
5750
5851 to_json(ReqData, Context) ->
5952 case rabbit_mgmt_util:vhost(ReqData) of
6053 none ->
6154 all_definitions(ReqData, Context);
6255 not_found ->
63 rabbit_mgmt_util:bad_request(list_to_binary("vhost_not_found"),
56 rabbit_mgmt_util:bad_request(rabbit_data_coercion:to_binary("vhost_not_found"),
6457 ReqData, Context);
6558 _VHost ->
6659 vhost_definitions(ReqData, Context)
7669 export_binding(B, QNames)],
7770 {ok, Vsn} = application:get_key(rabbit, vsn),
7871 rabbit_mgmt_util:reply(
79 [{rabbit_version, list_to_binary(Vsn)}] ++
72 [{rabbit_version, rabbit_data_coercion:to_binary(Vsn)}] ++
8073 filter(
81 [{users, rabbit_mgmt_wm_users:users()},
82 {vhosts, rabbit_mgmt_wm_vhosts:basic()},
83 {permissions, rabbit_mgmt_wm_permissions:permissions()},
84 {parameters, rabbit_mgmt_wm_parameters:basic(ReqData)},
85 {policies, rabbit_mgmt_wm_policies:basic(ReqData)},
86 {queues, Qs},
87 {exchanges, Xs},
88 {bindings, Bs}]),
89 case wrq:get_qs_value("download", ReqData) of
90 undefined -> ReqData;
91 Filename -> rabbit_mgmt_util:set_resp_header(
92 "Content-Disposition",
74 [{users, rabbit_mgmt_wm_users:users()},
75 {vhosts, rabbit_mgmt_wm_vhosts:basic()},
76 {permissions, rabbit_mgmt_wm_permissions:permissions()},
77 {parameters, rabbit_mgmt_wm_parameters:basic(ReqData)},
78 {global_parameters, rabbit_mgmt_wm_global_parameters:basic()},
79 {policies, rabbit_mgmt_wm_policies:basic(ReqData)},
80 {queues, Qs},
81 {exchanges, Xs},
82 {bindings, Bs}]),
83 case cowboy_req:qs_val(<<"download">>, ReqData) of
84 {undefined, _} -> ReqData;
85 {Filename, _} -> rabbit_mgmt_util:set_resp_header(
86 <<"Content-Disposition">>,
9387 "attachment; filename=" ++
94 mochiweb_util:unquote(Filename), ReqData)
88 binary_to_list(Filename), ReqData)
9589 end,
9690 Context).
9791
106100 export_binding(B, QNames)],
107101 {ok, Vsn} = application:get_key(rabbit, vsn),
108102 rabbit_mgmt_util:reply(
109 [{rabbit_version, list_to_binary(Vsn)}] ++
103 [{rabbit_version, rabbit_data_coercion:to_binary(Vsn)}] ++
110104 filter(
111105 [{policies, rabbit_mgmt_wm_policies:basic(ReqData)},
112106 {queues, Qs},
113107 {exchanges, Xs},
114108 {bindings, Bs}]),
115 case wrq:get_qs_value("download", ReqData) of
116 undefined -> ReqData;
117 Filename -> rabbit_mgmt_util:set_resp_header(
118 "Content-Disposition",
119 "attachment; filename=" ++
120 mochiweb_util:unquote(Filename), ReqData)
109 case cowboy_req:qs_val(<<"download">>, ReqData) of
110 {undefined, _} -> ReqData;
111 {Filename, _} -> rabbit_mgmt_util:set_resp_header(
112 <<"Content-Disposition">>,
113 "attachment; filename=" ++
114 mochiweb_util:unquote(Filename), ReqData)
121115 end,
122116 Context).
123117
124 accept_json(ReqData, Context) ->
125 accept(wrq:req_body(ReqData), ReqData, Context).
126
127 accept_multipart(ReqData, Context) ->
128 Parts = webmachine_multipart:get_all_parts(
129 wrq:req_body(ReqData),
130 webmachine_multipart:find_boundary(ReqData)),
131 Redirect = get_part("redirect", Parts),
132 Json = get_part("file", Parts),
118 accept_json(ReqData0, Context) ->
119 {ok, Body, ReqData} = cowboy_req:body(ReqData0),
120 accept(Body, ReqData, Context).
121
122 accept_multipart(ReqData0, Context) ->
123 {Parts, ReqData} = get_all_parts(ReqData0),
124 Redirect = get_part(<<"redirect">>, Parts),
125 Json = get_part(<<"file">>, Parts),
133126 Resp = {Res, _, _} = accept(Json, ReqData, Context),
134 case Res of
135 true ->
136 ReqData1 =
137 case Redirect of
138 unknown -> ReqData;
139 _ -> rabbit_mgmt_util:redirect(Redirect, ReqData)
140 end,
141 {true, ReqData1, Context};
142 _ ->
143 Resp
127 case {Res, Redirect} of
128 {true, unknown} -> {true, ReqData, Context};
129 {true, _} -> {{true, Redirect}, ReqData, Context};
130 _ -> Resp
144131 end.
145132
146133 is_authorized(ReqData, Context) ->
147 case wrq:get_qs_value("auth", ReqData) of
148 undefined -> rabbit_mgmt_util:is_authorized_admin(ReqData, Context);
149 Auth -> is_authorized_qs(ReqData, Context, Auth)
134 case cowboy_req:qs_val(<<"auth">>, ReqData) of
135 {undefined, _} -> rabbit_mgmt_util:is_authorized_admin(ReqData, Context);
136 {Auth, _} -> is_authorized_qs(ReqData, Context, Auth)
150137 end.
151138
152139 %% Support for the web UI - it can't add a normal "authorization"
166153 apply_defs(Body, fun() -> {true, ReqData, Context} end,
167154 fun(E) -> rabbit_mgmt_util:bad_request(E, ReqData, Context) end);
168155 not_found ->
169 rabbit_mgmt_util:bad_request(list_to_binary("vhost_not_found"),
156 rabbit_mgmt_util:bad_request(rabbit_data_coercion:to_binary("vhost_not_found"),
170157 ReqData, Context);
171158 VHost ->
172159 apply_defs(Body, fun() -> {true, ReqData, Context} end,
181168 {ok, _, All} ->
182169 Version = pget(rabbit_version, All),
183170 try
184 for_all(users, All, fun(User) ->
185 rabbit_mgmt_wm_user:put_user(
186 User,
187 Version)
188 end),
189 for_all(vhosts, All, fun add_vhost/1),
190 for_all(permissions, All, fun add_permission/1),
191 for_all(parameters, All, fun add_parameter/1),
192 for_all(policies, All, fun add_policy/1),
193 for_all(queues, All, fun add_queue/1),
194 for_all(exchanges, All, fun add_exchange/1),
195 for_all(bindings, All, fun add_binding/1),
171 for_all(users, All, fun(User) ->
172 rabbit_mgmt_wm_user:put_user(
173 User,
174 Version)
175 end),
176 for_all(vhosts, All, fun add_vhost/1),
177 for_all(permissions, All, fun add_permission/1),
178 for_all(parameters, All, fun add_parameter/1),
179 for_all(global_parameters, All, fun add_global_parameter/1),
180 for_all(policies, All, fun add_policy/1),
181 for_all(queues, All, fun add_queue/1),
182 for_all(exchanges, All, fun add_exchange/1),
183 for_all(bindings, All, fun add_binding/1),
196184 SuccessFun()
197185 catch {error, E} -> ErrorFun(format(E));
198186 exit:E -> ErrorFun(format(E))
216204 end.
217205
218206 format(#amqp_error{name = Name, explanation = Explanation}) ->
219 list_to_binary(rabbit_misc:format("~s: ~s", [Name, Explanation]));
207 rabbit_data_coercion:to_binary(rabbit_misc:format("~s: ~s", [Name, Explanation]));
220208 format(E) ->
221 list_to_binary(rabbit_misc:format("~p", [E])).
209 rabbit_data_coercion:to_binary(rabbit_misc:format("~p", [E])).
210
211 get_all_parts(ReqData) ->
212 get_all_parts(ReqData, []).
213
214 get_all_parts(ReqData0, Acc) ->
215 case cowboy_req:part(ReqData0) of
216 {done, ReqData} ->
217 {Acc, ReqData};
218 {ok, Headers, ReqData1} ->
219 Name = case cow_multipart:form_data(Headers) of
220 {data, N} -> N;
221 {file, N, _, _, _} -> N
222 end,
223 {ok, Body, ReqData} = cowboy_req:part_body(ReqData1),
224 get_all_parts(ReqData, [{Name, Body}|Acc])
225 end.
222226
223227 get_part(Name, Parts) ->
224 %% TODO any reason not to use lists:keyfind instead?
225 Filtered = [Value || {N, _Meta, Value} <- Parts, N == Name],
226 case Filtered of
227 [] -> unknown;
228 [F] -> F
228 case lists:keyfind(Name, 1, Parts) of
229 false -> unknown;
230 {_, Value} -> Value
229231 end.
230232
231233 export_queue(Queue) ->
251253 %%--------------------------------------------------------------------
252254
253255 rw_state() ->
254 [{users, [name, password_hash, hashing_algorithm, tags]},
255 {vhosts, [name]},
256 {permissions, [user, vhost, configure, write, read]},
257 {parameters, [vhost, component, name, value]},
258 {policies, [vhost, name, pattern, definition, priority, 'apply-to']},
259 {queues, [name, vhost, durable, auto_delete, arguments]},
260 {exchanges, [name, vhost, type, durable, auto_delete, internal,
261 arguments]},
262 {bindings, [source, vhost, destination, destination_type, routing_key,
263 arguments]}].
256 [{users, [name, password_hash, hashing_algorithm, tags]},
257 {vhosts, [name]},
258 {permissions, [user, vhost, configure, write, read]},
259 {parameters, [vhost, component, name, value]},
260 {global_parameters, [name, value]},
261 {policies, [vhost, name, pattern, definition, priority, 'apply-to']},
262 {queues, [name, vhost, durable, auto_delete, arguments]},
263 {exchanges, [name, vhost, type, durable, auto_delete, internal,
264 arguments]},
265 {bindings, [source, vhost, destination, destination_type, routing_key,
266 arguments]}].
264267
265268 filter(Items) ->
266269 [filter_items(N, V, proplists:get_value(N, rw_state())) || {N, V} <- Items].
279282 for_all(Name, All, Fun) ->
280283 case pget(Name, All) of
281284 undefined -> ok;
282 List -> [Fun([{atomise_name(K), V} || {K, V} <- I]) ||
283 {struct, I} <- List]
285 List -> _ = [Fun([{atomise_name(K), V} || {K, V} <- I]) ||
286 {struct, I} <- List],
287 ok
284288 end.
285289
286290 for_all(Name, All, VHost, Fun) ->
287291 case pget(Name, All) of
288292 undefined -> ok;
289 List -> [Fun(VHost, [{atomise_name(K), V} || {K, V} <- I]) ||
290 {struct, I} <- List]
291 end.
292
293 atomise_name(N) -> list_to_atom(binary_to_list(N)).
293 List -> _ = [Fun(VHost, [{atomise_name(K), V} || {K, V} <- I]) ||
294 {struct, I} <- List],
295 ok
296 end.
297
298 atomise_name(N) -> rabbit_data_coercion:to_atom(N).
294299
295300 %%--------------------------------------------------------------------
296301
301306 Term = rabbit_misc:json_to_term(pget(value, Param)),
302307 case rabbit_runtime_parameters:set(VHost, Comp, Key, Term, none) of
303308 ok -> ok;
304 {error_string, E} -> S = rabbit_misc:format(" (~s/~s/~s)",
305 [VHost, Comp, Key]),
306 exit(list_to_binary(E ++ S))
307 end.
309 {error_string, E} -> S = rabbit_misc:format(" (~s/~s/~s)", [VHost, Comp, Key]),
310 exit(rabbit_data_coercion:to_binary(rabbit_mgmt_format:escape_html_tags(E ++ S)))
311 end.
312
313 add_global_parameter(Param) ->
314 Key = pget(name, Param),
315 Term = rabbit_misc:json_to_term(pget(value, Param)),
316 rabbit_runtime_parameters:set_global(Key, Term).
308317
309318 add_policy(Param) ->
310319 VHost = pget(vhost, Param),
319328 pget('apply-to', Param, <<"all">>)) of
320329 ok -> ok;
321330 {error_string, E} -> S = rabbit_misc:format(" (~s/~s)", [VHost, Key]),
322 exit(list_to_binary(E ++ S))
331 exit(rabbit_data_coercion:to_binary(rabbit_mgmt_format:escape_html_tags(E ++ S)))
323332 end.
324333
325334 add_vhost(VHost) ->
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_exchange).
1717
18 -export([init/1, resource_exists/2, to_json/2,
18 -export([init/3, rest_init/2, resource_exists/2, to_json/2,
1919 content_types_provided/2, content_types_accepted/2,
2020 is_authorized/2, allowed_methods/2, accept_content/2,
2121 delete_resource/2, exchange/1, exchange/2]).
22 -export([finish_request/2]).
23 -export([encodings_provided/2]).
22 -export([variances/2]).
2423
25 -include("rabbit_mgmt.hrl").
26 -include_lib("webmachine/include/webmachine.hrl").
24 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2725 -include_lib("amqp_client/include/amqp_client.hrl").
2826
2927 %%--------------------------------------------------------------------
30 init(_Config) -> {ok, #context{}}.
3128
32 finish_request(ReqData, Context) ->
33 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), Context}.
29 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
30
31 rest_init(Req, _Config) ->
32 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
33
34 variances(Req, Context) ->
35 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3436
3537 content_types_provided(ReqData, Context) ->
36 {[{"application/json", to_json}], ReqData, Context}.
37
38 encodings_provided(ReqData, Context) ->
39 {[{"identity", fun(X) -> X end},
40 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
38 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4139
4240 content_types_accepted(ReqData, Context) ->
43 {[{"application/json", accept_content}], ReqData, Context}.
41 {[{'*', accept_content}], ReqData, Context}.
4442
4543 allowed_methods(ReqData, Context) ->
46 {['HEAD', 'GET', 'PUT', 'DELETE', 'OPTIONS'], ReqData, Context}.
44 {[<<"HEAD">>, <<"GET">>, <<"PUT">>, <<"DELETE">>, <<"OPTIONS">>], ReqData, Context}.
4745
4846 resource_exists(ReqData, Context) ->
4947 {case exchange(ReqData) of
5553 try
5654 [X] = rabbit_mgmt_db:augment_exchanges(
5755 [exchange(ReqData)], rabbit_mgmt_util:range(ReqData), full),
58 rabbit_mgmt_util:reply(X, ReqData, Context)
56 rabbit_mgmt_util:reply(rabbit_mgmt_format:strip_pids(X), ReqData, Context)
5957 catch
6058 {error, invalid_range_parameters, Reason} ->
6159 rabbit_mgmt_util:bad_request(iolist_to_binary(Reason), ReqData, Context)
6866 [{exchange, rabbit_mgmt_util:id(exchange, ReqData)}]).
6967
7068 delete_resource(ReqData, Context) ->
71 IfUnused = "true" =:= wrq:get_qs_value("if-unused", ReqData),
69 IfUnused = <<"true">> =:= element(1, cowboy_req:qs_val(<<"if-unused">>, ReqData)),
7270 rabbit_mgmt_util:amqp_request(
7371 rabbit_mgmt_util:vhost(ReqData), ReqData, Context,
7472 #'exchange.delete'{exchange = id(ReqData),
1515
1616 -module(rabbit_mgmt_wm_exchange_publish).
1717
18 -export([init/1, resource_exists/2, post_is_create/2, is_authorized/2,
19 allowed_methods/2, content_types_provided/2, process_post/2]).
20 -export([finish_request/2]).
21 -export([encodings_provided/2]).
18 -export([init/3, rest_init/2, resource_exists/2, is_authorized/2,
19 allowed_methods/2, content_types_provided/2, accept_content/2,
20 content_types_accepted/2]).
21 -export([variances/2]).
2222
23 -include("rabbit_mgmt.hrl").
24 -include_lib("webmachine/include/webmachine.hrl").
23 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2524 -include_lib("amqp_client/include/amqp_client.hrl").
2625
2726 %%--------------------------------------------------------------------
28 init(_Config) -> {ok, #context{}}.
2927
30 finish_request(ReqData, Context) ->
31 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), Context}.
28 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
29
30 rest_init(Req, _Config) ->
31 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
32
33 variances(Req, Context) ->
34 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3235
3336 allowed_methods(ReqData, Context) ->
34 {['POST', 'OPTIONS'], ReqData, Context}.
37 {[<<"POST">>, <<"OPTIONS">>], ReqData, Context}.
3538
3639 content_types_provided(ReqData, Context) ->
37 {[{"application/json", to_json}], ReqData, Context}.
38
39 encodings_provided(ReqData, Context) ->
40 {[{"identity", fun(X) -> X end},
41 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
40 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4241
4342 resource_exists(ReqData, Context) ->
4443 {case rabbit_mgmt_wm_exchange:exchange(ReqData) of
4645 _ -> true
4746 end, ReqData, Context}.
4847
49 post_is_create(ReqData, Context) ->
50 {false, ReqData, Context}.
48 content_types_accepted(ReqData, Context) ->
49 {[{'*', accept_content}], ReqData, Context}.
5150
52 process_post(ReqData, Context) ->
51 accept_content(ReqData, Context) ->
5352 rabbit_mgmt_util:post_respond(do_it(ReqData, Context)).
5453
55 do_it(ReqData, Context) ->
56 VHost = rabbit_mgmt_util:vhost(ReqData),
57 X = rabbit_mgmt_util:id(exchange, ReqData),
54 do_it(ReqData0, Context) ->
55 VHost = rabbit_mgmt_util:vhost(ReqData0),
56 X = rabbit_mgmt_util:id(exchange, ReqData0),
5857 rabbit_mgmt_util:with_decode(
59 [routing_key, properties, payload, payload_encoding], ReqData, Context,
60 fun ([RoutingKey, Props0, Payload0, Enc], _) when is_binary(Payload0) ->
58 [routing_key, properties, payload, payload_encoding], ReqData0, Context,
59 fun ([RoutingKey, Props0, Payload0, Enc], _, ReqData) when is_binary(Payload0) ->
6160 rabbit_mgmt_util:with_channel(
6261 VHost, ReqData, Context,
6362 fun (Ch) ->
8584 bad(Err, ReqData, Context)
8685 end
8786 end);
88 ([_RoutingKey, _Props, _Payload, _Enc], _) ->
87 ([_RoutingKey, _Props, _Payload, _Enc], _, _ReqData) ->
8988 throw({error, payload_not_string})
9089 end).
9190
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_exchanges).
1717
18 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2,
18 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, is_authorized/2,
1919 resource_exists/2, basic/1, augmented/2]).
20 -export([finish_request/2, allowed_methods/2]).
21 -export([encodings_provided/2]).
20 -export([variances/2]).
2221
23 -include("rabbit_mgmt.hrl").
24 -include_lib("webmachine/include/webmachine.hrl").
22 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2523 -include_lib("rabbit_common/include/rabbit.hrl").
2624
2725 %%--------------------------------------------------------------------
2826
29 init(_Config) -> {ok, #context{}}.
27 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3028
31 finish_request(ReqData, Context) ->
32 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), Context}.
29 rest_init(Req, _Config) ->
30 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
3331
34 allowed_methods(ReqData, Context) ->
35 {['HEAD', 'GET', 'OPTIONS'], ReqData, Context}.
32 variances(Req, Context) ->
33 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3634
3735 content_types_provided(ReqData, Context) ->
38 {[{"application/json", to_json}], ReqData, Context}.
39
40 encodings_provided(ReqData, Context) ->
41 {[{"identity", fun(X) -> X end},
42 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
36 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4337
4438 resource_exists(ReqData, Context) ->
4539 {case exchanges0(ReqData) of
1515
1616 -module(rabbit_mgmt_wm_extensions).
1717
18 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2]).
19 -export([finish_request/2, allowed_methods/2]).
20 -export([encodings_provided/2]).
18 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, is_authorized/2]).
19 -export([variances/2]).
2120
22 -include("rabbit_mgmt.hrl").
23 -include_lib("webmachine/include/webmachine.hrl").
21 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2422 -include_lib("rabbit_common/include/rabbit.hrl").
2523
2624 %%--------------------------------------------------------------------
27 init(_Config) -> {ok, #context{}}.
2825
29 finish_request(ReqData, Context) ->
30 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), Context}.
26 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3127
32 allowed_methods(ReqData, Context) ->
33 {['HEAD', 'GET', 'OPTIONS'], ReqData, Context}.
28 rest_init(Req, _Config) ->
29 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
30
31 variances(Req, Context) ->
32 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3433
3534 content_types_provided(ReqData, Context) ->
36 {[{"application/json", to_json}], ReqData, Context}.
37
38 encodings_provided(ReqData, Context) ->
39 {[{"identity", fun(X) -> X end},
40 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
35 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4136
4237 to_json(ReqData, Context) ->
4338 Modules = rabbit_mgmt_dispatcher:modules([]),
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License at
3 %% http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
7 %% License for the specific language governing rights and limitations
8 %% under the License.
9 %%
10 %% The Original Code is RabbitMQ Management Plugin.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_mgmt_wm_global_parameter).
17
18 -export([init/3, rest_init/2, resource_exists/2, to_json/2,
19 content_types_provided/2, content_types_accepted/2,
20 is_authorized/2, allowed_methods/2, accept_content/2,
21 delete_resource/2]).
22 -export([variances/2]).
23
24 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
25
26 %%--------------------------------------------------------------------
27
28 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
29
30 rest_init(Req, _Config) ->
31 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
32
33 variances(Req, Context) ->
34 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
35
36 content_types_provided(ReqData, Context) ->
37 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
38
39 content_types_accepted(ReqData, Context) ->
40 {[{'*', accept_content}], ReqData, Context}.
41
42 allowed_methods(ReqData, Context) ->
43 {[<<"HEAD">>, <<"GET">>, <<"PUT">>, <<"DELETE">>, <<"OPTIONS">>], ReqData, Context}.
44
45 resource_exists(ReqData, Context) ->
46 {case parameter(ReqData) of
47 not_found -> false;
48 _ -> true
49 end, ReqData, Context}.
50
51 to_json(ReqData, Context) ->
52 rabbit_mgmt_util:reply(rabbit_mgmt_format:parameter(parameter(ReqData)),
53 ReqData, Context).
54
55 accept_content(ReqData0, Context) ->
56 rabbit_mgmt_util:with_decode(
57 [value], ReqData0, Context,
58 fun([Value], _, ReqData) ->
59 case rabbit_runtime_parameters:set_global(
60 name(ReqData),rabbit_misc:json_to_term(Value)) of
61 ok ->
62 {true, ReqData, Context};
63 {error_string, Reason} ->
64 rabbit_mgmt_util:bad_request(
65 list_to_binary(Reason), ReqData, Context)
66 end
67 end).
68
69 delete_resource(ReqData, Context) ->
70 ok = rabbit_runtime_parameters:clear_global(name(ReqData)),
71 {true, ReqData, Context}.
72
73 is_authorized(ReqData, Context) ->
74 rabbit_mgmt_util:is_authorized_global_parameters(ReqData, Context).
75
76 %%--------------------------------------------------------------------
77
78 parameter(ReqData) ->
79 rabbit_runtime_parameters:lookup_global(name(ReqData)).
80
81 name(ReqData) -> rabbit_data_coercion:to_atom(rabbit_mgmt_util:id(name, ReqData)).
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License at
3 %% http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
7 %% License for the specific language governing rights and limitations
8 %% under the License.
9 %%
10 %% The Original Code is RabbitMQ Management Plugin.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_mgmt_wm_global_parameters).
17
18 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, is_authorized/2]).
19 -export([variances/2]).
20 -export([basic/0]).
21
22 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
23
24 %%--------------------------------------------------------------------
25
26 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
27
28 rest_init(Req, _Config) ->
29 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
30
31 variances(Req, Context) ->
32 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
33
34 content_types_provided(ReqData, Context) ->
35 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
36
37 to_json(ReqData, Context) ->
38 rabbit_mgmt_util:reply_list(basic(), ReqData, Context).
39
40 is_authorized(ReqData, Context) ->
41 rabbit_mgmt_util:is_authorized_global_parameters(ReqData, Context).
42
43 %%--------------------------------------------------------------------
44
45 basic() ->
46 rabbit_runtime_parameters:list_global().
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515 -module(rabbit_mgmt_wm_healthchecks).
1616
17 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2]).
18 -export([finish_request/2, allowed_methods/2]).
19 -export([encodings_provided/2]).
17 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, is_authorized/2]).
2018 -export([resource_exists/2]).
19 -export([variances/2]).
2120
22 -include("rabbit_mgmt.hrl").
23 -include_lib("webmachine/include/webmachine.hrl").
21 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2422
2523 %%--------------------------------------------------------------------
2624
27 init(_Config) -> {ok, #context{}}.
25 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
2826
29 finish_request(ReqData, Context) ->
30 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), Context}.
27 rest_init(Req, _Config) ->
28 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
3129
32 allowed_methods(ReqData, Context) ->
33 {['HEAD', 'GET', 'OPTIONS'], ReqData, Context}.
30 variances(Req, Context) ->
31 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3432
3533 content_types_provided(ReqData, Context) ->
36 {[{"application/json", to_json}], ReqData, Context}.
37
38 encodings_provided(ReqData, Context) ->
39 {[{"identity", fun(X) -> X end},
40 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
34 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4135
4236 resource_exists(ReqData, Context) ->
4337 {case node0(ReqData) of
4741
4842 to_json(ReqData, Context) ->
4943 Node = node0(ReqData),
50 try
51 Timeout = case wrq:get_req_header("timeout", ReqData) of
52 undefined -> 70000;
53 Val -> list_to_integer(Val)
54 end,
55 rabbit_health_check:node(Node, Timeout),
56 rabbit_mgmt_util:reply([{status, ok}], ReqData, Context)
57 catch
58 {node_is_ko, ErrorMsg, _ErrorCode} ->
59 rabbit_mgmt_util:reply([{status, failed},
60 {reason, rabbit_mgmt_format:print(ErrorMsg)}],
61 ReqData, Context)
44 Timeout = case cowboy_req:header(<<"timeout">>, ReqData) of
45 {undefined, _} -> 70000;
46 {Val, _} -> list_to_integer(binary_to_list(Val))
47 end,
48 case rabbit_health_check:node(Node, Timeout) of
49 ok ->
50 rabbit_mgmt_util:reply([{status, ok}], ReqData, Context);
51 {badrpc, Err} ->
52 failure(rabbit_mgmt_format:print("~p", Err), ReqData, Context);
53 {error_string, Err} ->
54 S = rabbit_mgmt_format:escape_html_tags(
55 rabbit_data_coercion:to_list(rabbit_mgmt_format:print(Err))),
56 failure(S, ReqData, Context)
6257 end.
58
59 failure(Message, ReqData, Context) ->
60 rabbit_mgmt_util:reply([{status, failed},
61 {reason, Message}],
62 ReqData, Context).
6363
6464 is_authorized(ReqData, Context) ->
6565 rabbit_mgmt_util:is_authorized(ReqData, Context).
1010 %% The Original Code is RabbitMQ Management Console.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_node).
1717
18 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2]).
19 -export([finish_request/2, allowed_methods/2]).
20 -export([encodings_provided/2]).
18 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, is_authorized/2]).
2119 -export([resource_exists/2]).
20 -export([variances/2]).
2221
23 -include("rabbit_mgmt.hrl").
24 -include_lib("webmachine/include/webmachine.hrl").
22 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2523 -include_lib("rabbit_common/include/rabbit.hrl").
2624
2725 %%--------------------------------------------------------------------
2826
29 init(_Config) -> {ok, #context{}}.
27 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3028
31 finish_request(ReqData, Context) ->
32 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), Context}.
29 rest_init(Req, _Config) ->
30 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
3331
34 allowed_methods(ReqData, Context) ->
35 {['HEAD', 'GET', 'OPTIONS'], ReqData, Context}.
32 variances(Req, Context) ->
33 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3634
3735 content_types_provided(ReqData, Context) ->
38 {[{"application/json", to_json}], ReqData, Context}.
39
40 encodings_provided(ReqData, Context) ->
41 {[{"identity", fun(X) -> X end},
42 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
36 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4337
4438 resource_exists(ReqData, Context) ->
4539 {case node0(ReqData) of
6862 Data, [memory, binary]).
6963
7064 augment(Key, ReqData, Node, Data) ->
71 case wrq:get_qs_value(atom_to_list(Key), ReqData) of
72 "true" -> Res = case rpc:call(Node, rabbit_vm, Key, [], infinity) of
65 case cowboy_req:qs_val(list_to_binary(atom_to_list(Key)), ReqData) of
66 {<<"true">>, _} -> Res = case rpc:call(Node, rabbit_vm, Key, [], infinity) of
7367 {badrpc, _} -> not_available;
7468 Result -> Result
7569 end,
1010 %% The Original Code is RabbitMQ Management Console.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_node_memory).
1717
18 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2]).
19 -export([finish_request/2, allowed_methods/2]).
20 -export([encodings_provided/2]).
18 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, is_authorized/2]).
2119 -export([resource_exists/2]).
20 -export([variances/2]).
2221
23 -include("rabbit_mgmt.hrl").
24 -include_lib("webmachine/include/webmachine.hrl").
22 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2523 -include_lib("rabbit_common/include/rabbit.hrl").
2624
2725 %%--------------------------------------------------------------------
2826
29 init([Mode]) -> {ok, {Mode, #context{}}}.
27 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3028
31 finish_request(ReqData, {Mode, Context}) ->
32 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), {Mode, Context}}.
29 rest_init(Req, [Mode]) -> {ok, Req, {Mode, #context{}}}.
3330
34 allowed_methods(ReqData, Context) ->
35 {['HEAD', 'GET', 'OPTIONS'], ReqData, Context}.
31 variances(Req, Context) ->
32 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3633
3734 content_types_provided(ReqData, Context) ->
38 {[{"application/json", to_json}], ReqData, Context}.
39
40 encodings_provided(ReqData, Context) ->
41 {[{"identity", fun(X) -> X end},
42 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
35 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4336
4437 resource_exists(ReqData, Context) ->
4538 {node_exists(ReqData, get_node(ReqData)), ReqData, Context}.
1010 %% The Original Code is RabbitMQ Management Console.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_node_memory_ets).
1717
18 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2]).
19 -export([finish_request/2, allowed_methods/2]).
20 -export([encodings_provided/2]).
18 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, is_authorized/2]).
2119 -export([resource_exists/2]).
20 -export([variances/2]).
2221
23 -include("rabbit_mgmt.hrl").
24 -include_lib("webmachine/include/webmachine.hrl").
22 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2523 -include_lib("rabbit_common/include/rabbit.hrl").
2624
2725 %%--------------------------------------------------------------------
2826
29 init([Mode]) -> {ok, {Mode, #context{}}}.
27 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3028
31 finish_request(ReqData, {Mode, Context}) ->
32 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), {Mode, Context}}.
29 rest_init(Req, [Mode]) -> {ok, Req, {Mode, #context{}}}.
3330
34 allowed_methods(ReqData, Context) ->
35 {['HEAD', 'GET', 'OPTIONS'], ReqData, Context}.
31 variances(Req, Context) ->
32 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3633
3734 content_types_provided(ReqData, Context) ->
38 {[{"application/json", to_json}], ReqData, Context}.
39
40 encodings_provided(ReqData, Context) ->
41 {[{"identity", fun(X) -> X end},
42 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
35 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4336
4437 resource_exists(ReqData, Context) ->
4538 {node_exists(ReqData, get_node(ReqData)), ReqData, Context}.
5851 get_filter(ReqData) ->
5952 case rabbit_mgmt_util:id(filter, ReqData) of
6053 none -> all;
61 <<"management">> -> rabbit_mgmt_event_collector;
54 <<"management">> -> rabbit_mgmt_storage;
6255 Other when is_binary(Other) -> list_to_atom(binary_to_list(Other));
6356 _ -> all
6457 end.
1010 %% The Original Code is RabbitMQ Management Console.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_nodes).
1717
18 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2]).
19 -export([finish_request/2, allowed_methods/2]).
18 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, is_authorized/2]).
2019 -export([all_nodes/1, all_nodes_raw/0]).
21 -export([encodings_provided/2]).
20 -export([variances/2]).
2221
23 -include("rabbit_mgmt.hrl").
24 -include_lib("webmachine/include/webmachine.hrl").
22 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2523 -include_lib("rabbit_common/include/rabbit.hrl").
2624
2725 %%--------------------------------------------------------------------
2826
29 init(_Config) -> {ok, #context{}}.
27 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3028
31 finish_request(ReqData, Context) ->
32 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), Context}.
29 rest_init(Req, _Config) ->
30 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
3331
34 allowed_methods(ReqData, Context) ->
35 {['HEAD', 'GET', 'OPTIONS'], ReqData, Context}.
32 variances(Req, Context) ->
33 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3634
3735 content_types_provided(ReqData, Context) ->
38 {[{"application/json", to_json}], ReqData, Context}.
39
40 encodings_provided(ReqData, Context) ->
41 {[{"identity", fun(X) -> X end},
42 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
36 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4337
4438 to_json(ReqData, Context) ->
4539 try
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_overview).
1717
18 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2]).
19 -export([finish_request/2, allowed_methods/2]).
20 -export([encodings_provided/2]).
18 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, is_authorized/2]).
19 -export([variances/2]).
2120
2221 -import(rabbit_misc, [pget/2, pget/3]).
2322
24 -include("rabbit_mgmt.hrl").
25 -include_lib("webmachine/include/webmachine.hrl").
23 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2624 -include_lib("rabbit_common/include/rabbit.hrl").
2725
2826 %%--------------------------------------------------------------------
2927
30 init(_Config) -> {ok, #context{}}.
28 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3129
32 finish_request(ReqData, Context) ->
33 {ok, rabbit_mgmt_cors:set_headers(ReqData, ?MODULE), Context}.
30 rest_init(Req, _Config) ->
31 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
3432
35 allowed_methods(ReqData, Context) ->
36 {['HEAD', 'GET', 'OPTIONS'], ReqData, Context}.
33 variances(Req, Context) ->
34 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3735
3836 content_types_provided(ReqData, Context) ->
39 {[{"application/json", to_json}], ReqData, Context}.
40
41 encodings_provided(ReqData, Context) ->
42 {[{"identity", fun(X) -> X end},
43 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
37 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4438
4539 to_json(ReqData, Context = #context{user = User = #user{tags = Tags}}) ->
46 {ok, RatesMode} = application:get_env(rabbitmq_management, rates_mode),
40 RatesMode = rabbit_mgmt_agent_config:get_env(rates_mode),
4741 %% NB: this duplicates what's in /nodes but we want a global idea
4842 %% of this. And /nodes is not accessible to non-monitor users.
4943 ExchangeTypes = rabbit_mgmt_external_stats:list_registry_plugins(exchange),
6357 [{K, maybe_struct(V)} ||
6458 {K,V} <- rabbit_mgmt_db:get_overview(Range)] ++
6559 [{node, node()},
66 {statistics_db_node, stats_db_node()},
6760 {listeners, listeners()},
6861 {contexts, web_contexts(ReqData)}];
6962 _ ->
8174 rabbit_mgmt_util:is_authorized(ReqData, Context).
8275
8376 %%--------------------------------------------------------------------
84
85 stats_db_node() ->
86 case global:whereis_name(rabbit_mgmt_db) of
87 undefined -> not_running;
88 Pid -> node(Pid)
89 end.
9077
9178 version(App) ->
9279 {ok, V} = application:get_key(App, vsn),
10996 [fmt_contexts(N) || N <- rabbit_mgmt_wm_nodes:all_nodes(ReqData)]),
11097 ["description", "port", "node"]).
11198
112 fmt_contexts(N) ->
113 [[{node, pget(name, N)} | C] || C <- pget(contexts, N, [])].
99 fmt_contexts(Node) ->
100 [fmt_context(Node, C) || C <- pget(contexts, Node, [])].
101
102 fmt_context(Node, C) ->
103 rabbit_mgmt_format:web_context([{node, pget(name, Node)} | C]).
114104
115105 erlang_version() -> list_to_binary(rabbit_misc:otp_release()).
116106
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_parameter).
1717
18 -export([init/1, resource_exists/2, to_json/2,
18 -export([init/3, rest_init/2, resource_exists/2, to_json/2,
1919 content_types_provided/2, content_types_accepted/2,
2020 is_authorized/2, allowed_methods/2, accept_content/2,
2121 delete_resource/2]).
22 -export([finish_request/2]).
23 -export([encodings_provided/2]).
22 -export([variances/2]).
2423
25 -import(rabbit_misc, [pget/2]).
26
27 -include("rabbit_mgmt.hrl").
28 -include_lib("webmachine/include/webmachine.hrl").
24 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2925 -include_lib("rabbit_common/include/rabbit.hrl").
3026
3127 %%--------------------------------------------------------------------
3228
33 init(_Config) -> {ok, #context{}}.
29 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3430
35 finish_request(ReqData, Context) ->
36 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), Context}.
31 rest_init(Req, _Config) ->
32 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
33
34 variances(Req, Context) ->
35 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3736
3837 content_types_provided(ReqData, Context) ->
39 {[{"application/json", to_json}], ReqData, Context}.
40
41 encodings_provided(ReqData, Context) ->
42 {[{"identity", fun(X) -> X end},
43 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
38 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4439
4540 content_types_accepted(ReqData, Context) ->
46 {[{"application/json", accept_content}], ReqData, Context}.
41 {[{'*', accept_content}], ReqData, Context}.
4742
4843 allowed_methods(ReqData, Context) ->
49 {['HEAD', 'GET', 'PUT', 'DELETE', 'OPTIONS'], ReqData, Context}.
44 {[<<"HEAD">>, <<"GET">>, <<"PUT">>, <<"DELETE">>, <<"OPTIONS">>], ReqData, Context}.
5045
5146 resource_exists(ReqData, Context) ->
5247 {case parameter(ReqData) of
5954 rabbit_mgmt_wm_parameters:fix_shovel_publish_properties(parameter(ReqData))),
6055 ReqData, Context).
6156
62 accept_content(ReqData, Context = #context{user = User}) ->
63 case rabbit_mgmt_util:vhost(ReqData) of
57 accept_content(ReqData0, Context = #context{user = User}) ->
58 case rabbit_mgmt_util:vhost(ReqData0) of
6459 not_found ->
65 rabbit_mgmt_util:not_found(vhost_not_found, ReqData, Context);
60 rabbit_mgmt_util:not_found(vhost_not_found, ReqData0, Context);
6661 VHost ->
6762 rabbit_mgmt_util:with_decode(
68 [value], ReqData, Context,
69 fun([Value], _) ->
63 [value], ReqData0, Context,
64 fun([Value], _, ReqData) ->
7065 case rabbit_runtime_parameters:set(
7166 VHost, component(ReqData), name(ReqData),
7267 rabbit_misc:json_to_term(Value), User) of
7368 ok ->
7469 {true, ReqData, Context};
7570 {error_string, Reason} ->
71 S = rabbit_mgmt_format:escape_html_tags(
72 rabbit_data_coercion:to_list(Reason)),
7673 rabbit_mgmt_util:bad_request(
77 list_to_binary(Reason), ReqData, Context)
74 rabbit_data_coercion:to_binary(S), ReqData, Context)
7875 end
7976 end)
8077 end.
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_parameters).
1717
18 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2,
18 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, is_authorized/2,
1919 resource_exists/2, basic/1]).
20 -export([finish_request/2, allowed_methods/2]).
21 -export([encodings_provided/2]).
2220 -export([fix_shovel_publish_properties/1]).
21 -export([variances/2]).
2322
24 -include("rabbit_mgmt.hrl").
25 -include_lib("webmachine/include/webmachine.hrl").
23 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2624 -include_lib("rabbit_common/include/rabbit.hrl").
2725
2826 %%--------------------------------------------------------------------
2927
30 init(_Config) -> {ok, #context{}}.
28 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3129
32 finish_request(ReqData, Context) ->
33 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), Context}.
30 rest_init(Req, _Config) ->
31 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
3432
35 allowed_methods(ReqData, Context) ->
36 {['HEAD', 'GET', 'OPTIONS'], ReqData, Context}.
33 variances(Req, Context) ->
34 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3735
3836 content_types_provided(ReqData, Context) ->
39 {[{"application/json", to_json}], ReqData, Context}.
40
41 encodings_provided(ReqData, Context) ->
42 {[{"identity", fun(X) -> X end},
43 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
37 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4438
4539 resource_exists(ReqData, Context) ->
4640 {case basic(ReqData) of
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_permission).
1717
18 -export([init/1, resource_exists/2, to_json/2,
18 -export([init/3, rest_init/2, resource_exists/2, to_json/2,
1919 content_types_provided/2, content_types_accepted/2,
2020 is_authorized/2, allowed_methods/2, accept_content/2,
2121 delete_resource/2]).
22 -export([finish_request/2]).
23 -export([encodings_provided/2]).
22 -export([variances/2]).
2423
25 -include("rabbit_mgmt.hrl").
26 -include_lib("webmachine/include/webmachine.hrl").
24 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2725 -include_lib("rabbit_common/include/rabbit.hrl").
2826
2927 %%--------------------------------------------------------------------
30 init(_Config) -> {ok, #context{}}.
3128
32 finish_request(ReqData, Context) ->
33 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), Context}.
29 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
30
31 rest_init(Req, _Config) ->
32 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
33
34 variances(Req, Context) ->
35 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3436
3537 content_types_provided(ReqData, Context) ->
36 {[{"application/json", to_json}], ReqData, Context}.
37
38 encodings_provided(ReqData, Context) ->
39 {[{"identity", fun(X) -> X end},
40 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
38 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4139
4240 content_types_accepted(ReqData, Context) ->
43 {[{"application/json", accept_content}], ReqData, Context}.
41 {[{'*', accept_content}], ReqData, Context}.
4442
4543 allowed_methods(ReqData, Context) ->
46 {['HEAD', 'GET', 'PUT', 'DELETE', 'OPTIONS'], ReqData, Context}.
44 {[<<"HEAD">>, <<"GET">>, <<"PUT">>, <<"DELETE">>, <<"OPTIONS">>], ReqData, Context}.
4745
4846 resource_exists(ReqData, Context) ->
4947 {case perms(ReqData) of
5553 to_json(ReqData, Context) ->
5654 rabbit_mgmt_util:reply(perms(ReqData), ReqData, Context).
5755
58 accept_content(ReqData, Context) ->
59 case perms(ReqData) of
56 accept_content(ReqData0, Context) ->
57 case perms(ReqData0) of
6058 not_found ->
6159 rabbit_mgmt_util:bad_request(vhost_or_user_not_found,
62 ReqData, Context);
60 ReqData0, Context);
6361 _ ->
64 User = rabbit_mgmt_util:id(user, ReqData),
65 VHost = rabbit_mgmt_util:id(vhost, ReqData),
62 User = rabbit_mgmt_util:id(user, ReqData0),
63 VHost = rabbit_mgmt_util:id(vhost, ReqData0),
6664 rabbit_mgmt_util:with_decode(
67 [configure, write, read], ReqData, Context,
68 fun([Conf, Write, Read], _) ->
65 [configure, write, read], ReqData0, Context,
66 fun([Conf, Write, Read], _, ReqData) ->
6967 rabbit_auth_backend_internal:set_permissions(
7068 User, VHost, Conf, Write, Read),
7169 {true, ReqData, Context}
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_permissions).
1717
18 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2]).
19 -export([finish_request/2, allowed_methods/2]).
18 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, is_authorized/2]).
2019 -export([permissions/0]).
21 -export([encodings_provided/2]).
20 -export([variances/2]).
2221
23 -include("rabbit_mgmt.hrl").
24 -include_lib("webmachine/include/webmachine.hrl").
22 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2523 -include_lib("rabbit_common/include/rabbit.hrl").
2624
2725 %%--------------------------------------------------------------------
2826
29 init(_Config) -> {ok, #context{}}.
27 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3028
31 finish_request(ReqData, Context) ->
32 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), Context}.
29 rest_init(Req, _Config) ->
30 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
3331
34 allowed_methods(ReqData, Context) ->
35 {['HEAD', 'GET', 'OPTIONS'], ReqData, Context}.
32 variances(Req, Context) ->
33 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3634
3735 content_types_provided(ReqData, Context) ->
38 {[{"application/json", to_json}], ReqData, Context}.
39
40 encodings_provided(ReqData, Context) ->
41 {[{"identity", fun(X) -> X end},
42 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
36 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4337
4438 to_json(ReqData, Context) ->
4539 rabbit_mgmt_util:reply_list(permissions(), ["vhost", "user"],
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_permissions_user).
1717
18 -export([init/1, to_json/2, content_types_provided/2, resource_exists/2,
18 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, resource_exists/2,
1919 is_authorized/2]).
20 -export([finish_request/2, allowed_methods/2]).
21 -export([encodings_provided/2]).
20 -export([variances/2]).
2221
23 -include("rabbit_mgmt.hrl").
24 -include_lib("webmachine/include/webmachine.hrl").
22 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2523 -include_lib("rabbit_common/include/rabbit.hrl").
2624
2725 %%--------------------------------------------------------------------
2826
29 init(_Config) -> {ok, #context{}}.
27 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3028
31 finish_request(ReqData, Context) ->
32 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), Context}.
29 rest_init(Req, _Config) ->
30 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
3331
34 allowed_methods(ReqData, Context) ->
35 {['HEAD', 'GET', 'OPTIONS'], ReqData, Context}.
32 variances(Req, Context) ->
33 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3634
3735 content_types_provided(ReqData, Context) ->
38 {[{"application/json", to_json}], ReqData, Context}.
39
40 encodings_provided(ReqData, Context) ->
41 {[{"identity", fun(X) -> X end},
42 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
36 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4337
4438 resource_exists(ReqData, Context) ->
4539 {case rabbit_mgmt_wm_user:user(ReqData) of
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_permissions_vhost).
1717
18 -export([init/1, to_json/2, content_types_provided/2, resource_exists/2,
18 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, resource_exists/2,
1919 is_authorized/2]).
20 -export([finish_request/2, allowed_methods/2]).
21 -export([encodings_provided/2]).
20 -export([variances/2]).
2221
23 -include("rabbit_mgmt.hrl").
24 -include_lib("webmachine/include/webmachine.hrl").
22 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2523 -include_lib("rabbit_common/include/rabbit.hrl").
2624
2725 %%--------------------------------------------------------------------
2826
29 init(_Config) -> {ok, #context{}}.
27 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3028
31 finish_request(ReqData, Context) ->
32 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), Context}.
29 rest_init(Req, _Config) ->
30 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
3331
34 allowed_methods(ReqData, Context) ->
35 {['HEAD', 'GET', 'OPTIONS'], ReqData, Context}.
32 variances(Req, Context) ->
33 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3634
3735 content_types_provided(ReqData, Context) ->
38 {[{"application/json", to_json}], ReqData, Context}.
39
40 encodings_provided(ReqData, Context) ->
41 {[{"identity", fun(X) -> X end},
42 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
36 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4337
4438 resource_exists(ReqData, Context) ->
4539 {rabbit_vhost:exists(rabbit_mgmt_wm_vhost:id(ReqData)), ReqData, Context}.
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_policies).
1717
18 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2,
18 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, is_authorized/2,
1919 resource_exists/2, basic/1]).
20 -export([finish_request/2, allowed_methods/2]).
21 -export([encodings_provided/2]).
20 -export([variances/2]).
2221
23 -include("rabbit_mgmt.hrl").
24 -include_lib("webmachine/include/webmachine.hrl").
22 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2523 -include_lib("rabbit_common/include/rabbit.hrl").
2624
2725 %%--------------------------------------------------------------------
2826
29 init(_Config) -> {ok, #context{}}.
27 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3028
31 finish_request(ReqData, Context) ->
32 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), Context}.
29 rest_init(Req, _Config) ->
30 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
3331
34 allowed_methods(ReqData, Context) ->
35 {['HEAD', 'GET', 'OPTIONS'], ReqData, Context}.
32 variances(Req, Context) ->
33 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3634
3735 content_types_provided(ReqData, Context) ->
38 {[{"application/json", to_json}], ReqData, Context}.
39
40 encodings_provided(ReqData, Context) ->
41 {[{"identity", fun(X) -> X end},
42 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
36 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4337
4438 resource_exists(ReqData, Context) ->
4539 {case basic(ReqData) of
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_policy).
1717
18 -export([init/1, resource_exists/2, to_json/2,
18 -export([init/3, rest_init/2, resource_exists/2, to_json/2,
1919 content_types_provided/2, content_types_accepted/2,
2020 is_authorized/2, allowed_methods/2, accept_content/2,
2121 delete_resource/2]).
22 -export([finish_request/2]).
23 -export([encodings_provided/2]).
22 -export([variances/2]).
2423
25 -import(rabbit_misc, [pget/2]).
26
27 -include("rabbit_mgmt.hrl").
28 -include_lib("webmachine/include/webmachine.hrl").
24 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2925 -include_lib("rabbit_common/include/rabbit.hrl").
3026
3127 %%--------------------------------------------------------------------
3228
33 init(_Config) -> {ok, #context{}}.
29 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3430
35 finish_request(ReqData, Context) ->
36 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), Context}.
31 rest_init(Req, _Config) ->
32 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
33
34 variances(Req, Context) ->
35 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3736
3837 content_types_provided(ReqData, Context) ->
39 {[{"application/json", to_json}], ReqData, Context}.
40
41 encodings_provided(ReqData, Context) ->
42 {[{"identity", fun(X) -> X end},
43 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
38 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4439
4540 content_types_accepted(ReqData, Context) ->
46 {[{"application/json", accept_content}], ReqData, Context}.
41 {[{'*', accept_content}], ReqData, Context}.
4742
4843 allowed_methods(ReqData, Context) ->
49 {['HEAD', 'GET', 'PUT', 'DELETE', 'OPTIONS'], ReqData, Context}.
44 {[<<"HEAD">>, <<"GET">>, <<"PUT">>, <<"DELETE">>, <<"OPTIONS">>], ReqData, Context}.
5045
5146 resource_exists(ReqData, Context) ->
5247 {case policy(ReqData) of
5752 to_json(ReqData, Context) ->
5853 rabbit_mgmt_util:reply(policy(ReqData), ReqData, Context).
5954
60 accept_content(ReqData, Context) ->
61 case rabbit_mgmt_util:vhost(ReqData) of
55 accept_content(ReqData0, Context) ->
56 case rabbit_mgmt_util:vhost(ReqData0) of
6257 not_found ->
63 rabbit_mgmt_util:not_found(vhost_not_found, ReqData, Context);
58 rabbit_mgmt_util:not_found(vhost_not_found, ReqData0, Context);
6459 VHost ->
6560 rabbit_mgmt_util:with_decode(
66 [pattern, definition], ReqData, Context,
67 fun([Pattern, Definition], Body) ->
61 [pattern, definition], ReqData0, Context,
62 fun([Pattern, Definition], Body, ReqData) ->
6863 case rabbit_policy:set(
6964 VHost, name(ReqData), Pattern,
7065 rabbit_misc:json_to_term(Definition),
7469 {true, ReqData, Context};
7570 {error_string, Reason} ->
7671 rabbit_mgmt_util:bad_request(
77 list_to_binary(Reason), ReqData, Context)
72 rabbit_mgmt_format:escape_html_tags(Reason), ReqData, Context)
7873 end
7974 end)
8075 end.
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_queue).
1717
18 -export([init/1, resource_exists/2, to_json/2,
18 -export([init/3, rest_init/2, resource_exists/2, to_json/2,
1919 content_types_provided/2, content_types_accepted/2,
2020 is_authorized/2, allowed_methods/2, accept_content/2,
2121 delete_resource/2, queue/1, queue/2]).
22 -export([finish_request/2]).
23 -export([encodings_provided/2]).
22 -export([variances/2]).
2423
25 -include("rabbit_mgmt.hrl").
26 -include_lib("webmachine/include/webmachine.hrl").
24 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2725 -include_lib("amqp_client/include/amqp_client.hrl").
2826
2927 %%--------------------------------------------------------------------
30 init(_Config) -> {ok, #context{}}.
3128
32 finish_request(ReqData, Context) ->
33 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), Context}.
29 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
30
31 rest_init(Req, _Config) ->
32 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
33
34 variances(Req, Context) ->
35 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3436
3537 content_types_provided(ReqData, Context) ->
36 {[{"application/json", to_json}], ReqData, Context}.
37
38 encodings_provided(ReqData, Context) ->
39 {[{"identity", fun(X) -> X end},
40 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
38 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4139
4240 content_types_accepted(ReqData, Context) ->
43 {[{"application/json", accept_content}], ReqData, Context}.
41 {[{'*', accept_content}], ReqData, Context}.
4442
4543 allowed_methods(ReqData, Context) ->
46 {['HEAD', 'GET', 'PUT', 'DELETE', 'OPTIONS'], ReqData, Context}.
44 {[<<"HEAD">>, <<"GET">>, <<"PUT">>, <<"DELETE">>, <<"OPTIONS">>], ReqData, Context}.
4745
4846 resource_exists(ReqData, Context) ->
4947 {case queue(ReqData) of
5452 to_json(ReqData, Context) ->
5553 try
5654 [Q] = rabbit_mgmt_db:augment_queues(
57 [queue(ReqData)], rabbit_mgmt_util:range_ceil(ReqData), full),
58 rabbit_mgmt_util:reply(rabbit_mgmt_format:strip_pids(Q), ReqData, Context)
55 [queue(ReqData)], rabbit_mgmt_util:range_ceil(ReqData), full),
56 Payload = rabbit_mgmt_format:clean_consumer_details(rabbit_mgmt_format:strip_pids(Q)),
57 rabbit_mgmt_util:reply(Payload, ReqData, Context)
5958 catch
6059 {error, invalid_range_parameters, Reason} ->
6160 rabbit_mgmt_util:bad_request(iolist_to_binary(Reason), ReqData, Context)
9493 {error, not_found} -> not_found
9594 end.
9695
97 qs_true(Key, ReqData) -> "true" =:= wrq:get_qs_value(Key, ReqData).
96 qs_true(Key, ReqData) ->
97 <<"true">> =:= element(1, cowboy_req:qs_val(list_to_binary(Key), ReqData)).
1515
1616 -module(rabbit_mgmt_wm_queue_actions).
1717
18 -export([init/1, resource_exists/2, post_is_create/2, is_authorized/2,
19 allowed_methods/2, process_post/2]).
20 -export([finish_request/2]).
21 -export([encodings_provided/2]).
18 -export([init/3, rest_init/2, resource_exists/2, is_authorized/2,
19 allowed_methods/2, content_types_accepted/2, accept_content/2]).
20 -export([variances/2]).
2221
23 -include("rabbit_mgmt.hrl").
24 -include_lib("webmachine/include/webmachine.hrl").
22 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2523 -include_lib("amqp_client/include/amqp_client.hrl").
2624
2725 %%--------------------------------------------------------------------
2826
29 init(_Config) -> {ok, #context{}}.
27 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3028
31 finish_request(ReqData, Context) ->
32 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), Context}.
29 rest_init(Req, _Config) ->
30 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
31
32 variances(Req, Context) ->
33 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3334
3435 allowed_methods(ReqData, Context) ->
35 {['POST', 'OPTIONS'], ReqData, Context}.
36
37 encodings_provided(ReqData, Context) ->
38 {[{"identity", fun(X) -> X end},
39 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
36 {[<<"POST">>, <<"OPTIONS">>], ReqData, Context}.
4037
4138 resource_exists(ReqData, Context) ->
4239 {case rabbit_mgmt_wm_queue:queue(ReqData) of
4441 _ -> true
4542 end, ReqData, Context}.
4643
47 post_is_create(ReqData, Context) ->
48 {false, ReqData, Context}.
44 content_types_accepted(ReqData, Context) ->
45 {[{'*', accept_content}], ReqData, Context}.
4946
50 process_post(ReqData, Context) ->
47 accept_content(ReqData, Context) ->
5148 rabbit_mgmt_util:post_respond(do_it(ReqData, Context)).
5249
53 do_it(ReqData, Context) ->
54 VHost = rabbit_mgmt_util:vhost(ReqData),
55 QName = rabbit_mgmt_util:id(queue, ReqData),
50 do_it(ReqData0, Context) ->
51 VHost = rabbit_mgmt_util:vhost(ReqData0),
52 QName = rabbit_mgmt_util:id(queue, ReqData0),
5653 rabbit_mgmt_util:with_decode(
57 [action], ReqData, Context,
58 fun([Action], _Body) ->
54 [action], ReqData0, Context,
55 fun([Action], _Body, ReqData) ->
5956 rabbit_amqqueue:with(
6057 rabbit_misc:r(VHost, queue, QName),
6158 fun(Q) -> action(Action, Q, ReqData, Context) end)
7168 {true, ReqData, Context};
7269
7370 action(<<"cancel_sync">>, #amqqueue{pid = QPid}, ReqData, Context) ->
74 rabbit_amqqueue:cancel_sync_mirrors(QPid),
71 _ = rabbit_amqqueue:cancel_sync_mirrors(QPid),
7572 {true, ReqData, Context};
7673
7774 action(Else, _Q, ReqData, Context) ->
1515
1616 -module(rabbit_mgmt_wm_queue_get).
1717
18 -export([init/1, resource_exists/2, post_is_create/2, is_authorized/2,
19 allowed_methods/2, process_post/2, content_types_provided/2]).
20 -export([finish_request/2]).
21 -export([encodings_provided/2]).
18 -export([init/3, rest_init/2, resource_exists/2, is_authorized/2,
19 allowed_methods/2, accept_content/2, content_types_provided/2,
20 content_types_accepted/2]).
21 -export([variances/2]).
2222
23 -include("rabbit_mgmt.hrl").
24 -include_lib("webmachine/include/webmachine.hrl").
23 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2524 -include_lib("amqp_client/include/amqp_client.hrl").
2625
2726 %%--------------------------------------------------------------------
2827
29 init(_Config) -> {ok, #context{}}.
28 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3029
31 finish_request(ReqData, Context) ->
32 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), Context}.
30 rest_init(Req, _Config) ->
31 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
32
33 variances(Req, Context) ->
34 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3335
3436 allowed_methods(ReqData, Context) ->
35 {['POST', 'OPTIONS'], ReqData, Context}.
37 {[<<"POST">>, <<"OPTIONS">>], ReqData, Context}.
3638
3739 content_types_provided(ReqData, Context) ->
38 {[{"application/json", to_json}], ReqData, Context}.
39
40 encodings_provided(ReqData, Context) ->
41 {[{"identity", fun(X) -> X end},
42 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
40 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4341
4442 resource_exists(ReqData, Context) ->
4543 {case rabbit_mgmt_wm_queue:queue(ReqData) of
4745 _ -> true
4846 end, ReqData, Context}.
4947
50 post_is_create(ReqData, Context) ->
51 {false, ReqData, Context}.
48 content_types_accepted(ReqData, Context) ->
49 {[{'*', accept_content}], ReqData, Context}.
5250
53 process_post(ReqData, Context) ->
51 accept_content(ReqData, Context) ->
5452 rabbit_mgmt_util:post_respond(do_it(ReqData, Context)).
5553
56 do_it(ReqData, Context) ->
57 VHost = rabbit_mgmt_util:vhost(ReqData),
58 Q = rabbit_mgmt_util:id(queue, ReqData),
54 do_it(ReqData0, Context) ->
55 VHost = rabbit_mgmt_util:vhost(ReqData0),
56 Q = rabbit_mgmt_util:id(queue, ReqData0),
5957 rabbit_mgmt_util:with_decode(
60 [requeue, count, encoding], ReqData, Context,
61 fun([RequeueBin, CountBin, EncBin], Body) ->
58 [requeue, count, encoding], ReqData0, Context,
59 fun([RequeueBin, CountBin, EncBin], Body, ReqData) ->
6260 rabbit_mgmt_util:with_channel(
6361 VHost, ReqData, Context,
6462 fun (Ch) ->
6765 Enc = case EncBin of
6866 <<"auto">> -> auto;
6967 <<"base64">> -> base64;
70 _ -> throw({error,
71 {bad_encoding,
72 EncBin}})
68 _ -> throw({error, <<"Unsupported encoding. Please use auto or base64.">>})
7369 end,
7470 Trunc = case proplists:get_value(truncate, Body) of
7571 undefined -> none;
125121 {PL, E} = case Enc of
126122 auto -> try
127123 %% TODO mochijson does this but is it safe?
128 xmerl_ucs:from_utf8(Payload),
124 _ = xmerl_ucs:from_utf8(Payload),
129125 {Payload, string}
130126 catch exit:{ucs, _} ->
131127 {base64:encode(Payload), base64}
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_queue_purge).
1717
18 -export([init/1, resource_exists/2, is_authorized/2, allowed_methods/2,
18 -export([init/3, rest_init/2, resource_exists/2, is_authorized/2, allowed_methods/2,
1919 delete_resource/2]).
20 -export([finish_request/2]).
21 -export([encodings_provided/2]).
20 -export([variances/2]).
2221
23 -include("rabbit_mgmt.hrl").
24 -include_lib("webmachine/include/webmachine.hrl").
22 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2523 -include_lib("amqp_client/include/amqp_client.hrl").
2624
2725 %%--------------------------------------------------------------------
28 init(_Config) -> {ok, #context{}}.
2926
30 finish_request(ReqData, Context) ->
31 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), Context}.
27 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
28
29 rest_init(Req, _Config) ->
30 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
31
32 variances(Req, Context) ->
33 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3234
3335 allowed_methods(ReqData, Context) ->
34 {['DELETE', 'OPTIONS'], ReqData, Context}.
35
36 encodings_provided(ReqData, Context) ->
37 {[{"identity", fun(X) -> X end},
38 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
36 {[<<"DELETE">>, <<"OPTIONS">>], ReqData, Context}.
3937
4038 resource_exists(ReqData, Context) ->
4139 {case rabbit_mgmt_wm_queue:queue(ReqData) of
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_queues).
1717
18 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2,
18 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, is_authorized/2,
1919 resource_exists/2, basic/1, augmented/2]).
20 -export([finish_request/2, allowed_methods/2]).
21 -export([encodings_provided/2]).
20 -export([variances/2]).
2221
23 -include("rabbit_mgmt.hrl").
24 -include_lib("webmachine/include/webmachine.hrl").
22 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2523 -include_lib("rabbit_common/include/rabbit.hrl").
2624
2725 %%--------------------------------------------------------------------
2826
29 init(_Config) -> {ok, #context{}}.
27 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3028
31 finish_request(ReqData, Context) ->
32 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), Context}.
29 rest_init(Req, _Config) ->
30 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
3331
34 allowed_methods(ReqData, Context) ->
35 {['HEAD', 'GET', 'OPTIONS'], ReqData, Context}.
32 variances(Req, Context) ->
33 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3634
3735 content_types_provided(ReqData, Context) ->
38 {[{"application/json", to_json}], ReqData, Context}.
39
40 encodings_provided(ReqData, Context) ->
41 {[{"identity", fun(X) -> X end},
42 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
36 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4337
4438 resource_exists(ReqData, Context) ->
4539 {case queues0(ReqData) of
5145 to_json(ReqData, Context) ->
5246 try
5347 rabbit_mgmt_util:reply_list_or_paginate(
54 augmented(ReqData, Context), ReqData, Context)
48 rabbit_mgmt_format:strip_pids(
49 augmented(ReqData, Context)), ReqData, Context)
5550 catch
5651 {error, invalid_range_parameters, Reason} ->
5752 rabbit_mgmt_util:bad_request(iolist_to_binary(Reason), ReqData, Context)
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License at
3 %% http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
7 %% License for the specific language governing rights and limitations
8 %% under the License.
9 %%
10 %% The Original Code is RabbitMQ Management Plugin.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_mgmt_wm_redirect).
17 -export([init/3, handle/2, terminate/2]).
18
19 init(_, Req, RedirectTo) ->
20 {ok, Req, RedirectTo}.
21
22 handle(Req0, RedirectTo) ->
23 {ok, Req} = cowboy_req:reply(301, [{<<"location">>, RedirectTo}], Req0),
24 {ok, Req, RedirectTo}.
25
26 terminate(_, _) ->
27 ok.
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License at
3 %% http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
7 %% License for the specific language governing rights and limitations
8 %% under the License.
9 %%
10 %% The Original Code is RabbitMQ Management Plugin.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_mgmt_wm_reset).
17
18 -export([init/3, rest_init/2, is_authorized/2, resource_exists/2,
19 allowed_methods/2, delete_resource/2]).
20 -export([variances/2]).
21
22 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
23
24 %%--------------------------------------------------------------------
25
26 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
27
28 rest_init(Req, _Config) ->
29 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
30
31 variances(Req, Context) ->
32 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
33
34 allowed_methods(ReqData, Context) ->
35 {[<<"DELETE">>, <<"OPTIONS">>], ReqData, Context}.
36
37 resource_exists(ReqData, Context) ->
38 case get_node(ReqData) of
39 none -> {true, ReqData, Context};
40 {ok, Node} -> {lists:member(Node, rabbit_nodes:all_running()),
41 ReqData, Context}
42 end.
43
44 delete_resource(ReqData, Context) ->
45 case get_node(ReqData) of
46 none ->
47 rabbit_mgmt_storage:reset_all();
48 {ok, Node} ->
49 rpc:call(Node, rabbit_mgmt_storage, reset, [])
50 end,
51 {true, ReqData, Context}.
52
53 is_authorized(ReqData, Context) ->
54 rabbit_mgmt_util:is_authorized_admin(ReqData, Context).
55
56
57 get_node(ReqData) ->
58 case rabbit_mgmt_util:id(node, ReqData) of
59 none ->
60 none;
61 Node0 ->
62 Node = list_to_atom(binary_to_list(Node0)),
63 {ok, Node}
64 end.
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License at
3 %% http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
7 %% License for the specific language governing rights and limitations
8 %% under the License.
9 %%
10 %% The Original Code is RabbitMQ Management Plugin.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 %% Alias for cowboy_static that accepts a list of directories
17 %% where static files can be found.
18
19 -module(rabbit_mgmt_wm_static).
20
21 -export([init/3]).
22 -export([rest_init/2]).
23 -export([malformed_request/2]).
24 -export([forbidden/2]).
25 -export([content_types_provided/2]).
26 -export([resource_exists/2]).
27 -export([last_modified/2]).
28 -export([generate_etag/2]).
29 -export([get_file/2]).
30
31 init(Transport, Req, Opts) ->
32 cowboy_static:init(Transport, Req, Opts).
33
34 rest_init(Req, [Path]) ->
35 cowboy_static:rest_init(Req, {dir, Path});
36 rest_init(Req, [Path|Tail]) ->
37 {PathInfo, _} = cowboy_req:path_info(Req),
38 Filepath = filename:join([Path|PathInfo]),
39 case filelib:is_regular(Filepath) of
40 true -> cowboy_static:rest_init(Req, {dir, Path});
41 false -> rest_init(Req, Tail)
42 end.
43
44 malformed_request(Req, State) ->
45 cowboy_static:malformed_request(Req, State).
46
47 forbidden(Req, State) ->
48 cowboy_static:forbidden(Req, State).
49
50 content_types_provided(Req, State) ->
51 cowboy_static:content_types_provided(Req, State).
52
53 resource_exists(Req, State) ->
54 cowboy_static:resource_exists(Req, State).
55
56 last_modified(Req, State) ->
57 cowboy_static:last_modified(Req, State).
58
59 generate_etag(Req, State) ->
60 cowboy_static:generate_etag(Req, State).
61
62 get_file(Req, State) ->
63 cowboy_static:get_file(Req, State).
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_user).
1717
18 -export([init/1, resource_exists/2, to_json/2,
18 -export([init/3, rest_init/2, resource_exists/2, to_json/2,
1919 content_types_provided/2, content_types_accepted/2,
2020 is_authorized/2, allowed_methods/2, accept_content/2,
2121 delete_resource/2, user/1, put_user/1, put_user/2]).
22 -export([finish_request/2]).
23 -export([encodings_provided/2]).
22 -export([variances/2]).
2423
2524 -import(rabbit_misc, [pget/2]).
2625
27 -include("rabbit_mgmt.hrl").
28 -include_lib("webmachine/include/webmachine.hrl").
26 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2927 -include_lib("rabbit_common/include/rabbit.hrl").
3028
3129 %%--------------------------------------------------------------------
32 init(_Config) -> {ok, #context{}}.
33
34 finish_request(ReqData, Context) ->
35 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), Context}.
30
31 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
32
33 rest_init(Req, _Config) ->
34 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
35
36 variances(Req, Context) ->
37 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3638
3739 content_types_provided(ReqData, Context) ->
38 {[{"application/json", to_json}], ReqData, Context}.
39
40 encodings_provided(ReqData, Context) ->
41 {[{"identity", fun(X) -> X end},
42 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
40 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4341
4442 content_types_accepted(ReqData, Context) ->
45 {[{"application/json", accept_content}], ReqData, Context}.
43 {[{'*', accept_content}], ReqData, Context}.
4644
4745 allowed_methods(ReqData, Context) ->
48 {['HEAD', 'GET', 'PUT', 'DELETE', 'OPTIONS'], ReqData, Context}.
46 {[<<"HEAD">>, <<"GET">>, <<"PUT">>, <<"DELETE">>, <<"OPTIONS">>], ReqData, Context}.
4947
5048 resource_exists(ReqData, Context) ->
5149 {case user(ReqData) of
5856 rabbit_mgmt_util:reply(rabbit_mgmt_format:internal_user(User),
5957 ReqData, Context).
6058
61 accept_content(ReqData, Context) ->
62 Username = rabbit_mgmt_util:id(user, ReqData),
59 accept_content(ReqData0, Context) ->
60 Username = rabbit_mgmt_util:id(user, ReqData0),
6361 rabbit_mgmt_util:with_decode(
64 [], ReqData, Context,
65 fun(_, User) ->
62 [], ReqData0, Context,
63 fun(_, User, ReqData) ->
6664 put_user([{name, Username} | User]),
6765 {true, ReqData, Context}
6866 end).
8381 put_user(User) -> put_user(User, undefined).
8482
8583 put_user(User, Version) ->
86 PasswordUpdateFun =
87 fun(Username) ->
88 case {proplists:is_defined(password, User),
89 proplists:is_defined(password_hash, User)} of
90 {true, _} ->
91 rabbit_auth_backend_internal:change_password(
92 Username, pget(password, User));
93 {_, true} ->
94 HashingAlgorithm = hashing_algorithm(User, Version),
95
96 Hash = rabbit_mgmt_util:b64decode_or_throw(
97 pget(password_hash, User)),
98 rabbit_auth_backend_internal:change_password_hash(
99 Username, Hash, HashingAlgorithm);
100 _ ->
101 rabbit_auth_backend_internal:clear_password(Username)
102 end
84 Username = pget(name, User),
85 HasPassword = proplists:is_defined(password, User),
86 HasPasswordHash = proplists:is_defined(password_hash, User),
87 Password = pget(password, User),
88 PasswordHash = pget(password_hash, User),
89
90 Tags = case {pget(tags, User), pget(administrator, User)} of
91 {undefined, undefined} ->
92 throw({error, tags_not_present});
93 {undefined, AdminS} ->
94 case rabbit_mgmt_util:parse_bool(AdminS) of
95 true -> [administrator];
96 false -> []
97 end;
98 {TagsS, _} ->
99 [list_to_atom(string:strip(T)) ||
100 T <- string:tokens(binary_to_list(TagsS), ",")]
101 end,
102
103 UserExists = case rabbit_auth_backend_internal:lookup_user(Username) of
104 %% expected
105 {error, not_found} -> false;
106 %% shouldn't normally happen but worth guarding
107 %% against
108 {error, _} -> false;
109 _ -> true
110 end,
111
112 PassedCredentialValidation =
113 case {HasPassword, HasPasswordHash} of
114 {true, false} ->
115 rabbit_credential_validation:validate(Username, Password) =:= ok;
116 {false, true} -> true;
117 _ -> false
103118 end,
104 put_user0(User, PasswordUpdateFun).
105
106 put_user0(User, PasswordUpdateFun) ->
107 Username = pget(name, User),
108 Tags = case {pget(tags, User), pget(administrator, User)} of
109 {undefined, undefined} ->
110 throw({error, tags_not_present});
111 {undefined, AdminS} ->
112 case rabbit_mgmt_util:parse_bool(AdminS) of
113 true -> [administrator];
114 false -> []
115 end;
116 {TagsS, _} ->
117 [list_to_atom(string:strip(T)) ||
118 T <- string:tokens(binary_to_list(TagsS), ",")]
119 end,
120 case rabbit_auth_backend_internal:lookup_user(Username) of
121 {error, not_found} ->
122 rabbit_auth_backend_internal:add_user(
123 Username, rabbit_guid:binary(rabbit_guid:gen_secure(), "tmp"));
124 _ ->
125 ok
126 end,
127 PasswordUpdateFun(Username),
128 ok = rabbit_auth_backend_internal:set_tags(Username, Tags).
119
120 case UserExists of
121 true ->
122 case {HasPassword, HasPasswordHash} of
123 {true, false} ->
124 update_user_password(PassedCredentialValidation, Username, Password, Tags);
125 {false, true} ->
126 update_user_password_hash(Username, PasswordHash, Tags, User, Version);
127 {true, true} ->
128 throw({error, both_password_and_password_hash_are_provided});
129 %% clears password
130 _ ->
131 rabbit_auth_backend_internal:clear_password(Username)
132 end;
133 false ->
134 case {HasPassword, HasPasswordHash} of
135 {true, false} ->
136 create_user_with_password(PassedCredentialValidation, Username, Password, Tags);
137 {false, true} ->
138 create_user_with_password_hash(Username, PasswordHash, Tags, User, Version);
139 {true, true} ->
140 throw({error, both_password_and_password_hash_are_provided});
141 {false, false} ->
142 throw({error, no_password_or_password_hash_provided})
143 end
144 end.
145
146 update_user_password(_PassedCredentialValidation = true, Username, Password, Tags) ->
147 rabbit_auth_backend_internal:change_password(Username, Password),
148 rabbit_auth_backend_internal:set_tags(Username, Tags);
149 update_user_password(_PassedCredentialValidation = false, _Username, _Password, _Tags) ->
150 %% we don't log here because
151 %% rabbit_auth_backend_internal will do it
152 throw({error, credential_validation_failed}).
153
154 update_user_password_hash(Username, PasswordHash, Tags, User, Version) ->
155 %% when a hash this provided, credential validation
156 %% is not applied
157 HashingAlgorithm = hashing_algorithm(User, Version),
158
159 Hash = rabbit_mgmt_util:b64decode_or_throw(PasswordHash),
160 rabbit_auth_backend_internal:change_password_hash(
161 Username, Hash, HashingAlgorithm),
162 rabbit_auth_backend_internal:set_tags(Username, Tags).
163
164 create_user_with_password(_PassedCredentialValidation = true, Username, Password, Tags) ->
165 rabbit_auth_backend_internal:add_user(Username, Password),
166 rabbit_auth_backend_internal:set_tags(Username, Tags);
167 create_user_with_password(_PassedCredentialValidation = false, _Username, _Password, _Tags) ->
168 %% we don't log here because
169 %% rabbit_auth_backend_internal will do it
170 throw({error, credential_validation_failed}).
171
172 create_user_with_password_hash(Username, PasswordHash, Tags, User, Version) ->
173 %% when a hash this provided, credential validation
174 %% is not applied
175 HashingAlgorithm = hashing_algorithm(User, Version),
176 Hash = rabbit_mgmt_util:b64decode_or_throw(PasswordHash),
177
178 %% first we create a user with dummy credentials and no
179 %% validation applied, then we update password hash
180 TmpPassword = rabbit_guid:binary(rabbit_guid:gen_secure(), "tmp"),
181 rabbit_auth_backend_internal:add_user_sans_validation(Username, TmpPassword),
182
183 rabbit_auth_backend_internal:change_password_hash(
184 Username, Hash, HashingAlgorithm),
185 rabbit_auth_backend_internal:set_tags(Username, Tags).
129186
130187 hashing_algorithm(User, Version) ->
131188 case pget(hashing_algorithm, User) of
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_users).
1717
18 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2]).
19 -export([finish_request/2, allowed_methods/2]).
20 -export([encodings_provided/2]).
18 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, is_authorized/2]).
19 -export([variances/2]).
2120 -export([users/0]).
2221
2322 -import(rabbit_misc, [pget/2]).
2423
25 -include("rabbit_mgmt.hrl").
26 -include_lib("webmachine/include/webmachine.hrl").
24 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2725 -include_lib("rabbit_common/include/rabbit.hrl").
2826
2927 %%--------------------------------------------------------------------
3028
31 init(_Config) -> {ok, #context{}}.
29 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3230
33 finish_request(ReqData, Context) ->
34 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), Context}.
31 rest_init(Req, _Config) ->
32 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
3533
36 allowed_methods(ReqData, Context) ->
37 {['HEAD', 'GET', 'OPTIONS'], ReqData, Context}.
34 variances(Req, Context) ->
35 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3836
3937 content_types_provided(ReqData, Context) ->
40 {[{"application/json", to_json}], ReqData, Context}.
41
42 encodings_provided(ReqData, Context) ->
43 {[{"identity", fun(X) -> X end},
44 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
38 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4539
4640 to_json(ReqData, Context) ->
4741 rabbit_mgmt_util:reply_list(users(), ReqData, Context).
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_vhost).
1717
18 -export([init/1, resource_exists/2, to_json/2,
18 -export([init/3, rest_init/2, resource_exists/2, to_json/2,
1919 content_types_provided/2, content_types_accepted/2,
2020 is_authorized/2, allowed_methods/2, accept_content/2,
2121 delete_resource/2, id/1, put_vhost/2]).
22 -export([finish_request/2]).
23 -export([encodings_provided/2]).
22 -export([variances/2]).
2423
2524 -import(rabbit_misc, [pget/2]).
2625
27 -include("rabbit_mgmt.hrl").
28 -include_lib("webmachine/include/webmachine.hrl").
26 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2927 -include_lib("rabbit_common/include/rabbit.hrl").
3028
3129 %%--------------------------------------------------------------------
32 init(_Config) -> {ok, #context{}}.
3330
34 finish_request(ReqData, Context) ->
35 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), Context}.
31 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
32
33 rest_init(Req, _Config) ->
34 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
35
36 variances(Req, Context) ->
37 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3638
3739 content_types_provided(ReqData, Context) ->
38 {[{"application/json", to_json}], ReqData, Context}.
39
40 encodings_provided(ReqData, Context) ->
41 {[{"identity", fun(X) -> X end},
42 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
40 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4341
4442 content_types_accepted(ReqData, Context) ->
45 {[{"application/json", accept_content}], ReqData, Context}.
43 {[{'*', accept_content}], ReqData, Context}.
4644
4745 allowed_methods(ReqData, Context) ->
48 {['HEAD', 'GET', 'PUT', 'DELETE', 'OPTIONS'], ReqData, Context}.
46 {[<<"HEAD">>, <<"GET">>, <<"PUT">>, <<"DELETE">>, <<"OPTIONS">>], ReqData, Context}.
4947
5048 resource_exists(ReqData, Context) ->
5149 {rabbit_vhost:exists(id(ReqData)), ReqData, Context}.
6159 rabbit_mgmt_util:bad_request(iolist_to_binary(Reason), ReqData, Context)
6260 end.
6361
64 accept_content(ReqData, Context) ->
65 Name = id(ReqData),
62 accept_content(ReqData0, Context) ->
63 Name = id(ReqData0),
6664 rabbit_mgmt_util:with_decode(
67 [], ReqData, Context,
68 fun(_, VHost) ->
65 [], ReqData0, Context,
66 fun(_, VHost, ReqData) ->
6967 put_vhost(Name, rabbit_mgmt_util:parse_bool(
7068 pget(tracing, VHost))),
7169 {true, ReqData, Context}
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_vhosts).
1717
18 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2]).
19 -export([finish_request/2, allowed_methods/2]).
20 -export([encodings_provided/2]).
18 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, is_authorized/2]).
19 -export([variances/2]).
2120 -export([basic/0, augmented/2]).
2221
23 -include("rabbit_mgmt.hrl").
24 -include_lib("webmachine/include/webmachine.hrl").
22 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2523 -include_lib("rabbit_common/include/rabbit.hrl").
2624
2725 %%--------------------------------------------------------------------
2826
29 init(_Config) -> {ok, #context{}}.
27 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3028
31 finish_request(ReqData, Context) ->
32 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), Context}.
29 rest_init(Req, _Config) ->
30 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
3331
34 allowed_methods(ReqData, Context) ->
35 {['HEAD', 'GET', 'OPTIONS'], ReqData, Context}.
32 variances(Req, Context) ->
33 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3634
3735 content_types_provided(ReqData, Context) ->
38 {[{"application/json", to_json}], ReqData, Context}.
39
40 encodings_provided(ReqData, Context) ->
41 {[{"identity", fun(X) -> X end},
42 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
36 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4337
4438 to_json(ReqData, Context) ->
4539 try
1010 %% The Original Code is RabbitMQ Management Plugin.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_wm_whoami).
1717
18 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2]).
19 -export([finish_request/2, allowed_methods/2]).
20 -export([encodings_provided/2]).
18 -export([init/3, rest_init/2, to_json/2, content_types_provided/2, is_authorized/2]).
19 -export([variances/2]).
2120
22 -include("rabbit_mgmt.hrl").
23 -include_lib("webmachine/include/webmachine.hrl").
21 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2422 -include_lib("rabbit_common/include/rabbit.hrl").
2523
2624 %%--------------------------------------------------------------------
27 init(_Config) -> {ok, #context{}}.
2825
29 finish_request(ReqData, Context) ->
30 {ok, rabbit_mgmt_cors:set_headers(ReqData, Context), Context}.
26 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
3127
32 allowed_methods(ReqData, Context) ->
33 {['HEAD', 'GET', 'OPTIONS'], ReqData, Context}.
28 rest_init(Req, _Config) ->
29 {ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
30
31 variances(Req, Context) ->
32 {[<<"accept-encoding">>, <<"origin">>], Req, Context}.
3433
3534 content_types_provided(ReqData, Context) ->
36 {[{"application/json", to_json}], ReqData, Context}.
37
38 encodings_provided(ReqData, Context) ->
39 {[{"identity", fun(X) -> X end},
40 {"gzip", fun(X) -> zlib:gzip(X) end}], ReqData, Context}.
35 {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
4136
4237 to_json(ReqData, Context = #context{user = User}) ->
4338 rabbit_mgmt_util:reply(rabbit_mgmt_format:user(User), ReqData, Context).
+0
-22
deps/rabbitmq_management/src/rabbitmq_management.app.src less more
0 {application, rabbitmq_management,
1 [{description, "RabbitMQ Management Console"},
2 {vsn, "3.6.6"},
3 {modules, []},
4 {registered, []},
5 {mod, {rabbit_mgmt_app, []}},
6 {env, [{listener, [{port, 15672}]},
7 {http_log_dir, none},
8 {load_definitions, none},
9 {rates_mode, basic},
10 {sample_retention_policies,
11 %% List of {MaxAgeInSeconds, SampleEveryNSeconds}
12 [{global, [{605, 5}, {3660, 60}, {29400, 600}, {86400, 1800}]},
13 {basic, [{605, 5}, {3600, 60}]},
14 {detailed, [{10, 5}]}]},
15 {process_stats_gc_timeout, 300000},
16 {stats_event_max_backlog, 250},
17 {cors_allow_origins, []},
18 {cors_max_age, 1800}
19 ]},
20 {applications, [kernel, stdlib, rabbit_common, rabbit, xmerl, rabbitmq_web_dispatch,
21 amqp_client, rabbitmq_management_agent]}]}.
00 PROJECT = rabbitmq_management_agent
1 PROJECT_DESCRIPTION = RabbitMQ Management Agent
2 PROJECT_MOD = rabbit_mgmt_agent_app
3
4 define PROJECT_ENV
5 [
6 {rates_mode, basic},
7 {sample_retention_policies,
8 %% List of {MaxAgeInSeconds, SampleEveryNSeconds}
9 [{global, [{605, 5}, {3660, 60}, {29400, 600}, {86400, 1800}]},
10 {basic, [{605, 5}, {3600, 60}]},
11 {detailed, [{605, 5}]}]}
12 ]
13 endef
114
215 DEPS = rabbit_common rabbit
3
16 TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers
17 LOCAL_DEPS += xmerl mnesia ranch ssl crypto public_key
18 DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
419 DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk
520
621 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
0 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
11 #
22 # Permission to use, copy, modify, and/or distribute this software for any
33 # purpose with or without fee is hereby granted, provided that the above
1111 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1212 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1313
14 .PHONY: all app apps deps search rel docs install-docs check tests clean distclean help erlang-mk
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
1515
1616 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17
18 ERLANG_MK_VERSION = 2.0.0-pre.2-144-g647ffd1
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
1931
2032 # Core configuration.
2133
2436
2537 PROJECT_VERSION ?= rolling
2638 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
2740
2841 # Verbosity.
2942
8497 rel::
8598 $(verbose) :
8699
100 relup:: deps app
101
87102 check:: tests
88103
89104 clean:: clean-crashdump
101116 help::
102117 $(verbose) printf "%s\n" \
103118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
104 "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
105120 "" \
106121 "Usage: [V=1] $(MAKE) [target]..." \
107122 "" \
149164 core_native_path = $1
150165 endif
151166
152 ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
153 define core_http_get
154 wget --no-check-certificate -O $(1) $(2)|| rm $(1)
155 endef
156 else
157 define core_http_get.erl
158 ssl:start(),
159 inets:start(),
160 case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
161 {ok, {{_, 200, _}, _, Body}} ->
162 case file:write_file("$(1)", Body) of
163 ok -> ok;
164 {error, R1} -> halt(R1)
165 end;
166 {error, R2} ->
167 halt(R2)
168 end,
169 halt(0).
170 endef
171
172 define core_http_get
173 $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
174 endef
175 endif
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
176168
177169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
178170
192184 ERLANG_MK_BUILD_CONFIG ?= build.config
193185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
194186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
195188 erlang-mk:
196189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
197190 ifdef ERLANG_MK_COMMIT
198191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
199192 endif
200193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
201 $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
202195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
203196 rm -rf $(ERLANG_MK_BUILD_DIR)
204197
285278 pkg_apns_repo = https://github.com/inaka/apns4erl
286279 pkg_apns_commit = master
287280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
288289 PACKAGES += azdht
289290 pkg_azdht_name = azdht
290291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
405406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
406407 pkg_bootstrap_commit = master
407408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
408417 PACKAGES += boss_db
409418 pkg_boss_db_name = boss_db
410419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
413422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
414423 pkg_boss_db_commit = master
415424
416 PACKAGES += boss
417 pkg_boss_name = boss
418 pkg_boss_description = Erlang web MVC, now featuring Comet
419 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
420 pkg_boss_fetch = git
421 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
422 pkg_boss_commit = master
423
424425 PACKAGES += brod
425426 pkg_brod_name = brod
426427 pkg_brod_description = Kafka client in Erlang
565566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
566567 pkg_cloudi_service_api_requests_commit = master
567568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
568585 PACKAGES += cloudi_service_db_cassandra_cql
569586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
570587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
573590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
574591 pkg_cloudi_service_db_cassandra_cql_commit = master
575592
576 PACKAGES += cloudi_service_db_cassandra
577 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
579 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
580 pkg_cloudi_service_db_cassandra_fetch = git
581 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
582 pkg_cloudi_service_db_cassandra_commit = master
583
584593 PACKAGES += cloudi_service_db_couchdb
585594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
586595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
637646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
638647 pkg_cloudi_service_db_tokyotyrant_commit = master
639648
640 PACKAGES += cloudi_service_db
641 pkg_cloudi_service_db_name = cloudi_service_db
642 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
643 pkg_cloudi_service_db_homepage = http://cloudi.org/
644 pkg_cloudi_service_db_fetch = git
645 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
646 pkg_cloudi_service_db_commit = master
647
648649 PACKAGES += cloudi_service_filesystem
649650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
650651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
10371038 pkg_edown_repo = https://github.com/uwiger/edown
10381039 pkg_edown_commit = master
10391040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
10401049 PACKAGES += eep_app
10411050 pkg_eep_app_name = eep_app
10421051 pkg_eep_app_description = Embedded Event Processing
10451054 pkg_eep_app_repo = https://github.com/darach/eep-erl
10461055 pkg_eep_app_commit = master
10471056
1048 PACKAGES += eep
1049 pkg_eep_name = eep
1050 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1051 pkg_eep_homepage = https://github.com/virtan/eep
1052 pkg_eep_fetch = git
1053 pkg_eep_repo = https://github.com/virtan/eep
1054 pkg_eep_commit = master
1055
10561057 PACKAGES += efene
10571058 pkg_efene_name = efene
10581059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
12371238 pkg_eqm_repo = https://github.com/loucash/eqm
12381239 pkg_eqm_commit = master
12391240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
12401249 PACKAGES += eredis_pool
12411250 pkg_eredis_pool_name = eredis_pool
12421251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
12451254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
12461255 pkg_eredis_pool_commit = master
12471256
1248 PACKAGES += eredis
1249 pkg_eredis_name = eredis
1250 pkg_eredis_description = Erlang Redis client
1251 pkg_eredis_homepage = https://github.com/wooga/eredis
1252 pkg_eredis_fetch = git
1253 pkg_eredis_repo = https://github.com/wooga/eredis
1254 pkg_eredis_commit = master
1255
12561257 PACKAGES += erl_streams
12571258 pkg_erl_streams_name = erl_streams
12581259 pkg_erl_streams_description = Streams in Erlang
15411542 pkg_etap_repo = https://github.com/ngerakines/etap
15421543 pkg_etap_commit = master
15431544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
15441553 PACKAGES += etest_http
15451554 pkg_etest_http_name = etest_http
15461555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
15491558 pkg_etest_http_repo = https://github.com/wooga/etest_http
15501559 pkg_etest_http_commit = master
15511560
1552 PACKAGES += etest
1553 pkg_etest_name = etest
1554 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1555 pkg_etest_homepage = https://github.com/wooga/etest
1556 pkg_etest_fetch = git
1557 pkg_etest_repo = https://github.com/wooga/etest
1558 pkg_etest_commit = master
1559
15601561 PACKAGES += etoml
15611562 pkg_etoml_name = etoml
15621563 pkg_etoml_description = TOML language erlang parser
15651566 pkg_etoml_repo = https://github.com/kalta/etoml
15661567 pkg_etoml_commit = master
15671568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
15681577 PACKAGES += eunit_formatters
15691578 pkg_eunit_formatters_name = eunit_formatters
15701579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
15731582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
15741583 pkg_eunit_formatters_commit = master
15751584
1576 PACKAGES += eunit
1577 pkg_eunit_name = eunit
1578 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1579 pkg_eunit_homepage = https://github.com/richcarl/eunit
1580 pkg_eunit_fetch = git
1581 pkg_eunit_repo = https://github.com/richcarl/eunit
1582 pkg_eunit_commit = master
1583
15841585 PACKAGES += euthanasia
15851586 pkg_euthanasia_name = euthanasia
15861587 pkg_euthanasia_description = Merciful killer for your Erlang processes
15981599 pkg_evum_commit = master
15991600
16001601 PACKAGES += exec
1601 pkg_exec_name = exec
1602 pkg_exec_name = erlexec
16021603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
16031604 pkg_exec_homepage = http://saleyn.github.com/erlexec
16041605 pkg_exec_fetch = git
17171718 pkg_fn_repo = https://github.com/reiddraper/fn
17181719 pkg_fn_commit = master
17191720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
17201729 PACKAGES += folsom_cowboy
17211730 pkg_folsom_cowboy_name = folsom_cowboy
17221731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
17251734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
17261735 pkg_folsom_cowboy_commit = master
17271736
1728 PACKAGES += folsom
1729 pkg_folsom_name = folsom
1730 pkg_folsom_description = Expose Erlang Events and Metrics
1731 pkg_folsom_homepage = https://github.com/boundary/folsom
1732 pkg_folsom_fetch = git
1733 pkg_folsom_repo = https://github.com/boundary/folsom
1734 pkg_folsom_commit = master
1735
17361737 PACKAGES += folsomite
17371738 pkg_folsomite_name = folsomite
17381739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
20932094 pkg_jesse_repo = https://github.com/for-GET/jesse
20942095 pkg_jesse_commit = master
20952096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
20962105 PACKAGES += jiffy_v
20972106 pkg_jiffy_v_name = jiffy_v
20982107 pkg_jiffy_v_description = JSON validation utility
21012110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
21022111 pkg_jiffy_v_commit = master
21032112
2104 PACKAGES += jiffy
2105 pkg_jiffy_name = jiffy
2106 pkg_jiffy_description = JSON NIFs for Erlang.
2107 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2108 pkg_jiffy_fetch = git
2109 pkg_jiffy_repo = https://github.com/davisp/jiffy
2110 pkg_jiffy_commit = master
2111
21122113 PACKAGES += jobs
21132114 pkg_jobs_name = jobs
21142115 pkg_jobs_description = a Job scheduler for load regulation
21252126 pkg_joxa_repo = https://github.com/joxa/joxa
21262127 pkg_joxa_commit = master
21272128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
21282137 PACKAGES += json_rec
21292138 pkg_json_rec_name = json_rec
21302139 pkg_json_rec_description = JSON to erlang record
21332142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
21342143 pkg_json_rec_commit = master
21352144
2136 PACKAGES += json
2137 pkg_json_name = json
2138 pkg_json_description = a high level json library for erlang (17.0+)
2139 pkg_json_homepage = https://github.com/talentdeficit/json
2140 pkg_json_fetch = git
2141 pkg_json_repo = https://github.com/talentdeficit/json
2142 pkg_json_commit = master
2143
21442145 PACKAGES += jsone
21452146 pkg_jsone_name = jsone
21462147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
21812182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
21822183 pkg_jsx_commit = master
21832184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
21842193 PACKAGES += kafka_protocol
21852194 pkg_kafka_protocol_name = kafka_protocol
21862195 pkg_kafka_protocol_description = Kafka protocol Erlang library
21892198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
21902199 pkg_kafka_protocol_commit = master
21912200
2192 PACKAGES += kafka
2193 pkg_kafka_name = kafka
2194 pkg_kafka_description = Kafka consumer and producer in Erlang
2195 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2196 pkg_kafka_fetch = git
2197 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2198 pkg_kafka_commit = master
2199
22002201 PACKAGES += kai
22012202 pkg_kai_name = kai
22022203 pkg_kai_description = DHT storage by Takeshi Inoue
22932294 pkg_kvs_repo = https://github.com/synrc/kvs
22942295 pkg_kvs_commit = master
22952296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
22962305 PACKAGES += lager_amqp_backend
22972306 pkg_lager_amqp_backend_name = lager_amqp_backend
22982307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
23092318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
23102319 pkg_lager_syslog_commit = master
23112320
2312 PACKAGES += lager
2313 pkg_lager_name = lager
2314 pkg_lager_description = A logging framework for Erlang/OTP.
2315 pkg_lager_homepage = https://github.com/basho/lager
2316 pkg_lager_fetch = git
2317 pkg_lager_repo = https://github.com/basho/lager
2318 pkg_lager_commit = master
2319
23202321 PACKAGES += lambdapad
23212322 pkg_lambdapad_name = lambdapad
23222323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
25732574 pkg_mixer_repo = https://github.com/chef/mixer
25742575 pkg_mixer_commit = master
25752576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
25762585 PACKAGES += mochiweb_xpath
25772586 pkg_mochiweb_xpath_name = mochiweb_xpath
25782587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
25812590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
25822591 pkg_mochiweb_xpath_commit = master
25832592
2584 PACKAGES += mochiweb
2585 pkg_mochiweb_name = mochiweb
2586 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2587 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2588 pkg_mochiweb_fetch = git
2589 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2590 pkg_mochiweb_commit = master
2591
25922593 PACKAGES += mockgyver
25932594 pkg_mockgyver_name = mockgyver
25942595 pkg_mockgyver_description = A mocking library for Erlang
30613062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
30623063 pkg_quickrand_commit = master
30633064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
30643073 PACKAGES += rabbit_exchange_type_riak
30653074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
30663075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
30693078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
30703079 pkg_rabbit_exchange_type_riak_commit = master
30713080
3072 PACKAGES += rabbit
3073 pkg_rabbit_name = rabbit
3074 pkg_rabbit_description = RabbitMQ Server
3075 pkg_rabbit_homepage = https://www.rabbitmq.com/
3076 pkg_rabbit_fetch = git
3077 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3078 pkg_rabbit_commit = master
3079
30803081 PACKAGES += rack
30813082 pkg_rack_name = rack
30823083 pkg_rack_description = Rack handler for erlang
34933494 pkg_smother_repo = https://github.com/ramsay-t/Smother
34943495 pkg_smother_commit = master
34953496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
34963505 PACKAGES += social
34973506 pkg_social_name = social
34983507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
35413550 pkg_stable_repo = https://github.com/dvv/stable
35423551 pkg_stable_commit = master
35433552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
35443561 PACKAGES += statebox_riak
35453562 pkg_statebox_riak_name = statebox_riak
35463563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
35493566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
35503567 pkg_statebox_riak_commit = master
35513568
3552 PACKAGES += statebox
3553 pkg_statebox_name = statebox
3554 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3555 pkg_statebox_homepage = https://github.com/mochi/statebox
3556 pkg_statebox_fetch = git
3557 pkg_statebox_repo = https://github.com/mochi/statebox
3558 pkg_statebox_commit = master
3559
35603569 PACKAGES += statman
35613570 pkg_statman_name = statman
35623571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
37253734 pkg_tirerl_repo = https://github.com/inaka/tirerl
37263735 pkg_tirerl_commit = master
37273736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
37283745 PACKAGES += traffic_tools
37293746 pkg_traffic_tools_name = traffic_tools
37303747 pkg_traffic_tools_description = Simple traffic limiting library
40614078 pkg_zucchini_repo = https://github.com/devinus/zucchini
40624079 pkg_zucchini_commit = master
40634080
4064 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
40654082 # This file is part of erlang.mk and subject to the terms of the ISC License.
40664083
40674084 .PHONY: search
40884105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
40894106 endif
40904107
4091 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
40924109 # This file is part of erlang.mk and subject to the terms of the ISC License.
40934110
4094 .PHONY: distclean-deps
4111 .PHONY: distclean-deps clean-tmp-deps.log
40954112
40964113 # Configuration.
40974114
41104127
41114128 REBAR_DEPS_DIR = $(DEPS_DIR)
41124129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
41134150
41144151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
41154152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
41164153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
41174154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
41184155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
41194157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
41204158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
41214159
41384176
41394177 # Core targets.
41404178
4141 ifdef IS_APP
4142 apps::
4143 else
4144 apps:: $(ALL_APPS_DIRS)
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
41454180 ifeq ($(IS_APP)$(IS_DEP),)
41464181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
41474182 endif
41494184 # Create ebin directory for all apps to make sure Erlang recognizes them
41504185 # as proper OTP applications when using -include_lib. This is a temporary
41514186 # fix, a proper fix would be to compile apps/* in the right order.
4152 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4153 mkdir -p $$dep/ebin || exit $$?; \
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
41544190 done
4155 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
41564196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
41574197 :; \
41584198 else \
41594199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4160 $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
4200 $(MAKE) -C $$dep IS_APP=1; \
41614201 fi \
41624202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
41634207 endif
41644208
41654209 ifneq ($(SKIP_DEPS),)
41664210 deps::
41674211 else
4168 deps:: $(ALL_DEPS_DIRS) apps
4169 ifeq ($(IS_APP)$(IS_DEP),)
4170 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4171 endif
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
41724213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4173 $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
41744215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
41754216 :; \
41764217 else \
41774218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
41784219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4179 $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
4220 $(MAKE) -C $$dep IS_DEP=1; \
41804221 else \
4181 echo "Error: No Makefile to build dependency $$dep."; \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
41824223 exit 2; \
41834224 fi \
41844225 fi \
41924233 # in practice only Makefile is needed so far.
41934234 define dep_autopatch
41944235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
41954237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
41964238 $(call dep_autopatch_erlang_mk,$(1)); \
41974239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4198 if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
41994243 $(call dep_autopatch2,$(1)); \
4200 elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
42014245 $(call dep_autopatch2,$(1)); \
4202 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
42034247 $(call dep_autopatch2,$(1)); \
4204 else \
4205 $(call erlang,$(call dep_autopatch_app.erl,$(1))); \
42064248 fi \
42074249 else \
42084250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
42144256 endef
42154257
42164258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
42174262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
42184263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
42194264 fi; \
42204265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4221 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
42224267 $(call dep_autopatch_fetch_rebar); \
42234268 $(call dep_autopatch_rebar,$(1)); \
42244269 else \
42304275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
42314276 endef
42324277
4233 # Overwrite erlang.mk with the current file by default.
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
42344280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
42354281 define dep_autopatch_erlang_mk
4236 echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \
4237 > $(DEPS_DIR)/$1/erlang.mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
42384287 endef
42394288 else
42404289 define dep_autopatch_erlang_mk
42534302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
42544303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
42554304 cd $(ERLANG_MK_TMP)/rebar; \
4256 git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
42574306 $(MAKE); \
42584307 cd -; \
42594308 fi
42704319 define dep_autopatch_rebar.erl
42714320 application:load(rebar),
42724321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
42734323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
42744324 {ok, Conf0} -> Conf0;
42754325 _ -> []
44234473 [] -> ok;
44244474 _ ->
44254475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4426 PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
44274477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4428 PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L \\"~s\\" -lerl_interface -lei\n",
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
44294479 [code:lib_dir(erl_interface, lib)])),
44304480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
44314481 FilterEnv = fun(Env) ->
44644514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44654515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44664516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4467 [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
44684518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
44694519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
44704520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
44764526 end,
44774527 [PortSpec(S) || S <- PortSpecs]
44784528 end,
4479 Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"),
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
44804530 RunPlugin = fun(Plugin, Step) ->
44814531 case erlang:function_exported(Plugin, Step, 2) of
44824532 false -> ok;
45244574 halt()
45254575 endef
45264576
4527 define dep_autopatch_app.erl
4528 UpdateModules = fun(App) ->
4529 case filelib:is_regular(App) of
4530 false -> ok;
4531 true ->
4532 {ok, [{application, '$(1)', L0}]} = file:consult(App),
4533 Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true,
4534 fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
4535 L = lists:keystore(modules, 1, L0, {modules, Mods}),
4536 ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}]))
4537 end
4538 end,
4539 UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"),
4540 halt()
4541 endef
4542
45434577 define dep_autopatch_appsrc_script.erl
45444578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
45454579 AppSrcScript = AppSrc ++ ".script",
45874621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
45884622 endef
45894623
4590 define dep_fetch_hex.erl
4591 ssl:start(),
4592 inets:start(),
4593 {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
4594 {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
4595 [], [{body_format, binary}]),
4596 {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
4597 {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
4598 ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
4599 halt()
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
46004626 endef
46014627
46024628 # Hex only has a package version. No need to look in the Erlang.mk packages.
46034629 define dep_fetch_hex
4604 $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
46054634 endef
46064635
46074636 define dep_fetch_fail
46314660 $(eval DEP_NAME := $(call dep_name,$1))
46324661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
46334662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4634 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
46354664 exit 17; \
46364665 fi
46374666 $(verbose) mkdir -p $(DEPS_DIR)
46734702 clean:: clean-apps
46744703
46754704 clean-apps:
4676 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4677 $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
46784707 done
46794708
46804709 distclean:: distclean-apps
46814710
46824711 distclean-apps:
4683 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4684 $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
46854714 done
46864715 endif
46874716
47014730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
47024731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
47034732
4704 # External plugins.
4705
4706 DEP_PLUGINS ?=
4707
4708 define core_dep_plugin
4709 -include $(DEPS_DIR)/$(1)
4710
4711 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4712 endef
4713
4714 $(foreach p,$(DEP_PLUGINS),\
4715 $(eval $(if $(findstring /,$p),\
4716 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4717 $(call core_dep_plugin,$p/plugins.mk,$p))))
4718
4719 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4720 # This file is part of erlang.mk and subject to the terms of the ISC License.
4721
4722 # Configuration.
4723
4724 DTL_FULL_PATH ?=
4725 DTL_PATH ?= templates/
4726 DTL_SUFFIX ?= _dtl
4727 DTL_OPTS ?=
4728
4729 # Verbosity.
4730
4731 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
4732 dtl_verbose = $(dtl_verbose_$(V))
4733
4734 # Core targets.
4735
4736 DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl))
4737
4738 ifneq ($(DTL_FILES),)
4739
4740 ifdef DTL_FULL_PATH
4741 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
4742 else
4743 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
4744 endif
4745
4746 ifneq ($(words $(DTL_FILES)),0)
4747 # Rebuild templates when the Makefile changes.
4748 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
4749 @mkdir -p $(ERLANG_MK_TMP)
4750 @if test -f $@; then \
4751 touch $(DTL_FILES); \
4752 fi
4753 @touch $@
4754
4755 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
4756 endif
4757
4758 define erlydtl_compile.erl
4759 [begin
4760 Module0 = case "$(strip $(DTL_FULL_PATH))" of
4761 "" ->
4762 filename:basename(F, ".dtl");
4763 _ ->
4764 "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
4765 re:replace(F2, "/", "_", [{return, list}, global])
4766 end,
4767 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
4768 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
4769 ok -> ok;
4770 {ok, _} -> ok
4771 end
4772 end || F <- string:tokens("$(1)", " ")],
4773 halt().
4774 endef
4775
4776 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
4777 $(if $(strip $?),\
4778 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
4779
4780 endif
4781
4782 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
47834734 # This file is part of erlang.mk and subject to the terms of the ISC License.
47844735
47854736 # Verbosity.
47984749
47994750 define compile_proto.erl
48004751 [begin
4801 Dir = filename:dirname(filename:dirname(F)),
48024752 protobuffs_compile:generate_source(F,
4803 [{output_include_dir, Dir ++ "/include"},
4804 {output_src_dir, Dir ++ "/ebin"}])
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
48054755 end || F <- string:tokens("$(1)", " ")],
48064756 halt().
48074757 endef
48114761 $(if $(strip $?),$(call compile_proto,$?))
48124762 endif
48134763
4814 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
48154765 # This file is part of erlang.mk and subject to the terms of the ISC License.
48164766
48174767 .PHONY: clean-app
48254775 ERLC_EXCLUDE ?=
48264776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
48274777
4778 ERLC_ASN1_OPTS ?=
4779
48284780 ERLC_MIB_OPTS ?=
48294781 COMPILE_MIB_FIRST ?=
48304782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
48744826
48754827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
48764828 define app_file
4877 {application, $(PROJECT), [
4829 {application, '$(PROJECT)', [
48784830 {description, "$(PROJECT_DESCRIPTION)"},
48794831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48804832 {id$(comma)$(space)"$(1)"}$(comma))
48814833 {modules, [$(call comma_list,$(2))]},
48824834 {registered, []},
4883 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48844837 ]}.
48854838 endef
48864839 else
48874840 define app_file
4888 {application, $(PROJECT), [
4841 {application, '$(PROJECT)', [
48894842 {description, "$(PROJECT_DESCRIPTION)"},
48904843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48914844 {id$(comma)$(space)"$(1)"}$(comma))
48924845 {modules, [$(call comma_list,$(2))]},
48934846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
48944847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4895 {mod, {$(PROJECT_MOD), []}}
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48964850 ]}.
48974851 endef
48984852 endif
49024856
49034857 # Source files.
49044858
4905 ERL_FILES = $(sort $(call core_find,src/,*.erl))
4906 CORE_FILES = $(sort $(call core_find,src/,*.core))
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
49074863
49084864 # ASN.1 files.
49094865
49134869
49144870 define compile_asn1
49154871 $(verbose) mkdir -p include/
4916 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
49174873 $(verbose) mv asn1/*.erl src/
49184874 $(verbose) mv asn1/*.hrl include/
49194875 $(verbose) mv asn1/*.asn1db include/
49364892
49374893 # Leex and Yecc files.
49384894
4939 XRL_FILES = $(sort $(call core_find,src/,*.xrl))
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
49404896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
49414897 ERL_FILES += $(XRL_ERL_FILES)
49424898
4943 YRL_FILES = $(sort $(call core_find,src/,*.yrl))
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
49444900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
49454901 ERL_FILES += $(YRL_ERL_FILES)
49464902
49474903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4948 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
49494905
49504906 # Erlang and Core Erlang files.
49514907
49954951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49964952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49974953 (F, Mod, import, {Imp, _}) ->
4998 case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
49994960 false -> ok;
50004961 true -> Add(Mod, Imp)
50014962 end;
50194980 end || F <- ErlFiles],
50204981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
50214982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
50224991 ok = file:write_file("$(1)", [
50234992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
5024 "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n"
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
50254994 ]),
50264995 halt()
50274996 endef
50345003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
50355004 # Rebuild everything when the Makefile changes.
50365005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5037 @mkdir -p $(ERLANG_MK_TMP)
5038 @if test -f $@; then \
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
50395008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
50405009 touch -c $(PROJECT).d; \
50415010 fi
5042 @touch $@
5011 $(verbose) touch $@
50435012
50445013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
50455014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
50465015 endif
50475016
5048 -include $(PROJECT).d
5017 include $(wildcard $(PROJECT).d)
50495018
50505019 ebin/$(PROJECT).app:: ebin/
50515020
50645033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
50655034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
50665035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5067 $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
50685037 > ebin/$(PROJECT).app
50695038 else
50705039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
50885057
50895058 endif
50905059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
50915061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
50925062 # This file is part of erlang.mk and subject to the terms of the ISC License.
50935063
51055075 doc-deps:
51065076 else
51075077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5108 $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5109 endif
5110
5111 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51125082 # This file is part of erlang.mk and subject to the terms of the ISC License.
51135083
51145084 .PHONY: rel-deps
51255095 rel-deps:
51265096 else
51275097 rel-deps: $(ALL_REL_DEPS_DIRS)
5128 $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5129 endif
5130
5131 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51325102 # This file is part of erlang.mk and subject to the terms of the ISC License.
51335103
51345104 .PHONY: test-deps test-dir test-build clean-test-dir
51505120 test-deps:
51515121 else
51525122 test-deps: $(ALL_TEST_DEPS_DIRS)
5153 $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
51545124 endif
51555125
51565126 ifneq ($(wildcard $(TEST_DIR)),)
51835153 endif
51845154 endif
51855155
5186 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51875157 # This file is part of erlang.mk and subject to the terms of the ISC License.
51885158
51895159 .PHONY: rebar.config
52195189 rebar.config:
52205190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
52215191
5222 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
52235193 # This file is part of erlang.mk and subject to the terms of the ISC License.
52245194
5225 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
5226
5227 MAN_INSTALL_PATH ?= /usr/local/share/man
5228 MAN_SECTIONS ?= 3 7
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
52295200
52305201 docs:: asciidoc
52315202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
52325207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
52335210
52345211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
52355212 asciidoc-guide:
52365213 else
5237 asciidoc-guide: distclean-asciidoc doc-deps
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
52385215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
52395216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5240 endif
5241
5242 ifeq ($(wildcard doc/src/manual/*.asciidoc),)
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
52435227 asciidoc-manual:
52445228 else
5245 asciidoc-manual: distclean-asciidoc doc-deps
5246 for f in doc/src/manual/*.asciidoc ; do \
5247 a2x -v -f manpage $$f ; \
5248 done
5249 for s in $(MAN_SECTIONS); do \
5250 mkdir -p doc/man$$s/ ; \
5251 mv doc/src/manual/*.$$s doc/man$$s/ ; \
5252 gzip doc/man$$s/*.$$s ; \
5253 done
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
52545262
52555263 install-docs:: install-asciidoc
52565264
52575265 install-asciidoc: asciidoc-manual
5258 for s in $(MAN_SECTIONS); do \
5259 mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
5260 install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
5261 done
5262 endif
5263
5264 distclean:: distclean-asciidoc
5265
5266 distclean-asciidoc:
5267 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
5268
5269 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
52705276 # This file is part of erlang.mk and subject to the terms of the ISC License.
52715277
52725278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
53235329 define bs_Makefile
53245330 PROJECT = $p
53255331 PROJECT_DESCRIPTION = New project
5326 PROJECT_VERSION = 0.0.1
5332 PROJECT_VERSION = 0.1.0
53275333
53285334 # Whitespace to be used when creating files from templates.
53295335 SP = $(SP)
53335339 define bs_Makefile
53345340 PROJECT = $p
53355341 PROJECT_DESCRIPTION = New project
5336 PROJECT_VERSION = 0.0.1
5342 PROJECT_VERSION = 0.1.0
53375343
53385344 endef
53395345 endif
53415347 define bs_apps_Makefile
53425348 PROJECT = $p
53435349 PROJECT_DESCRIPTION = New project
5344 PROJECT_VERSION = 0.0.1
5350 PROJECT_VERSION = 0.1.0
53455351
53465352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
53475353 endef
53615367 endef
53625368
53635369 define bs_relx_config
5364 {release, {$p_release, "1"}, [$p]}.
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
53655371 {extended_start_script, true}.
53665372 {sys_config, "rel/sys.config"}.
53675373 {vm_args, "rel/vm.args"}.
57125718 ifndef t
57135719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57145720 endif
5715 ifndef tpl_$(t)
5716 $(error Unknown template)
5717 endif
57185721 ifndef n
57195722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57205723 endif
57275730 list-templates:
57285731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
57295732
5730 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
57315734 # This file is part of erlang.mk and subject to the terms of the ISC License.
57325735
57335736 .PHONY: clean-c_src distclean-c_src-env
59625965 $(call render_template,bs_erl_nif,src/$n.erl)
59635966 endif
59645967
5965 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
59665969 # This file is part of erlang.mk and subject to the terms of the ISC License.
59675970
5968 .PHONY: ci ci-setup distclean-kerl
5969
5970 KERL ?= $(CURDIR)/kerl
5971 export KERL
5972
5973 KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
5974
5975 OTP_GIT ?= https://github.com/erlang/otp
5976
5977 CI_INSTALL_DIR ?= $(HOME)/erlang
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
59785973 CI_OTP ?=
5979
5980 ifeq ($(strip $(CI_OTP)),)
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
59815986 ci::
59825987 else
5983 ci:: $(addprefix ci-,$(CI_OTP))
5984
5985 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
59866007
59876008 ci-setup::
6009
6010 ci-extra::
59886011
59896012 ci_verbose_0 = @echo " CI " $(1);
59906013 ci_verbose = $(ci_verbose_$(V))
59916014
59926015 define ci_target
5993 ci-$(1): $(CI_INSTALL_DIR)/$(1)
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
59946018 $(ci_verbose) \
5995 PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
5996 CI_OTP_RELEASE="$(1)" \
5997 CT_OPTS="-label $(1)" \
5998 $(MAKE) clean ci-setup tests
5999 endef
6000
6001 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
60026030
60036031 define ci_otp_target
60046032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
60056033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6006 $(KERL) build git $(OTP_GIT) $(1) $(1)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
60076035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
60086036 endif
60096037 endef
60106038
60116039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
60126040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
60136052 $(KERL):
6014 $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
60156056 $(verbose) chmod +x $(KERL)
60166057
60176058 help::
60286069 $(gen_verbose) rm -rf $(KERL)
60296070 endif
60306071
6031 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
60326073 # This file is part of erlang.mk and subject to the terms of the ISC License.
60336074
60346075 .PHONY: ct apps-ct distclean-ct
60366077 # Configuration.
60376078
60386079 CT_OPTS ?=
6080
60396081 ifneq ($(wildcard $(TEST_DIR)),)
6040 CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6041 else
6042 CT_SUITES ?=
6043 endif
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
60446088
60456089 # Core targets.
60466090
60636107 -noinput \
60646108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
60656109 -dir $(TEST_DIR) \
6066 -logdir $(CURDIR)/logs
6110 -logdir $(CT_LOGS_DIR)
60676111
60686112 ifeq ($(CT_SUITES),)
60696113 ct: $(if $(IS_APP),,apps-ct)
60706114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
60716117 ct: test-build $(if $(IS_APP),,apps-ct)
6072 $(verbose) mkdir -p $(CURDIR)/logs/
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
60736119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
60746121 endif
60756122
60766123 ifneq ($(ALL_APPS_DIRS),)
60976144
60986145 define ct_suite_target
60996146 ct-$(1): test-build
6100 $(verbose) mkdir -p $(CURDIR)/logs/
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
61016148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
61026149 endef
61036150
61046151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
61056152
61066153 distclean-ct:
6107 $(gen_verbose) rm -rf $(CURDIR)/logs/
6108
6109 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61106157 # This file is part of erlang.mk and subject to the terms of the ISC License.
61116158
61126159 .PHONY: plt distclean-plt dialyze
61506197 endef
61516198
61526199 $(DIALYZER_PLT): deps app
6153 $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
61546204
61556205 plt: $(DIALYZER_PLT)
61566206
61646214 endif
61656215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
61666216
6167 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61686218 # This file is part of erlang.mk and subject to the terms of the ISC License.
61696219
61706220 .PHONY: distclean-edoc edoc
61726222 # Configuration.
61736223
61746224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
61756235
61766236 # Core targets.
61776237
6178 ifneq ($(wildcard doc/overview.edoc),)
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
61796239 docs:: edoc
61806240 endif
61816241
61846244 # Plugin-specific targets.
61856245
61866246 edoc: distclean-edoc doc-deps
6187 $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
61886248
61896249 distclean-edoc:
61906250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
61916251
6192 # Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61936253 # This file is part of erlang.mk and subject to the terms of the ISC License.
61946254
6195 .PHONY: distclean-escript escript
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
61966320
61976321 # Configuration.
61986322
61996323 ESCRIPT_NAME ?= $(PROJECT)
62006324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
62016325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
62026327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6203
6204 ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
6205 ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
6206 ESCRIPT_EMU_ARGS ?= -pa . \
6207 -sasl errlog_type error \
6208 -escript main $(ESCRIPT_NAME)
6209 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6210 ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
62116332
62126333 # Core targets.
62136334
62206341
62216342 # Plugin-specific targets.
62226343
6223 # Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
6224 # Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
6225 # Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
6226 # Software may only be used for the great good and the true happiness of all
6227 # sentient beings.
6228
6229 define ESCRIPT_RAW
6230 'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
6231 'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
6232 ' [F || F <- A, not filelib:is_dir(F) ] end,'\
6233 'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
6234 'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
6235 'Ez = fun(Escript) ->'\
6236 ' Static = Files([$(ESCRIPT_STATIC)]),'\
6237 ' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
6238 ' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
6239 ' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
6240 ' {archive, Archive, [memory]},'\
6241 ' {shebang, "$(ESCRIPT_SHEBANG)"},'\
6242 ' {comment, "$(ESCRIPT_COMMENT)"},'\
6243 ' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
6244 ' ]),'\
6245 ' file:change_mode(Escript, 8#755)'\
6246 'end,'\
6247 'Ez("$(ESCRIPT_FILE)"),'\
6248 'halt().'
6249 endef
6250
6251 ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
6252
6253 escript:: distclean-escript deps app
6254 $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
62556360
62566361 distclean-escript:
6257 $(gen_verbose) rm -f $(ESCRIPT_NAME)
6258
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
62596365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6260 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
62616366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
62626367
62636368 .PHONY: eunit apps-eunit
63216426
63226427 ifneq ($(ALL_APPS_DIRS),)
63236428 apps-eunit:
6324 $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done
6325 endif
6326 endif
6327
6328 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
63296436 # This file is part of erlang.mk and subject to the terms of the ISC License.
63306437
6331 .PHONY: relx-rel distclean-relx-rel distclean-relx run
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
63326439
63336440 # Configuration.
63346441
6335 RELX ?= $(CURDIR)/relx
6442 RELX ?= $(ERLANG_MK_TMP)/relx
63366443 RELX_CONFIG ?= $(CURDIR)/relx.config
63376444
63386445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
63396446 RELX_OPTS ?=
63406447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
63416454
63426455 ifeq ($(firstword $(RELX_OPTS)),-o)
63436456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
63506463 ifeq ($(IS_DEP),)
63516464 ifneq ($(wildcard $(RELX_CONFIG)),)
63526465 rel:: relx-rel
6353 endif
6354 endif
6355
6356 distclean:: distclean-relx-rel distclean-relx
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
63576472
63586473 # Plugin-specific targets.
63596474
63626477 $(verbose) chmod +x $(RELX)
63636478
63646479 relx-rel: $(RELX) rel-deps app
6365 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
63666484
63676485 distclean-relx-rel:
63686486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6369
6370 distclean-relx:
6371 $(gen_verbose) rm -rf $(RELX)
63726487
63736488 # Run target.
63746489
63776492 else
63786493
63796494 define get_relx_release.erl
6380 {ok, Config} = file:consult("$(RELX_CONFIG)"),
6381 {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
6382 io:format("~s", [Name]),
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
63836504 halt(0).
63846505 endef
63856506
6386 RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
63876514
63886515 run: all
6389 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
63906517
63916518 help::
63926519 $(verbose) printf "%s\n" "" \
63956522
63966523 endif
63976524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
63986526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6399 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
64006527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
64016528
64026529 .PHONY: shell
64216548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
64226549
64236550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6424 $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
64256552
64266553 shell: build-shell-deps
64276554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
64286555
6429 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
64306571 # This file is part of erlang.mk and subject to the terms of the ISC License.
64316572
64326573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
64376578 tests:: triq
64386579
64396580 define triq_check.erl
6440 code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
64416582 try
64426583 case $(1) of
64436584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
64696610 endif
64706611 endif
64716612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
64726614 # Copyright (c) 2015, Erlang Solutions Ltd.
64736615 # This file is part of erlang.mk and subject to the terms of the ISC License.
64746616
64856627 XREFR ?= $(CURDIR)/xrefr
64866628 export XREFR
64876629
6488 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
64896631
64906632 # Core targets.
64916633
64926634 help::
6493 $(verbose) printf "%s\n" "" \
6494 "Xref targets:" \
6495 " xref Run Xrefr using $XREF_CONFIG as config file if defined"
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
64966638
64976639 distclean:: distclean-xref
64986640
65086650 distclean-xref:
65096651 $(gen_verbose) rm -rf $(XREFR)
65106652
6511 # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
65126655 # This file is part of erlang.mk and subject to the terms of the ISC License.
65136656
65146657 COVER_REPORT_DIR = cover
65176660
65186661 ifdef COVER
65196662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
65206664 # All modules in 'ebin'
65216665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
65226666
65316675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
65326676 endif
65336677 endif
6678 endif
65346679
65356680 # Core targets
65366681
65526697 "Cover targets:" \
65536698 " cover-report Generate a HTML coverage report from previously collected" \
65546699 " cover data." \
6555 " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
65566701 "" \
65576702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
65586703 "target tests additionally generates a HTML coverage report from the combined" \
65656710
65666711 .PHONY: coverdata-clean
65676712 coverdata-clean:
6568 $(gen_verbose) rm -f *.coverdata ct.cover.spec
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
65696714
65706715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
65716721 all.coverdata: $(COVERDATA)
6572 $(gen_verbose) $(ERL) -eval ' \
6573 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
6574 cover:export("$@"), halt(0).'
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
65756723
65766724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
65776725 # empty if you want the coverdata files but not the HTML report.
65896737 # Modules which include eunit.hrl always contain one line without coverage
65906738 # because eunit defines test/0 which is never called. We compensate for this.
65916739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6592 grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
65936741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
65946742
65956743 define cover_report.erl
66246772 endef
66256773
66266774 cover-report:
6627 $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
66286776 $(gen_verbose) $(call erlang,$(cover_report.erl))
66296777
66306778 endif
66316779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
66326839
66336840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
66346841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
66976904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
66986905 endif
66996906 ifndef IS_APP
6700 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
67016908 $(MAKE) -C $$dep $@ \
67026909 IS_APP=1 \
6703 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6704 || exit $$?; \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67056911 done
67066912 endif
6707 $(verbose) for dep in $^ ; do \
6913 $(verbose) set -e; for dep in $^ ; do \
67086914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
67096915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6710 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
67116917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
67126918 $(MAKE) -C $$dep fetch-deps \
67136919 IS_DEP=1 \
6714 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6715 || exit $$?; \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67166921 fi \
67176922 fi \
67186923 done
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License at
3 %% http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
7 %% License for the specific language governing rights and limitations
8 %% under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is Pivotal Software, Inc.
13 %% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -type(event_type() :: queue_stats | queue_exchange_stats | vhost_stats
17 | channel_queue_stats | channel_stats
18 | channel_exchange_stats | exchange_stats
19 | node_stats | node_node_stats | connection_stats).
20 -type(type() :: deliver_get | fine_stats | queue_msg_rates | queue_msg_counts
21 | coarse_node_stats | coarse_node_node_stats | coarse_conn_stats
22 | process_stats).
23
24 -type(table_name() :: atom()).
25
26 -define(TABLES, [{connection_stats_coarse_conn_stats, set},
27 {vhost_stats_coarse_conn_stats, set},
28 {connection_created_stats, set},
29 {connection_stats, set},
30 {channel_created_stats, set},
31 {channel_stats, set},
32 {channel_stats_fine_stats, set},
33 {channel_exchange_stats_fine_stats, set},
34 {channel_queue_stats_deliver_stats, set},
35 {vhost_stats_fine_stats, set},
36 {queue_stats_deliver_stats, set},
37 {vhost_stats_deliver_stats, set},
38 {channel_stats_deliver_stats, set},
39 {channel_process_stats, set},
40 {queue_stats_publish, set},
41 {queue_exchange_stats_publish, set},
42 {exchange_stats_publish_out, set},
43 {exchange_stats_publish_in, set},
44 {consumer_stats, set},
45 {queue_stats, set},
46 {queue_msg_stats, set},
47 {vhost_msg_stats, set},
48 {queue_process_stats, set},
49 {node_stats, set},
50 {node_coarse_stats, set},
51 {node_persister_stats, set},
52 {node_node_stats, set},
53 {node_node_coarse_stats, set},
54 {queue_msg_rates, set},
55 {vhost_msg_rates, set}]).
56
57 -define(INDEX_TABLES, [consumer_stats_queue_index,
58 consumer_stats_channel_index,
59 channel_exchange_stats_fine_stats_exchange_index,
60 channel_exchange_stats_fine_stats_channel_index,
61 channel_queue_stats_deliver_stats_queue_index,
62 channel_queue_stats_deliver_stats_channel_index,
63 queue_exchange_stats_publish_queue_index,
64 queue_exchange_stats_publish_exchange_index,
65 node_node_coarse_stats_node_index]).
66
67 -define(GC_EVENTS, [connection_closed, channel_closed, consumer_deleted,
68 exchange_deleted, queue_deleted, vhost_deleted,
69 node_node_deleted, channel_consumer_deleted]).
70
71 -define(DELEGATE_PREFIX, "delegate_management_").
72
73 %%------------------------------------------------------------------------------
74 %% Only for documentation and testing purposes, so we keep track of the number and
75 %% order of the metrics
76 -define(connection_stats_coarse_conn_stats(Recv_oct, Send_oct, Reductions),
77 {Recv_oct, Send_oct, Reductions}).
78 -define(vhost_stats_coarse_conn_stats(Recv_oct, Send_oct), {Recv_oct, Send_oct}).
79 -define(connection_created_stats(Id, Name, Props), {Id, Name, Props}).
80 -define(connection_stats(Id, Props), {Id, Props}).
81 -define(channel_created_stats(Id, Name, Props), {Id, Name, Props}).
82 -define(channel_consumer_created_stats(Queue, ChPid, ConsumerTag),
83 {Queue, {ChPid, ConsumerTag}}).
84 -define(channel_stats(Id, Props), {Id, Props}).
85 -define(channel_stats_fine_stats(Publish, Confirm, Return_unroutable),
86 {Publish, Confirm, Return_unroutable}).
87 -define(channel_exchange_stats_fine_stats(Publish, Confirm, Return_unroutable),
88 {Publish, Confirm, Return_unroutable}).
89 -define(channel_queue_stats_deliver_stats(Get, Get_no_ack, Deliver, Deliver_no_ack,
90 Redeliver, Ack, Deliver_get),
91 {Get, Get_no_ack, Deliver, Deliver_no_ack, Redeliver, Ack, Deliver_get}).
92 -define(vhost_stats_fine_stats(Publish, Confirm, Return_unroutable),
93 {Publish, Confirm, Return_unroutable}).
94 -define(queue_stats_deliver_stats(Get, Get_no_ack, Deliver, Deliver_no_ack,
95 Redeliver, Ack, Deliver_get),
96 {Get, Get_no_ack, Deliver, Deliver_no_ack, Redeliver, Ack, Deliver_get}).
97 -define(vhost_stats_deliver_stats(Get, Get_no_ack, Deliver, Deliver_no_ack,
98 Redeliver, Ack, Deliver_get),
99 {Get, Get_no_ack, Deliver, Deliver_no_ack, Redeliver, Ack, Deliver_get}).
100 -define(channel_stats_deliver_stats(Get, Get_no_ack, Deliver, Deliver_no_ack,
101 Redeliver, Ack, Deliver_get),
102 {Get, Get_no_ack, Deliver, Deliver_no_ack, Redeliver, Ack, Deliver_get}).
103 -define(channel_process_stats(Reductions), {Reductions}).
104 -define(queue_stats_publish(Publish), {Publish}).
105 -define(queue_exchange_stats_publish(Publish), {Publish}).
106 -define(exchange_stats_publish_out(Publish_out), {Publish_out}).
107 -define(exchange_stats_publish_in(Publish_in), {Publish_in}).
108 -define(consumer_stats(Id, Props), {Id, Props}).
109 -define(queue_stats(Id, Props), {Id, Props}).
110 -define(queue_msg_stats(Messages_ready, Messages_unacknowledged, Messages),
111 {Messages_ready, Messages_unacknowledged, Messages}).
112 -define(vhost_msg_stats(Messages_ready, Messages_unacknowledged, Messages),
113 {Messages_ready, Messages_unacknowledged, Messages}).
114 -define(queue_process_stats(Reductions), {Reductions}).
115 -define(node_stats(Id, Props), {Id, Props}).
116 -define(node_coarse_stats(Fd_used, Sockets_used, Mem_used, Disk_free, Proc_used,
117 Gc_num, Gc_bytes_reclaimed, Context_switches),
118 {Fd_used, Sockets_used, Mem_used, Disk_free, Proc_used, Gc_num,
119 Gc_bytes_reclaimed, Context_switches}).
120 -define(node_persister_stats(Io_read_count, Io_read_bytes, Io_read_avg_time, Io_write_count,
121 Io_write_bytes, Io_write_avg_time, Io_sync_count, Io_sync_avg_time,
122 Io_seek_count, Io_seek_avg_time, Io_reopen_count, Mnesia_ram_tx_count,
123 Mnesia_disk_tx_count, Msg_store_read_count, Msg_store_write_count,
124 Queue_index_journal_write_count, Queue_index_write_count,
125 Queue_index_read_count, Io_file_handle_open_attempt_count,
126 Io_file_handle_open_attempt_avg_time),
127 {Io_read_count, Io_read_bytes, Io_read_avg_time, Io_write_count, Io_write_bytes,
128 Io_write_avg_time, Io_sync_count, Io_sync_avg_time, Io_seek_count, Io_seek_avg_time,
129 Io_reopen_count, Mnesia_ram_tx_count, Mnesia_disk_tx_count, Msg_store_read_count,
130 Msg_store_write_count, Queue_index_journal_write_count, Queue_index_write_count,
131 Queue_index_read_count, Io_file_handle_open_attempt_count,
132 Io_file_handle_open_attempt_avg_time}).
133 -define(node_node_stats(Send_bytes, Recv_bytes), {Send_bytes, Recv_bytes}).
134 -define(node_node_coarse_stats(Send_bytes, Recv_bytes), {Send_bytes, Recv_bytes}).
135 -define(queue_msg_rates(Disk_reads, Disk_writes), {Disk_reads, Disk_writes}).
136 -define(vhost_msg_rates(Disk_reads, Disk_writes), {Disk_reads, Disk_writes}).
137 -define(old_aggr_stats(Id, Stats), {Id, Stats}).
138
139
140 -define(stats_per_table(Table),
141 case Table of
142 connection_stats_coarse_conn_stats ->
143 [recv_oct, send_oct, reductions];
144 vhost_stats_coarse_conn_stats ->
145 [recv_oct, send_oct];
146 T when T =:= channel_stats_fine_stats;
147 T =:= channel_exchange_stats_fine_stats;
148 T =:= vhost_stats_fine_stats ->
149 [publish, confirm, return_unroutable];
150 T when T =:= channel_queue_stats_deliver_stats;
151 T =:= queue_stats_deliver_stats;
152 T =:= vhost_stats_deliver_stats;
153 T =:= channel_stats_deliver_stats ->
154 [get, get_no_ack, deliver, deliver_no_ack, redeliver, ack, deliver_get];
155 T when T =:= channel_process_stats;
156 T =:= queue_process_stats ->
157 [reductions];
158 T when T =:= queue_stats_publish;
159 T =:= queue_exchange_stats_publish ->
160 [publish];
161 exchange_stats_publish_out ->
162 [publish_out];
163 exchange_stats_publish_in ->
164 [publish_in];
165 T when T =:= queue_msg_stats;
166 T =:= vhost_msg_stats ->
167 [messages_ready, messages_unacknowledged, messages];
168 node_coarse_stats ->
169 [fd_used, sockets_used, mem_used, disk_free, proc_used, gc_num,
170 gc_bytes_reclaimed, context_switches];
171 node_persister_stats ->
172 [io_read_count, io_read_bytes, io_read_avg_time, io_write_count,
173 io_write_bytes, io_write_avg_time, io_sync_count, io_sync_avg_time,
174 io_seek_count, io_seek_avg_time, io_reopen_count, mnesia_ram_tx_count,
175 mnesia_disk_tx_count, msg_store_read_count, msg_store_write_count,
176 queue_index_journal_write_count, queue_index_write_count,
177 queue_index_read_count, io_file_handle_open_attempt_count,
178 io_file_handle_open_attempt_avg_time];
179 node_node_coarse_stats ->
180 [send_bytes, recv_bytes];
181 T when T =:= queue_msg_rates;
182 T =:= vhost_msg_rates ->
183 [disk_reads, disk_writes]
184 end).
185 %%------------------------------------------------------------------------------
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License at
3 %% http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
7 %% License for the specific language governing rights and limitations
8 %% under the License.
9 %%
10 %% The Original Code is RabbitMQ Management Console.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -record(context, {user,
17 password = none,
18 impl}). % storage for a context of the resource handler
19
20 -record(range, {first :: integer(),
21 last :: integer(),
22 incr :: integer()}).
23
24
22 # before the inclusion of erlang.mk leading to the wrong target becoming
33 # the default.
44 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
526 endif
627
728 # --------------------------------------------------------------------
2041 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
2142 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
2243 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
2345 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
2446 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
2547 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
2648 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
2749 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
2851 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
2952 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
3054 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
3155 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
3256 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
3559 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
3660 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
3761 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
3863 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
3964 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
4065 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
7095
7196 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
7297
73 # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
74 # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
75 # needs to add "ranch" as a BUILD_DEPS. The list of projects needing
76 # this workaround are:
77 # o rabbitmq-web-stomp
78 dep_ranch = git https://github.com/ninenines/ranch 1.2.1
98 # Third-party dependencies version pinning.
99 #
100 # We do that in this file, which is copied in all projects, to ensure
101 # all projects use the same versions. It avoids conflicts and makes it
102 # possible to work with rabbitmq-public-umbrella.
103
104 dep_cowboy_commit = 1.0.4
105 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
106 dep_ranch_commit = 1.3.1
107 dep_webmachine_commit = 1.10.8p2
79108
80109 RABBITMQ_COMPONENTS = amqp_client \
81110 rabbit \
82111 rabbit_common \
83112 rabbitmq_amqp1_0 \
84113 rabbitmq_auth_backend_amqp \
114 rabbitmq_auth_backend_cache \
85115 rabbitmq_auth_backend_http \
86116 rabbitmq_auth_backend_ldap \
87117 rabbitmq_auth_mechanism_ssl \
88118 rabbitmq_boot_steps_visualiser \
89119 rabbitmq_clusterer \
120 rabbitmq_cli \
90121 rabbitmq_codegen \
91122 rabbitmq_consistent_hash_exchange \
123 rabbitmq_ct_client_helpers \
92124 rabbitmq_ct_helpers \
93125 rabbitmq_delayed_message_exchange \
94126 rabbitmq_dotnet_client \
97129 rabbitmq_federation_management \
98130 rabbitmq_java_client \
99131 rabbitmq_jms_client \
132 rabbitmq_jms_cts \
100133 rabbitmq_jms_topic_exchange \
101134 rabbitmq_lvc \
102135 rabbitmq_management \
0 %% This file is a copy of exometer_slide.erl from https://github.com/Feuerlabs/exometer_core,
1 %% with the following modifications:
2 %%
3 %% 1) The elements are tuples of numbers
4 %%
5 %% 2) Only one element for each expected interval point is added, intermediate values
6 %% are discarded. Thus, if we have a window of 60s and interval of 5s, at max 12 elements
7 %% are stored.
8 %%
9 %% 3) Additions can be provided as increments to the last value stored
10 %%
11 %% 4) sum/1 implements the sum of several slides, generating a new timestamp sequence based
12 %% on the given intervals. Elements on each window are added to the closest interval point.
13 %%
14 %% Original commit: https://github.com/Feuerlabs/exometer_core/commit/2759edc804211b5245867b32c9a20c8fe1d93441
15 %%
16 %% -------------------------------------------------------------------
17 %%
18 %% Copyright (c) 2014 Basho Technologies, Inc. All Rights Reserved.
19 %%
20 %% This Source Code Form is subject to the terms of the Mozilla Public
21 %% License, v. 2.0. If a copy of the MPL was not distributed with this
22 %% file, You can obtain one at http://mozilla.org/MPL/2.0/.
23 %%
24 %% -------------------------------------------------------------------
25 %%
26 %% @author Tony Rogvall <tony@rogvall.se>
27 %% @author Ulf Wiger <ulf@feuerlabs.com>
28 %% @author Magnus Feuer <magnus@feuerlabs.com>
29 %%
30 %% @doc Efficient sliding-window buffer
31 %%
32 %% Initial implementation: 29 Sep 2009 by Tony Rogvall
33 %%
34 %% This module implements an efficient sliding window, maintaining
35 %% two lists - a primary and a secondary. Values are paired with a
36 %% timestamp (millisecond resolution, see `timestamp/0')
37 %% and prepended to the primary list. When the time span between the oldest
38 %% and the newest entry in the primary list exceeds the given window size,
39 %% the primary list is shifted into the secondary list position, and the
40 %% new entry is added to a new (empty) primary list.
41 %%
42 %% The window can be converted to a list using `to_list/1'.
43 %% @end
44 %%
45 %%
46 %% All modifications are (C) 2007-2016 Pivotal Software, Inc. All rights reserved.
47 %% The Initial Developer of the Original Code is Basho Technologies, Inc.
48
49 -module(exometer_slide).
50
51 -export([new/2, new/3,
52 reset/1,
53 add_element/3,
54 to_list/2,
55 to_list/3,
56 foldl/5,
57 to_normalized_list/5]).
58
59 -export([timestamp/0,
60 last_two/1,
61 last/1]).
62
63 -export([sum/1,
64 sum/2,
65 sum/5,
66 optimize/1]).
67
68 %% For testing
69 -export([buffer/1]).
70
71 -compile(inline).
72 -compile(inline_list_funcs).
73
74
75 -type value() :: tuple().
76 -type internal_value() :: tuple() | drop.
77 -type timestamp() :: non_neg_integer().
78
79 -type fold_acc() :: any().
80 -type fold_fun() :: fun(({timestamp(), internal_value()}, fold_acc()) -> fold_acc()).
81
82 %% Fixed size event buffer
83 -record(slide, {size = 0 :: integer(), % ms window
84 n = 0 :: integer(), % number of elements in buf1
85 max_n :: infinity | integer(), % max no of elements
86 incremental = false :: boolean(),
87 interval :: integer(),
88 last = 0 :: integer(), % millisecond timestamp
89 first = undefined :: undefined | integer(), % millisecond timestamp
90 buf1 = [] :: [internal_value()],
91 buf2 = [] :: [internal_value()],
92 total :: undefined | value()}).
93
94 -opaque slide() :: #slide{}.
95
96 -export_type([slide/0, timestamp/0]).
97
98 -spec timestamp() -> timestamp().
99 %% @doc Generate a millisecond-resolution timestamp.
100 %%
101 %% This timestamp format is used e.g. by the `exometer_slide' and
102 %% `exometer_histogram' implementations.
103 %% @end
104 timestamp() ->
105 time_compat:os_system_time(milli_seconds).
106
107 -spec new(_Size::integer(), _Options::list()) -> slide().
108 %% @doc Create a new sliding-window buffer.
109 %%
110 %% `Size' determines the size in milliseconds of the sliding window.
111 %% The implementation prepends values into a primary list until the oldest
112 %% element in the list is `Size' ms older than the current value. It then
113 %% swaps the primary list into a secondary list, and starts prepending to
114 %% a new primary list. This means that more data than fits inside the window
115 %% will be kept - upwards of twice as much. On the other hand, updating the
116 %% buffer is very cheap.
117 %% @end
118 new(Size, Opts) -> new(timestamp(), Size, Opts).
119
120 -spec new(Timestamp :: timestamp(), Size::integer(), Options::list()) -> slide().
121 new(TS, Size, Opts) ->
122 #slide{size = Size,
123 max_n = proplists:get_value(max_n, Opts, infinity),
124 interval = proplists:get_value(interval, Opts, infinity),
125 last = TS,
126 first = undefined,
127 incremental = proplists:get_value(incremental, Opts, false),
128 buf1 = [],
129 buf2 = []}.
130
131 -spec reset(slide()) -> slide().
132
133 %% @doc Empty the buffer
134 %%
135 reset(Slide) ->
136 Slide#slide{n = 0, buf1 = [], buf2 = [], last = 0, first = undefined}.
137
138 %% @doc Add an element to the buffer, tagged with the given timestamp.
139 %%
140 %% Apart from the specified timestamp, this function works just like
141 %% {@link add_element/2}.
142 %% @end
143 -spec add_element(timestamp(), value(), slide()) -> slide().
144 add_element(_TS, _Evt, Slide) when Slide#slide.size == 0 ->
145 Slide;
146 add_element(TS, Evt, #slide{last = Last, interval = Interval, total = Total0,
147 incremental = true} = Slide)
148 when (TS - Last) < Interval ->
149 Total = add_to_total(Evt, Total0),
150 Slide#slide{total = Total};
151 add_element(TS, Evt, #slide{last = Last, interval = Interval} = Slide)
152 when (TS - Last) < Interval ->
153 Slide#slide{total = Evt};
154 add_element(TS, Evt, #slide{last = Last, size = Sz, incremental = true,
155 n = N, max_n = MaxN, total = Total0,
156 buf1 = Buf1} = Slide) ->
157 N1 = N+1,
158 Total = add_to_total(Evt, Total0),
159 %% Total could be the same as the last sample, by adding and substracting
160 %% the same amout to the totals. That is not strictly a drop, but should
161 %% generate new samples.
162 %% I.e. 0, 0, -14, 14 (total = 0, samples = 14, -14, 0, drop)
163 case {is_zeros(Evt), Buf1} of
164 {_, []} ->
165 Slide#slide{n = N1, first = TS, buf1 = [{TS, Total} | Buf1],
166 last = TS, total = Total};
167 _ when TS - Last > Sz; N1 > MaxN ->
168 %% swap
169 Slide#slide{last = TS, n = 1, buf1 = [{TS, Total}],
170 buf2 = Buf1, total = Total};
171 {true, [{_, Total}, {_, drop} = Drop | Tail]} ->
172 %% Memory optimisation
173 Slide#slide{buf1 = [{TS, Total}, Drop | Tail],
174 n = N1, last = TS};
175 {true, [{DropTS, Total} | Tail]} ->
176 %% Memory optimisation
177 Slide#slide{buf1 = [{TS, Total}, {DropTS, drop} | Tail],
178 n = N1, last = TS};
179 _ ->
180 Slide#slide{n = N1, buf1 = [{TS, Total} | Buf1],
181 last = TS, total = Total}
182 end;
183 add_element(TS, Evt, #slide{last = Last, size = Sz, n = N, max_n = MaxN,
184 buf1 = Buf1} = Slide)
185 when TS - Last > Sz; N + 1 > MaxN ->
186 Slide#slide{last = TS, n = 1, buf1 = [{TS, Evt}],
187 buf2 = Buf1, total = Evt};
188 add_element(TS, Evt, #slide{buf1 = [{_, Evt}, {_, drop} = Drop | Tail],
189 n = N} = Slide) ->
190 %% Memory optimisation
191 Slide#slide{buf1 = [{TS, Evt}, Drop | Tail], n = N + 1, last = TS};
192 add_element(TS, Evt, #slide{buf1 = [{DropTS, Evt} | Tail], n = N} = Slide) ->
193 %% Memory optimisation
194 Slide#slide{buf1 = [{TS, Evt}, {DropTS, drop} | Tail],
195 n = N + 1, last = TS};
196 add_element(TS, Evt, #slide{n = N, buf1 = Buf1} = Slide) ->
197 N1 = N+1,
198 case Buf1 of
199 [] ->
200 Slide#slide{n = N1, buf1 = [{TS, Evt} | Buf1],
201 last = TS, first = TS, total = Evt};
202 _ ->
203 Slide#slide{n = N1, buf1 = [{TS, Evt} | Buf1],
204 last = TS, total = Evt}
205 end.
206
207 add_to_total(Evt, undefined) ->
208 Evt;
209 add_to_total({A0}, {B0}) ->
210 {B0 + A0};
211 add_to_total({A0, A1}, {B0, B1}) ->
212 {B0 + A0, B1 + A1};
213 add_to_total({A0, A1, A2}, {B0, B1, B2}) ->
214 {B0 + A0, B1 + A1, B2 + A2};
215 add_to_total({A0, A1, A2, A3, A4, A5, A6}, {B0, B1, B2, B3, B4, B5, B6}) ->
216 {B0 + A0, B1 + A1, B2 + A2, B3 + A3, B4 + A4, B5 + A5, B6 + A6};
217 add_to_total({A0, A1, A2, A3, A4, A5, A6, A7}, {B0, B1, B2, B3, B4, B5, B6, B7}) ->
218 {B0 + A0, B1 + A1, B2 + A2, B3 + A3, B4 + A4, B5 + A5, B6 + A6, B7 + A7};
219 add_to_total({A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14,
220 A15, A16, A17, A18, A19},
221 {B0, B1, B2, B3, B4, B5, B6, B7, B8, B9, B10, B11, B12, B13, B14,
222 B15, B16, B17, B18, B19}) ->
223 {B0 + A0, B1 + A1, B2 + A2, B3 + A3, B4 + A4, B5 + A5, B6 + A6, B7 + A7, B8 + A8,
224 B9 + A9, B10 + A10, B11 + A11, B12 + A12, B13 + A13, B14 + A14, B15 + A15, B16 + A16,
225 B17 + A17, B18 + A18, B19 + A19}.
226
227 is_zeros({0}) ->
228 true;
229 is_zeros({0, 0}) ->
230 true;
231 is_zeros({0, 0, 0}) ->
232 true;
233 is_zeros({0, 0, 0, 0, 0, 0, 0}) ->
234 true;
235 is_zeros({0, 0, 0, 0, 0, 0, 0, 0}) ->
236 true;
237 is_zeros({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) ->
238 true;
239 is_zeros(_) ->
240 false.
241
242 -spec optimize(slide()) -> slide().
243 optimize(#slide{buf2 = []} = Slide) ->
244 Slide;
245 optimize(#slide{buf1 = Buf1, buf2 = Buf2, max_n = MaxN, n = N} = Slide)
246 when is_integer(MaxN) andalso length(Buf1) < MaxN ->
247 Slide#slide{buf1 = Buf1,
248 buf2 = lists:sublist(Buf2, n_diff(MaxN, N) + 1)};
249 optimize(Slide) -> Slide.
250
251 snd(T) when is_tuple(T) ->
252 element(2, T).
253
254
255 -spec to_list(timestamp(), slide()) -> [{timestamp(), value()}].
256 %% @doc Convert the sliding window into a list of timestamped values.
257 %% @end
258 to_list(_Now, #slide{size = Sz}) when Sz == 0 ->
259 [];
260 to_list(Now, #slide{size = Sz} = Slide) ->
261 snd(to_list_from(Now, Now - Sz, Slide)).
262
263 to_list(Now, Start, Slide) ->
264 snd(to_list_from(Now, Start, Slide)).
265
266 to_list_from(Now, Start0, #slide{max_n = MaxN, buf2 = Buf2, first = FirstTS,
267 interval = Interval} = Slide) ->
268
269 {NewN, Buf1} = maybe_add_last_sample(Now, Slide),
270 Start = first_max(FirstTS, Start0),
271 {Prev0, Buf1_1} = take_since(Buf1, Now, Start, first_max(MaxN, NewN), [], Interval),
272 case take_since(Buf2, Now, Start, first_max(MaxN, NewN), Buf1_1, Interval) of
273 {undefined, Buf1_1} ->
274 {Prev0, Buf1_1};
275 {_Prev, Buf1_1} = Res ->
276 case Prev0 of
277 undefined ->
278 Res;
279 _ ->
280 %% If take_since returns the same buffer, that means we don't
281 %% need Buf2 at all. We might be returning a too old sample
282 %% in previous, so we must use the one from Buf1
283 {Prev0, Buf1_1}
284 end;
285 Res ->
286 Res
287 end.
288
289 first_max(F, X) when is_integer(F) -> max(F, X);
290 first_max(_, X) -> X.
291
292 -spec last_two(slide()) -> [{timestamp(), value()}].
293 %% @doc Returns the newest 2 elements on the sample
294 last_two(#slide{buf1 = [{TS, Evt} = H1, {_, drop} | _], interval = Interval}) ->
295 [H1, {TS - Interval, Evt}];
296 last_two(#slide{buf1 = [H1, H2_0 | _], interval = Interval}) ->
297 H2 = adjust_timestamp(H1, H2_0, Interval),
298 [H1, H2];
299 last_two(#slide{buf1 = [H1], buf2 = [H2_0 | _],
300 interval = Interval}) ->
301 H2 = adjust_timestamp(H1, H2_0, Interval),
302 [H1, H2];
303 last_two(#slide{buf1 = [H1], buf2 = []}) ->
304 [H1];
305 last_two(_) ->
306 [].
307
308 adjust_timestamp({TS1, _}, {TS2, V2}, Interval) ->
309 case TS1 - TS2 > Interval of
310 true -> {TS1 - Interval, V2};
311 false -> {TS2, V2}
312 end.
313
314 -spec last(slide()) -> value() | undefined.
315 last(#slide{total = T}) when T =/= undefined ->
316 T;
317 last(#slide{buf1 = [{_TS, T} | _]}) ->
318 T;
319 last(#slide{buf2 = [{_TS, T} | _]}) ->
320 T;
321 last(_) ->
322 undefined.
323
324 -spec foldl(timestamp(), timestamp(), fold_fun(), fold_acc(), slide()) -> fold_acc().
325 %% @doc Fold over the sliding window, starting from `Timestamp'.
326 %% Now provides a reference point to evaluate whether to include
327 %% partial, unrealised sample values in the sequence. Unrealised values will be
328 %% appended to the sequence when Now >= LastTS + Interval
329 %%
330 %% The fun should as `fun({Timestamp, Value}, Acc) -> NewAcc'.
331 %% The values are processed in order from oldest to newest.
332 %% @end
333 foldl(_Now, _Timestamp, _Fun, _Acc, #slide{size = Sz}) when Sz == 0 ->
334 [];
335 foldl(Now, Start0, Fun, Acc, #slide{max_n = _MaxN, buf2 = _Buf2,
336 interval = _Interval} = Slide) ->
337 lists:foldl(Fun, Acc, element(2, to_list_from(Now, Start0, Slide)) ++ [last]).
338
339 maybe_add_last_sample(_Now, #slide{total = T, n = N,
340 buf1 = [{_, T} | _] = Buf1}) ->
341 {N, Buf1};
342 maybe_add_last_sample(Now, #slide{total = T,
343 n = N,
344 last = Last,
345 interval = I,
346 buf1 = Buf1})
347 when T =/= undefined andalso Now >= Last + I ->
348 {N + 1, [{Last + I, T} | Buf1]};
349 maybe_add_last_sample(_Now, #slide{buf1 = Buf1, n = N}) ->
350 {N, Buf1}.
351
352
353 create_normalized_lookup(Start, Interval, RoundFun, Samples) ->
354 lists:foldl(fun({TS, Value}, Dict) when TS - Start >= 0 ->
355 NewTS = map_timestamp(TS, Start, Interval, RoundFun),
356 orddict:update(NewTS, fun({T, V}) when T > TS ->
357 {T, V};
358 (_) -> {TS, Value}
359 end, {TS, Value}, Dict);
360 (_, Dict) -> Dict end, orddict:new(),
361 Samples).
362
363 -spec to_normalized_list(timestamp(), timestamp(), integer(), slide(),
364 no_pad | tuple()) -> [tuple()].
365 to_normalized_list(Now, Start, Interval, Slide, Empty) ->
366 to_normalized_list(Now, Start, Interval, Slide, Empty, fun ceil/1).
367
368 to_normalized_list(Now, Start, Interval, #slide{first = FirstTS0,
369 total = Total} = Slide,
370 Empty, RoundFun) ->
371
372 RoundTSFun = fun (TS) -> map_timestamp(TS, Start, Interval, RoundFun) end,
373
374 % add interval as we don't want to miss a sample due to rounding
375 {Prev, Samples} = to_list_from(Now + Interval, Start, Slide),
376 Lookup = create_normalized_lookup(Start, Interval, RoundFun, Samples),
377
378 NowRound = RoundTSFun(Now),
379
380 Pad = case Samples of
381 _ when Empty =:= no_pad ->
382 [];
383 [{TS, _} | _] when Prev =/= undefined, Start =< TS ->
384 [{T, snd(Prev)}
385 || T <- lists:seq(RoundTSFun(TS) - Interval, Start,
386 -Interval)];
387 [{TS, _} | _] when is_number(FirstTS0) andalso Start < FirstTS0 ->
388 % only if we know there is nothing in the past can we
389 % generate a 0 pad
390 [{T, Empty} || T <- lists:seq(RoundTSFun(TS) - Interval, Start,
391 -Interval)];
392 _ when FirstTS0 =:= undefined andalso Total =:= undefined ->
393 [{T, Empty} || T <- lists:seq(NowRound, Start, -Interval)];
394 [] -> % samples have been seen, use the total to pad
395 [{T, Total} || T <- lists:seq(NowRound, Start, -Interval)];
396 _ -> []
397 end,
398
399 {_, Res1} = lists:foldl(
400 fun(T, {Last, Acc}) ->
401 case orddict:find(T, Lookup) of
402 {ok, {_, V}} ->
403 {V, [{T, V} | Acc]};
404 error when Last =:= undefined ->
405 {Last, Acc};
406 error -> % this pads the last value into the future
407 {Last, [{T, Last} | Acc]}
408 end
409 end, {undefined, []}, lists:seq(Start, NowRound, Interval)),
410 Res1 ++ Pad.
411
412
413 %% @doc Sums a list of slides
414 %%
415 %% Takes the last known timestamp and creates an template version of the
416 %% sliding window. Timestamps are then truncated and summed with the value
417 %% in the template slide.
418 %% @end
419 -spec sum([slide()]) -> slide().
420 sum(Slides) -> sum(Slides, no_pad).
421
422 sum([#slide{size = Size, interval = Interval} | _] = Slides, Pad) ->
423 % take the freshest timestamp as reference point for summing operation
424 Now = lists:max([Last || #slide{last = Last} <- Slides]),
425 Start = Now - Size,
426 sum(Now, Start, Interval, Slides, Pad).
427
428
429 sum(Now, Start, Interval, [Slide | _ ] = All, Pad) ->
430 Fun = fun({TS, Value}, Dict) ->
431 orddict:update(TS, fun(V) -> add_to_total(V, Value) end,
432 Value, Dict)
433 end,
434 {Total, Dict} =
435 lists:foldl(fun(#slide{total = T} = S, {Tot, Acc}) ->
436 Samples = to_normalized_list(Now, Start, Interval, S,
437 Pad, fun ceil/1),
438 Total = add_to_total(T, Tot),
439 Folded = lists:foldl(Fun, Acc, Samples),
440 {Total, Folded}
441 end, {undefined, orddict:new()}, All),
442
443 {First, Buffer} = case orddict:to_list(Dict) of
444 [] ->
445 F = case [TS || #slide{first = TS} <- All,
446 is_integer(TS)] of
447 [] -> undefined;
448 FS -> lists:min(FS)
449 end,
450 {F, []};
451 [{F, _} | _ ] = B ->
452 {F, lists:reverse(B)}
453 end,
454 Slide#slide{buf1 = Buffer, buf2 = [], total = Total, n = length(Buffer),
455 first = First, last = Now}.
456
457
458 truncated_seq(_First, _Last, _Incr, 0) ->
459 [];
460 truncated_seq(TS, TS, _Incr, MaxN) when MaxN > 0 ->
461 [TS];
462 truncated_seq(First, Last, Incr, MaxN) when First =< Last andalso MaxN > 0 ->
463 End = min(Last, First + (MaxN * Incr) - Incr),
464 lists:seq(First, End, Incr);
465 truncated_seq(First, Last, Incr, MaxN) ->
466 End = max(Last, First + (MaxN * Incr) - Incr),
467 lists:seq(First, End, Incr).
468
469
470 take_since([{DropTS, drop} | T], Now, Start, N, [{TS, Evt} | _] = Acc,
471 Interval) ->
472 case T of
473 [] ->
474 Fill = [{TS0, Evt} || TS0 <- truncated_seq(TS - Interval,
475 max(DropTS, Start),
476 -Interval, N)],
477 {undefined, lists:reverse(Fill) ++ Acc};
478 [{TS0, _} = E | Rest] when TS0 >= Start, N > 0 ->
479 Fill = [{TS1, Evt} || TS1 <- truncated_seq(TS0 + Interval,
480 max(TS0 + Interval, TS - Interval),
481 Interval, N)],
482 take_since(Rest, Now, Start, decr(N), [E | Fill ++ Acc], Interval);
483 [Prev | _] -> % next sample is out of range so needs to be filled from Start
484 Fill = [{TS1, Evt} || TS1 <- truncated_seq(Start, max(Start, TS - Interval),
485 Interval, N)],
486 {Prev, Fill ++ Acc}
487 end;
488 take_since([{TS, V} = H | T], Now, Start, N, Acc, Interval) when TS >= Start,
489 N > 0,
490 TS =< Now,
491 is_tuple(V) ->
492 take_since(T, Now, Start, decr(N), [H|Acc], Interval);
493 take_since([{TS,_} | T], Now, Start, N, Acc, Interval) when TS >= Start, N > 0 ->
494 take_since(T, Now, Start, decr(N), Acc, Interval);
495 take_since([Prev | _], _, _, _, Acc, _) ->
496 {Prev, Acc};
497 take_since(_, _, _, _, Acc, _) ->
498 %% Don't reverse; already the wanted order.
499 {undefined, Acc}.
500
501 decr(N) when is_integer(N) ->
502 N-1;
503 decr(N) -> N.
504
505 n_diff(A, B) when is_integer(A) ->
506 A - B.
507
508 ceil(X) when X < 0 ->
509 trunc(X);
510 ceil(X) ->
511 T = trunc(X),
512 case X - T == 0 of
513 true -> T;
514 false -> T + 1
515 end.
516
517 map_timestamp(TS, Start, Interval, Round) ->
518 Factor = Round((TS - Start) / Interval),
519 Start + Interval * Factor.
520
521 buffer(#slide{buf1 = Buf1, buf2 = Buf2}) ->
522 Buf1 ++ Buf2.
1010 %% The Original Code is RabbitMQ Management Console.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_agent_app).
1919 -export([start/2, stop/1]).
2020
2121 start(_Type, _StartArgs) ->
22 rabbit_mgmt_agent_sup:start_link().
22 rabbit_mgmt_agent_sup_sup:start_link().
2323
2424 stop(_State) ->
2525 ok.
0 -module(rabbit_mgmt_agent_config).
1
2 -export([get_env/1, get_env/2]).
3
4 %% some people have reasons to only run with the agent enabled:
5 %% make it possible for them to configure key management app
6 %% settings such as rates_mode.
7 get_env(Key) ->
8 rabbit_misc:get_env(rabbitmq_management, Key,
9 rabbit_misc:get_env(rabbitmq_management_agent, Key,
10 undefined)).
11
12 get_env(Key, Default) ->
13 rabbit_misc:get_env(rabbitmq_management, Key,
14 rabbit_misc:get_env(rabbitmq_management_agent, Key,
15 Default)).
1010 %% The Original Code is RabbitMQ Management Console.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_agent_sup).
1717
1818 -behaviour(supervisor).
1919
20 -include_lib("rabbit_common/include/rabbit.hrl").
21 -include_lib("rabbit_common/include/rabbit_core_metrics.hrl").
22 -include("rabbit_mgmt_metrics.hrl").
23
2024 -export([init/1]).
2125 -export([start_link/0]).
2226
2327 init([]) ->
28 pg2:create(management_db),
29 ok = pg2:join(management_db, self()),
30 ST = {rabbit_mgmt_storage, {rabbit_mgmt_storage, start_link, []},
31 permanent, ?WORKER_WAIT, worker, [rabbit_mgmt_storage]},
32 MD = {delegate_management_sup, {delegate_sup, start_link, [5, ?DELEGATE_PREFIX]},
33 permanent, ?SUPERVISOR_WAIT, supervisor, [delegate_sup]},
34 MC = [{rabbit_mgmt_metrics_collector:name(Table),
35 {rabbit_mgmt_metrics_collector, start_link, [Table]},
36 permanent, ?WORKER_WAIT, worker, [rabbit_mgmt_metrics_collector]}
37 || {Table, _} <- ?CORE_TABLES],
38 MGC = [{rabbit_mgmt_metrics_gc:name(Event),
39 {rabbit_mgmt_metrics_gc, start_link, [Event]},
40 permanent, ?WORKER_WAIT, worker, [rabbit_mgmt_metrics_gc]}
41 || Event <- ?GC_EVENTS],
2442 ExternalStats = {rabbit_mgmt_external_stats,
2543 {rabbit_mgmt_external_stats, start_link, []},
2644 permanent, 5000, worker, [rabbit_mgmt_external_stats]},
27 {ok, {{one_for_one, 10, 10}, [ExternalStats]}}.
45 GC = {rabbit_mgmt_gc, {rabbit_mgmt_gc, start_link, []},
46 permanent, ?WORKER_WAIT, worker, [rabbit_mgmt_gc]},
47 {ok, {{one_for_one, 100, 10}, [ST, MD, ExternalStats, GC | MC ++ MGC]}}.
2848
2949 start_link() ->
3050 supervisor:start_link({local, ?MODULE}, ?MODULE, []).
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License at
3 %% http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
7 %% License for the specific language governing rights and limitations
8 %% under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
13 %%
14
15 -module(rabbit_mgmt_agent_sup_sup).
16
17 -behaviour(supervisor2).
18
19 -export([init/1]).
20 -export([start_link/0, start_child/0]).
21
22 -include_lib("rabbit_common/include/rabbit.hrl").
23
24 start_child() ->
25 supervisor2:start_child(?MODULE, sup()).
26
27 sup() ->
28 {rabbit_mgmt_agent_sup, {rabbit_mgmt_agent_sup, start_link, []},
29 temporary, ?SUPERVISOR_WAIT, supervisor, [rabbit_mgmt_agent_sup]}.
30
31 init([]) ->
32 {ok, {{one_for_one, 0, 1}, [sup()]}}.
33
34 start_link() ->
35 supervisor2:start_link({local, ?MODULE}, ?MODULE, []).
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License at
3 %% http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
7 %% License for the specific language governing rights and limitations
8 %% under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2016 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_mgmt_data).
17
18 -include("rabbit_mgmt_records.hrl").
19 -include("rabbit_mgmt_metrics.hrl").
20 -include_lib("rabbit_common/include/rabbit.hrl").
21 -include_lib("rabbit_common/include/rabbit_core_metrics.hrl").
22
23 -export([empty/2, pick_range/2]).
24
25 % delegate api
26 -export([overview_data/4,
27 consumer_data/2,
28 all_list_queue_data/3,
29 all_detail_queue_data/3,
30 all_exchange_data/3,
31 all_connection_data/3,
32 all_list_channel_data/3,
33 all_detail_channel_data/3,
34 all_vhost_data/3,
35 all_node_data/3,
36 augmented_created_stats/2,
37 augmented_created_stats/3,
38 augment_channel_pids/2,
39 augment_details/2,
40 lookup_element/2,
41 lookup_element/3
42 ]).
43
44
45 -import(rabbit_misc, [pget/2]).
46
47 -type maybe_slide() :: exometer_slide:slide() | not_found.
48 -type ranges() :: {maybe_range(), maybe_range(), maybe_range(), maybe_range()}.
49 -type maybe_range() :: no_range | #range{}.
50
51 %%----------------------------------------------------------------------------
52 %% Internal, query-time - node-local operations
53 %%----------------------------------------------------------------------------
54
55 created_stats(Name, Type) ->
56 case ets:select(Type, [{{'_', '$2', '$3'}, [{'==', Name, '$2'}], ['$3']}]) of
57 [] -> not_found;
58 [Elem] -> Elem
59 end.
60
61 created_stats(Type) ->
62 %% TODO better tab2list?
63 ets:select(Type, [{{'_', '_', '$3'}, [], ['$3']}]).
64
65 -spec all_detail_queue_data(pid(), [any()], ranges()) -> dict:dict(atom(), any()).
66 all_detail_queue_data(_Pid, Ids, Ranges) ->
67 lists:foldl(fun (Id, Acc) ->
68 Data = detail_queue_data(Ranges, Id),
69 dict:store(Id, Data, Acc)
70 end, dict:new(), Ids).
71
72 all_list_queue_data(_Pid, Ids, Ranges) ->
73 lists:foldl(fun (Id, Acc) ->
74 Data = list_queue_data(Ranges, Id),
75 dict:store(Id, Data, Acc)
76 end, dict:new(), Ids).
77
78 all_detail_channel_data(_Pid, Ids, Ranges) ->
79 lists:foldl(fun (Id, Acc) ->
80 Data = detail_channel_data(Ranges, Id),
81 dict:store(Id, Data, Acc)
82 end, dict:new(), Ids).
83
84 all_list_channel_data(_Pid, Ids, Ranges) ->
85 lists:foldl(fun (Id, Acc) ->
86 Data = list_channel_data(Ranges, Id),
87 dict:store(Id, Data, Acc)
88 end, dict:new(), Ids).
89
90 connection_data(Ranges, Id) ->
91 dict:from_list([raw_message_data(connection_stats_coarse_conn_stats,
92 pick_range(coarse_conn_stats, Ranges), Id),
93 {connection_stats, lookup_element(connection_stats, Id)}]).
94
95 exchange_data(Ranges, Id) ->
96 dict:from_list(
97 exchange_raw_detail_stats_data(Ranges, Id) ++
98 [raw_message_data(exchange_stats_publish_out,
99 pick_range(fine_stats, Ranges), Id),
100 raw_message_data(exchange_stats_publish_in,
101 pick_range(fine_stats, Ranges), Id)]).
102
103 vhost_data(Ranges, Id) ->
104 dict:from_list([raw_message_data(vhost_stats_coarse_conn_stats,
105 pick_range(coarse_conn_stats, Ranges), Id),
106 raw_message_data(vhost_msg_stats,
107 pick_range(queue_msg_rates, Ranges), Id),
108 raw_message_data(vhost_stats_fine_stats,
109 pick_range(fine_stats, Ranges), Id),
110 raw_message_data(vhost_stats_deliver_stats,
111 pick_range(deliver_get, Ranges), Id)]).
112
113 node_data(Ranges, Id) ->
114 dict:from_list(
115 [{mgmt_stats, mgmt_qeue_length_stats()}] ++
116 node_raw_detail_stats_data(Ranges, Id) ++
117 [raw_message_data(node_coarse_stats,
118 pick_range(coarse_node_stats, Ranges), Id),
119 raw_message_data(node_persister_stats,
120 pick_range(coarse_node_stats, Ranges), Id),
121 {node_stats, lookup_element(node_stats, Id)}]).
122
123 overview_data(_Pid, User, Ranges, VHosts) ->
124 Raw = [raw_all_message_data(vhost_msg_stats, pick_range(queue_msg_counts, Ranges), VHosts),
125 raw_all_message_data(vhost_stats_fine_stats, pick_range(fine_stats, Ranges), VHosts),
126 raw_all_message_data(vhost_msg_rates, pick_range(queue_msg_rates, Ranges), VHosts),
127 raw_all_message_data(vhost_stats_deliver_stats, pick_range(deliver_get, Ranges), VHosts)],
128
129 dict:from_list(Raw ++
130 [{connections_count, count_created_stats(connection_created_stats, User)},
131 {channels_count, count_created_stats(channel_created_stats, User)},
132 {consumers_count, ets:info(consumer_stats, size)}]).
133
134 consumer_data(_Pid, VHost) ->
135 dict:from_list(
136 [{C, augment_msg_stats(augment_consumer(C))}
137 || C <- consumers_by_vhost(VHost)]).
138
139 all_connection_data(_Pid, Ids, Ranges) ->
140 dict:from_list([{Id, connection_data(Ranges, Id)} || Id <- Ids]).
141
142 all_exchange_data(_Pid, Ids, Ranges) ->
143 dict:from_list([{Id, exchange_data(Ranges, Id)} || Id <- Ids]).
144
145 all_vhost_data(_Pid, Ids, Ranges) ->
146 dict:from_list([{Id, vhost_data(Ranges, Id)} || Id <- Ids]).
147
148 all_node_data(_Pid, Ids, Ranges) ->
149 dict:from_list([{Id, node_data(Ranges, Id)} || Id <- Ids]).
150
151 channel_raw_message_data(Ranges, Id) ->
152 [raw_message_data(channel_stats_fine_stats, pick_range(fine_stats, Ranges), Id),
153 raw_message_data(channel_stats_deliver_stats, pick_range(deliver_get, Ranges), Id),
154 raw_message_data(channel_process_stats, pick_range(process_stats, Ranges), Id)].
155
156 queue_raw_message_data(Ranges, Id) ->
157 [raw_message_data(queue_stats_publish, pick_range(fine_stats, Ranges), Id),
158 raw_message_data(queue_stats_deliver_stats, pick_range(deliver_get, Ranges), Id),
159 raw_message_data(queue_process_stats, pick_range(process_stats, Ranges), Id),
160 raw_message_data(queue_msg_stats, pick_range(queue_msg_counts, Ranges), Id)].
161
162 queue_raw_deliver_stats_data(Ranges, Id) ->
163 [raw_message_data2(channel_queue_stats_deliver_stats,
164 pick_range(deliver_get, Ranges), Key)
165 || Key <- get_table_keys(channel_queue_stats_deliver_stats, second(Id))] ++
166 [raw_message_data2(queue_exchange_stats_publish,
167 pick_range(fine_stats, Ranges), Key)
168 || Key <- get_table_keys(queue_exchange_stats_publish, first(Id))].
169
170 node_raw_detail_stats_data(Ranges, Id) ->
171 [raw_message_data2(node_node_coarse_stats,
172 pick_range(coarse_node_node_stats, Ranges), Key)
173 || Key <- get_table_keys(node_node_coarse_stats, first(Id))].
174
175 exchange_raw_detail_stats_data(Ranges, Id) ->
176 [raw_message_data2(channel_exchange_stats_fine_stats,
177 pick_range(fine_stats, Ranges), Key)
178 || Key <- get_table_keys(channel_exchange_stats_fine_stats, second(Id))] ++
179 [raw_message_data2(queue_exchange_stats_publish,
180 pick_range(fine_stats, Ranges), Key)
181 || Key <- get_table_keys(queue_exchange_stats_publish, second(Id))].
182
183 channel_raw_detail_stats_data(Ranges, Id) ->
184 [raw_message_data2(channel_exchange_stats_fine_stats,
185 pick_range(fine_stats, Ranges), Key)
186 || Key <- get_table_keys(channel_exchange_stats_fine_stats, first(Id))] ++
187 [raw_message_data2(channel_queue_stats_deliver_stats,
188 pick_range(fine_stats, Ranges), Key)
189 || Key <- get_table_keys(channel_queue_stats_deliver_stats, first(Id))].
190
191 raw_message_data2(Table, no_range, Id) ->
192 SmallSample = lookup_smaller_sample(Table, Id),
193 {{Table, Id}, {SmallSample, not_found}};
194 raw_message_data2(Table, Range, Id) ->
195 SmallSample = lookup_smaller_sample(Table, Id),
196 Samples = lookup_samples(Table, Id, Range),
197 {{Table, Id}, {SmallSample, Samples}}.
198
199 detail_queue_data(Ranges, Id) ->
200 dict:from_list(queue_raw_message_data(Ranges, Id) ++
201 queue_raw_deliver_stats_data(Ranges, Id) ++
202 [{queue_stats, lookup_element(queue_stats, Id)},
203 {consumer_stats, get_queue_consumer_stats(Id)}]).
204
205 list_queue_data(Ranges, Id) ->
206 dict:from_list(queue_raw_message_data(Ranges, Id) ++
207 queue_raw_deliver_stats_data(Ranges, Id) ++
208 [{queue_stats, lookup_element(queue_stats, Id)}]).
209
210 detail_channel_data(Ranges, Id) ->
211 dict:from_list(channel_raw_message_data(Ranges, Id) ++
212 channel_raw_detail_stats_data(Ranges, Id) ++
213 [{channel_stats, lookup_element(channel_stats, Id)},
214 {consumer_stats, get_consumer_stats(Id)}]).
215
216 list_channel_data(Ranges, Id) ->
217 dict:from_list(channel_raw_message_data(Ranges, Id) ++
218 channel_raw_detail_stats_data(Ranges, Id) ++
219 [{channel_stats, lookup_element(channel_stats, Id)}]).
220
221 -spec raw_message_data(atom(), maybe_range(), any()) ->
222 {atom(), {maybe_slide(), maybe_slide()}}.
223 raw_message_data(Table, no_range, Id) ->
224 SmallSample = lookup_smaller_sample(Table, Id),
225 {Table, {SmallSample, not_found}};
226 raw_message_data(Table, Range, Id) ->
227 SmallSample = lookup_smaller_sample(Table, Id),
228 Samples = lookup_samples(Table, Id, Range),
229 {Table, {SmallSample, Samples}}.
230
231 raw_all_message_data(Table, Range, VHosts) ->
232 SmallSample = lookup_all(Table, VHosts, select_smaller_sample(Table)),
233 RangeSample = case Range of
234 no_range -> not_found;
235 _ ->
236 lookup_all(Table, VHosts, select_range_sample(Table,
237 Range))
238 end,
239 {Table, {SmallSample, RangeSample}}.
240
241 get_queue_consumer_stats(Id) ->
242 Consumers = ets:select(consumer_stats, match_queue_consumer_spec(Id)),
243 [augment_consumer(C) || C <- Consumers].
244
245 get_consumer_stats(Id) ->
246 Consumers = ets:select(consumer_stats, match_consumer_spec(Id)),
247 [augment_consumer(C) || C <- Consumers].
248
249 count_created_stats(Type, all) ->
250 ets:info(Type, size);
251 count_created_stats(Type, User) ->
252 length(filter_user(created_stats(Type), User)).
253
254 augment_consumer({{Q, Ch, CTag}, Props}) ->
255 [{queue, format_resource(Q)},
256 {channel_details, augment_channel_pid(Ch)},
257 {channel_pid, Ch},
258 {consumer_tag, CTag} | Props].
259
260 consumers_by_vhost(VHost) ->
261 ets:select(consumer_stats,
262 [{{{#resource{virtual_host = '$1', _ = '_'}, '_', '_'}, '_'},
263 [{'orelse', {'==', 'all', VHost}, {'==', VHost, '$1'}}],
264 ['$_']}]).
265
266 augment_msg_stats(Props) ->
267 augment_details(Props, []) ++ Props.
268
269 augment_details([{_, none} | T], Acc) ->
270 augment_details(T, Acc);
271 augment_details([{_, unknown} | T], Acc) ->
272 augment_details(T, Acc);
273 augment_details([{connection, Value} | T], Acc) ->
274 augment_details(T, [{connection_details, augment_connection_pid(Value)} | Acc]);
275 augment_details([{channel, Value} | T], Acc) ->
276 augment_details(T, [{channel_details, augment_channel_pid(Value)} | Acc]);
277 augment_details([{owner_pid, Value} | T], Acc) ->
278 augment_details(T, [{owner_pid_details, augment_connection_pid(Value)} | Acc]);
279 augment_details([_ | T], Acc) ->
280 augment_details(T, Acc);
281 augment_details([], Acc) ->
282 Acc.
283
284 augment_channel_pids(_Pid, ChPids) ->
285 lists:map(fun (ChPid) -> augment_channel_pid(ChPid) end, ChPids).
286
287 augment_channel_pid(Pid) ->
288 Ch = lookup_element(channel_created_stats, Pid, 3),
289 Conn = lookup_element(connection_created_stats, pget(connection, Ch), 3),
290 case Conn of
291 [] -> %% If the connection has just been opened, we might not yet have the data
292 [];
293 _ ->
294 [{name, pget(name, Ch)},
295 {pid, pget(pid, Ch)},
296 {number, pget(number, Ch)},
297 {user, pget(user, Ch)},
298 {connection_name, pget(name, Conn)},
299 {peer_port, pget(peer_port, Conn)},
300 {peer_host, pget(peer_host, Conn)}]
301 end.
302
303 augment_connection_pid(Pid) ->
304 Conn = lookup_element(connection_created_stats, Pid, 3),
305 case Conn of
306 [] -> %% If the connection has just been opened, we might not yet have the data
307 [];
308 _ ->
309 [{name, pget(name, Conn)},
310 {peer_port, pget(peer_port, Conn)},
311 {peer_host, pget(peer_host, Conn)}]
312 end.
313
314 augmented_created_stats(_Pid, Key, Type) ->
315 case created_stats(Key, Type) of
316 not_found -> not_found;
317 S -> augment_msg_stats(S)
318 end.
319
320 augmented_created_stats(_Pid, Type) ->
321 [ augment_msg_stats(S) || S <- created_stats(Type) ].
322
323 match_consumer_spec(Id) ->
324 [{{{'_', '$1', '_'}, '_'}, [{'==', Id, '$1'}], ['$_']}].
325
326 match_queue_consumer_spec(Id) ->
327 [{{{'$1', '_', '_'}, '_'}, [{'==', {Id}, '$1'}], ['$_']}].
328
329 lookup_element(Table, Key) -> lookup_element(Table, Key, 2).
330
331 lookup_element(Table, Key, Pos) ->
332 try ets:lookup_element(Table, Key, Pos)
333 catch error:badarg -> []
334 end.
335
336 -spec lookup_smaller_sample(atom(), any()) -> maybe_slide().
337 lookup_smaller_sample(Table, Id) ->
338 case ets:lookup(Table, {Id, select_smaller_sample(Table)}) of
339 [] ->
340 not_found;
341 [{_, Slide}] ->
342 exometer_slide:optimize(Slide)
343 end.
344
345 -spec lookup_samples(atom(), any(), #range{}) -> maybe_slide().
346 lookup_samples(Table, Id, Range) ->
347 case ets:lookup(Table, {Id, select_range_sample(Table, Range)}) of
348 [] ->
349 not_found;
350 [{_, Slide}] ->
351 exometer_slide:optimize(Slide)
352 end.
353
354 lookup_all(Table, Ids, SecondKey) ->
355 Slides = lists:foldl(fun(Id, Acc) ->
356 case ets:lookup(Table, {Id, SecondKey}) of
357 [] ->
358 Acc;
359 [{_, Slide}] ->
360 [Slide | Acc]
361 end
362 end, [], Ids),
363 case Slides of
364 [] ->
365 not_found;
366 _ ->
367 exometer_slide:sum(Slides, empty(Table, 0))
368 end.
369
370 get_table_keys(Table, Id0) ->
371 ets:select(Table, match_spec_keys(Id0)).
372
373 match_spec_keys(Id) ->
374 MatchCondition = to_match_condition(Id),
375 MatchHead = {{{'$1', '$2'}, '_'}, '_'},
376 [{MatchHead, [MatchCondition], [{{'$1', '$2'}}]}].
377
378 to_match_condition({'_', Id1}) when is_tuple(Id1) ->
379 {'==', {Id1}, '$2'};
380 to_match_condition({'_', Id1}) ->
381 {'==', Id1, '$2'};
382 to_match_condition({Id0, '_'}) when is_tuple(Id0) ->
383 {'==', {Id0}, '$1'};
384 to_match_condition({Id0, '_'}) ->
385 {'==', Id0, '$1'}.
386 mgmt_qeue_length_stats() ->
387 GCsQueueLengths = lists:map(fun (T) ->
388 case whereis(rabbit_mgmt_metrics_gc:name(T)) of
389 P when is_pid(P) ->
390 {message_queue_len, Len} =
391 erlang:process_info(P, message_queue_len),
392 {T, Len};
393 _ -> {T, 0}
394 end
395 end,
396 ?GC_EVENTS),
397 [{metrics_gc_queue_length, GCsQueueLengths}].
398
399 select_range_sample(Table, #range{first = First, last = Last}) ->
400 Range = Last - First,
401 Policies = rabbit_mgmt_agent_config:get_env(sample_retention_policies),
402 Policy = retention_policy(Table),
403 [T | _] = TablePolicies = lists:sort(proplists:get_value(Policy, Policies)),
404 {_, Sample} = select_smallest_above(T, TablePolicies, Range),
405 Sample.
406
407 select_smaller_sample(Table) ->
408 Policies = rabbit_mgmt_agent_config:get_env(sample_retention_policies),
409 Policy = retention_policy(Table),
410 TablePolicies = proplists:get_value(Policy, Policies),
411 [V | _] = lists:sort([I || {_, I} <- TablePolicies]),
412 V.
413
414 select_smallest_above(V, [], _) ->
415 V;
416 select_smallest_above(_, [{H, _} = S | _T], Interval) when (H * 1000) > Interval ->
417 S;
418 select_smallest_above(_, [H | T], Interval) ->
419 select_smallest_above(H, T, Interval).
420
421 pick_range(queue_msg_counts, {RangeL, _RangeM, _RangeD, _RangeN}) ->
422 RangeL;
423 pick_range(K, {_RangeL, RangeM, _RangeD, _RangeN}) when K == fine_stats;
424 K == deliver_get;
425 K == queue_msg_rates ->
426 RangeM;
427 pick_range(K, {_RangeL, _RangeM, RangeD, _RangeN}) when K == coarse_conn_stats;
428 K == process_stats ->
429 RangeD;
430 pick_range(K, {_RangeL, _RangeM, _RangeD, RangeN})
431 when K == coarse_node_stats;
432 K == coarse_node_node_stats ->
433 RangeN.
434
435 first(Id) ->
436 {Id, '_'}.
437
438 second(Id) ->
439 {'_', Id}.
440
441 empty(Type, V) when Type =:= connection_stats_coarse_conn_stats;
442 Type =:= channel_stats_fine_stats;
443 Type =:= channel_exchange_stats_fine_stats;
444 Type =:= vhost_stats_fine_stats;
445 Type =:= queue_msg_stats;
446 Type =:= vhost_msg_stats ->
447 {V, V, V};
448 empty(Type, V) when Type =:= channel_queue_stats_deliver_stats;
449 Type =:= queue_stats_deliver_stats;
450 Type =:= vhost_stats_deliver_stats;
451 Type =:= channel_stats_deliver_stats ->
452 {V, V, V, V, V, V, V};
453 empty(Type, V) when Type =:= channel_process_stats;
454 Type =:= queue_process_stats;
455 Type =:= queue_stats_publish;
456 Type =:= queue_exchange_stats_publish;
457 Type =:= exchange_stats_publish_out;
458 Type =:= exchange_stats_publish_in ->
459 {V};
460 empty(node_coarse_stats, V) ->
461 {V, V, V, V, V, V, V, V};
462 empty(node_persister_stats, V) ->
463 {V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V};
464 empty(Type, V) when Type =:= node_node_coarse_stats;
465 Type =:= vhost_stats_coarse_conn_stats;
466 Type =:= queue_msg_rates;
467 Type =:= vhost_msg_rates ->
468 {V, V}.
469
470 retention_policy(connection_stats_coarse_conn_stats) ->
471 basic;
472 retention_policy(channel_stats_fine_stats) ->
473 basic;
474 retention_policy(channel_queue_stats_deliver_stats) ->
475 detailed;
476 retention_policy(channel_exchange_stats_fine_stats) ->
477 detailed;
478 retention_policy(channel_process_stats) ->
479 basic;
480 retention_policy(vhost_stats_fine_stats) ->
481 global;
482 retention_policy(vhost_stats_deliver_stats) ->
483 global;
484 retention_policy(vhost_stats_coarse_conn_stats) ->
485 global;
486 retention_policy(vhost_msg_rates) ->
487 global;
488 retention_policy(channel_stats_deliver_stats) ->
489 basic;
490 retention_policy(queue_stats_deliver_stats) ->
491 basic;
492 retention_policy(queue_stats_publish) ->
493 basic;
494 retention_policy(queue_exchange_stats_publish) ->
495 basic;
496 retention_policy(exchange_stats_publish_out) ->
497 basic;
498 retention_policy(exchange_stats_publish_in) ->
499 basic;
500 retention_policy(queue_process_stats) ->
501 basic;
502 retention_policy(queue_msg_stats) ->
503 basic;
504 retention_policy(queue_msg_rates) ->
505 basic;
506 retention_policy(vhost_msg_stats) ->
507 global;
508 retention_policy(node_coarse_stats) ->
509 global;
510 retention_policy(node_persister_stats) ->
511 global;
512 retention_policy(node_node_coarse_stats) ->
513 global.
514
515 format_resource(unknown) -> unknown;
516 format_resource(Res) -> format_resource(name, Res).
517
518 format_resource(_, unknown) ->
519 unknown;
520 format_resource(NameAs, #resource{name = Name, virtual_host = VHost}) ->
521 [{NameAs, Name}, {vhost, VHost}].
522
523 filter_user(List, #user{username = Username, tags = Tags}) ->
524 case is_monitor(Tags) of
525 true -> List;
526 false -> [I || I <- List, pget(user, I) == Username]
527 end.
528
529 is_monitor(T) -> intersects(T, [administrator, monitoring]).
530 intersects(A, B) -> lists:any(fun(I) -> lists:member(I, B) end, A).
1010 %% The Original Code is RabbitMQ Management Console.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_db_handler).
4343 gc() ->
4444 erlang:garbage_collect(whereis(rabbit_event)).
4545
46 %% some people have reasons to only run with the agent enabled:
47 %% make it possible for them to configure key management app
48 %% settings such as rates_mode.
49 get_management_env(Key) ->
50 rabbit_misc:get_env(
51 rabbitmq_management, Key,
52 rabbit_misc:get_env(rabbitmq_management_agent, Key, undefined)).
53
5446 rates_mode() ->
55 case get_management_env(rates_mode) of
47 case rabbit_mgmt_agent_config:get_env(rates_mode) of
5648 undefined -> basic;
5749 Mode -> Mode
5850 end.
5951
6052 handle_force_fine_statistics() ->
61 case get_management_env(force_fine_statistics) of
53 case rabbit_mgmt_agent_config:get_env(force_fine_statistics) of
6254 undefined ->
6355 ok;
6456 X ->
9486 handle_call(_Request, State) ->
9587 {ok, not_understood, State}.
9688
97 handle_event(#event{type = Type} = Event, State) when Type == channel_stats;
98 Type == channel_created;
99 Type == channel_closed ->
100 gen_server:cast({global, rabbit_mgmt_channel_stats_collector}, {event, Event}),
89 handle_event(#event{type = Type} = Event, State)
90 when Type == connection_closed; Type == channel_closed; Type == queue_deleted;
91 Type == exchange_deleted; Type == vhost_deleted;
92 Type == consumer_deleted; Type == node_node_deleted;
93 Type == channel_consumer_deleted ->
94 gen_server:cast(rabbit_mgmt_metrics_gc:name(Type), {event, Event}),
10195 {ok, State};
102 handle_event(#event{type = Type} = Event, State) when Type == queue_stats;
103 Type == queue_deleted ->
104 gen_server:cast({global, rabbit_mgmt_queue_stats_collector}, {event, Event}),
105 {ok, State};
106 handle_event(Event, State) ->
107 gen_server:cast({global, rabbit_mgmt_event_collector}, {event, Event}),
96 handle_event(_, State) ->
10897 {ok, State}.
10998
11099 handle_info(_Info, State) ->
1010 %% The Original Code is RabbitMQ Management Console.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mgmt_external_stats).
2727
2828 -include_lib("rabbit_common/include/rabbit.hrl").
2929
30 -define(REFRESH_RATIO, 5000).
31 -define(KEYS, [name, partitions, os_pid, fd_used, fd_total,
32 sockets_used, sockets_total, mem_used, mem_limit, mem_alarm,
33 disk_free_limit, disk_free, disk_free_alarm,
34 proc_used, proc_total, rates_mode,
35 uptime, run_queue, processors, exchange_types,
36 auth_mechanisms, applications, contexts,
37 log_file, sasl_log_file, db_dir, config_files, net_ticktime,
38 enabled_plugins, persister_stats, gc_num, gc_bytes_reclaimed,
39 context_switches]).
30 -define(METRICS_KEYS, [fd_used, sockets_used, mem_used, disk_free, proc_used, gc_num,
31 gc_bytes_reclaimed, context_switches]).
32
33 -define(PERSISTER_KEYS, [persister_stats]).
34
35 -define(OTHER_KEYS, [name, partitions, os_pid, fd_total, sockets_total, mem_limit,
36 mem_alarm, disk_free_limit, disk_free_alarm, proc_total,
37 rates_mode, uptime, run_queue, processors, exchange_types,
38 auth_mechanisms, applications, contexts, log_file,
39 sasl_log_file, db_dir, config_files, net_ticktime, enabled_plugins]).
4040
4141 %%--------------------------------------------------------------------
4242
4444 fd_total,
4545 fhc_stats,
4646 node_owners,
47 last_ts
47 last_ts,
48 interval
4849 }).
4950
5051 %%--------------------------------------------------------------------
5556 %%--------------------------------------------------------------------
5657
5758 get_used_fd() ->
58 get_used_fd(os:type()).
59 case get_used_fd(os:type()) of
60 Fd when is_number(Fd) ->
61 Fd;
62 _Other ->
63 %% Defaults to 0 if data is not available
64 0
65 end.
5966
6067 get_used_fd({unix, linux}) ->
6168 case file:list_dir("/proc/" ++ os:getpid() ++ "/fd") of
7481 lists:all(Digit, (lists:nth(4, string:tokens(Line, " "))))
7582 end, string:tokens(Output, "\n")))
7683 catch _:Error ->
77 case get(logged_used_fd_error) of
78 undefined -> rabbit_log:warning(
79 "Could not parse fstat output:~n~s~n~p~n",
80 [Output, {Error, erlang:get_stacktrace()}]),
81 put(logged_used_fd_error, true);
82 _ -> ok
83 end,
84 unknown
84 log_fd_error("Could not parse fstat output:~n~s~n~p~n",
85 [Output, {Error, erlang:get_stacktrace()}])
8586 end;
8687
8788 get_used_fd({unix, _}) ->
8990 "lsof -d \"0-9999999\" -lna -p ~s || echo failed", [os:getpid()]),
9091 Res = os:cmd(Cmd),
9192 case string:right(Res, 7) of
92 "failed\n" -> unknown;
93 "failed\n" -> log_fd_error("Could not obtain lsof output~n", []);
9394 _ -> string:words(Res, $\n) - 1
9495 end;
9596
130131 Handle = rabbit_misc:os_cmd(
131132 "handle.exe /accepteula -s -p " ++ os:getpid() ++ " 2> nul"),
132133 case Handle of
133 [] -> install_handle_from_sysinternals;
134 _ -> find_files_line(string:tokens(Handle, "\r\n"))
135 end;
136
137 get_used_fd(_) ->
138 unknown.
134 [] -> log_fd_error("Could not find handle.exe, please install from "
135 "sysinternals~n", []);
136 _ -> case find_files_line(string:tokens(Handle, "\r\n")) of
137 unknown ->
138 log_fd_error("Could not parse handle.exe output: ~p~n",
139 [Handle]);
140 Any ->
141 Any
142 end
143 end.
139144
140145 find_files_line([]) ->
141146 unknown;
157162 get_disk_free() -> ?SAFE_CALL(rabbit_disk_monitor:get_disk_free(),
158163 disk_free_monitoring_disabled).
159164
165 log_fd_error(Fmt, Args) ->
166 case get(logged_used_fd_error) of
167 undefined -> rabbit_log:warning(Fmt, Args),
168 put(logged_used_fd_error, true);
169 _ -> ok
170 end.
160171 %%--------------------------------------------------------------------
161172
162173 infos(Items, State) -> [{Item, i(Item, State)} || Item <- Items].
324335 %%--------------------------------------------------------------------
325336
326337 init([]) ->
338 {ok, Interval} = application:get_env(rabbit, collect_statistics_interval),
327339 State = #state{fd_total = file_handle_cache:ulimit(),
328340 fhc_stats = file_handle_cache_stats:get(),
329 node_owners = sets:new()},
330 %% If we emit an update straight away we will do so just before
331 %% the mgmt db starts up - and then have to wait ?REFRESH_RATIO
332 %% until we send another. So let's have a shorter wait in the hope
333 %% that the db will have started by the time we emit an update,
334 %% and thus shorten that little gap at startup where mgmt knows
335 %% nothing about any nodes.
336 erlang:send_after(1000, self(), emit_update),
337 {ok, State}.
341 node_owners = sets:new(),
342 interval = Interval},
343 %% We can update stats straight away as they need to be available
344 %% when the mgmt plugin starts a collector
345 {ok, emit_update(State)}.
338346
339347 handle_call(_Req, _From, State) ->
340348 {reply, unknown_request, State}.
356364
357365 emit_update(State0) ->
358366 State = update_state(State0),
359 Stats = infos(?KEYS, State),
360 rabbit_event:notify(node_stats, Stats),
361 erlang:send_after(?REFRESH_RATIO, self(), emit_update),
367 MStats = infos(?METRICS_KEYS, State),
368 [{persister_stats, PStats0}] = PStats = infos(?PERSISTER_KEYS, State),
369 [{name, _Name} | OStats0] = OStats = infos(?OTHER_KEYS, State),
370 rabbit_core_metrics:node_stats(persister_metrics, PStats0),
371 rabbit_core_metrics:node_stats(coarse_metrics, MStats),
372 rabbit_core_metrics:node_stats(node_metrics, OStats0),
373 rabbit_event:notify(node_stats, PStats ++ MStats ++ OStats),
374 erlang:send_after(State#state.interval, self(), emit_update),
362375 emit_node_node_stats(State).
363376
364377 emit_node_node_stats(State = #state{node_owners = Owners}) ->
367380 Dead = sets:to_list(sets:subtract(Owners, NewOwners)),
368381 [rabbit_event:notify(
369382 node_node_deleted, [{route, Route}]) || {Node, _Owner} <- Dead,
370 Route <- [{node(), Node},
371 {Node, node()}]],
372 [rabbit_event:notify(
373 node_node_stats, [{route, {node(), Node}} | Stats]) ||
374 {Node, _Owner, Stats} <- Links],
383 Route <- [{node(), Node},
384 {Node, node()}]],
385 [begin
386 rabbit_core_metrics:node_node_stats({node(), Node}, Stats),
387 rabbit_event:notify(
388 node_node_stats, [{route, {node(), Node}} | Stats])
389 end || {Node, _Owner, Stats} <- Links],
375390 State#state{node_owners = NewOwners}.
376391
377392 update_state(State0) ->
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License at
3 %% http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
7 %% License for the specific language governing rights and limitations
8 %% under the License.
9 %%
10 %% The Original Code is RabbitMQ Management Plugin.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_mgmt_format).
17
18 -export([format/2, ip/1, ipb/1, amqp_table/1, tuple/1]).
19 -export([parameter/1, now_to_str/1, now_to_str_ms/1, strip_pids/1]).
20 -export([protocol/1, resource/1, queue/1, queue_state/1]).
21 -export([exchange/1, user/1, internal_user/1, binding/1, url/2]).
22 -export([pack_binding_props/2, tokenise/1]).
23 -export([to_amqp_table/1, listener/1, web_context/1, properties/1, basic_properties/1]).
24 -export([record/2, to_basic_properties/1]).
25 -export([addr/1, port/1]).
26 -export([format_nulls/1, escape_html_tags/1]).
27 -export([print/2, print/1]).
28
29 -export([format_queue_stats/1, format_channel_stats/1,
30 format_arguments/1, format_connection_created/1,
31 format_accept_content/1, format_args/1]).
32
33 -export([strip_queue_pids/1]).
34
35 -export([clean_consumer_details/1, clean_channel_details/1]).
36
37 -import(rabbit_misc, [pget/2, pget/3, pset/3]).
38
39 -include_lib("rabbit_common/include/rabbit.hrl").
40 -include_lib("rabbit_common/include/rabbit_framing.hrl").
41
42 %%--------------------------------------------------------------------
43
44 format(Stats, {[], _}) ->
45 [Stat || {_Name, Value} = Stat <- Stats, Value =/= unknown];
46 format(Stats, {Fs, true}) ->
47 [Fs(Stat) || {_Name, Value} = Stat <- Stats, Value =/= unknown];
48 format(Stats, {Fs, false}) ->
49 lists:concat([Fs(Stat) || {_Name, Value} = Stat <- Stats,
50 Value =/= unknown]).
51
52 format_queue_stats({reductions, _}) ->
53 [];
54 format_queue_stats({exclusive_consumer_pid, _}) ->
55 [];
56 format_queue_stats({slave_pids, ''}) ->
57 [];
58 format_queue_stats({slave_pids, Pids}) ->
59 [{slave_nodes, [node(Pid) || Pid <- Pids]}];
60 format_queue_stats({synchronised_slave_pids, ''}) ->
61 [];
62 format_queue_stats({synchronised_slave_pids, Pids}) ->
63 [{synchronised_slave_nodes, [node(Pid) || Pid <- Pids]}];
64 format_queue_stats({backing_queue_status, Value}) ->
65 [{backing_queue_status, properties(Value)}];
66 format_queue_stats({idle_since, Value}) ->
67 [{idle_since, now_to_str(Value)}];
68 format_queue_stats({state, Value}) ->
69 queue_state(Value);
70 format_queue_stats({disk_reads, _}) ->
71 [];
72 format_queue_stats({disk_writes, _}) ->
73 [];
74 format_queue_stats(Stat) ->
75 [Stat].
76
77 format_channel_stats({idle_since, Value}) ->
78 {idle_since, now_to_str(Value)};
79 format_channel_stats(Stat) ->
80 Stat.
81
82 format_arguments({arguments, Value}) ->
83 {arguments, amqp_table(Value)};
84 format_arguments(Stat) ->
85 Stat.
86
87 format_args({arguments, Value}) ->
88 {arguments, args(Value)};
89 format_args(Stat) ->
90 Stat.
91
92 format_connection_created({host, Value}) ->
93 {host, addr(Value)};
94 format_connection_created({peer_host, Value}) ->
95 {peer_host, addr(Value)};
96 format_connection_created({port, Value}) ->
97 {port, port(Value)};
98 format_connection_created({peer_port, Value}) ->
99 {peer_port, port(Value)};
100 format_connection_created({protocol, Value}) ->
101 {protocol, protocol(Value)};
102 format_connection_created({client_properties, Value}) ->
103 {client_properties, amqp_table(Value)};
104 format_connection_created(Stat) ->
105 Stat.
106
107 format_exchange_and_queue({policy, Value}) ->
108 policy(Value);
109 format_exchange_and_queue({arguments, Value}) ->
110 [{arguments, amqp_table(Value)}];
111 format_exchange_and_queue({name, Value}) ->
112 resource(Value);
113 format_exchange_and_queue(Stat) ->
114 [Stat].
115
116 format_binding({source, Value}) ->
117 resource(source, Value);
118 format_binding({arguments, Value}) ->
119 [{arguments, amqp_table(Value)}];
120 format_binding(Stat) ->
121 [Stat].
122
123 format_basic_properties({headers, Value}) ->
124 {headers, amqp_table(Value)};
125 format_basic_properties(Stat) ->
126 Stat.
127
128 format_accept_content({durable, Value}) ->
129 {durable, parse_bool(Value)};
130 format_accept_content({auto_delete, Value}) ->
131 {auto_delete, parse_bool(Value)};
132 format_accept_content({internal, Value}) ->
133 {internal, parse_bool(Value)};
134 format_accept_content(Stat) ->
135 Stat.
136
137 print(Fmt, Val) when is_list(Val) ->
138 list_to_binary(lists:flatten(io_lib:format(Fmt, Val)));
139 print(Fmt, Val) ->
140 print(Fmt, [Val]).
141
142 print(Val) when is_list(Val) ->
143 list_to_binary(lists:flatten(Val));
144 print(Val) ->
145 Val.
146
147 %% TODO - can we remove all these "unknown" cases? Coverage never hits them.
148
149 ip(unknown) -> unknown;
150 ip(IP) -> list_to_binary(rabbit_misc:ntoa(IP)).
151
152 ipb(unknown) -> unknown;
153 ipb(IP) -> list_to_binary(rabbit_misc:ntoab(IP)).
154
155 addr(S) when is_list(S); is_atom(S); is_binary(S) -> print("~s", S);
156 addr(Addr) when is_tuple(Addr) -> ip(Addr).
157
158 port(Port) when is_number(Port) -> Port;
159 port(Port) -> print("~w", Port).
160
161 properties(unknown) -> unknown;
162 properties(Table) -> {struct, [{Name, tuple(Value)} ||
163 {Name, Value} <- Table]}.
164
165 amqp_table(unknown) -> unknown;
166 amqp_table(undefined) -> amqp_table([]);
167 amqp_table(Table) -> {struct, [{Name, amqp_value(Type, Value)} ||
168 {Name, Type, Value} <- Table]}.
169
170 amqp_value(array, Vs) -> [amqp_value(T, V) || {T, V} <- Vs];
171 amqp_value(table, V) -> amqp_table(V);
172 amqp_value(_Type, V) when is_binary(V) -> utf8_safe(V);
173 amqp_value(_Type, V) -> V.
174
175 utf8_safe(V) ->
176 try
177 _ = xmerl_ucs:from_utf8(V),
178 V
179 catch exit:{ucs, _} ->
180 Enc = split_lines(base64:encode(V)),
181 <<"Not UTF-8, base64 is: ", Enc/binary>>
182 end.
183
184 % MIME enforces a limit on line length of base 64-encoded data to 76 characters.
185 split_lines(<<Text:76/binary, Rest/binary>>) ->
186 <<Text/binary, $\n, (split_lines(Rest))/binary>>;
187 split_lines(Text) ->
188 Text.
189
190 parameter(P) -> pset(value, rabbit_misc:term_to_json(pget(value, P)), P).
191
192 tuple(unknown) -> unknown;
193 tuple(Tuple) when is_tuple(Tuple) -> [tuple(E) || E <- tuple_to_list(Tuple)];
194 tuple(Term) -> Term.
195
196 protocol(unknown) ->
197 unknown;
198 protocol(Version = {_Major, _Minor, _Revision}) ->
199 protocol({'AMQP', Version});
200 protocol({Family, Version}) ->
201 print("~s ~s", [Family, protocol_version(Version)]).
202
203 protocol_version(Arbitrary)
204 when is_list(Arbitrary) -> Arbitrary;
205 protocol_version({Major, Minor}) -> io_lib:format("~B-~B", [Major, Minor]);
206 protocol_version({Major, Minor, 0}) -> protocol_version({Major, Minor});
207 protocol_version({Major, Minor, Revision}) -> io_lib:format("~B-~B-~B",
208 [Major, Minor, Revision]).
209
210 now_to_str(unknown) ->
211 unknown;
212 now_to_str(MilliSeconds) ->
213 BaseDate = calendar:datetime_to_gregorian_seconds({{1970, 1, 1},
214 {0, 0, 0}}),
215 Seconds = BaseDate + (MilliSeconds div 1000),
216 {{Y, M, D}, {H, Min, S}} = calendar:gregorian_seconds_to_datetime(Seconds),
217 print("~w-~2.2.0w-~2.2.0w ~w:~2.2.0w:~2.2.0w", [Y, M, D, H, Min, S]).
218
219 now_to_str_ms(unknown) ->
220 unknown;
221 now_to_str_ms(MilliSeconds) ->
222 print("~s:~3.3.0w", [now_to_str(MilliSeconds), MilliSeconds rem 1000]).
223
224 resource(unknown) -> unknown;
225 resource(Res) -> resource(name, Res).
226
227 resource(_, unknown) ->
228 unknown;
229 resource(NameAs, #resource{name = Name, virtual_host = VHost}) ->
230 [{NameAs, Name}, {vhost, VHost}].
231
232 policy('') -> [];
233 policy(Policy) -> [{policy, Policy}].
234
235 internal_user(User) ->
236 [{name, User#internal_user.username},
237 {password_hash, base64:encode(User#internal_user.password_hash)},
238 {hashing_algorithm, rabbit_auth_backend_internal:hashing_module_for_user(
239 User)},
240 {tags, tags(User#internal_user.tags)}].
241
242 user(User) ->
243 [{name, User#user.username},
244 {tags, tags(User#user.tags)}].
245
246 tags(Tags) ->
247 list_to_binary(string:join([atom_to_list(T) || T <- Tags], ",")).
248
249 listener(#listener{node = Node, protocol = Protocol,
250 ip_address = IPAddress, port = Port, opts=Opts}) ->
251 [{node, Node},
252 {protocol, Protocol},
253 {ip_address, ip(IPAddress)},
254 {port, Port},
255 {socket_opts, format_socket_opts(Opts)}].
256
257 web_context(Props0) ->
258 SslOpts = pget(ssl_opts, Props0, []),
259 Props = proplists:delete(ssl_opts, Props0),
260 [{ssl_opts, format_socket_opts(SslOpts)} | Props].
261
262 format_socket_opts(Opts) ->
263 format_socket_opts(Opts, []).
264
265 format_socket_opts([], Acc) ->
266 lists:reverse(Acc);
267 %% for HTTP API listeners this will be included into
268 %% socket_opts
269 format_socket_opts([{ssl_opts, Value} | Tail], Acc) ->
270 format_socket_opts(Tail, [{ssl_opts, format_socket_opts(Value)} | Acc]);
271 %% exclude options that have values that are nested
272 %% data structures or may include functions. They are fairly
273 %% obscure and not worth reporting via HTTP API.
274 format_socket_opts([{verify_fun, _Value} | Tail], Acc) ->
275 format_socket_opts(Tail, Acc);
276 format_socket_opts([{crl_cache, _Value} | Tail], Acc) ->
277 format_socket_opts(Tail, Acc);
278 format_socket_opts([{partial_chain, _Value} | Tail], Acc) ->
279 format_socket_opts(Tail, Acc);
280 format_socket_opts([{user_lookup_fun, _Value} | Tail], Acc) ->
281 format_socket_opts(Tail, Acc);
282 format_socket_opts([{sni_fun, _Value} | Tail], Acc) ->
283 format_socket_opts(Tail, Acc);
284 format_socket_opts([{reuse_session, _Value} | Tail], Acc) ->
285 format_socket_opts(Tail, Acc);
286 %% we do not want to report configured cipher suites, even
287 %% though formatting them is straightforward
288 format_socket_opts([{ciphers, _Value} | Tail], Acc) ->
289 format_socket_opts(Tail, Acc);
290 %% single atom options, e.g. `binary`
291 format_socket_opts([Head | Tail], Acc) when is_atom(Head) ->
292 format_socket_opts(Tail, [{Head, true} | Acc]);
293 %% verify_fun value is a tuple that includes a function
294 format_socket_opts([_Head = {verify_fun, _Value} | Tail], Acc) ->
295 format_socket_opts(Tail, Acc);
296 format_socket_opts([Head = {Name, Value} | Tail], Acc) when is_list(Value) ->
297 case io_lib:printable_unicode_list(Value) of
298 true -> format_socket_opts(Tail, [{Name, unicode:characters_to_binary(Value)} | Acc]);
299 false -> format_socket_opts(Tail, [Head | Acc])
300 end;
301 format_socket_opts([{Name, Value} | Tail], Acc) when is_tuple(Value) ->
302 format_socket_opts(Tail, [{Name, tuple_to_list(Value)} | Acc]);
303 %% exclude functions from JSON encoding
304 format_socket_opts([_Head = {_Name, Value} | Tail], Acc) when is_function(Value) ->
305 format_socket_opts(Tail, Acc);
306 format_socket_opts([Head | Tail], Acc) ->
307 format_socket_opts(Tail, [Head | Acc]).
308
309 pack_binding_props(<<"">>, []) ->
310 <<"~">>;
311 pack_binding_props(Key, []) ->
312 list_to_binary(quote_binding(Key));
313 pack_binding_props(Key, Args) ->
314 ArgsEnc = args_hash(Args),
315 list_to_binary(quote_binding(Key) ++ "~" ++ quote_binding(ArgsEnc)).
316
317 quote_binding(Name) ->
318 re:replace(mochiweb_util:quote_plus(Name), "~", "%7E", [global]).
319
320 %% Unfortunately string:tokens("foo~~bar", "~"). -> ["foo","bar"], we lose
321 %% the fact that there's a double ~.
322 tokenise("") ->
323 [];
324 tokenise(Str) ->
325 Count = string:cspan(Str, "~"),
326 case length(Str) of
327 Count -> [Str];
328 _ -> [string:sub_string(Str, 1, Count) |
329 tokenise(string:sub_string(Str, Count + 2))]
330 end.
331
332 to_amqp_table({struct, T}) ->
333 to_amqp_table(T);
334 to_amqp_table(T) ->
335 [to_amqp_table_row(K, V) || {K, V} <- T].
336
337 to_amqp_table_row(K, V) ->
338 {T, V2} = type_val(V),
339 {K, T, V2}.
340
341 to_amqp_array(L) ->
342 [type_val(I) || I <- L].
343
344 type_val({struct, M}) -> {table, to_amqp_table(M)};
345 type_val(L) when is_list(L) -> {array, to_amqp_array(L)};
346 type_val(X) when is_binary(X) -> {longstr, X};
347 type_val(X) when is_integer(X) -> {long, X};
348 type_val(X) when is_number(X) -> {double, X};
349 type_val(true) -> {bool, true};
350 type_val(false) -> {bool, false};
351 type_val(null) -> throw({error, null_not_allowed});
352 type_val(X) -> throw({error, {unhandled_type, X}}).
353
354 url(Fmt, Vals) ->
355 print(Fmt, [mochiweb_util:quote_plus(V) || V <- Vals]).
356
357 exchange(X) ->
358 format(X, {fun format_exchange_and_queue/1, false}).
359
360 %% We get queues using rabbit_amqqueue:list/1 rather than :info_all/1 since
361 %% the latter wakes up each queue. Therefore we have a record rather than a
362 %% proplist to deal with.
363 queue(#amqqueue{name = Name,
364 durable = Durable,
365 auto_delete = AutoDelete,
366 exclusive_owner = ExclusiveOwner,
367 arguments = Arguments,
368 pid = Pid,
369 state = State}) ->
370 format(
371 [{name, Name},
372 {durable, Durable},
373 {auto_delete, AutoDelete},
374 {exclusive, is_pid(ExclusiveOwner)},
375 {owner_pid, ExclusiveOwner},
376 {arguments, Arguments},
377 {pid, Pid},
378 {state, State}],
379 {fun format_exchange_and_queue/1, false}).
380
381 queue_state({syncing, Msgs}) -> [{state, syncing},
382 {sync_messages, Msgs}];
383 queue_state(Status) -> [{state, Status}].
384
385 %% We get bindings using rabbit_binding:list_*/1 rather than :info_all/1 since
386 %% there are no per-exchange / queue / etc variants for the latter. Therefore
387 %% we have a record rather than a proplist to deal with.
388 binding(#binding{source = S,
389 key = Key,
390 destination = D,
391 args = Args}) ->
392 format(
393 [{source, S},
394 {destination, D#resource.name},
395 {destination_type, D#resource.kind},
396 {routing_key, Key},
397 {arguments, Args},
398 {properties_key, pack_binding_props(Key, Args)}],
399 {fun format_binding/1, false}).
400
401 basic_properties(Props = #'P_basic'{}) ->
402 Res = record(Props, record_info(fields, 'P_basic')),
403 format(Res, {fun format_basic_properties/1, true}).
404
405 record(Record, Fields) ->
406 {Res, _Ix} = lists:foldl(fun (K, {L, Ix}) ->
407 {case element(Ix, Record) of
408 undefined -> L;
409 V -> [{K, V}|L]
410 end, Ix + 1}
411 end, {[], 2}, Fields),
412 Res.
413
414 to_basic_properties({struct, P}) ->
415 to_basic_properties(P);
416
417 to_basic_properties(Props) ->
418 E = fun err/2,
419 Fmt = fun (headers, H) -> to_amqp_table(H);
420 (delivery_mode, V) when is_integer(V) -> V;
421 (delivery_mode, _V) -> E(not_int,delivery_mode);
422 (priority, V) when is_integer(V) -> V;
423 (priority, _V) -> E(not_int, priority);
424 (timestamp, V) when is_integer(V) -> V;
425 (timestamp, _V) -> E(not_int, timestamp);
426 (_, V) when is_binary(V) -> V;
427 (K, _V) -> E(not_string, K)
428 end,
429 {Res, _Ix} = lists:foldl(
430 fun (K, {P, Ix}) ->
431 {case proplists:get_value(a2b(K), Props) of
432 undefined -> P;
433 V -> setelement(Ix, P, Fmt(K, V))
434 end, Ix + 1}
435 end, {#'P_basic'{}, 2},
436 record_info(fields, 'P_basic')),
437 Res.
438
439 -spec err(term(), term()) -> no_return().
440 err(A, B) ->
441 throw({error, {A, B}}).
442
443 a2b(A) ->
444 list_to_binary(atom_to_list(A)).
445
446 strip_queue_pids(Item) ->
447 strip_queue_pids(Item, []).
448
449 strip_queue_pids([{_, unknown} | T], Acc) ->
450 strip_queue_pids(T, Acc);
451 strip_queue_pids([{pid, Pid} | T], Acc) when is_pid(Pid) ->
452 strip_queue_pids(T, [{node, node(Pid)} | Acc]);
453 strip_queue_pids([{pid, _} | T], Acc) ->
454 strip_queue_pids(T, Acc);
455 strip_queue_pids([{owner_pid, _} | T], Acc) ->
456 strip_queue_pids(T, Acc);
457 strip_queue_pids([Any | T], Acc) ->
458 strip_queue_pids(T, [Any | Acc]);
459 strip_queue_pids([], Acc) ->
460 Acc.
461
462 %% Items can be connections, channels, consumers or queues, hence remove takes
463 %% various items.
464 strip_pids(Item = [T | _]) when is_tuple(T) ->
465 strip_pids(Item, []);
466
467 strip_pids(Items) -> [strip_pids(I) || I <- Items].
468
469 strip_pids([{_, unknown} | T], Acc) ->
470 strip_pids(T, Acc);
471 strip_pids([{pid, Pid} | T], Acc) when is_pid(Pid) ->
472 strip_pids(T, [{node, node(Pid)} | Acc]);
473 strip_pids([{pid, _} | T], Acc) ->
474 strip_pids(T, Acc);
475 strip_pids([{connection, _} | T], Acc) ->
476 strip_pids(T, Acc);
477 strip_pids([{owner_pid, _} | T], Acc) ->
478 strip_pids(T, Acc);
479 strip_pids([{channel, _} | T], Acc) ->
480 strip_pids(T, Acc);
481 strip_pids([{channel_pid, _} | T], Acc) ->
482 strip_pids(T, Acc);
483 strip_pids([{exclusive_consumer_pid, _} | T], Acc) ->
484 strip_pids(T, Acc);
485 strip_pids([{slave_pids, ''} | T], Acc) ->
486 strip_pids(T, Acc);
487 strip_pids([{slave_pids, Pids} | T], Acc) ->
488 strip_pids(T, [{slave_nodes, [node(Pid) || Pid <- Pids]} | Acc]);
489 strip_pids([{synchronised_slave_pids, ''} | T], Acc) ->
490 strip_pids(T, Acc);
491 strip_pids([{synchronised_slave_pids, Pids} | T], Acc) ->
492 strip_pids(T, [{synchronised_slave_nodes, [node(Pid) || Pid <- Pids]} | Acc]);
493 strip_pids([{K, [P|_] = Nested} | T], Acc) when is_tuple(P) -> % recurse
494 strip_pids(T, [{K, strip_pids(Nested)} | Acc]);
495 strip_pids([{K, [L|_] = Nested} | T], Acc) when is_list(L) -> % recurse
496 strip_pids(T, [{K, strip_pids(Nested)} | Acc]);
497 strip_pids([Any | T], Acc) ->
498 strip_pids(T, [Any | Acc]);
499 strip_pids([], Acc) ->
500 Acc.
501
502 %% Format for JSON replies. Transforms '' into null
503 format_nulls(Items) when is_list(Items) ->
504 [format_null_item(Pair) || Pair <- Items];
505 format_nulls(Item) ->
506 format_null_item(Item).
507
508 format_null_item({Key, ''}) ->
509 {Key, null};
510 format_null_item({Key, Value}) when is_list(Value) ->
511 {Key, format_nulls(Value)};
512 format_null_item({Key, {struct, Struct}}) ->
513 {Key, {struct, format_nulls(Struct)}};
514 format_null_item({Key, {array, Struct}}) ->
515 {Key, {array, format_nulls(Struct)}};
516 format_null_item({Key, Value}) ->
517 {Key, Value};
518 format_null_item([{_K, _V} | _T] = L) ->
519 format_nulls(L);
520 format_null_item(Value) ->
521 Value.
522
523
524 -spec escape_html_tags(string()) -> binary().
525
526 escape_html_tags(S) ->
527 escape_html_tags(rabbit_data_coercion:to_list(S), []).
528
529
530 -spec escape_html_tags(string(), string()) -> binary().
531
532 escape_html_tags([], Acc) ->
533 rabbit_data_coercion:to_binary(lists:reverse(Acc));
534 escape_html_tags("<" ++ Rest, Acc) ->
535 escape_html_tags(Rest, lists:reverse("&lt;", Acc));
536 escape_html_tags(">" ++ Rest, Acc) ->
537 escape_html_tags(Rest, lists:reverse("&gt;", Acc));
538 escape_html_tags("&" ++ Rest, Acc) ->
539 escape_html_tags(Rest, lists:reverse("&amp;", Acc));
540 escape_html_tags([C | Rest], Acc) ->
541 escape_html_tags(Rest, [C | Acc]).
542
543
544 -spec clean_consumer_details(proplists:proplist()) -> proplists:proplist().
545 clean_consumer_details(Obj) ->
546 case pget(consumer_details, Obj) of
547 undefined -> Obj;
548 Cds ->
549 Cons = [format_consumer_arguments(clean_channel_details(Con)) || Con <- Cds],
550 pset(consumer_details, Cons, Obj)
551 end.
552
553 -spec clean_channel_details(proplists:proplist()) -> proplists:proplist().
554 clean_channel_details(Obj) ->
555 Obj0 = lists:keydelete(channel_pid, 1, Obj),
556 case pget(channel_details, Obj0) of
557 undefined -> Obj0;
558 Chd ->
559 pset(channel_details,
560 lists:keydelete(pid, 1, Chd),
561 Obj0)
562 end.
563
564 -spec format_consumer_arguments(proplists:proplist()) -> proplists:proplist().
565 format_consumer_arguments(Obj) ->
566 case pget(arguments, Obj) of
567 undefined -> Obj;
568 [] -> Obj;
569 Args -> pset(arguments, amqp_table(Args), Obj)
570 end.
571
572 %% Converts a proplist into an AMQP 0-9-1 table.
573 %% For JSON serialisation formatting see amqp_table/1.
574 args({struct, L}) -> args(L);
575 args(L) -> to_amqp_table(L).
576
577 parse_bool(<<"true">>) -> true;
578 parse_bool(<<"false">>) -> false;
579 parse_bool(true) -> true;
580 parse_bool(false) -> false;
581 parse_bool(undefined) -> undefined;
582 parse_bool(V) -> throw({error, {not_boolean, V}}).
583
584 args_hash(Args) ->
585 list_to_binary(rabbit_misc:base64url(erlang:md5(term_to_binary(Args)))).
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15 -module(rabbit_mgmt_gc).
16
17 -include_lib("rabbit_common/include/rabbit.hrl").
18
19 -record(state, {timer,
20 interval
21 }).
22
23 -spec start_link() -> rabbit_types:ok_pid_or_error().
24
25 -export([start_link/0]).
26 -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
27 code_change/3]).
28
29 start_link() ->
30 gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
31
32 init(_) ->
33 Interval = rabbit_misc:get_env(rabbitmq_management_agent, metrics_gc_interval, 120000),
34 {ok, start_timer(#state{interval = Interval})}.
35
36 handle_call(test, _From, State) ->
37 {reply, ok, State}.
38
39 handle_cast(_Request, State) ->
40 {noreply, State}.
41
42 handle_info(start_gc, State) ->
43 gc_connections(),
44 gc_vhosts(),
45 gc_channels(),
46 gc_queues(),
47 gc_exchanges(),
48 gc_nodes(),
49 {noreply, start_timer(State)}.
50
51 terminate(_Reason, #state{timer = TRef}) ->
52 _ = erlang:cancel_timer(TRef),
53 ok.
54
55 code_change(_OldVsn, State, _Extra) ->
56 {ok, State}.
57
58 start_timer(#state{interval = Interval} = St) ->
59 TRef = erlang:send_after(Interval, self(), start_gc),
60 St#state{timer = TRef}.
61
62 gc_connections() ->
63 gc_process(connection_stats_coarse_conn_stats),
64 gc_process(connection_created_stats),
65 gc_process(connection_stats).
66
67 gc_vhosts() ->
68 VHosts = rabbit_vhost:list(),
69 GbSet = gb_sets:from_list(VHosts),
70 gc_entity(vhost_stats_coarse_conn_stats, GbSet),
71 gc_entity(vhost_stats_fine_stats, GbSet),
72 gc_entity(vhost_msg_stats, GbSet),
73 gc_entity(vhost_msg_rates, GbSet),
74 gc_entity(vhost_stats_deliver_stats, GbSet).
75
76 gc_channels() ->
77 gc_process(channel_created_stats),
78 gc_process(channel_stats),
79 gc_process(channel_stats_fine_stats),
80 gc_process(channel_process_stats),
81 gc_process(channel_stats_deliver_stats),
82 ok.
83
84 gc_queues() ->
85 Queues = rabbit_amqqueue:list_names(),
86 GbSet = gb_sets:from_list(Queues),
87 gc_entity(queue_stats_publish, GbSet),
88 gc_entity(queue_stats, GbSet),
89 gc_entity(queue_msg_stats, GbSet),
90 gc_entity(queue_process_stats, GbSet),
91 gc_entity(queue_msg_rates, GbSet),
92 gc_entity(queue_stats_deliver_stats, GbSet),
93 gc_process_and_entity(channel_queue_stats_deliver_stats_queue_index, GbSet),
94 gc_process_and_entity(consumer_stats_queue_index, GbSet),
95 gc_process_and_entity(consumer_stats_channel_index, GbSet),
96 gc_process_and_entity(consumer_stats, GbSet),
97 gc_process_and_entity(channel_exchange_stats_fine_stats_channel_index, GbSet),
98 gc_process_and_entity(channel_queue_stats_deliver_stats, GbSet),
99 gc_process_and_entity(channel_queue_stats_deliver_stats_channel_index, GbSet),
100 ExchangeGbSet = gb_sets:from_list(rabbit_exchange:list_names()),
101 gc_entities(queue_exchange_stats_publish, GbSet, ExchangeGbSet),
102 gc_entities(queue_exchange_stats_publish_queue_index, GbSet, ExchangeGbSet),
103 gc_entities(queue_exchange_stats_publish_exchange_index, GbSet, ExchangeGbSet).
104
105 gc_exchanges() ->
106 Exchanges = rabbit_exchange:list_names(),
107 GbSet = gb_sets:from_list(Exchanges),
108 gc_entity(exchange_stats_publish_in, GbSet),
109 gc_entity(exchange_stats_publish_out, GbSet),
110 gc_entity(channel_exchange_stats_fine_stats_exchange_index, GbSet),
111 gc_process_and_entity(channel_exchange_stats_fine_stats, GbSet).
112
113 gc_nodes() ->
114 Nodes = rabbit_mnesia:cluster_nodes(all),
115 GbSet = gb_sets:from_list(Nodes),
116 gc_entity(node_stats, GbSet),
117 gc_entity(node_coarse_stats, GbSet),
118 gc_entity(node_persister_stats, GbSet),
119 gc_entity(node_node_coarse_stats_node_index, GbSet),
120 gc_entity(node_node_stats, GbSet),
121 gc_entity(node_node_coarse_stats, GbSet).
122
123 gc_process(Table) ->
124 ets:foldl(fun({{Pid, _} = Key, _}, none) ->
125 gc_process(Pid, Table, Key);
126 ({Pid = Key, _}, none) ->
127 gc_process(Pid, Table, Key);
128 ({Pid = Key, _, _}, none) ->
129 gc_process(Pid, Table, Key);
130 ({{Pid, _} = Key, _, _, _, _}, none) ->
131 gc_process(Pid, Table, Key)
132 end, none, Table).
133
134 gc_process(Pid, Table, Key) ->
135 case rabbit_misc:is_process_alive(Pid) of
136 true ->
137 none;
138 false ->
139 ets:delete(Table, Key),
140 none
141 end.
142
143 gc_entity(Table, GbSet) ->
144 ets:foldl(fun({{_, Id} = Key, _}, none) when Table == node_node_stats ->
145 gc_entity(Id, Table, Key, GbSet);
146 ({{{_, Id}, _} = Key, _}, none) when Table == node_node_coarse_stats ->
147 gc_entity(Id, Table, Key, GbSet);
148 ({{Id, _} = Key, _}, none) ->
149 gc_entity(Id, Table, Key, GbSet);
150 ({Id = Key, _}, none) ->
151 gc_entity(Id, Table, Key, GbSet);
152 ({{Id, _} = Key, _}, none) ->
153 gc_entity(Id, Table, Key, GbSet)
154 end, none, Table).
155
156 gc_entity(Id, Table, Key, GbSet) ->
157 case gb_sets:is_member(Id, GbSet) of
158 true ->
159 none;
160 false ->
161 ets:delete(Table, Key),
162 none
163 end.
164
165 gc_process_and_entity(Table, GbSet) ->
166 ets:foldl(fun({{Id, Pid, _} = Key, _}, none) when Table == consumer_stats ->
167 gc_process_and_entity(Id, Pid, Table, Key, GbSet);
168 ({Id = Key, {_, Pid, _}} = Object, none)
169 when Table == consumer_stats_queue_index ->
170 gc_object(Pid, Table, Object),
171 gc_entity(Id, Table, Key, GbSet);
172 ({Pid = Key, {Id, _, _}} = Object, none)
173 when Table == consumer_stats_channel_index ->
174 gc_object(Id, Table, Object, GbSet),
175 gc_process(Pid, Table, Key);
176 ({Id = Key, {{Pid, _}, _}} = Object, none)
177 when Table == channel_exchange_stats_fine_stats_exchange_index;
178 Table == channel_queue_stats_deliver_stats_queue_index ->
179 gc_object(Pid, Table, Object),
180 gc_entity(Id, Table, Key, GbSet);
181 ({Pid = Key, {{_, Id}, _}} = Object, none)
182 when Table == channel_exchange_stats_fine_stats_channel_index;
183 Table == channel_queue_stats_deliver_stats_channel_index ->
184 gc_object(Id, Table, Object, GbSet),
185 gc_process(Pid, Table, Key);
186 ({{{Pid, Id}, _} = Key, _}, none)
187 when Table == channel_queue_stats_deliver_stats;
188 Table == channel_exchange_stats_fine_stats ->
189 gc_process_and_entity(Id, Pid, Table, Key, GbSet);
190 ({{{Pid, Id}, _} = Key, _, _, _, _, _, _, _, _}, none) ->
191 gc_process_and_entity(Id, Pid, Table, Key, GbSet);
192 ({{{Pid, Id}, _} = Key, _, _, _, _}, none) ->
193 gc_process_and_entity(Id, Pid, Table, Key, GbSet)
194 end, none, Table).
195
196 gc_process_and_entity(Id, Pid, Table, Key, GbSet) ->
197 case rabbit_misc:is_process_alive(Pid) andalso gb_sets:is_member(Id, GbSet) of
198 true ->
199 none;
200 false ->
201 ets:delete(Table, Key),
202 none
203 end.
204
205 gc_object(Pid, Table, Object) ->
206 case rabbit_misc:is_process_alive(Pid) of
207 true ->
208 none;
209 false ->
210 ets:delete_object(Table, Object),
211 none
212 end.
213
214 gc_object(Id, Table, Object, GbSet) ->
215 case gb_sets:is_member(Id, GbSet) of
216 true ->
217 none;
218 false ->
219 ets:delete_object(Table, Object),
220 none
221 end.
222
223 gc_entities(Table, QueueGbSet, ExchangeGbSet) ->
224 ets:foldl(fun({{{Q, X}, _} = Key, _}, none)
225 when Table == queue_exchange_stats_publish ->
226 gc_entity(Q, Table, Key, QueueGbSet),
227 gc_entity(X, Table, Key, ExchangeGbSet);
228 ({Q, {{_, X}, _}} = Object, none)
229 when Table == queue_exchange_stats_publish_queue_index ->
230 gc_object(X, Table, Object, ExchangeGbSet),
231 gc_entity(Q, Table, Q, QueueGbSet);
232 ({X, {{Q, _}, _}} = Object, none)
233 when Table == queue_exchange_stats_publish_exchange_index ->
234 gc_object(Q, Table, Object, QueueGbSet),
235 gc_entity(X, Table, X, ExchangeGbSet)
236 end, none, Table).
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15 -module(rabbit_mgmt_metrics_collector).
16
17 -include_lib("rabbit_common/include/rabbit.hrl").
18 -include_lib("rabbit_common/include/rabbit_core_metrics.hrl").
19 -include("rabbit_mgmt_metrics.hrl").
20
21 -behaviour(gen_server).
22
23 -spec start_link(atom()) -> rabbit_types:ok_pid_or_error().
24
25 -export([name/1]).
26 -export([start_link/1]).
27 -export([override_lookups/2, reset_lookups/1]).
28 -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
29 code_change/3]).
30 -export([index_table/2]).
31 -export([reset_all/0]).
32
33 -import(rabbit_mgmt_data, [lookup_element/3]).
34
35 -record(state, {table, interval, policies, rates_mode, lookup_queue,
36 lookup_exchange, old_aggr_stats}).
37
38 %% Data is stored in ETS tables:
39 %% * One ETS table per metric (queue_stats, channel_stats_deliver_stats...)
40 %% (see ?TABLES in rabbit_mgmt_metrics.hrl)
41 %% * Stats are stored as key value pairs where the key is a tuple of
42 %% some value (such as a channel pid) and the retention interval.
43 %% The value is an instance of an exometer_slide providing a sliding window
44 %% of samples for some {Object, Interval}.
45 %% * Each slide can store multiple stats. See stats_per_table in
46 %% rabbit_mgmt_metrics.hrl for a map of which stats are recorded in which
47 %% table.
48
49 reset_all() ->
50 [reset(Table) || {Table, _} <- ?CORE_TABLES].
51
52 reset(Table) ->
53 gen_server:cast(name(Table), reset).
54
55 name(Table) ->
56 list_to_atom((atom_to_list(Table) ++ "_metrics_collector")).
57
58
59 start_link(Table) ->
60 gen_server:start_link({local, name(Table)}, ?MODULE, [Table], []).
61
62 override_lookups(Table, Lookups) ->
63 gen_server:call(name(Table), {override_lookups, Lookups}, infinity).
64
65 reset_lookups(Table) ->
66 gen_server:call(name(Table), reset_lookups, infinity).
67
68 init([Table]) ->
69 {RatesMode, Policies} = load_config(),
70 Policy = retention_policy(Table),
71 Interval = take_smaller(proplists:get_value(Policy, Policies, [])) * 1000,
72 erlang:send_after(Interval, self(), collect_metrics),
73 {ok, #state{table = Table, interval = Interval,
74 policies = {proplists:get_value(basic, Policies),
75 proplists:get_value(detailed, Policies),
76 proplists:get_value(global, Policies)},
77 rates_mode = RatesMode,
78 old_aggr_stats = dict:new(),
79 lookup_queue = fun queue_exists/1,
80 lookup_exchange = fun exchange_exists/1}}.
81
82 handle_call(reset_lookups, _From, State) ->
83 {reply, ok, State#state{lookup_queue = fun queue_exists/1,
84 lookup_exchange = fun exchange_exists/1}};
85 handle_call({override_lookups, Lookups}, _From, State) ->
86 {reply, ok, State#state{lookup_queue = pget(queue, Lookups),
87 lookup_exchange = pget(exchange, Lookups)}};
88 handle_call({submit, Fun}, _From, State) ->
89 {reply, Fun(), State};
90 handle_call(_Request, _From, State) ->
91 {noreply, State}.
92
93 handle_cast(reset, State) ->
94 {noreply, State#state{old_aggr_stats = dict:new()}};
95 handle_cast(_Msg, State) ->
96 {noreply, State}.
97
98
99 handle_info(collect_metrics, #state{interval = Interval} = State0) ->
100 Timestamp = exometer_slide:timestamp(),
101 State = aggregate_metrics(Timestamp, State0),
102 erlang:send_after(Interval, self(), collect_metrics),
103 {noreply, State};
104 handle_info(purge_old_stats, State) ->
105 {noreply, State#state{old_aggr_stats = dict:new()}};
106 handle_info(_Msg, State) ->
107 {noreply, State}.
108
109 terminate(_Reason, _State) ->
110 ok.
111
112 code_change(_OldVsn, State, _Extra) ->
113 {ok, State}.
114
115 retention_policy(connection_created) -> basic; %% really nothing
116 retention_policy(connection_metrics) -> basic;
117 retention_policy(connection_coarse_metrics) -> basic;
118 retention_policy(channel_created) -> basic;
119 retention_policy(channel_metrics) -> basic;
120 retention_policy(channel_queue_exchange_metrics) -> detailed;
121 retention_policy(channel_exchange_metrics) -> detailed;
122 retention_policy(channel_queue_metrics) -> detailed;
123 retention_policy(channel_process_metrics) -> basic;
124 retention_policy(consumer_created) -> basic;
125 retention_policy(queue_metrics) -> basic;
126 retention_policy(queue_coarse_metrics) -> basic;
127 retention_policy(node_persister_metrics) -> global;
128 retention_policy(node_coarse_metrics) -> global;
129 retention_policy(node_metrics) -> basic;
130 retention_policy(node_node_metrics) -> global.
131
132 take_smaller(Policies) ->
133 Intervals = [I || {_, I} <- Policies],
134 case Intervals of
135 [] -> throw(missing_sample_retention_policies);
136 _ -> lists:min(Intervals)
137 end.
138
139 insert_old_aggr_stats(NextStats, Id, Stat) ->
140 dict:store(Id, Stat, NextStats).
141
142 handle_deleted_queues(queue_coarse_metrics, Remainders, GPolicies) ->
143 TS = exometer_slide:timestamp(),
144 lists:foreach(fun ({Queue, {R, U, M}}) ->
145 NegStats = ?vhost_msg_stats(-R, -U, -M),
146 [insert_entry(vhost_msg_stats, vhost(Queue), TS,
147 NegStats, Size, Interval, true)
148 || {Size, Interval} <- GPolicies]
149 end,
150 dict:to_list(Remainders));
151 handle_deleted_queues(_T, _R, _P) -> ok.
152
153 aggregate_metrics(Timestamp, #state{table = Table,
154 policies = {_, _, GPolicies}} = State0) ->
155 Table = State0#state.table,
156 {Next, Ops, #state{old_aggr_stats = Remainders}} =
157 ets:foldl(fun(R, {NextStats, O, State}) ->
158 aggregate_entry(R, NextStats, O, State)
159 end, {dict:new(), dict:new(), State0}, Table),
160 dict:fold(fun(Tbl, TblOps, Acc) ->
161 _ = exec_table_ops(Tbl, Timestamp, TblOps),
162 Acc
163 end, no_acc, Ops),
164
165 handle_deleted_queues(Table, Remainders, GPolicies),
166 State0#state{old_aggr_stats = Next}.
167
168 exec_table_ops(Table, Timestamp, TableOps) ->
169 dict:fold(fun(_Id, {insert, Entry}, A) ->
170 ets:insert(Table, Entry),
171 A;
172 (Id, {insert_with_index, Entry}, A) ->
173 insert_with_index(Table, Id, Entry),
174 A;
175 ({Id, Size, Interval, Incremental},
176 {insert_entry, Entry}, A) ->
177 insert_entry(Table, Id, Timestamp, Entry, Size,
178 Interval, Incremental),
179 A
180 end, no_acc, TableOps).
181
182
183 aggregate_entry({Id, Metrics}, NextStats, Ops0,
184 #state{table = connection_created} = State) ->
185 Ftd = rabbit_mgmt_format:format(
186 Metrics,
187 {fun rabbit_mgmt_format:format_connection_created/1, true}),
188 Entry = ?connection_created_stats(Id, pget(name, Ftd, unknown), Ftd),
189 Ops = insert_op(connection_created_stats, Id, Entry, Ops0),
190 {NextStats, Ops, State};
191 aggregate_entry({Id, Metrics}, NextStats, Ops0,
192 #state{table = connection_metrics} = State) ->
193 Entry = ?connection_stats(Id, Metrics),
194 Ops = insert_op(connection_stats, Id, Entry, Ops0),
195 {NextStats, Ops, State};
196 aggregate_entry({Id, RecvOct, SendOct, Reductions, 0}, NextStats, Ops0,
197 #state{table = connection_coarse_metrics,
198 policies = {BPolicies, _, GPolicies}} = State) ->
199 Stats = ?vhost_stats_coarse_conn_stats(RecvOct, SendOct),
200 Diff = get_difference(Id, Stats, State),
201
202 Ops1 = insert_entry_ops(vhost_stats_coarse_conn_stats,
203 vhost({connection_created, Id}), true, Diff, Ops0,
204 GPolicies),
205
206 Entry = ?connection_stats_coarse_conn_stats(RecvOct, SendOct, Reductions),
207 Ops2 = insert_entry_ops(connection_stats_coarse_conn_stats, Id, false, Entry,
208 Ops1, BPolicies),
209 {insert_old_aggr_stats(NextStats, Id, Stats), Ops2, State};
210 aggregate_entry({Id, RecvOct, SendOct, _Reductions, 1}, NextStats, Ops0,
211 #state{table = connection_coarse_metrics,
212 policies = {_BPolicies, _, GPolicies}} = State) ->
213 Stats = ?vhost_stats_coarse_conn_stats(RecvOct, SendOct),
214 Diff = get_difference(Id, Stats, State),
215 Ops1 = insert_entry_ops(vhost_stats_coarse_conn_stats,
216 vhost({connection_created, Id}), true, Diff, Ops0,
217 GPolicies),
218 rabbit_core_metrics:delete(connection_coarse_metrics, Id),
219 {NextStats, Ops1, State};
220 aggregate_entry({Id, Metrics}, NextStats, Ops0,
221 #state{table = channel_created} = State) ->
222 Ftd = rabbit_mgmt_format:format(Metrics, {[], false}),
223 Entry = ?channel_created_stats(Id, pget(name, Ftd, unknown), Ftd),
224 Ops = insert_op(channel_created_stats, Id, Entry, Ops0),
225 {NextStats, Ops, State};
226 aggregate_entry({Id, Metrics}, NextStats, Ops0,
227 #state{table = channel_metrics} = State) ->
228 Ftd = rabbit_mgmt_format:format(Metrics,
229 {fun rabbit_mgmt_format:format_channel_stats/1, true}),
230
231 Entry = ?channel_stats(Id, Ftd),
232 Ops = insert_op(channel_stats, Id, Entry, Ops0),
233 {NextStats, Ops, State};
234 aggregate_entry({{Ch, X} = Id, Publish0, Confirm, ReturnUnroutable, 0},
235 NextStats, Ops0,
236 #state{table = channel_exchange_metrics,
237 policies = {BPolicies, DPolicies, GPolicies},
238 rates_mode = RatesMode,
239 lookup_exchange = ExchangeFun} = State) ->
240 Stats = ?channel_stats_fine_stats(Publish0, Confirm, ReturnUnroutable),
241 {Publish, _, _} = Diff = get_difference(Id, Stats, State),
242
243 Ops1 = insert_entry_ops(channel_stats_fine_stats, Ch, true, Diff, Ops0,
244 BPolicies),
245 Ops2 = insert_entry_ops(vhost_stats_fine_stats, vhost(X), true, Diff, Ops1,
246 GPolicies),
247 Ops3 = case {ExchangeFun(X), RatesMode} of
248 {true, basic} ->
249 Entry = ?exchange_stats_publish_in(Publish),
250 insert_entry_ops(exchange_stats_publish_in, X, true, Entry,
251 Ops2, DPolicies);
252 {true, _} ->
253 Entry = ?exchange_stats_publish_in(Publish),
254 O = insert_entry_ops(exchange_stats_publish_in, X, true,
255 Entry, Ops2, DPolicies),
256 insert_entry_ops(channel_exchange_stats_fine_stats, Id,
257 false, Stats, O, DPolicies);
258 _ ->
259 Ops2
260 end,
261 {insert_old_aggr_stats(NextStats, Id, Stats), Ops3, State};
262 aggregate_entry({{_Ch, X} = Id, Publish0, Confirm, ReturnUnroutable, 1},
263 NextStats, Ops0,
264 #state{table = channel_exchange_metrics,
265 policies = {_BPolicies, DPolicies, GPolicies},
266 lookup_exchange = ExchangeFun} = State) ->
267 Stats = ?channel_stats_fine_stats(Publish0, Confirm, ReturnUnroutable),
268 {Publish, _, _} = Diff = get_difference(Id, Stats, State),
269 Ops1 = insert_entry_ops(vhost_stats_fine_stats, vhost(X), true, Diff, Ops0,
270 GPolicies),
271 Ops2 = case ExchangeFun(X) of
272 true ->
273 Entry = ?exchange_stats_publish_in(Publish),
274 insert_entry_ops(exchange_stats_publish_in, X, true, Entry,
275 Ops1, DPolicies);
276 _ ->
277 Ops1
278 end,
279 rabbit_core_metrics:delete(channel_exchange_metrics, Id),
280 {NextStats, Ops2, State};
281 aggregate_entry({{Ch, Q} = Id, Get, GetNoAck, Deliver, DeliverNoAck,
282 Redeliver, Ack, 0}, NextStats, Ops0,
283 #state{table = channel_queue_metrics,
284 policies = {BPolicies, DPolicies, GPolicies},
285 rates_mode = RatesMode,
286 lookup_queue = QueueFun} = State) ->
287 Stats = ?vhost_stats_deliver_stats(Get, GetNoAck, Deliver, DeliverNoAck,
288 Redeliver, Ack,
289 Deliver + DeliverNoAck + Get + GetNoAck),
290 Diff = get_difference(Id, Stats, State),
291
292 Ops1 = insert_entry_ops(vhost_stats_deliver_stats, vhost(Q), true, Diff,
293 Ops0, GPolicies),
294
295 Ops2 = insert_entry_ops(channel_stats_deliver_stats, Ch, true, Diff, Ops1,
296 BPolicies),
297
298 Ops3 = case {QueueFun(Q), RatesMode} of
299 {true, basic} ->
300 insert_entry_ops(queue_stats_deliver_stats, Q, true, Diff,
301 Ops2, BPolicies);
302 {true, _} ->
303 O = insert_entry_ops(queue_stats_deliver_stats, Q, true,
304 Diff, Ops2, BPolicies),
305 insert_entry_ops(channel_queue_stats_deliver_stats, Id,
306 false, Stats, O, DPolicies);
307 _ ->
308 Ops2
309 end,
310 {insert_old_aggr_stats(NextStats, Id, Stats), Ops3, State};
311 aggregate_entry({{_, Q} = Id, Get, GetNoAck, Deliver, DeliverNoAck,
312 Redeliver, Ack, 1}, NextStats, Ops0,
313 #state{table = channel_queue_metrics,
314 policies = {BPolicies, _, GPolicies},
315 lookup_queue = QueueFun} = State) ->
316 Stats = ?vhost_stats_deliver_stats(Get, GetNoAck, Deliver, DeliverNoAck,
317 Redeliver, Ack,
318 Deliver + DeliverNoAck + Get + GetNoAck),
319 Diff = get_difference(Id, Stats, State),
320
321 Ops1 = insert_entry_ops(vhost_stats_deliver_stats, vhost(Q), true, Diff,
322 Ops0, GPolicies),
323 Ops2 = case QueueFun(Q) of
324 true ->
325 insert_entry_ops(queue_stats_deliver_stats, Q, true, Diff,
326 Ops1, BPolicies);
327 _ ->
328 Ops1
329 end,
330 rabbit_core_metrics:delete(channel_queue_metrics, Id),
331 {NextStats, Ops2, State};
332 aggregate_entry({{_Ch, {Q, X}} = Id, Publish, ToDelete}, NextStats, Ops0,
333 #state{table = channel_queue_exchange_metrics,
334 policies = {BPolicies, _, _},
335 rates_mode = RatesMode,
336 lookup_queue = QueueFun,
337 lookup_exchange = ExchangeFun} = State) ->
338 Stats = ?queue_stats_publish(Publish),
339 Diff = get_difference(Id, Stats, State),
340 Ops1 = case {QueueFun(Q), ExchangeFun(X), RatesMode, ToDelete} of
341 {true, false, _, _} ->
342 insert_entry_ops(queue_stats_publish, Q, true, Diff,
343 Ops0, BPolicies);
344 {false, true, _, _} ->
345 insert_entry_ops(exchange_stats_publish_out, X, true, Diff,
346 Ops0, BPolicies);
347 {true, true, basic, _} ->
348 O = insert_entry_ops(queue_stats_publish, Q, true, Diff,
349 Ops0, BPolicies),
350 insert_entry_ops(exchange_stats_publish_out, X, true, Diff,
351 O, BPolicies);
352 {true, true, _, 0} ->
353 O1 = insert_entry_ops(queue_stats_publish, Q, true, Diff,
354 Ops0, BPolicies),
355 O2 = insert_entry_ops(exchange_stats_publish_out, X, true,
356 Diff, O1, BPolicies),
357 insert_entry_ops(queue_exchange_stats_publish, {Q, X},
358 true, Diff, O2, BPolicies);
359 {true, true, _, 1} ->
360 O = insert_entry_ops(queue_stats_publish, Q, true, Diff,
361 Ops0, BPolicies),
362 insert_entry_ops(exchange_stats_publish_out, X, true,
363 Diff, O, BPolicies);
364 _ ->
365 Ops0
366 end,
367 case ToDelete of
368 0 ->
369 {insert_old_aggr_stats(NextStats, Id, Stats), Ops1, State};
370 1 ->
371 rabbit_core_metrics:delete(channel_queue_exchange_metrics, Id),
372 {NextStats, Ops1, State}
373 end;
374 aggregate_entry({Id, Reductions}, NextStats, Ops0,
375 #state{table = channel_process_metrics,
376 policies = {BPolicies, _, _}} = State) ->
377 Entry = ?channel_process_stats(Reductions),
378 Ops = insert_entry_ops(channel_process_stats, Id, false,
379 Entry, Ops0, BPolicies),
380 {NextStats, Ops, State};
381 aggregate_entry({Id, Exclusive, AckRequired, PrefetchCount, Args},
382 NextStats, Ops0,
383 #state{table = consumer_created} = State) ->
384 Fmt = rabbit_mgmt_format:format([{exclusive, Exclusive},
385 {ack_required, AckRequired},
386 {prefetch_count, PrefetchCount},
387 {arguments, Args}], {[], false}),
388 Entry = ?consumer_stats(Id, Fmt),
389 Ops = insert_with_index_op(consumer_stats, Id, Entry, Ops0),
390 {NextStats, Ops ,State};
391 aggregate_entry({Id, Metrics, 0}, NextStats, Ops0,
392 #state{table = queue_metrics,
393 policies = {BPolicies, _, GPolicies},
394 lookup_queue = QueueFun} = State) ->
395 Stats = ?queue_msg_rates(pget(disk_reads, Metrics, 0),
396 pget(disk_writes, Metrics, 0)),
397 Diff = get_difference(Id, Stats, State),
398 Ops1 = insert_entry_ops(vhost_msg_rates, vhost(Id), true, Diff, Ops0,
399 GPolicies),
400 Ops2 = case QueueFun(Id) of
401 true ->
402 O = insert_entry_ops(queue_msg_rates, Id, false, Stats, Ops1,
403 BPolicies),
404 Fmt = rabbit_mgmt_format:format(
405 Metrics,
406 {fun rabbit_mgmt_format:format_queue_stats/1, false}),
407 insert_op(queue_stats, Id, ?queue_stats(Id, Fmt), O);
408 false ->
409 Ops1
410 end,
411 {insert_old_aggr_stats(NextStats, Id, Stats), Ops2, State};
412 aggregate_entry({Id, Metrics, 1}, NextStats, Ops0,
413 #state{table = queue_metrics,
414 policies = {_, _, GPolicies}} = State) ->
415 Stats = ?queue_msg_rates(pget(disk_reads, Metrics, 0),
416 pget(disk_writes, Metrics, 0)),
417 Diff = get_difference(Id, Stats, State),
418 Ops = insert_entry_ops(vhost_msg_rates, vhost(Id), true, Diff, Ops0,
419 GPolicies),
420 rabbit_core_metrics:delete(queue_metrics, Id),
421 {NextStats, Ops, State};
422 aggregate_entry({Name, Ready, Unack, Msgs, Red}, NextStats, Ops0,
423 #state{table = queue_coarse_metrics,
424 old_aggr_stats = Old,
425 policies = {BPolicies, _, GPolicies},
426 lookup_queue = QueueFun} = State) ->
427 Stats = ?vhost_msg_stats(Ready, Unack, Msgs),
428 Diff = get_difference(Name, Stats, State),
429 Ops1 = insert_entry_ops(vhost_msg_stats, vhost(Name), true, Diff, Ops0,
430 GPolicies),
431 Ops2 = case QueueFun(Name) of
432 true ->
433 QPS =?queue_process_stats(Red),
434 O1 = insert_entry_ops(queue_process_stats, Name, false, QPS,
435 Ops1, BPolicies),
436 QMS = ?queue_msg_stats(Ready, Unack, Msgs),
437 insert_entry_ops(queue_msg_stats, Name, false, QMS,
438 O1, BPolicies);
439 _ ->
440 Ops1
441 end,
442 State1 = State#state{old_aggr_stats = dict:erase(Name, Old)},
443 {insert_old_aggr_stats(NextStats, Name, Stats), Ops2, State1};
444 aggregate_entry({Id, Metrics}, NextStats, Ops0,
445 #state{table = node_metrics} = State) ->
446 Ops = insert_op(node_stats, Id, {Id, Metrics}, Ops0),
447 {NextStats, Ops, State};
448 aggregate_entry({Id, Metrics}, NextStats, Ops0,
449 #state{table = node_coarse_metrics,
450 policies = {_, _, GPolicies}} = State) ->
451 Stats = ?node_coarse_stats(
452 pget(fd_used, Metrics, 0), pget(sockets_used, Metrics, 0),
453 pget(mem_used, Metrics, 0), pget(disk_free, Metrics, 0),
454 pget(proc_used, Metrics, 0), pget(gc_num, Metrics, 0),
455 pget(gc_bytes_reclaimed, Metrics, 0),
456 pget(context_switches, Metrics, 0)),
457 Ops = insert_entry_ops(node_coarse_stats, Id, false, Stats, Ops0,
458 GPolicies),
459 {NextStats, Ops, State};
460 aggregate_entry({Id, Metrics}, NextStats, Ops0,
461 #state{table = node_persister_metrics,
462 policies = {_, _, GPolicies}} = State) ->
463 Stats = ?node_persister_stats(
464 pget(io_read_count, Metrics, 0), pget(io_read_bytes, Metrics, 0),
465 pget(io_read_time, Metrics, 0), pget(io_write_count, Metrics, 0),
466 pget(io_write_bytes, Metrics, 0), pget(io_write_time, Metrics, 0),
467 pget(io_sync_count, Metrics, 0), pget(io_sync_time, Metrics, 0),
468 pget(io_seek_count, Metrics, 0), pget(io_seek_time, Metrics, 0),
469 pget(io_reopen_count, Metrics, 0), pget(mnesia_ram_tx_count, Metrics, 0),
470 pget(mnesia_disk_tx_count, Metrics, 0), pget(msg_store_read_count, Metrics, 0),
471 pget(msg_store_write_count, Metrics, 0),
472 pget(queue_index_journal_write_count, Metrics, 0),
473 pget(queue_index_write_count, Metrics, 0), pget(queue_index_read_count, Metrics, 0),
474 pget(io_file_handle_open_attempt_count, Metrics, 0),
475 pget(io_file_handle_open_attempt_time, Metrics, 0)),
476 Ops = insert_entry_ops(node_persister_stats, Id, false, Stats, Ops0,
477 GPolicies),
478 {NextStats, Ops, State};
479 aggregate_entry({Id, Metrics}, NextStats, Ops0,
480 #state{table = node_node_metrics,
481 policies = {_, _, GPolicies}} = State) ->
482 Stats = ?node_node_coarse_stats(pget(send_bytes, Metrics, 0),
483 pget(recv_bytes, Metrics, 0)),
484 CleanMetrics = lists:keydelete(recv_bytes, 1,
485 lists:keydelete(send_bytes, 1, Metrics)),
486 Ops1 = insert_op(node_node_stats, Id, ?node_node_stats(Id, CleanMetrics),
487 Ops0),
488 Ops = insert_entry_ops(node_node_coarse_stats, Id, false, Stats, Ops1,
489 GPolicies),
490 {NextStats, Ops, State}.
491
492 insert_entry(Table, Id, TS, Entry, Size, Interval0, Incremental) ->
493 Key = {Id, Interval0},
494 Slide =
495 case ets:lookup(Table, Key) of
496 [{Key, S}] ->
497 S;
498 [] ->
499 IntervalMs = Interval0 * 1000,
500 % add some margin to Size and max_n to reduce chances of off-by-one errors
501 exometer_slide:new(TS - IntervalMs, (Size + Interval0) * 1000,
502 [{interval, IntervalMs},
503 {max_n, ceil(Size / Interval0) + 1},
504 {incremental, Incremental}])
505 end,
506 insert_with_index(Table, Key, {Key, exometer_slide:add_element(TS, Entry,
507 Slide)}).
508
509 update_op(Table, Key, Op, Ops) ->
510 TableOps = case dict:find(Table, Ops) of
511 {ok, Inner} ->
512 dict:store(Key, Op, Inner);
513 error ->
514 Inner = dict:new(),
515 dict:store(Key, Op, Inner)
516 end,
517 dict:store(Table, TableOps, Ops).
518
519 insert_with_index_op(Table, Key, Entry, Ops) ->
520 update_op(Table, Key, {insert_with_index, Entry}, Ops).
521
522 insert_op(Table, Key, Entry, Ops) ->
523 update_op(Table, Key, {insert, Entry}, Ops).
524
525 insert_entry_op(Table, Key, Entry, Ops) ->
526 TableOps0 = case dict:find(Table, Ops) of
527 {ok, Inner} -> Inner;
528 error -> dict:new()
529 end,
530 TableOps = dict:update(Key, fun({insert_entry, Entry0}) ->
531 {insert_entry, sum_entry(Entry0, Entry)}
532 end, {insert_entry, Entry}, TableOps0),
533 dict:store(Table, TableOps, Ops).
534
535 insert_entry_ops(Table, Id, Incr, Entry, Ops, Policies) ->
536 lists:foldl(fun({Size, Interval}, Acc) ->
537 Key = {Id, Size, Interval, Incr},
538 insert_entry_op(Table, Key, Entry, Acc)
539 end, Ops, Policies).
540
541 get_difference(Id, Stats, #state{old_aggr_stats = OldStats}) ->
542 case dict:find(Id, OldStats) of
543 error ->
544 Stats;
545 {ok, OldStat} ->
546 difference(OldStat, Stats)
547 end.
548
549 sum_entry({A0}, {B0}) ->
550 {B0 + A0};
551 sum_entry({A0, A1}, {B0, B1}) ->
552 {B0 + A0, B1 + A1};
553 sum_entry({A0, A1, A2}, {B0, B1, B2}) ->
554 {B0 + A0, B1 + A1, B2 + A2};
555 sum_entry({A0, A1, A2, A3, A4, A5, A6}, {B0, B1, B2, B3, B4, B5, B6}) ->
556 {B0 + A0, B1 + A1, B2 + A2, B3 + A3, B4 + A4, B5 + A5, B6 + A6}.
557
558 difference({A0}, {B0}) ->
559 {B0 - A0};
560 difference({A0, A1}, {B0, B1}) ->
561 {B0 - A0, B1 - A1};
562 difference({A0, A1, A2}, {B0, B1, B2}) ->
563 {B0 - A0, B1 - A1, B2 - A2};
564 difference({A0, A1, A2, A3, A4, A5, A6}, {B0, B1, B2, B3, B4, B5, B6}) ->
565 {B0 - A0, B1 - A1, B2 - A2, B3 - A3, B4 - A4, B5 - A5, B6 - A6}.
566
567 vhost(#resource{virtual_host = VHost}) ->
568 VHost;
569 vhost({queue_stats, #resource{virtual_host = VHost}}) ->
570 VHost;
571 vhost({TName, Pid}) ->
572 pget(vhost, lookup_element(TName, Pid, 2)).
573
574 exchange_exists(Name) ->
575 case rabbit_exchange:lookup(Name) of
576 {ok, _} ->
577 true;
578 _ ->
579 false
580 end.
581
582 queue_exists(Name) ->
583 case rabbit_amqqueue:lookup(Name) of
584 {ok, _} ->
585 true;
586 _ ->
587 false
588 end.
589
590 insert_with_index(Table, Key, Tuple) ->
591 Insert = ets:insert(Table, Tuple),
592 insert_index(Table, Key),
593 Insert.
594
595 insert_index(consumer_stats, {Q, Ch, _} = Key) ->
596 ets:insert(index_table(consumer_stats, queue), {Q, Key}),
597 ets:insert(index_table(consumer_stats, channel), {Ch, Key});
598 insert_index(channel_exchange_stats_fine_stats, {{Ch, Ex}, _} = Key) ->
599 ets:insert(index_table(channel_exchange_stats_fine_stats, exchange), {Ex, Key}),
600 ets:insert(index_table(channel_exchange_stats_fine_stats, channel), {Ch, Key});
601 insert_index(channel_queue_stats_deliver_stats, {{Ch, Q}, _} = Key) ->
602 ets:insert(index_table(channel_queue_stats_deliver_stats, queue), {Q, Key}),
603 ets:insert(index_table(channel_queue_stats_deliver_stats, channel), {Ch, Key});
604 insert_index(queue_exchange_stats_publish, {{Q, Ex}, _} = Key) ->
605 ets:insert(index_table(queue_exchange_stats_publish, queue), {Q, Key}),
606 ets:insert(index_table(queue_exchange_stats_publish, exchange), {Ex, Key});
607 insert_index(node_node_coarse_stats, {{_, Node}, _} = Key) ->
608 ets:insert(index_table(node_node_coarse_stats, node), {Node, Key});
609 insert_index(_, _) -> ok.
610
611 index_table(consumer_stats, queue) -> consumer_stats_queue_index;
612 index_table(consumer_stats, channel) -> consumer_stats_channel_index;
613 index_table(channel_exchange_stats_fine_stats, exchange) -> channel_exchange_stats_fine_stats_exchange_index;
614 index_table(channel_exchange_stats_fine_stats, channel) -> channel_exchange_stats_fine_stats_channel_index;
615 index_table(channel_queue_stats_deliver_stats, queue) -> channel_queue_stats_deliver_stats_queue_index;
616 index_table(channel_queue_stats_deliver_stats, channel) -> channel_queue_stats_deliver_stats_channel_index;
617 index_table(queue_exchange_stats_publish, queue) -> queue_exchange_stats_publish_queue_index;
618 index_table(queue_exchange_stats_publish, exchange) -> queue_exchange_stats_publish_exchange_index;
619 index_table(node_node_coarse_stats, node) -> node_node_coarse_stats_node_index.
620
621 load_config() ->
622 RatesMode = rabbit_mgmt_agent_config:get_env(rates_mode),
623 Policies = rabbit_mgmt_agent_config:get_env(sample_retention_policies, []),
624 {RatesMode, Policies}.
625
626 ceil(X) when X < 0 ->
627 trunc(X);
628 ceil(X) ->
629 T = trunc(X),
630 case X - T == 0 of
631 true -> T;
632 false -> T + 1
633 end.
634
635 pget(Key, List) -> pget(Key, List, unknown).
636
637 pget(Key, List, Default) when is_number(Default) ->
638 case rabbit_misc:pget(Key, List) of
639 Number when is_number(Number) ->
640 Number;
641 _Other ->
642 Default
643 end;
644 pget(Key, List, Default) ->
645 rabbit_misc:pget(Key, List, Default).
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15 -module(rabbit_mgmt_metrics_gc).
16
17 -record(state, {basic_i,
18 detailed_i,
19 global_i}).
20
21 -include_lib("rabbit_common/include/rabbit.hrl").
22
23 -spec start_link(atom()) -> rabbit_types:ok_pid_or_error().
24
25 -export([name/1]).
26 -export([start_link/1]).
27 -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
28 code_change/3]).
29
30 name(EventType) ->
31 list_to_atom((atom_to_list(EventType) ++ "_metrics_gc")).
32
33 start_link(EventType) ->
34 gen_server:start_link({local, name(EventType)}, ?MODULE, [], []).
35
36 init(_) ->
37 Policies = rabbit_mgmt_agent_config:get_env(sample_retention_policies),
38 {ok, #state{basic_i = intervals(basic, Policies),
39 global_i = intervals(global, Policies),
40 detailed_i = intervals(detailed, Policies)}}.
41
42 handle_call(_Request, _From, State) ->
43 {noreply, State}.
44
45 handle_cast({event, #event{type = connection_closed, props = Props}},
46 State = #state{basic_i = BIntervals}) ->
47 Pid = pget(pid, Props),
48 remove_connection(Pid, BIntervals),
49 {noreply, State};
50 handle_cast({event, #event{type = channel_closed, props = Props}},
51 State = #state{basic_i = BIntervals}) ->
52 Pid = pget(pid, Props),
53 remove_channel(Pid, BIntervals),
54 {noreply, State};
55 handle_cast({event, #event{type = consumer_deleted, props = Props}}, State) ->
56 remove_consumer(Props),
57 {noreply, State};
58 handle_cast({event, #event{type = exchange_deleted, props = Props}},
59 State = #state{basic_i = BIntervals}) ->
60 Name = pget(name, Props),
61 remove_exchange(Name, BIntervals),
62 {noreply, State};
63 handle_cast({event, #event{type = queue_deleted, props = Props}},
64 State = #state{basic_i = BIntervals}) ->
65 Name = pget(name, Props),
66 remove_queue(Name, BIntervals),
67 {noreply, State};
68 handle_cast({event, #event{type = vhost_deleted, props = Props}},
69 State = #state{global_i = GIntervals}) ->
70 Name = pget(name, Props),
71 remove_vhost(Name, GIntervals),
72 {noreply, State};
73 handle_cast({event, #event{type = node_node_deleted, props = Props}}, State) ->
74 Name = pget(route, Props),
75 remove_node_node(Name),
76 {noreply, State}.
77
78 handle_info(_Msg, State) ->
79 {noreply, State}.
80
81 terminate(_Reason, _State) ->
82 ok.
83
84 code_change(_OldVsn, State, _Extra) ->
85 {ok, State}.
86
87 remove_connection(Id, BIntervals) ->
88 ets:delete(connection_created_stats, Id),
89 ets:delete(connection_stats, Id),
90 delete_samples(connection_stats_coarse_conn_stats, Id, BIntervals),
91 ok.
92
93 remove_channel(Id, BIntervals) ->
94 ets:delete(channel_created_stats, Id),
95 ets:delete(channel_stats, Id),
96 delete_samples(channel_process_stats, Id, BIntervals),
97 delete_samples(channel_stats_fine_stats, Id, BIntervals),
98 delete_samples(channel_stats_deliver_stats, Id, BIntervals),
99 index_delete(consumer_stats, channel, Id),
100 index_delete(channel_exchange_stats_fine_stats, channel, Id),
101 index_delete(channel_queue_stats_deliver_stats, channel, Id),
102 ok.
103
104 remove_consumer(Props) ->
105 Id = {pget(queue, Props), pget(channel, Props), pget(consumer_tag, Props)},
106 ets:delete(consumer_stats, Id),
107 cleanup_index(consumer_stats, Id),
108 ok.
109
110 remove_exchange(Name, BIntervals) ->
111 delete_samples(exchange_stats_publish_out, Name, BIntervals),
112 delete_samples(exchange_stats_publish_in, Name, BIntervals),
113 index_delete(queue_exchange_stats_publish, exchange, Name),
114 index_delete(channel_exchange_stats_fine_stats, exchange, Name),
115 ok.
116
117 remove_queue(Name, BIntervals) ->
118 ets:delete(queue_stats, Name),
119 delete_samples(queue_stats_publish, Name, BIntervals),
120 delete_samples(queue_stats_deliver_stats, Name, BIntervals),
121 delete_samples(queue_process_stats, Name, BIntervals),
122 delete_samples(queue_msg_stats, Name, BIntervals),
123 delete_samples(queue_msg_rates, Name, BIntervals),
124 index_delete(channel_queue_stats_deliver_stats, queue, Name),
125 index_delete(queue_exchange_stats_publish, queue, Name),
126 index_delete(consumer_stats, queue, Name),
127
128 ok.
129
130 remove_vhost(Name, GIntervals) ->
131 delete_samples(vhost_stats_coarse_conn_stats, Name, GIntervals),
132 delete_samples(vhost_stats_fine_stats, Name, GIntervals),
133 delete_samples(vhost_stats_deliver_stats, Name, GIntervals),
134 ok.
135
136 remove_node_node(Name) ->
137 index_delete(node_node_coarse_stats, node, Name),
138 ok.
139
140 intervals(Type, Policies) ->
141 [I || {_, I} <- proplists:get_value(Type, Policies)].
142
143 delete_samples(Table, Id, Intervals) ->
144 [ets:delete(Table, {Id, I}) || I <- Intervals],
145 ok.
146
147 index_delete(Table, Type, Id) ->
148 IndexTable = rabbit_mgmt_metrics_collector:index_table(Table, Type),
149 Keys = ets:lookup(IndexTable, Id),
150 [ begin
151 ets:delete(Table, Key),
152 cleanup_index(Table, Key)
153 end
154 || {_Index, Key} <- Keys ],
155 ets:delete(IndexTable, Id),
156 ok.
157
158 cleanup_index(consumer_stats, {Q, Ch, _} = Key) ->
159 delete_index(consumer_stats, queue, {Q, Key}),
160 delete_index(consumer_stats, channel, {Ch, Key}),
161 ok;
162 cleanup_index(channel_exchange_stats_fine_stats, {{Ch, Ex}, _} = Key) ->
163 delete_index(channel_exchange_stats_fine_stats, exchange, {Ex, Key}),
164 delete_index(channel_exchange_stats_fine_stats, channel, {Ch, Key}),
165 ok;
166 cleanup_index(channel_queue_stats_deliver_stats, {{Ch, Q}, _} = Key) ->
167 delete_index(channel_queue_stats_deliver_stats, queue, {Q, Key}),
168 delete_index(channel_queue_stats_deliver_stats, channel, {Ch, Key}),
169 ok;
170 cleanup_index(queue_exchange_stats_publish, {{Q, Ex}, _} = Key) ->
171 delete_index(queue_exchange_stats_publish, queue, {Q, Key}),
172 delete_index(queue_exchange_stats_publish, exchange, {Ex, Key}),
173 ok;
174 cleanup_index(node_node_coarse_stats, {{_, Node}, _} = Key) ->
175 delete_index(node_node_coarse_stats, node, {Node, Key}),
176 ok;
177 cleanup_index(_, _) -> ok.
178
179 delete_index(Table, Index, Obj) ->
180 ets:delete_object(rabbit_mgmt_metrics_collector:index_table(Table, Index),
181 Obj).
182
183 pget(Key, List) -> rabbit_misc:pget(Key, List, unknown).
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15 -module(rabbit_mgmt_storage).
16 -behaviour(gen_server2).
17 -record(state, {}).
18
19 -spec start_link() -> rabbit_types:ok_pid_or_error().
20
21 -export([start_link/0]).
22 -export([reset/0, reset_all/0]).
23 -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
24 code_change/3]).
25
26 -include("rabbit_mgmt_metrics.hrl").
27
28 %% ETS owner
29 start_link() ->
30 gen_server2:start_link({local, ?MODULE}, ?MODULE, [], []).
31
32 reset() ->
33 rabbit_log:warning("Resetting RabbitMQ management storage~n"),
34 [ets:delete_all_objects(IndexTable) || IndexTable <- ?INDEX_TABLES],
35 [ets:delete_all_objects(Table) || {Table, _} <- ?TABLES],
36 _ = rabbit_mgmt_metrics_collector:reset_all(),
37 ok.
38
39 reset_all() ->
40 [rpc:call(Node, rabbit_mgmt_storage, reset, [])
41 || Node <- rabbit_nodes:all_running()].
42
43 init(_) ->
44 _ = [ets:new(IndexTable, [public, bag, named_table])
45 || IndexTable <- ?INDEX_TABLES],
46 _ = [ets:new(Table, [public, Type, named_table])
47 || {Table, Type} <- ?TABLES],
48 _ = ets:new(rabbit_mgmt_db_cache, [public, set, named_table]),
49 {ok, #state{}}.
50
51 handle_call(_Request, _From, State) ->
52 {noreply, State}.
53
54 handle_cast(_Msg, State) ->
55 {noreply, State}.
56
57 handle_info(_Msg, State) ->
58 {noreply, State}.
59
60 terminate(_Reason, _State) ->
61 ok.
62
63 code_change(_OldVsn, State, _Extra) ->
64 {ok, State}.
+0
-8
deps/rabbitmq_management_agent/src/rabbitmq_management_agent.app.src less more
0 {application, rabbitmq_management_agent,
1 [{description, "RabbitMQ Management Agent"},
2 {vsn, "3.6.6"},
3 {modules, []},
4 {registered, []},
5 {mod, {rabbit_mgmt_agent_app, []}},
6 {env, []},
7 {applications, [kernel, stdlib, rabbit_common, rabbit]}]}.
00 PROJECT = rabbitmq_management_visualiser
1 PROJECT_DESCRIPTION = RabbitMQ Visualiser
12
2 DEPS = rabbit_common rabbit rabbitmq_management webmachine
3 DEPS = rabbit_common rabbit rabbitmq_management
4 TEST_DEPS = rabbitmq_ct_helpers
35
6 DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
47 DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk
58
69 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
0 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
11 #
22 # Permission to use, copy, modify, and/or distribute this software for any
33 # purpose with or without fee is hereby granted, provided that the above
1111 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1212 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1313
14 .PHONY: all app apps deps search rel docs install-docs check tests clean distclean help erlang-mk
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
1515
1616 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17
18 ERLANG_MK_VERSION = 2.0.0-pre.2-144-g647ffd1
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
1931
2032 # Core configuration.
2133
2436
2537 PROJECT_VERSION ?= rolling
2638 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
2740
2841 # Verbosity.
2942
8497 rel::
8598 $(verbose) :
8699
100 relup:: deps app
101
87102 check:: tests
88103
89104 clean:: clean-crashdump
101116 help::
102117 $(verbose) printf "%s\n" \
103118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
104 "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
105120 "" \
106121 "Usage: [V=1] $(MAKE) [target]..." \
107122 "" \
149164 core_native_path = $1
150165 endif
151166
152 ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
153 define core_http_get
154 wget --no-check-certificate -O $(1) $(2)|| rm $(1)
155 endef
156 else
157 define core_http_get.erl
158 ssl:start(),
159 inets:start(),
160 case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
161 {ok, {{_, 200, _}, _, Body}} ->
162 case file:write_file("$(1)", Body) of
163 ok -> ok;
164 {error, R1} -> halt(R1)
165 end;
166 {error, R2} ->
167 halt(R2)
168 end,
169 halt(0).
170 endef
171
172 define core_http_get
173 $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
174 endef
175 endif
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
176168
177169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
178170
192184 ERLANG_MK_BUILD_CONFIG ?= build.config
193185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
194186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
195188 erlang-mk:
196189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
197190 ifdef ERLANG_MK_COMMIT
198191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
199192 endif
200193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
201 $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
202195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
203196 rm -rf $(ERLANG_MK_BUILD_DIR)
204197
285278 pkg_apns_repo = https://github.com/inaka/apns4erl
286279 pkg_apns_commit = master
287280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
288289 PACKAGES += azdht
289290 pkg_azdht_name = azdht
290291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
405406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
406407 pkg_bootstrap_commit = master
407408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
408417 PACKAGES += boss_db
409418 pkg_boss_db_name = boss_db
410419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
413422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
414423 pkg_boss_db_commit = master
415424
416 PACKAGES += boss
417 pkg_boss_name = boss
418 pkg_boss_description = Erlang web MVC, now featuring Comet
419 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
420 pkg_boss_fetch = git
421 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
422 pkg_boss_commit = master
423
424425 PACKAGES += brod
425426 pkg_brod_name = brod
426427 pkg_brod_description = Kafka client in Erlang
565566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
566567 pkg_cloudi_service_api_requests_commit = master
567568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
568585 PACKAGES += cloudi_service_db_cassandra_cql
569586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
570587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
573590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
574591 pkg_cloudi_service_db_cassandra_cql_commit = master
575592
576 PACKAGES += cloudi_service_db_cassandra
577 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
579 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
580 pkg_cloudi_service_db_cassandra_fetch = git
581 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
582 pkg_cloudi_service_db_cassandra_commit = master
583
584593 PACKAGES += cloudi_service_db_couchdb
585594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
586595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
637646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
638647 pkg_cloudi_service_db_tokyotyrant_commit = master
639648
640 PACKAGES += cloudi_service_db
641 pkg_cloudi_service_db_name = cloudi_service_db
642 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
643 pkg_cloudi_service_db_homepage = http://cloudi.org/
644 pkg_cloudi_service_db_fetch = git
645 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
646 pkg_cloudi_service_db_commit = master
647
648649 PACKAGES += cloudi_service_filesystem
649650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
650651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
10371038 pkg_edown_repo = https://github.com/uwiger/edown
10381039 pkg_edown_commit = master
10391040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
10401049 PACKAGES += eep_app
10411050 pkg_eep_app_name = eep_app
10421051 pkg_eep_app_description = Embedded Event Processing
10451054 pkg_eep_app_repo = https://github.com/darach/eep-erl
10461055 pkg_eep_app_commit = master
10471056
1048 PACKAGES += eep
1049 pkg_eep_name = eep
1050 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1051 pkg_eep_homepage = https://github.com/virtan/eep
1052 pkg_eep_fetch = git
1053 pkg_eep_repo = https://github.com/virtan/eep
1054 pkg_eep_commit = master
1055
10561057 PACKAGES += efene
10571058 pkg_efene_name = efene
10581059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
12371238 pkg_eqm_repo = https://github.com/loucash/eqm
12381239 pkg_eqm_commit = master
12391240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
12401249 PACKAGES += eredis_pool
12411250 pkg_eredis_pool_name = eredis_pool
12421251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
12451254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
12461255 pkg_eredis_pool_commit = master
12471256
1248 PACKAGES += eredis
1249 pkg_eredis_name = eredis
1250 pkg_eredis_description = Erlang Redis client
1251 pkg_eredis_homepage = https://github.com/wooga/eredis
1252 pkg_eredis_fetch = git
1253 pkg_eredis_repo = https://github.com/wooga/eredis
1254 pkg_eredis_commit = master
1255
12561257 PACKAGES += erl_streams
12571258 pkg_erl_streams_name = erl_streams
12581259 pkg_erl_streams_description = Streams in Erlang
15411542 pkg_etap_repo = https://github.com/ngerakines/etap
15421543 pkg_etap_commit = master
15431544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
15441553 PACKAGES += etest_http
15451554 pkg_etest_http_name = etest_http
15461555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
15491558 pkg_etest_http_repo = https://github.com/wooga/etest_http
15501559 pkg_etest_http_commit = master
15511560
1552 PACKAGES += etest
1553 pkg_etest_name = etest
1554 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1555 pkg_etest_homepage = https://github.com/wooga/etest
1556 pkg_etest_fetch = git
1557 pkg_etest_repo = https://github.com/wooga/etest
1558 pkg_etest_commit = master
1559
15601561 PACKAGES += etoml
15611562 pkg_etoml_name = etoml
15621563 pkg_etoml_description = TOML language erlang parser
15651566 pkg_etoml_repo = https://github.com/kalta/etoml
15661567 pkg_etoml_commit = master
15671568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
15681577 PACKAGES += eunit_formatters
15691578 pkg_eunit_formatters_name = eunit_formatters
15701579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
15731582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
15741583 pkg_eunit_formatters_commit = master
15751584
1576 PACKAGES += eunit
1577 pkg_eunit_name = eunit
1578 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1579 pkg_eunit_homepage = https://github.com/richcarl/eunit
1580 pkg_eunit_fetch = git
1581 pkg_eunit_repo = https://github.com/richcarl/eunit
1582 pkg_eunit_commit = master
1583
15841585 PACKAGES += euthanasia
15851586 pkg_euthanasia_name = euthanasia
15861587 pkg_euthanasia_description = Merciful killer for your Erlang processes
15981599 pkg_evum_commit = master
15991600
16001601 PACKAGES += exec
1601 pkg_exec_name = exec
1602 pkg_exec_name = erlexec
16021603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
16031604 pkg_exec_homepage = http://saleyn.github.com/erlexec
16041605 pkg_exec_fetch = git
17171718 pkg_fn_repo = https://github.com/reiddraper/fn
17181719 pkg_fn_commit = master
17191720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
17201729 PACKAGES += folsom_cowboy
17211730 pkg_folsom_cowboy_name = folsom_cowboy
17221731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
17251734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
17261735 pkg_folsom_cowboy_commit = master
17271736
1728 PACKAGES += folsom
1729 pkg_folsom_name = folsom
1730 pkg_folsom_description = Expose Erlang Events and Metrics
1731 pkg_folsom_homepage = https://github.com/boundary/folsom
1732 pkg_folsom_fetch = git
1733 pkg_folsom_repo = https://github.com/boundary/folsom
1734 pkg_folsom_commit = master
1735
17361737 PACKAGES += folsomite
17371738 pkg_folsomite_name = folsomite
17381739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
20932094 pkg_jesse_repo = https://github.com/for-GET/jesse
20942095 pkg_jesse_commit = master
20952096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
20962105 PACKAGES += jiffy_v
20972106 pkg_jiffy_v_name = jiffy_v
20982107 pkg_jiffy_v_description = JSON validation utility
21012110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
21022111 pkg_jiffy_v_commit = master
21032112
2104 PACKAGES += jiffy
2105 pkg_jiffy_name = jiffy
2106 pkg_jiffy_description = JSON NIFs for Erlang.
2107 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2108 pkg_jiffy_fetch = git
2109 pkg_jiffy_repo = https://github.com/davisp/jiffy
2110 pkg_jiffy_commit = master
2111
21122113 PACKAGES += jobs
21132114 pkg_jobs_name = jobs
21142115 pkg_jobs_description = a Job scheduler for load regulation
21252126 pkg_joxa_repo = https://github.com/joxa/joxa
21262127 pkg_joxa_commit = master
21272128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
21282137 PACKAGES += json_rec
21292138 pkg_json_rec_name = json_rec
21302139 pkg_json_rec_description = JSON to erlang record
21332142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
21342143 pkg_json_rec_commit = master
21352144
2136 PACKAGES += json
2137 pkg_json_name = json
2138 pkg_json_description = a high level json library for erlang (17.0+)
2139 pkg_json_homepage = https://github.com/talentdeficit/json
2140 pkg_json_fetch = git
2141 pkg_json_repo = https://github.com/talentdeficit/json
2142 pkg_json_commit = master
2143
21442145 PACKAGES += jsone
21452146 pkg_jsone_name = jsone
21462147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
21812182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
21822183 pkg_jsx_commit = master
21832184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
21842193 PACKAGES += kafka_protocol
21852194 pkg_kafka_protocol_name = kafka_protocol
21862195 pkg_kafka_protocol_description = Kafka protocol Erlang library
21892198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
21902199 pkg_kafka_protocol_commit = master
21912200
2192 PACKAGES += kafka
2193 pkg_kafka_name = kafka
2194 pkg_kafka_description = Kafka consumer and producer in Erlang
2195 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2196 pkg_kafka_fetch = git
2197 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2198 pkg_kafka_commit = master
2199
22002201 PACKAGES += kai
22012202 pkg_kai_name = kai
22022203 pkg_kai_description = DHT storage by Takeshi Inoue
22932294 pkg_kvs_repo = https://github.com/synrc/kvs
22942295 pkg_kvs_commit = master
22952296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
22962305 PACKAGES += lager_amqp_backend
22972306 pkg_lager_amqp_backend_name = lager_amqp_backend
22982307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
23092318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
23102319 pkg_lager_syslog_commit = master
23112320
2312 PACKAGES += lager
2313 pkg_lager_name = lager
2314 pkg_lager_description = A logging framework for Erlang/OTP.
2315 pkg_lager_homepage = https://github.com/basho/lager
2316 pkg_lager_fetch = git
2317 pkg_lager_repo = https://github.com/basho/lager
2318 pkg_lager_commit = master
2319
23202321 PACKAGES += lambdapad
23212322 pkg_lambdapad_name = lambdapad
23222323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
25732574 pkg_mixer_repo = https://github.com/chef/mixer
25742575 pkg_mixer_commit = master
25752576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
25762585 PACKAGES += mochiweb_xpath
25772586 pkg_mochiweb_xpath_name = mochiweb_xpath
25782587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
25812590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
25822591 pkg_mochiweb_xpath_commit = master
25832592
2584 PACKAGES += mochiweb
2585 pkg_mochiweb_name = mochiweb
2586 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2587 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2588 pkg_mochiweb_fetch = git
2589 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2590 pkg_mochiweb_commit = master
2591
25922593 PACKAGES += mockgyver
25932594 pkg_mockgyver_name = mockgyver
25942595 pkg_mockgyver_description = A mocking library for Erlang
30613062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
30623063 pkg_quickrand_commit = master
30633064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
30643073 PACKAGES += rabbit_exchange_type_riak
30653074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
30663075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
30693078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
30703079 pkg_rabbit_exchange_type_riak_commit = master
30713080
3072 PACKAGES += rabbit
3073 pkg_rabbit_name = rabbit
3074 pkg_rabbit_description = RabbitMQ Server
3075 pkg_rabbit_homepage = https://www.rabbitmq.com/
3076 pkg_rabbit_fetch = git
3077 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3078 pkg_rabbit_commit = master
3079
30803081 PACKAGES += rack
30813082 pkg_rack_name = rack
30823083 pkg_rack_description = Rack handler for erlang
34933494 pkg_smother_repo = https://github.com/ramsay-t/Smother
34943495 pkg_smother_commit = master
34953496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
34963505 PACKAGES += social
34973506 pkg_social_name = social
34983507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
35413550 pkg_stable_repo = https://github.com/dvv/stable
35423551 pkg_stable_commit = master
35433552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
35443561 PACKAGES += statebox_riak
35453562 pkg_statebox_riak_name = statebox_riak
35463563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
35493566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
35503567 pkg_statebox_riak_commit = master
35513568
3552 PACKAGES += statebox
3553 pkg_statebox_name = statebox
3554 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3555 pkg_statebox_homepage = https://github.com/mochi/statebox
3556 pkg_statebox_fetch = git
3557 pkg_statebox_repo = https://github.com/mochi/statebox
3558 pkg_statebox_commit = master
3559
35603569 PACKAGES += statman
35613570 pkg_statman_name = statman
35623571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
37253734 pkg_tirerl_repo = https://github.com/inaka/tirerl
37263735 pkg_tirerl_commit = master
37273736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
37283745 PACKAGES += traffic_tools
37293746 pkg_traffic_tools_name = traffic_tools
37303747 pkg_traffic_tools_description = Simple traffic limiting library
40614078 pkg_zucchini_repo = https://github.com/devinus/zucchini
40624079 pkg_zucchini_commit = master
40634080
4064 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
40654082 # This file is part of erlang.mk and subject to the terms of the ISC License.
40664083
40674084 .PHONY: search
40884105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
40894106 endif
40904107
4091 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
40924109 # This file is part of erlang.mk and subject to the terms of the ISC License.
40934110
4094 .PHONY: distclean-deps
4111 .PHONY: distclean-deps clean-tmp-deps.log
40954112
40964113 # Configuration.
40974114
41104127
41114128 REBAR_DEPS_DIR = $(DEPS_DIR)
41124129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
41134150
41144151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
41154152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
41164153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
41174154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
41184155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
41194157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
41204158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
41214159
41384176
41394177 # Core targets.
41404178
4141 ifdef IS_APP
4142 apps::
4143 else
4144 apps:: $(ALL_APPS_DIRS)
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
41454180 ifeq ($(IS_APP)$(IS_DEP),)
41464181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
41474182 endif
41494184 # Create ebin directory for all apps to make sure Erlang recognizes them
41504185 # as proper OTP applications when using -include_lib. This is a temporary
41514186 # fix, a proper fix would be to compile apps/* in the right order.
4152 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4153 mkdir -p $$dep/ebin || exit $$?; \
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
41544190 done
4155 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
41564196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
41574197 :; \
41584198 else \
41594199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4160 $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
4200 $(MAKE) -C $$dep IS_APP=1; \
41614201 fi \
41624202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
41634207 endif
41644208
41654209 ifneq ($(SKIP_DEPS),)
41664210 deps::
41674211 else
4168 deps:: $(ALL_DEPS_DIRS) apps
4169 ifeq ($(IS_APP)$(IS_DEP),)
4170 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4171 endif
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
41724213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4173 $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
41744215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
41754216 :; \
41764217 else \
41774218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
41784219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4179 $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
4220 $(MAKE) -C $$dep IS_DEP=1; \
41804221 else \
4181 echo "Error: No Makefile to build dependency $$dep."; \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
41824223 exit 2; \
41834224 fi \
41844225 fi \
41924233 # in practice only Makefile is needed so far.
41934234 define dep_autopatch
41944235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
41954237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
41964238 $(call dep_autopatch_erlang_mk,$(1)); \
41974239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4198 if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
41994243 $(call dep_autopatch2,$(1)); \
4200 elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
42014245 $(call dep_autopatch2,$(1)); \
4202 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
42034247 $(call dep_autopatch2,$(1)); \
4204 else \
4205 $(call erlang,$(call dep_autopatch_app.erl,$(1))); \
42064248 fi \
42074249 else \
42084250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
42144256 endef
42154257
42164258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
42174262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
42184263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
42194264 fi; \
42204265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4221 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
42224267 $(call dep_autopatch_fetch_rebar); \
42234268 $(call dep_autopatch_rebar,$(1)); \
42244269 else \
42304275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
42314276 endef
42324277
4233 # Overwrite erlang.mk with the current file by default.
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
42344280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
42354281 define dep_autopatch_erlang_mk
4236 echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \
4237 > $(DEPS_DIR)/$1/erlang.mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
42384287 endef
42394288 else
42404289 define dep_autopatch_erlang_mk
42534302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
42544303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
42554304 cd $(ERLANG_MK_TMP)/rebar; \
4256 git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
42574306 $(MAKE); \
42584307 cd -; \
42594308 fi
42704319 define dep_autopatch_rebar.erl
42714320 application:load(rebar),
42724321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
42734323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
42744324 {ok, Conf0} -> Conf0;
42754325 _ -> []
44234473 [] -> ok;
44244474 _ ->
44254475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4426 PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
44274477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4428 PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L \\"~s\\" -lerl_interface -lei\n",
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
44294479 [code:lib_dir(erl_interface, lib)])),
44304480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
44314481 FilterEnv = fun(Env) ->
44644514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44654515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44664516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4467 [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
44684518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
44694519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
44704520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
44764526 end,
44774527 [PortSpec(S) || S <- PortSpecs]
44784528 end,
4479 Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"),
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
44804530 RunPlugin = fun(Plugin, Step) ->
44814531 case erlang:function_exported(Plugin, Step, 2) of
44824532 false -> ok;
45244574 halt()
45254575 endef
45264576
4527 define dep_autopatch_app.erl
4528 UpdateModules = fun(App) ->
4529 case filelib:is_regular(App) of
4530 false -> ok;
4531 true ->
4532 {ok, [{application, '$(1)', L0}]} = file:consult(App),
4533 Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true,
4534 fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
4535 L = lists:keystore(modules, 1, L0, {modules, Mods}),
4536 ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}]))
4537 end
4538 end,
4539 UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"),
4540 halt()
4541 endef
4542
45434577 define dep_autopatch_appsrc_script.erl
45444578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
45454579 AppSrcScript = AppSrc ++ ".script",
45874621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
45884622 endef
45894623
4590 define dep_fetch_hex.erl
4591 ssl:start(),
4592 inets:start(),
4593 {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
4594 {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
4595 [], [{body_format, binary}]),
4596 {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
4597 {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
4598 ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
4599 halt()
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
46004626 endef
46014627
46024628 # Hex only has a package version. No need to look in the Erlang.mk packages.
46034629 define dep_fetch_hex
4604 $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
46054634 endef
46064635
46074636 define dep_fetch_fail
46314660 $(eval DEP_NAME := $(call dep_name,$1))
46324661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
46334662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4634 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
46354664 exit 17; \
46364665 fi
46374666 $(verbose) mkdir -p $(DEPS_DIR)
46734702 clean:: clean-apps
46744703
46754704 clean-apps:
4676 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4677 $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
46784707 done
46794708
46804709 distclean:: distclean-apps
46814710
46824711 distclean-apps:
4683 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4684 $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
46854714 done
46864715 endif
46874716
47014730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
47024731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
47034732
4704 # External plugins.
4705
4706 DEP_PLUGINS ?=
4707
4708 define core_dep_plugin
4709 -include $(DEPS_DIR)/$(1)
4710
4711 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4712 endef
4713
4714 $(foreach p,$(DEP_PLUGINS),\
4715 $(eval $(if $(findstring /,$p),\
4716 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4717 $(call core_dep_plugin,$p/plugins.mk,$p))))
4718
4719 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4720 # This file is part of erlang.mk and subject to the terms of the ISC License.
4721
4722 # Configuration.
4723
4724 DTL_FULL_PATH ?=
4725 DTL_PATH ?= templates/
4726 DTL_SUFFIX ?= _dtl
4727 DTL_OPTS ?=
4728
4729 # Verbosity.
4730
4731 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
4732 dtl_verbose = $(dtl_verbose_$(V))
4733
4734 # Core targets.
4735
4736 DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl))
4737
4738 ifneq ($(DTL_FILES),)
4739
4740 ifdef DTL_FULL_PATH
4741 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
4742 else
4743 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
4744 endif
4745
4746 ifneq ($(words $(DTL_FILES)),0)
4747 # Rebuild templates when the Makefile changes.
4748 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
4749 @mkdir -p $(ERLANG_MK_TMP)
4750 @if test -f $@; then \
4751 touch $(DTL_FILES); \
4752 fi
4753 @touch $@
4754
4755 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
4756 endif
4757
4758 define erlydtl_compile.erl
4759 [begin
4760 Module0 = case "$(strip $(DTL_FULL_PATH))" of
4761 "" ->
4762 filename:basename(F, ".dtl");
4763 _ ->
4764 "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
4765 re:replace(F2, "/", "_", [{return, list}, global])
4766 end,
4767 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
4768 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
4769 ok -> ok;
4770 {ok, _} -> ok
4771 end
4772 end || F <- string:tokens("$(1)", " ")],
4773 halt().
4774 endef
4775
4776 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
4777 $(if $(strip $?),\
4778 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
4779
4780 endif
4781
4782 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
47834734 # This file is part of erlang.mk and subject to the terms of the ISC License.
47844735
47854736 # Verbosity.
47984749
47994750 define compile_proto.erl
48004751 [begin
4801 Dir = filename:dirname(filename:dirname(F)),
48024752 protobuffs_compile:generate_source(F,
4803 [{output_include_dir, Dir ++ "/include"},
4804 {output_src_dir, Dir ++ "/ebin"}])
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
48054755 end || F <- string:tokens("$(1)", " ")],
48064756 halt().
48074757 endef
48114761 $(if $(strip $?),$(call compile_proto,$?))
48124762 endif
48134763
4814 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
48154765 # This file is part of erlang.mk and subject to the terms of the ISC License.
48164766
48174767 .PHONY: clean-app
48254775 ERLC_EXCLUDE ?=
48264776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
48274777
4778 ERLC_ASN1_OPTS ?=
4779
48284780 ERLC_MIB_OPTS ?=
48294781 COMPILE_MIB_FIRST ?=
48304782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
48744826
48754827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
48764828 define app_file
4877 {application, $(PROJECT), [
4829 {application, '$(PROJECT)', [
48784830 {description, "$(PROJECT_DESCRIPTION)"},
48794831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48804832 {id$(comma)$(space)"$(1)"}$(comma))
48814833 {modules, [$(call comma_list,$(2))]},
48824834 {registered, []},
4883 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48844837 ]}.
48854838 endef
48864839 else
48874840 define app_file
4888 {application, $(PROJECT), [
4841 {application, '$(PROJECT)', [
48894842 {description, "$(PROJECT_DESCRIPTION)"},
48904843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48914844 {id$(comma)$(space)"$(1)"}$(comma))
48924845 {modules, [$(call comma_list,$(2))]},
48934846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
48944847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4895 {mod, {$(PROJECT_MOD), []}}
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48964850 ]}.
48974851 endef
48984852 endif
49024856
49034857 # Source files.
49044858
4905 ERL_FILES = $(sort $(call core_find,src/,*.erl))
4906 CORE_FILES = $(sort $(call core_find,src/,*.core))
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
49074863
49084864 # ASN.1 files.
49094865
49134869
49144870 define compile_asn1
49154871 $(verbose) mkdir -p include/
4916 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
49174873 $(verbose) mv asn1/*.erl src/
49184874 $(verbose) mv asn1/*.hrl include/
49194875 $(verbose) mv asn1/*.asn1db include/
49364892
49374893 # Leex and Yecc files.
49384894
4939 XRL_FILES = $(sort $(call core_find,src/,*.xrl))
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
49404896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
49414897 ERL_FILES += $(XRL_ERL_FILES)
49424898
4943 YRL_FILES = $(sort $(call core_find,src/,*.yrl))
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
49444900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
49454901 ERL_FILES += $(YRL_ERL_FILES)
49464902
49474903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4948 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
49494905
49504906 # Erlang and Core Erlang files.
49514907
49954951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49964952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49974953 (F, Mod, import, {Imp, _}) ->
4998 case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
49994960 false -> ok;
50004961 true -> Add(Mod, Imp)
50014962 end;
50194980 end || F <- ErlFiles],
50204981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
50214982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
50224991 ok = file:write_file("$(1)", [
50234992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
5024 "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n"
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
50254994 ]),
50264995 halt()
50274996 endef
50345003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
50355004 # Rebuild everything when the Makefile changes.
50365005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5037 @mkdir -p $(ERLANG_MK_TMP)
5038 @if test -f $@; then \
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
50395008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
50405009 touch -c $(PROJECT).d; \
50415010 fi
5042 @touch $@
5011 $(verbose) touch $@
50435012
50445013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
50455014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
50465015 endif
50475016
5048 -include $(PROJECT).d
5017 include $(wildcard $(PROJECT).d)
50495018
50505019 ebin/$(PROJECT).app:: ebin/
50515020
50645033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
50655034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
50665035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5067 $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
50685037 > ebin/$(PROJECT).app
50695038 else
50705039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
50885057
50895058 endif
50905059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
50915061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
50925062 # This file is part of erlang.mk and subject to the terms of the ISC License.
50935063
51055075 doc-deps:
51065076 else
51075077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5108 $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5109 endif
5110
5111 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51125082 # This file is part of erlang.mk and subject to the terms of the ISC License.
51135083
51145084 .PHONY: rel-deps
51255095 rel-deps:
51265096 else
51275097 rel-deps: $(ALL_REL_DEPS_DIRS)
5128 $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5129 endif
5130
5131 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51325102 # This file is part of erlang.mk and subject to the terms of the ISC License.
51335103
51345104 .PHONY: test-deps test-dir test-build clean-test-dir
51505120 test-deps:
51515121 else
51525122 test-deps: $(ALL_TEST_DEPS_DIRS)
5153 $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
51545124 endif
51555125
51565126 ifneq ($(wildcard $(TEST_DIR)),)
51835153 endif
51845154 endif
51855155
5186 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51875157 # This file is part of erlang.mk and subject to the terms of the ISC License.
51885158
51895159 .PHONY: rebar.config
52195189 rebar.config:
52205190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
52215191
5222 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
52235193 # This file is part of erlang.mk and subject to the terms of the ISC License.
52245194
5225 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
5226
5227 MAN_INSTALL_PATH ?= /usr/local/share/man
5228 MAN_SECTIONS ?= 3 7
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
52295200
52305201 docs:: asciidoc
52315202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
52325207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
52335210
52345211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
52355212 asciidoc-guide:
52365213 else
5237 asciidoc-guide: distclean-asciidoc doc-deps
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
52385215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
52395216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5240 endif
5241
5242 ifeq ($(wildcard doc/src/manual/*.asciidoc),)
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
52435227 asciidoc-manual:
52445228 else
5245 asciidoc-manual: distclean-asciidoc doc-deps
5246 for f in doc/src/manual/*.asciidoc ; do \
5247 a2x -v -f manpage $$f ; \
5248 done
5249 for s in $(MAN_SECTIONS); do \
5250 mkdir -p doc/man$$s/ ; \
5251 mv doc/src/manual/*.$$s doc/man$$s/ ; \
5252 gzip doc/man$$s/*.$$s ; \
5253 done
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
52545262
52555263 install-docs:: install-asciidoc
52565264
52575265 install-asciidoc: asciidoc-manual
5258 for s in $(MAN_SECTIONS); do \
5259 mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
5260 install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
5261 done
5262 endif
5263
5264 distclean:: distclean-asciidoc
5265
5266 distclean-asciidoc:
5267 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
5268
5269 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
52705276 # This file is part of erlang.mk and subject to the terms of the ISC License.
52715277
52725278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
53235329 define bs_Makefile
53245330 PROJECT = $p
53255331 PROJECT_DESCRIPTION = New project
5326 PROJECT_VERSION = 0.0.1
5332 PROJECT_VERSION = 0.1.0
53275333
53285334 # Whitespace to be used when creating files from templates.
53295335 SP = $(SP)
53335339 define bs_Makefile
53345340 PROJECT = $p
53355341 PROJECT_DESCRIPTION = New project
5336 PROJECT_VERSION = 0.0.1
5342 PROJECT_VERSION = 0.1.0
53375343
53385344 endef
53395345 endif
53415347 define bs_apps_Makefile
53425348 PROJECT = $p
53435349 PROJECT_DESCRIPTION = New project
5344 PROJECT_VERSION = 0.0.1
5350 PROJECT_VERSION = 0.1.0
53455351
53465352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
53475353 endef
53615367 endef
53625368
53635369 define bs_relx_config
5364 {release, {$p_release, "1"}, [$p]}.
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
53655371 {extended_start_script, true}.
53665372 {sys_config, "rel/sys.config"}.
53675373 {vm_args, "rel/vm.args"}.
57125718 ifndef t
57135719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57145720 endif
5715 ifndef tpl_$(t)
5716 $(error Unknown template)
5717 endif
57185721 ifndef n
57195722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57205723 endif
57275730 list-templates:
57285731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
57295732
5730 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
57315734 # This file is part of erlang.mk and subject to the terms of the ISC License.
57325735
57335736 .PHONY: clean-c_src distclean-c_src-env
59625965 $(call render_template,bs_erl_nif,src/$n.erl)
59635966 endif
59645967
5965 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
59665969 # This file is part of erlang.mk and subject to the terms of the ISC License.
59675970
5968 .PHONY: ci ci-setup distclean-kerl
5969
5970 KERL ?= $(CURDIR)/kerl
5971 export KERL
5972
5973 KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
5974
5975 OTP_GIT ?= https://github.com/erlang/otp
5976
5977 CI_INSTALL_DIR ?= $(HOME)/erlang
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
59785973 CI_OTP ?=
5979
5980 ifeq ($(strip $(CI_OTP)),)
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
59815986 ci::
59825987 else
5983 ci:: $(addprefix ci-,$(CI_OTP))
5984
5985 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
59866007
59876008 ci-setup::
6009
6010 ci-extra::
59886011
59896012 ci_verbose_0 = @echo " CI " $(1);
59906013 ci_verbose = $(ci_verbose_$(V))
59916014
59926015 define ci_target
5993 ci-$(1): $(CI_INSTALL_DIR)/$(1)
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
59946018 $(ci_verbose) \
5995 PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
5996 CI_OTP_RELEASE="$(1)" \
5997 CT_OPTS="-label $(1)" \
5998 $(MAKE) clean ci-setup tests
5999 endef
6000
6001 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
60026030
60036031 define ci_otp_target
60046032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
60056033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6006 $(KERL) build git $(OTP_GIT) $(1) $(1)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
60076035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
60086036 endif
60096037 endef
60106038
60116039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
60126040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
60136052 $(KERL):
6014 $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
60156056 $(verbose) chmod +x $(KERL)
60166057
60176058 help::
60286069 $(gen_verbose) rm -rf $(KERL)
60296070 endif
60306071
6031 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
60326073 # This file is part of erlang.mk and subject to the terms of the ISC License.
60336074
60346075 .PHONY: ct apps-ct distclean-ct
60366077 # Configuration.
60376078
60386079 CT_OPTS ?=
6080
60396081 ifneq ($(wildcard $(TEST_DIR)),)
6040 CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6041 else
6042 CT_SUITES ?=
6043 endif
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
60446088
60456089 # Core targets.
60466090
60636107 -noinput \
60646108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
60656109 -dir $(TEST_DIR) \
6066 -logdir $(CURDIR)/logs
6110 -logdir $(CT_LOGS_DIR)
60676111
60686112 ifeq ($(CT_SUITES),)
60696113 ct: $(if $(IS_APP),,apps-ct)
60706114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
60716117 ct: test-build $(if $(IS_APP),,apps-ct)
6072 $(verbose) mkdir -p $(CURDIR)/logs/
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
60736119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
60746121 endif
60756122
60766123 ifneq ($(ALL_APPS_DIRS),)
60976144
60986145 define ct_suite_target
60996146 ct-$(1): test-build
6100 $(verbose) mkdir -p $(CURDIR)/logs/
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
61016148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
61026149 endef
61036150
61046151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
61056152
61066153 distclean-ct:
6107 $(gen_verbose) rm -rf $(CURDIR)/logs/
6108
6109 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61106157 # This file is part of erlang.mk and subject to the terms of the ISC License.
61116158
61126159 .PHONY: plt distclean-plt dialyze
61506197 endef
61516198
61526199 $(DIALYZER_PLT): deps app
6153 $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
61546204
61556205 plt: $(DIALYZER_PLT)
61566206
61646214 endif
61656215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
61666216
6167 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61686218 # This file is part of erlang.mk and subject to the terms of the ISC License.
61696219
61706220 .PHONY: distclean-edoc edoc
61726222 # Configuration.
61736223
61746224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
61756235
61766236 # Core targets.
61776237
6178 ifneq ($(wildcard doc/overview.edoc),)
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
61796239 docs:: edoc
61806240 endif
61816241
61846244 # Plugin-specific targets.
61856245
61866246 edoc: distclean-edoc doc-deps
6187 $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
61886248
61896249 distclean-edoc:
61906250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
61916251
6192 # Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61936253 # This file is part of erlang.mk and subject to the terms of the ISC License.
61946254
6195 .PHONY: distclean-escript escript
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
61966320
61976321 # Configuration.
61986322
61996323 ESCRIPT_NAME ?= $(PROJECT)
62006324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
62016325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
62026327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6203
6204 ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
6205 ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
6206 ESCRIPT_EMU_ARGS ?= -pa . \
6207 -sasl errlog_type error \
6208 -escript main $(ESCRIPT_NAME)
6209 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6210 ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
62116332
62126333 # Core targets.
62136334
62206341
62216342 # Plugin-specific targets.
62226343
6223 # Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
6224 # Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
6225 # Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
6226 # Software may only be used for the great good and the true happiness of all
6227 # sentient beings.
6228
6229 define ESCRIPT_RAW
6230 'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
6231 'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
6232 ' [F || F <- A, not filelib:is_dir(F) ] end,'\
6233 'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
6234 'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
6235 'Ez = fun(Escript) ->'\
6236 ' Static = Files([$(ESCRIPT_STATIC)]),'\
6237 ' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
6238 ' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
6239 ' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
6240 ' {archive, Archive, [memory]},'\
6241 ' {shebang, "$(ESCRIPT_SHEBANG)"},'\
6242 ' {comment, "$(ESCRIPT_COMMENT)"},'\
6243 ' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
6244 ' ]),'\
6245 ' file:change_mode(Escript, 8#755)'\
6246 'end,'\
6247 'Ez("$(ESCRIPT_FILE)"),'\
6248 'halt().'
6249 endef
6250
6251 ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
6252
6253 escript:: distclean-escript deps app
6254 $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
62556360
62566361 distclean-escript:
6257 $(gen_verbose) rm -f $(ESCRIPT_NAME)
6258
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
62596365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6260 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
62616366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
62626367
62636368 .PHONY: eunit apps-eunit
63216426
63226427 ifneq ($(ALL_APPS_DIRS),)
63236428 apps-eunit:
6324 $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done
6325 endif
6326 endif
6327
6328 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
63296436 # This file is part of erlang.mk and subject to the terms of the ISC License.
63306437
6331 .PHONY: relx-rel distclean-relx-rel distclean-relx run
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
63326439
63336440 # Configuration.
63346441
6335 RELX ?= $(CURDIR)/relx
6442 RELX ?= $(ERLANG_MK_TMP)/relx
63366443 RELX_CONFIG ?= $(CURDIR)/relx.config
63376444
63386445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
63396446 RELX_OPTS ?=
63406447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
63416454
63426455 ifeq ($(firstword $(RELX_OPTS)),-o)
63436456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
63506463 ifeq ($(IS_DEP),)
63516464 ifneq ($(wildcard $(RELX_CONFIG)),)
63526465 rel:: relx-rel
6353 endif
6354 endif
6355
6356 distclean:: distclean-relx-rel distclean-relx
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
63576472
63586473 # Plugin-specific targets.
63596474
63626477 $(verbose) chmod +x $(RELX)
63636478
63646479 relx-rel: $(RELX) rel-deps app
6365 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
63666484
63676485 distclean-relx-rel:
63686486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6369
6370 distclean-relx:
6371 $(gen_verbose) rm -rf $(RELX)
63726487
63736488 # Run target.
63746489
63776492 else
63786493
63796494 define get_relx_release.erl
6380 {ok, Config} = file:consult("$(RELX_CONFIG)"),
6381 {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
6382 io:format("~s", [Name]),
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
63836504 halt(0).
63846505 endef
63856506
6386 RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
63876514
63886515 run: all
6389 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
63906517
63916518 help::
63926519 $(verbose) printf "%s\n" "" \
63956522
63966523 endif
63976524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
63986526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6399 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
64006527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
64016528
64026529 .PHONY: shell
64216548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
64226549
64236550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6424 $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
64256552
64266553 shell: build-shell-deps
64276554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
64286555
6429 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
64306571 # This file is part of erlang.mk and subject to the terms of the ISC License.
64316572
64326573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
64376578 tests:: triq
64386579
64396580 define triq_check.erl
6440 code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
64416582 try
64426583 case $(1) of
64436584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
64696610 endif
64706611 endif
64716612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
64726614 # Copyright (c) 2015, Erlang Solutions Ltd.
64736615 # This file is part of erlang.mk and subject to the terms of the ISC License.
64746616
64856627 XREFR ?= $(CURDIR)/xrefr
64866628 export XREFR
64876629
6488 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
64896631
64906632 # Core targets.
64916633
64926634 help::
6493 $(verbose) printf "%s\n" "" \
6494 "Xref targets:" \
6495 " xref Run Xrefr using $XREF_CONFIG as config file if defined"
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
64966638
64976639 distclean:: distclean-xref
64986640
65086650 distclean-xref:
65096651 $(gen_verbose) rm -rf $(XREFR)
65106652
6511 # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
65126655 # This file is part of erlang.mk and subject to the terms of the ISC License.
65136656
65146657 COVER_REPORT_DIR = cover
65176660
65186661 ifdef COVER
65196662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
65206664 # All modules in 'ebin'
65216665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
65226666
65316675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
65326676 endif
65336677 endif
6678 endif
65346679
65356680 # Core targets
65366681
65526697 "Cover targets:" \
65536698 " cover-report Generate a HTML coverage report from previously collected" \
65546699 " cover data." \
6555 " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
65566701 "" \
65576702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
65586703 "target tests additionally generates a HTML coverage report from the combined" \
65656710
65666711 .PHONY: coverdata-clean
65676712 coverdata-clean:
6568 $(gen_verbose) rm -f *.coverdata ct.cover.spec
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
65696714
65706715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
65716721 all.coverdata: $(COVERDATA)
6572 $(gen_verbose) $(ERL) -eval ' \
6573 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
6574 cover:export("$@"), halt(0).'
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
65756723
65766724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
65776725 # empty if you want the coverdata files but not the HTML report.
65896737 # Modules which include eunit.hrl always contain one line without coverage
65906738 # because eunit defines test/0 which is never called. We compensate for this.
65916739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6592 grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
65936741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
65946742
65956743 define cover_report.erl
66246772 endef
66256773
66266774 cover-report:
6627 $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
66286776 $(gen_verbose) $(call erlang,$(cover_report.erl))
66296777
66306778 endif
66316779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
66326839
66336840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
66346841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
66976904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
66986905 endif
66996906 ifndef IS_APP
6700 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
67016908 $(MAKE) -C $$dep $@ \
67026909 IS_APP=1 \
6703 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6704 || exit $$?; \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67056911 done
67066912 endif
6707 $(verbose) for dep in $^ ; do \
6913 $(verbose) set -e; for dep in $^ ; do \
67086914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
67096915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6710 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
67116917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
67126918 $(MAKE) -C $$dep fetch-deps \
67136919 IS_DEP=1 \
6714 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6715 || exit $$?; \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67166921 fi \
67176922 fi \
67186923 done
00 dispatcher_add(function(sammy) {});
11
2 NAVIGATION['Visualiser'] = ['visualiser/', "management"];
2 NAVIGATION['Visualiser'] = ['visualiser/index.html', "management"];
22 # before the inclusion of erlang.mk leading to the wrong target becoming
33 # the default.
44 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
526 endif
627
728 # --------------------------------------------------------------------
2041 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
2142 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
2243 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
2345 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
2446 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
2547 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
2648 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
2749 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
2851 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
2952 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
3054 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
3155 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
3256 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
3559 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
3660 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
3761 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
3863 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
3964 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
4065 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
7095
7196 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
7297
73 # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
74 # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
75 # needs to add "ranch" as a BUILD_DEPS. The list of projects needing
76 # this workaround are:
77 # o rabbitmq-web-stomp
78 dep_ranch = git https://github.com/ninenines/ranch 1.2.1
98 # Third-party dependencies version pinning.
99 #
100 # We do that in this file, which is copied in all projects, to ensure
101 # all projects use the same versions. It avoids conflicts and makes it
102 # possible to work with rabbitmq-public-umbrella.
103
104 dep_cowboy_commit = 1.0.4
105 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
106 dep_ranch_commit = 1.3.1
107 dep_webmachine_commit = 1.10.8p2
79108
80109 RABBITMQ_COMPONENTS = amqp_client \
81110 rabbit \
82111 rabbit_common \
83112 rabbitmq_amqp1_0 \
84113 rabbitmq_auth_backend_amqp \
114 rabbitmq_auth_backend_cache \
85115 rabbitmq_auth_backend_http \
86116 rabbitmq_auth_backend_ldap \
87117 rabbitmq_auth_mechanism_ssl \
88118 rabbitmq_boot_steps_visualiser \
89119 rabbitmq_clusterer \
120 rabbitmq_cli \
90121 rabbitmq_codegen \
91122 rabbitmq_consistent_hash_exchange \
123 rabbitmq_ct_client_helpers \
92124 rabbitmq_ct_helpers \
93125 rabbitmq_delayed_message_exchange \
94126 rabbitmq_dotnet_client \
97129 rabbitmq_federation_management \
98130 rabbitmq_java_client \
99131 rabbitmq_jms_client \
132 rabbitmq_jms_cts \
100133 rabbitmq_jms_topic_exchange \
101134 rabbitmq_lvc \
102135 rabbitmq_management \
1010 %% The Original Code is RabbitMQ Visualiser.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 -module(rabbit_mgmt_wm_all).
1515
16 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2,
16 -export([init/3]).
17 -export([rest_init/2, to_json/2, content_types_provided/2, is_authorized/2,
1718 resource_exists/2]).
1819
1920 -import(rabbit_misc, [pget/2]).
2021
21 -include_lib("rabbitmq_management/include/rabbit_mgmt.hrl").
22 -include_lib("webmachine/include/webmachine.hrl").
22 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2323 -include_lib("amqp_client/include/amqp_client.hrl").
2424
2525 %%--------------------------------------------------------------------
26 init(_Config) -> {ok, #context{}}.
26 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
27
28 rest_init(ReqData, _) -> {ok, ReqData, #context{}}.
2729
2830 content_types_provided(ReqData, Context) ->
29 {[{"application/json", to_json}], ReqData, Context}.
31 {[{<<"application/json">>, to_json}], ReqData, Context}.
3032
3133 resource_exists(ReqData, Context) ->
3234 {case rabbit_mgmt_util:vhost(ReqData) of
3638
3739 to_json(ReqData, Context) ->
3840 rabbit_mgmt_util:reply(
39 [{Key, Mod:augmented(ReqData, Context)}
40 || {Key, Mod} <- [{queues, rabbit_mgmt_wm_queues},
41 {exchanges, rabbit_mgmt_wm_exchanges},
42 {bindings, rabbit_mgmt_wm_bindings},
43 {channels, rabbit_mgmt_wm_channels},
44 {connections, rabbit_mgmt_wm_connections},
45 {vhosts, rabbit_mgmt_wm_vhosts}]
46 ], ReqData, Context).
41 rabbit_mgmt_format:strip_pids(
42 [{Key, Mod:augmented(ReqData, Context)}
43 || {Key, Mod} <- [{queues, rabbit_mgmt_wm_queues},
44 {exchanges, rabbit_mgmt_wm_exchanges},
45 {bindings, rabbit_mgmt_wm_bindings},
46 {channels, rabbit_mgmt_wm_channels},
47 {connections, rabbit_mgmt_wm_connections},
48 {vhosts, rabbit_mgmt_wm_vhosts}]
49 ]),
50 ReqData, Context).
4751
4852 is_authorized(ReqData, Context) ->
4953 rabbit_mgmt_util:is_authorized(ReqData, Context).
1010 %% The Original Code is RabbitMQ Visualiser.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_visualiser_mgmt).
1919
2020 -export([dispatcher/0, web_ui/0]).
2121
22 dispatcher() -> [{["all"], rabbit_mgmt_wm_all, []},
23 {["all", vhost], rabbit_mgmt_wm_all, []}].
22 dispatcher() -> [{"/all", rabbit_mgmt_wm_all, []},
23 {"/all/:vhost", rabbit_mgmt_wm_all, []}].
2424 web_ui() -> [{javascript, <<"visualiser.js">>}].
+0
-6
deps/rabbitmq_management_visualiser/src/rabbitmq_management_visualiser.app.src less more
0 {application, rabbitmq_management_visualiser,
1 [{description, "RabbitMQ Visualiser"},
2 {vsn, "3.6.6"},
3 {modules, []},
4 {registered, []},
5 {applications, [kernel, stdlib, rabbit_common, rabbit, rabbitmq_management]}]}.
00 PROJECT = rabbitmq_mqtt
1 PROJECT_DESCRIPTION = RabbitMQ MQTT Adapter
2 PROJECT_MOD = rabbit_mqtt
3
4 define PROJECT_ENV
5 [
6 {default_user, <<"guest">>},
7 {default_pass, <<"guest">>},
8 {ssl_cert_login,false},
9 %% To satisfy an unfortunate expectation from popular MQTT clients.
10 {allow_anonymous, true},
11 {vhost, <<"/">>},
12 {exchange, <<"amq.topic">>},
13 {subscription_ttl, 86400000}, %% 24 hours
14 {retained_message_store, rabbit_mqtt_retained_msg_store_dets},
15 %% only used by DETS store
16 {retained_message_store_dets_sync_interval, 2000},
17 {prefetch, 10},
18 {ssl_listeners, []},
19 {num_ssl_acceptors, 1},
20 {tcp_listeners, [1883]},
21 {num_tcp_acceptors, 10},
22 {tcp_listen_options, [{backlog, 128},
23 {nodelay, true}]}
24 ]
25 endef
126
227 DEPS = ranch rabbit_common rabbit amqp_client
3 TEST_DEPS = emqttc ct_helper rabbitmq_ct_helpers
28 TEST_DEPS = emqttc ct_helper rabbitmq_ct_helpers rabbitmq_ct_client_helpers
429
530 dep_ct_helper = git https://github.com/extend/ct_helper.git master
631 dep_emqttc = git https://github.com/emqtt/emqttc.git master
732
33 DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
834 DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk
935
1036 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
0 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
11 #
22 # Permission to use, copy, modify, and/or distribute this software for any
33 # purpose with or without fee is hereby granted, provided that the above
1111 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1212 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1313
14 .PHONY: all app apps deps search rel docs install-docs check tests clean distclean help erlang-mk
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
1515
1616 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17
18 ERLANG_MK_VERSION = 2.0.0-pre.2-144-g647ffd1
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
1931
2032 # Core configuration.
2133
2436
2537 PROJECT_VERSION ?= rolling
2638 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
2740
2841 # Verbosity.
2942
8497 rel::
8598 $(verbose) :
8699
100 relup:: deps app
101
87102 check:: tests
88103
89104 clean:: clean-crashdump
101116 help::
102117 $(verbose) printf "%s\n" \
103118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
104 "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
105120 "" \
106121 "Usage: [V=1] $(MAKE) [target]..." \
107122 "" \
149164 core_native_path = $1
150165 endif
151166
152 ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
153 define core_http_get
154 wget --no-check-certificate -O $(1) $(2)|| rm $(1)
155 endef
156 else
157 define core_http_get.erl
158 ssl:start(),
159 inets:start(),
160 case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
161 {ok, {{_, 200, _}, _, Body}} ->
162 case file:write_file("$(1)", Body) of
163 ok -> ok;
164 {error, R1} -> halt(R1)
165 end;
166 {error, R2} ->
167 halt(R2)
168 end,
169 halt(0).
170 endef
171
172 define core_http_get
173 $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
174 endef
175 endif
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
176168
177169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
178170
192184 ERLANG_MK_BUILD_CONFIG ?= build.config
193185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
194186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
195188 erlang-mk:
196189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
197190 ifdef ERLANG_MK_COMMIT
198191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
199192 endif
200193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
201 $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
202195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
203196 rm -rf $(ERLANG_MK_BUILD_DIR)
204197
285278 pkg_apns_repo = https://github.com/inaka/apns4erl
286279 pkg_apns_commit = master
287280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
288289 PACKAGES += azdht
289290 pkg_azdht_name = azdht
290291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
405406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
406407 pkg_bootstrap_commit = master
407408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
408417 PACKAGES += boss_db
409418 pkg_boss_db_name = boss_db
410419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
413422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
414423 pkg_boss_db_commit = master
415424
416 PACKAGES += boss
417 pkg_boss_name = boss
418 pkg_boss_description = Erlang web MVC, now featuring Comet
419 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
420 pkg_boss_fetch = git
421 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
422 pkg_boss_commit = master
423
424425 PACKAGES += brod
425426 pkg_brod_name = brod
426427 pkg_brod_description = Kafka client in Erlang
565566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
566567 pkg_cloudi_service_api_requests_commit = master
567568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
568585 PACKAGES += cloudi_service_db_cassandra_cql
569586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
570587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
573590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
574591 pkg_cloudi_service_db_cassandra_cql_commit = master
575592
576 PACKAGES += cloudi_service_db_cassandra
577 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
579 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
580 pkg_cloudi_service_db_cassandra_fetch = git
581 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
582 pkg_cloudi_service_db_cassandra_commit = master
583
584593 PACKAGES += cloudi_service_db_couchdb
585594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
586595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
637646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
638647 pkg_cloudi_service_db_tokyotyrant_commit = master
639648
640 PACKAGES += cloudi_service_db
641 pkg_cloudi_service_db_name = cloudi_service_db
642 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
643 pkg_cloudi_service_db_homepage = http://cloudi.org/
644 pkg_cloudi_service_db_fetch = git
645 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
646 pkg_cloudi_service_db_commit = master
647
648649 PACKAGES += cloudi_service_filesystem
649650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
650651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
10371038 pkg_edown_repo = https://github.com/uwiger/edown
10381039 pkg_edown_commit = master
10391040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
10401049 PACKAGES += eep_app
10411050 pkg_eep_app_name = eep_app
10421051 pkg_eep_app_description = Embedded Event Processing
10451054 pkg_eep_app_repo = https://github.com/darach/eep-erl
10461055 pkg_eep_app_commit = master
10471056
1048 PACKAGES += eep
1049 pkg_eep_name = eep
1050 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1051 pkg_eep_homepage = https://github.com/virtan/eep
1052 pkg_eep_fetch = git
1053 pkg_eep_repo = https://github.com/virtan/eep
1054 pkg_eep_commit = master
1055
10561057 PACKAGES += efene
10571058 pkg_efene_name = efene
10581059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
12371238 pkg_eqm_repo = https://github.com/loucash/eqm
12381239 pkg_eqm_commit = master
12391240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
12401249 PACKAGES += eredis_pool
12411250 pkg_eredis_pool_name = eredis_pool
12421251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
12451254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
12461255 pkg_eredis_pool_commit = master
12471256
1248 PACKAGES += eredis
1249 pkg_eredis_name = eredis
1250 pkg_eredis_description = Erlang Redis client
1251 pkg_eredis_homepage = https://github.com/wooga/eredis
1252 pkg_eredis_fetch = git
1253 pkg_eredis_repo = https://github.com/wooga/eredis
1254 pkg_eredis_commit = master
1255
12561257 PACKAGES += erl_streams
12571258 pkg_erl_streams_name = erl_streams
12581259 pkg_erl_streams_description = Streams in Erlang
15411542 pkg_etap_repo = https://github.com/ngerakines/etap
15421543 pkg_etap_commit = master
15431544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
15441553 PACKAGES += etest_http
15451554 pkg_etest_http_name = etest_http
15461555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
15491558 pkg_etest_http_repo = https://github.com/wooga/etest_http
15501559 pkg_etest_http_commit = master
15511560
1552 PACKAGES += etest
1553 pkg_etest_name = etest
1554 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1555 pkg_etest_homepage = https://github.com/wooga/etest
1556 pkg_etest_fetch = git
1557 pkg_etest_repo = https://github.com/wooga/etest
1558 pkg_etest_commit = master
1559
15601561 PACKAGES += etoml
15611562 pkg_etoml_name = etoml
15621563 pkg_etoml_description = TOML language erlang parser
15651566 pkg_etoml_repo = https://github.com/kalta/etoml
15661567 pkg_etoml_commit = master
15671568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
15681577 PACKAGES += eunit_formatters
15691578 pkg_eunit_formatters_name = eunit_formatters
15701579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
15731582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
15741583 pkg_eunit_formatters_commit = master
15751584
1576 PACKAGES += eunit
1577 pkg_eunit_name = eunit
1578 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1579 pkg_eunit_homepage = https://github.com/richcarl/eunit
1580 pkg_eunit_fetch = git
1581 pkg_eunit_repo = https://github.com/richcarl/eunit
1582 pkg_eunit_commit = master
1583
15841585 PACKAGES += euthanasia
15851586 pkg_euthanasia_name = euthanasia
15861587 pkg_euthanasia_description = Merciful killer for your Erlang processes
15981599 pkg_evum_commit = master
15991600
16001601 PACKAGES += exec
1601 pkg_exec_name = exec
1602 pkg_exec_name = erlexec
16021603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
16031604 pkg_exec_homepage = http://saleyn.github.com/erlexec
16041605 pkg_exec_fetch = git
17171718 pkg_fn_repo = https://github.com/reiddraper/fn
17181719 pkg_fn_commit = master
17191720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
17201729 PACKAGES += folsom_cowboy
17211730 pkg_folsom_cowboy_name = folsom_cowboy
17221731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
17251734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
17261735 pkg_folsom_cowboy_commit = master
17271736
1728 PACKAGES += folsom
1729 pkg_folsom_name = folsom
1730 pkg_folsom_description = Expose Erlang Events and Metrics
1731 pkg_folsom_homepage = https://github.com/boundary/folsom
1732 pkg_folsom_fetch = git
1733 pkg_folsom_repo = https://github.com/boundary/folsom
1734 pkg_folsom_commit = master
1735
17361737 PACKAGES += folsomite
17371738 pkg_folsomite_name = folsomite
17381739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
20932094 pkg_jesse_repo = https://github.com/for-GET/jesse
20942095 pkg_jesse_commit = master
20952096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
20962105 PACKAGES += jiffy_v
20972106 pkg_jiffy_v_name = jiffy_v
20982107 pkg_jiffy_v_description = JSON validation utility
21012110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
21022111 pkg_jiffy_v_commit = master
21032112
2104 PACKAGES += jiffy
2105 pkg_jiffy_name = jiffy
2106 pkg_jiffy_description = JSON NIFs for Erlang.
2107 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2108 pkg_jiffy_fetch = git
2109 pkg_jiffy_repo = https://github.com/davisp/jiffy
2110 pkg_jiffy_commit = master
2111
21122113 PACKAGES += jobs
21132114 pkg_jobs_name = jobs
21142115 pkg_jobs_description = a Job scheduler for load regulation
21252126 pkg_joxa_repo = https://github.com/joxa/joxa
21262127 pkg_joxa_commit = master
21272128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
21282137 PACKAGES += json_rec
21292138 pkg_json_rec_name = json_rec
21302139 pkg_json_rec_description = JSON to erlang record
21332142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
21342143 pkg_json_rec_commit = master
21352144
2136 PACKAGES += json
2137 pkg_json_name = json
2138 pkg_json_description = a high level json library for erlang (17.0+)
2139 pkg_json_homepage = https://github.com/talentdeficit/json
2140 pkg_json_fetch = git
2141 pkg_json_repo = https://github.com/talentdeficit/json
2142 pkg_json_commit = master
2143
21442145 PACKAGES += jsone
21452146 pkg_jsone_name = jsone
21462147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
21812182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
21822183 pkg_jsx_commit = master
21832184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
21842193 PACKAGES += kafka_protocol
21852194 pkg_kafka_protocol_name = kafka_protocol
21862195 pkg_kafka_protocol_description = Kafka protocol Erlang library
21892198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
21902199 pkg_kafka_protocol_commit = master
21912200
2192 PACKAGES += kafka
2193 pkg_kafka_name = kafka
2194 pkg_kafka_description = Kafka consumer and producer in Erlang
2195 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2196 pkg_kafka_fetch = git
2197 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2198 pkg_kafka_commit = master
2199
22002201 PACKAGES += kai
22012202 pkg_kai_name = kai
22022203 pkg_kai_description = DHT storage by Takeshi Inoue
22932294 pkg_kvs_repo = https://github.com/synrc/kvs
22942295 pkg_kvs_commit = master
22952296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
22962305 PACKAGES += lager_amqp_backend
22972306 pkg_lager_amqp_backend_name = lager_amqp_backend
22982307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
23092318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
23102319 pkg_lager_syslog_commit = master
23112320
2312 PACKAGES += lager
2313 pkg_lager_name = lager
2314 pkg_lager_description = A logging framework for Erlang/OTP.
2315 pkg_lager_homepage = https://github.com/basho/lager
2316 pkg_lager_fetch = git
2317 pkg_lager_repo = https://github.com/basho/lager
2318 pkg_lager_commit = master
2319
23202321 PACKAGES += lambdapad
23212322 pkg_lambdapad_name = lambdapad
23222323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
25732574 pkg_mixer_repo = https://github.com/chef/mixer
25742575 pkg_mixer_commit = master
25752576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
25762585 PACKAGES += mochiweb_xpath
25772586 pkg_mochiweb_xpath_name = mochiweb_xpath
25782587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
25812590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
25822591 pkg_mochiweb_xpath_commit = master
25832592
2584 PACKAGES += mochiweb
2585 pkg_mochiweb_name = mochiweb
2586 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2587 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2588 pkg_mochiweb_fetch = git
2589 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2590 pkg_mochiweb_commit = master
2591
25922593 PACKAGES += mockgyver
25932594 pkg_mockgyver_name = mockgyver
25942595 pkg_mockgyver_description = A mocking library for Erlang
30613062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
30623063 pkg_quickrand_commit = master
30633064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
30643073 PACKAGES += rabbit_exchange_type_riak
30653074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
30663075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
30693078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
30703079 pkg_rabbit_exchange_type_riak_commit = master
30713080
3072 PACKAGES += rabbit
3073 pkg_rabbit_name = rabbit
3074 pkg_rabbit_description = RabbitMQ Server
3075 pkg_rabbit_homepage = https://www.rabbitmq.com/
3076 pkg_rabbit_fetch = git
3077 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3078 pkg_rabbit_commit = master
3079
30803081 PACKAGES += rack
30813082 pkg_rack_name = rack
30823083 pkg_rack_description = Rack handler for erlang
34933494 pkg_smother_repo = https://github.com/ramsay-t/Smother
34943495 pkg_smother_commit = master
34953496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
34963505 PACKAGES += social
34973506 pkg_social_name = social
34983507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
35413550 pkg_stable_repo = https://github.com/dvv/stable
35423551 pkg_stable_commit = master
35433552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
35443561 PACKAGES += statebox_riak
35453562 pkg_statebox_riak_name = statebox_riak
35463563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
35493566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
35503567 pkg_statebox_riak_commit = master
35513568
3552 PACKAGES += statebox
3553 pkg_statebox_name = statebox
3554 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3555 pkg_statebox_homepage = https://github.com/mochi/statebox
3556 pkg_statebox_fetch = git
3557 pkg_statebox_repo = https://github.com/mochi/statebox
3558 pkg_statebox_commit = master
3559
35603569 PACKAGES += statman
35613570 pkg_statman_name = statman
35623571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
37253734 pkg_tirerl_repo = https://github.com/inaka/tirerl
37263735 pkg_tirerl_commit = master
37273736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
37283745 PACKAGES += traffic_tools
37293746 pkg_traffic_tools_name = traffic_tools
37303747 pkg_traffic_tools_description = Simple traffic limiting library
40614078 pkg_zucchini_repo = https://github.com/devinus/zucchini
40624079 pkg_zucchini_commit = master
40634080
4064 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
40654082 # This file is part of erlang.mk and subject to the terms of the ISC License.
40664083
40674084 .PHONY: search
40884105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
40894106 endif
40904107
4091 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
40924109 # This file is part of erlang.mk and subject to the terms of the ISC License.
40934110
4094 .PHONY: distclean-deps
4111 .PHONY: distclean-deps clean-tmp-deps.log
40954112
40964113 # Configuration.
40974114
41104127
41114128 REBAR_DEPS_DIR = $(DEPS_DIR)
41124129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
41134150
41144151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
41154152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
41164153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
41174154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
41184155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
41194157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
41204158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
41214159
41384176
41394177 # Core targets.
41404178
4141 ifdef IS_APP
4142 apps::
4143 else
4144 apps:: $(ALL_APPS_DIRS)
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
41454180 ifeq ($(IS_APP)$(IS_DEP),)
41464181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
41474182 endif
41494184 # Create ebin directory for all apps to make sure Erlang recognizes them
41504185 # as proper OTP applications when using -include_lib. This is a temporary
41514186 # fix, a proper fix would be to compile apps/* in the right order.
4152 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4153 mkdir -p $$dep/ebin || exit $$?; \
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
41544190 done
4155 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
41564196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
41574197 :; \
41584198 else \
41594199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4160 $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
4200 $(MAKE) -C $$dep IS_APP=1; \
41614201 fi \
41624202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
41634207 endif
41644208
41654209 ifneq ($(SKIP_DEPS),)
41664210 deps::
41674211 else
4168 deps:: $(ALL_DEPS_DIRS) apps
4169 ifeq ($(IS_APP)$(IS_DEP),)
4170 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4171 endif
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
41724213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4173 $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
41744215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
41754216 :; \
41764217 else \
41774218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
41784219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4179 $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
4220 $(MAKE) -C $$dep IS_DEP=1; \
41804221 else \
4181 echo "Error: No Makefile to build dependency $$dep."; \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
41824223 exit 2; \
41834224 fi \
41844225 fi \
41924233 # in practice only Makefile is needed so far.
41934234 define dep_autopatch
41944235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
41954237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
41964238 $(call dep_autopatch_erlang_mk,$(1)); \
41974239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4198 if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
41994243 $(call dep_autopatch2,$(1)); \
4200 elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
42014245 $(call dep_autopatch2,$(1)); \
4202 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
42034247 $(call dep_autopatch2,$(1)); \
4204 else \
4205 $(call erlang,$(call dep_autopatch_app.erl,$(1))); \
42064248 fi \
42074249 else \
42084250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
42144256 endef
42154257
42164258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
42174262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
42184263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
42194264 fi; \
42204265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4221 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
42224267 $(call dep_autopatch_fetch_rebar); \
42234268 $(call dep_autopatch_rebar,$(1)); \
42244269 else \
42304275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
42314276 endef
42324277
4233 # Overwrite erlang.mk with the current file by default.
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
42344280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
42354281 define dep_autopatch_erlang_mk
4236 echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \
4237 > $(DEPS_DIR)/$1/erlang.mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
42384287 endef
42394288 else
42404289 define dep_autopatch_erlang_mk
42534302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
42544303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
42554304 cd $(ERLANG_MK_TMP)/rebar; \
4256 git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
42574306 $(MAKE); \
42584307 cd -; \
42594308 fi
42704319 define dep_autopatch_rebar.erl
42714320 application:load(rebar),
42724321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
42734323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
42744324 {ok, Conf0} -> Conf0;
42754325 _ -> []
44234473 [] -> ok;
44244474 _ ->
44254475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4426 PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
44274477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4428 PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L \\"~s\\" -lerl_interface -lei\n",
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
44294479 [code:lib_dir(erl_interface, lib)])),
44304480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
44314481 FilterEnv = fun(Env) ->
44644514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44654515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44664516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4467 [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
44684518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
44694519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
44704520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
44764526 end,
44774527 [PortSpec(S) || S <- PortSpecs]
44784528 end,
4479 Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"),
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
44804530 RunPlugin = fun(Plugin, Step) ->
44814531 case erlang:function_exported(Plugin, Step, 2) of
44824532 false -> ok;
45244574 halt()
45254575 endef
45264576
4527 define dep_autopatch_app.erl
4528 UpdateModules = fun(App) ->
4529 case filelib:is_regular(App) of
4530 false -> ok;
4531 true ->
4532 {ok, [{application, '$(1)', L0}]} = file:consult(App),
4533 Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true,
4534 fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
4535 L = lists:keystore(modules, 1, L0, {modules, Mods}),
4536 ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}]))
4537 end
4538 end,
4539 UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"),
4540 halt()
4541 endef
4542
45434577 define dep_autopatch_appsrc_script.erl
45444578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
45454579 AppSrcScript = AppSrc ++ ".script",
45874621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
45884622 endef
45894623
4590 define dep_fetch_hex.erl
4591 ssl:start(),
4592 inets:start(),
4593 {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
4594 {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
4595 [], [{body_format, binary}]),
4596 {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
4597 {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
4598 ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
4599 halt()
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
46004626 endef
46014627
46024628 # Hex only has a package version. No need to look in the Erlang.mk packages.
46034629 define dep_fetch_hex
4604 $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
46054634 endef
46064635
46074636 define dep_fetch_fail
46314660 $(eval DEP_NAME := $(call dep_name,$1))
46324661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
46334662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4634 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
46354664 exit 17; \
46364665 fi
46374666 $(verbose) mkdir -p $(DEPS_DIR)
46734702 clean:: clean-apps
46744703
46754704 clean-apps:
4676 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4677 $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
46784707 done
46794708
46804709 distclean:: distclean-apps
46814710
46824711 distclean-apps:
4683 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4684 $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
46854714 done
46864715 endif
46874716
47014730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
47024731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
47034732
4704 # External plugins.
4705
4706 DEP_PLUGINS ?=
4707
4708 define core_dep_plugin
4709 -include $(DEPS_DIR)/$(1)
4710
4711 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4712 endef
4713
4714 $(foreach p,$(DEP_PLUGINS),\
4715 $(eval $(if $(findstring /,$p),\
4716 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4717 $(call core_dep_plugin,$p/plugins.mk,$p))))
4718
4719 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4720 # This file is part of erlang.mk and subject to the terms of the ISC License.
4721
4722 # Configuration.
4723
4724 DTL_FULL_PATH ?=
4725 DTL_PATH ?= templates/
4726 DTL_SUFFIX ?= _dtl
4727 DTL_OPTS ?=
4728
4729 # Verbosity.
4730
4731 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
4732 dtl_verbose = $(dtl_verbose_$(V))
4733
4734 # Core targets.
4735
4736 DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl))
4737
4738 ifneq ($(DTL_FILES),)
4739
4740 ifdef DTL_FULL_PATH
4741 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
4742 else
4743 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
4744 endif
4745
4746 ifneq ($(words $(DTL_FILES)),0)
4747 # Rebuild templates when the Makefile changes.
4748 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
4749 @mkdir -p $(ERLANG_MK_TMP)
4750 @if test -f $@; then \
4751 touch $(DTL_FILES); \
4752 fi
4753 @touch $@
4754
4755 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
4756 endif
4757
4758 define erlydtl_compile.erl
4759 [begin
4760 Module0 = case "$(strip $(DTL_FULL_PATH))" of
4761 "" ->
4762 filename:basename(F, ".dtl");
4763 _ ->
4764 "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
4765 re:replace(F2, "/", "_", [{return, list}, global])
4766 end,
4767 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
4768 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
4769 ok -> ok;
4770 {ok, _} -> ok
4771 end
4772 end || F <- string:tokens("$(1)", " ")],
4773 halt().
4774 endef
4775
4776 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
4777 $(if $(strip $?),\
4778 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
4779
4780 endif
4781
4782 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
47834734 # This file is part of erlang.mk and subject to the terms of the ISC License.
47844735
47854736 # Verbosity.
47984749
47994750 define compile_proto.erl
48004751 [begin
4801 Dir = filename:dirname(filename:dirname(F)),
48024752 protobuffs_compile:generate_source(F,
4803 [{output_include_dir, Dir ++ "/include"},
4804 {output_src_dir, Dir ++ "/ebin"}])
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
48054755 end || F <- string:tokens("$(1)", " ")],
48064756 halt().
48074757 endef
48114761 $(if $(strip $?),$(call compile_proto,$?))
48124762 endif
48134763
4814 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
48154765 # This file is part of erlang.mk and subject to the terms of the ISC License.
48164766
48174767 .PHONY: clean-app
48254775 ERLC_EXCLUDE ?=
48264776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
48274777
4778 ERLC_ASN1_OPTS ?=
4779
48284780 ERLC_MIB_OPTS ?=
48294781 COMPILE_MIB_FIRST ?=
48304782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
48744826
48754827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
48764828 define app_file
4877 {application, $(PROJECT), [
4829 {application, '$(PROJECT)', [
48784830 {description, "$(PROJECT_DESCRIPTION)"},
48794831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48804832 {id$(comma)$(space)"$(1)"}$(comma))
48814833 {modules, [$(call comma_list,$(2))]},
48824834 {registered, []},
4883 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48844837 ]}.
48854838 endef
48864839 else
48874840 define app_file
4888 {application, $(PROJECT), [
4841 {application, '$(PROJECT)', [
48894842 {description, "$(PROJECT_DESCRIPTION)"},
48904843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48914844 {id$(comma)$(space)"$(1)"}$(comma))
48924845 {modules, [$(call comma_list,$(2))]},
48934846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
48944847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4895 {mod, {$(PROJECT_MOD), []}}
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48964850 ]}.
48974851 endef
48984852 endif
49024856
49034857 # Source files.
49044858
4905 ERL_FILES = $(sort $(call core_find,src/,*.erl))
4906 CORE_FILES = $(sort $(call core_find,src/,*.core))
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
49074863
49084864 # ASN.1 files.
49094865
49134869
49144870 define compile_asn1
49154871 $(verbose) mkdir -p include/
4916 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
49174873 $(verbose) mv asn1/*.erl src/
49184874 $(verbose) mv asn1/*.hrl include/
49194875 $(verbose) mv asn1/*.asn1db include/
49364892
49374893 # Leex and Yecc files.
49384894
4939 XRL_FILES = $(sort $(call core_find,src/,*.xrl))
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
49404896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
49414897 ERL_FILES += $(XRL_ERL_FILES)
49424898
4943 YRL_FILES = $(sort $(call core_find,src/,*.yrl))
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
49444900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
49454901 ERL_FILES += $(YRL_ERL_FILES)
49464902
49474903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4948 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
49494905
49504906 # Erlang and Core Erlang files.
49514907
49954951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49964952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49974953 (F, Mod, import, {Imp, _}) ->
4998 case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
49994960 false -> ok;
50004961 true -> Add(Mod, Imp)
50014962 end;
50194980 end || F <- ErlFiles],
50204981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
50214982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
50224991 ok = file:write_file("$(1)", [
50234992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
5024 "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n"
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
50254994 ]),
50264995 halt()
50274996 endef
50345003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
50355004 # Rebuild everything when the Makefile changes.
50365005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5037 @mkdir -p $(ERLANG_MK_TMP)
5038 @if test -f $@; then \
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
50395008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
50405009 touch -c $(PROJECT).d; \
50415010 fi
5042 @touch $@
5011 $(verbose) touch $@
50435012
50445013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
50455014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
50465015 endif
50475016
5048 -include $(PROJECT).d
5017 include $(wildcard $(PROJECT).d)
50495018
50505019 ebin/$(PROJECT).app:: ebin/
50515020
50645033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
50655034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
50665035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5067 $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
50685037 > ebin/$(PROJECT).app
50695038 else
50705039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
50885057
50895058 endif
50905059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
50915061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
50925062 # This file is part of erlang.mk and subject to the terms of the ISC License.
50935063
51055075 doc-deps:
51065076 else
51075077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5108 $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5109 endif
5110
5111 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51125082 # This file is part of erlang.mk and subject to the terms of the ISC License.
51135083
51145084 .PHONY: rel-deps
51255095 rel-deps:
51265096 else
51275097 rel-deps: $(ALL_REL_DEPS_DIRS)
5128 $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5129 endif
5130
5131 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51325102 # This file is part of erlang.mk and subject to the terms of the ISC License.
51335103
51345104 .PHONY: test-deps test-dir test-build clean-test-dir
51505120 test-deps:
51515121 else
51525122 test-deps: $(ALL_TEST_DEPS_DIRS)
5153 $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
51545124 endif
51555125
51565126 ifneq ($(wildcard $(TEST_DIR)),)
51835153 endif
51845154 endif
51855155
5186 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51875157 # This file is part of erlang.mk and subject to the terms of the ISC License.
51885158
51895159 .PHONY: rebar.config
52195189 rebar.config:
52205190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
52215191
5222 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
52235193 # This file is part of erlang.mk and subject to the terms of the ISC License.
52245194
5225 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
5226
5227 MAN_INSTALL_PATH ?= /usr/local/share/man
5228 MAN_SECTIONS ?= 3 7
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
52295200
52305201 docs:: asciidoc
52315202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
52325207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
52335210
52345211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
52355212 asciidoc-guide:
52365213 else
5237 asciidoc-guide: distclean-asciidoc doc-deps
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
52385215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
52395216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5240 endif
5241
5242 ifeq ($(wildcard doc/src/manual/*.asciidoc),)
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
52435227 asciidoc-manual:
52445228 else
5245 asciidoc-manual: distclean-asciidoc doc-deps
5246 for f in doc/src/manual/*.asciidoc ; do \
5247 a2x -v -f manpage $$f ; \
5248 done
5249 for s in $(MAN_SECTIONS); do \
5250 mkdir -p doc/man$$s/ ; \
5251 mv doc/src/manual/*.$$s doc/man$$s/ ; \
5252 gzip doc/man$$s/*.$$s ; \
5253 done
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
52545262
52555263 install-docs:: install-asciidoc
52565264
52575265 install-asciidoc: asciidoc-manual
5258 for s in $(MAN_SECTIONS); do \
5259 mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
5260 install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
5261 done
5262 endif
5263
5264 distclean:: distclean-asciidoc
5265
5266 distclean-asciidoc:
5267 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
5268
5269 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
52705276 # This file is part of erlang.mk and subject to the terms of the ISC License.
52715277
52725278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
53235329 define bs_Makefile
53245330 PROJECT = $p
53255331 PROJECT_DESCRIPTION = New project
5326 PROJECT_VERSION = 0.0.1
5332 PROJECT_VERSION = 0.1.0
53275333
53285334 # Whitespace to be used when creating files from templates.
53295335 SP = $(SP)
53335339 define bs_Makefile
53345340 PROJECT = $p
53355341 PROJECT_DESCRIPTION = New project
5336 PROJECT_VERSION = 0.0.1
5342 PROJECT_VERSION = 0.1.0
53375343
53385344 endef
53395345 endif
53415347 define bs_apps_Makefile
53425348 PROJECT = $p
53435349 PROJECT_DESCRIPTION = New project
5344 PROJECT_VERSION = 0.0.1
5350 PROJECT_VERSION = 0.1.0
53455351
53465352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
53475353 endef
53615367 endef
53625368
53635369 define bs_relx_config
5364 {release, {$p_release, "1"}, [$p]}.
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
53655371 {extended_start_script, true}.
53665372 {sys_config, "rel/sys.config"}.
53675373 {vm_args, "rel/vm.args"}.
57125718 ifndef t
57135719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57145720 endif
5715 ifndef tpl_$(t)
5716 $(error Unknown template)
5717 endif
57185721 ifndef n
57195722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57205723 endif
57275730 list-templates:
57285731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
57295732
5730 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
57315734 # This file is part of erlang.mk and subject to the terms of the ISC License.
57325735
57335736 .PHONY: clean-c_src distclean-c_src-env
59625965 $(call render_template,bs_erl_nif,src/$n.erl)
59635966 endif
59645967
5965 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
59665969 # This file is part of erlang.mk and subject to the terms of the ISC License.
59675970
5968 .PHONY: ci ci-setup distclean-kerl
5969
5970 KERL ?= $(CURDIR)/kerl
5971 export KERL
5972
5973 KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
5974
5975 OTP_GIT ?= https://github.com/erlang/otp
5976
5977 CI_INSTALL_DIR ?= $(HOME)/erlang
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
59785973 CI_OTP ?=
5979
5980 ifeq ($(strip $(CI_OTP)),)
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
59815986 ci::
59825987 else
5983 ci:: $(addprefix ci-,$(CI_OTP))
5984
5985 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
59866007
59876008 ci-setup::
6009
6010 ci-extra::
59886011
59896012 ci_verbose_0 = @echo " CI " $(1);
59906013 ci_verbose = $(ci_verbose_$(V))
59916014
59926015 define ci_target
5993 ci-$(1): $(CI_INSTALL_DIR)/$(1)
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
59946018 $(ci_verbose) \
5995 PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
5996 CI_OTP_RELEASE="$(1)" \
5997 CT_OPTS="-label $(1)" \
5998 $(MAKE) clean ci-setup tests
5999 endef
6000
6001 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
60026030
60036031 define ci_otp_target
60046032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
60056033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6006 $(KERL) build git $(OTP_GIT) $(1) $(1)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
60076035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
60086036 endif
60096037 endef
60106038
60116039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
60126040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
60136052 $(KERL):
6014 $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
60156056 $(verbose) chmod +x $(KERL)
60166057
60176058 help::
60286069 $(gen_verbose) rm -rf $(KERL)
60296070 endif
60306071
6031 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
60326073 # This file is part of erlang.mk and subject to the terms of the ISC License.
60336074
60346075 .PHONY: ct apps-ct distclean-ct
60366077 # Configuration.
60376078
60386079 CT_OPTS ?=
6080
60396081 ifneq ($(wildcard $(TEST_DIR)),)
6040 CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6041 else
6042 CT_SUITES ?=
6043 endif
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
60446088
60456089 # Core targets.
60466090
60636107 -noinput \
60646108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
60656109 -dir $(TEST_DIR) \
6066 -logdir $(CURDIR)/logs
6110 -logdir $(CT_LOGS_DIR)
60676111
60686112 ifeq ($(CT_SUITES),)
60696113 ct: $(if $(IS_APP),,apps-ct)
60706114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
60716117 ct: test-build $(if $(IS_APP),,apps-ct)
6072 $(verbose) mkdir -p $(CURDIR)/logs/
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
60736119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
60746121 endif
60756122
60766123 ifneq ($(ALL_APPS_DIRS),)
60976144
60986145 define ct_suite_target
60996146 ct-$(1): test-build
6100 $(verbose) mkdir -p $(CURDIR)/logs/
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
61016148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
61026149 endef
61036150
61046151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
61056152
61066153 distclean-ct:
6107 $(gen_verbose) rm -rf $(CURDIR)/logs/
6108
6109 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61106157 # This file is part of erlang.mk and subject to the terms of the ISC License.
61116158
61126159 .PHONY: plt distclean-plt dialyze
61506197 endef
61516198
61526199 $(DIALYZER_PLT): deps app
6153 $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
61546204
61556205 plt: $(DIALYZER_PLT)
61566206
61646214 endif
61656215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
61666216
6167 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61686218 # This file is part of erlang.mk and subject to the terms of the ISC License.
61696219
61706220 .PHONY: distclean-edoc edoc
61726222 # Configuration.
61736223
61746224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
61756235
61766236 # Core targets.
61776237
6178 ifneq ($(wildcard doc/overview.edoc),)
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
61796239 docs:: edoc
61806240 endif
61816241
61846244 # Plugin-specific targets.
61856245
61866246 edoc: distclean-edoc doc-deps
6187 $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
61886248
61896249 distclean-edoc:
61906250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
61916251
6192 # Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61936253 # This file is part of erlang.mk and subject to the terms of the ISC License.
61946254
6195 .PHONY: distclean-escript escript
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
61966320
61976321 # Configuration.
61986322
61996323 ESCRIPT_NAME ?= $(PROJECT)
62006324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
62016325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
62026327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6203
6204 ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
6205 ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
6206 ESCRIPT_EMU_ARGS ?= -pa . \
6207 -sasl errlog_type error \
6208 -escript main $(ESCRIPT_NAME)
6209 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6210 ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
62116332
62126333 # Core targets.
62136334
62206341
62216342 # Plugin-specific targets.
62226343
6223 # Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
6224 # Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
6225 # Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
6226 # Software may only be used for the great good and the true happiness of all
6227 # sentient beings.
6228
6229 define ESCRIPT_RAW
6230 'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
6231 'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
6232 ' [F || F <- A, not filelib:is_dir(F) ] end,'\
6233 'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
6234 'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
6235 'Ez = fun(Escript) ->'\
6236 ' Static = Files([$(ESCRIPT_STATIC)]),'\
6237 ' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
6238 ' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
6239 ' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
6240 ' {archive, Archive, [memory]},'\
6241 ' {shebang, "$(ESCRIPT_SHEBANG)"},'\
6242 ' {comment, "$(ESCRIPT_COMMENT)"},'\
6243 ' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
6244 ' ]),'\
6245 ' file:change_mode(Escript, 8#755)'\
6246 'end,'\
6247 'Ez("$(ESCRIPT_FILE)"),'\
6248 'halt().'
6249 endef
6250
6251 ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
6252
6253 escript:: distclean-escript deps app
6254 $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
62556360
62566361 distclean-escript:
6257 $(gen_verbose) rm -f $(ESCRIPT_NAME)
6258
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
62596365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6260 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
62616366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
62626367
62636368 .PHONY: eunit apps-eunit
63216426
63226427 ifneq ($(ALL_APPS_DIRS),)
63236428 apps-eunit:
6324 $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done
6325 endif
6326 endif
6327
6328 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
63296436 # This file is part of erlang.mk and subject to the terms of the ISC License.
63306437
6331 .PHONY: relx-rel distclean-relx-rel distclean-relx run
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
63326439
63336440 # Configuration.
63346441
6335 RELX ?= $(CURDIR)/relx
6442 RELX ?= $(ERLANG_MK_TMP)/relx
63366443 RELX_CONFIG ?= $(CURDIR)/relx.config
63376444
63386445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
63396446 RELX_OPTS ?=
63406447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
63416454
63426455 ifeq ($(firstword $(RELX_OPTS)),-o)
63436456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
63506463 ifeq ($(IS_DEP),)
63516464 ifneq ($(wildcard $(RELX_CONFIG)),)
63526465 rel:: relx-rel
6353 endif
6354 endif
6355
6356 distclean:: distclean-relx-rel distclean-relx
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
63576472
63586473 # Plugin-specific targets.
63596474
63626477 $(verbose) chmod +x $(RELX)
63636478
63646479 relx-rel: $(RELX) rel-deps app
6365 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
63666484
63676485 distclean-relx-rel:
63686486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6369
6370 distclean-relx:
6371 $(gen_verbose) rm -rf $(RELX)
63726487
63736488 # Run target.
63746489
63776492 else
63786493
63796494 define get_relx_release.erl
6380 {ok, Config} = file:consult("$(RELX_CONFIG)"),
6381 {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
6382 io:format("~s", [Name]),
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
63836504 halt(0).
63846505 endef
63856506
6386 RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
63876514
63886515 run: all
6389 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
63906517
63916518 help::
63926519 $(verbose) printf "%s\n" "" \
63956522
63966523 endif
63976524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
63986526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6399 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
64006527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
64016528
64026529 .PHONY: shell
64216548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
64226549
64236550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6424 $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
64256552
64266553 shell: build-shell-deps
64276554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
64286555
6429 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
64306571 # This file is part of erlang.mk and subject to the terms of the ISC License.
64316572
64326573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
64376578 tests:: triq
64386579
64396580 define triq_check.erl
6440 code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
64416582 try
64426583 case $(1) of
64436584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
64696610 endif
64706611 endif
64716612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
64726614 # Copyright (c) 2015, Erlang Solutions Ltd.
64736615 # This file is part of erlang.mk and subject to the terms of the ISC License.
64746616
64856627 XREFR ?= $(CURDIR)/xrefr
64866628 export XREFR
64876629
6488 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
64896631
64906632 # Core targets.
64916633
64926634 help::
6493 $(verbose) printf "%s\n" "" \
6494 "Xref targets:" \
6495 " xref Run Xrefr using $XREF_CONFIG as config file if defined"
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
64966638
64976639 distclean:: distclean-xref
64986640
65086650 distclean-xref:
65096651 $(gen_verbose) rm -rf $(XREFR)
65106652
6511 # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
65126655 # This file is part of erlang.mk and subject to the terms of the ISC License.
65136656
65146657 COVER_REPORT_DIR = cover
65176660
65186661 ifdef COVER
65196662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
65206664 # All modules in 'ebin'
65216665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
65226666
65316675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
65326676 endif
65336677 endif
6678 endif
65346679
65356680 # Core targets
65366681
65526697 "Cover targets:" \
65536698 " cover-report Generate a HTML coverage report from previously collected" \
65546699 " cover data." \
6555 " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
65566701 "" \
65576702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
65586703 "target tests additionally generates a HTML coverage report from the combined" \
65656710
65666711 .PHONY: coverdata-clean
65676712 coverdata-clean:
6568 $(gen_verbose) rm -f *.coverdata ct.cover.spec
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
65696714
65706715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
65716721 all.coverdata: $(COVERDATA)
6572 $(gen_verbose) $(ERL) -eval ' \
6573 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
6574 cover:export("$@"), halt(0).'
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
65756723
65766724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
65776725 # empty if you want the coverdata files but not the HTML report.
65896737 # Modules which include eunit.hrl always contain one line without coverage
65906738 # because eunit defines test/0 which is never called. We compensate for this.
65916739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6592 grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
65936741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
65946742
65956743 define cover_report.erl
66246772 endef
66256773
66266774 cover-report:
6627 $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
66286776 $(gen_verbose) $(call erlang,$(cover_report.erl))
66296777
66306778 endif
66316779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
66326839
66336840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
66346841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
66976904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
66986905 endif
66996906 ifndef IS_APP
6700 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
67016908 $(MAKE) -C $$dep $@ \
67026909 IS_APP=1 \
6703 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6704 || exit $$?; \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67056911 done
67066912 endif
6707 $(verbose) for dep in $^ ; do \
6913 $(verbose) set -e; for dep in $^ ; do \
67086914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
67096915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6710 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
67116917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
67126918 $(MAKE) -C $$dep fetch-deps \
67136919 IS_DEP=1 \
6714 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6715 || exit $$?; \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67166921 fi \
67176922 fi \
67186923 done
6060 %% the table name
6161 -record(retained_message, {topic,
6262 mqtt_msg}).
63
64 -define(INFO_ITEMS,
65 [host,
66 port,
67 peer_host,
68 peer_port,
69 protocol,
70 channels,
71 channel_max,
72 frame_max,
73 client_properties,
74 ssl,
75 ssl_protocol,
76 ssl_key_exchange,
77 ssl_cipher,
78 ssl_hash,
79 conn_name,
80 connection_state,
81 connection,
82 consumer_tags,
83 unacked_pubs,
84 awaiting_ack,
85 awaiting_seqno,
86 message_id,
87 client_id,
88 clean_sess,
89 will_msg,
90 channels,
91 exchange,
92 ssl_login_name,
93 retainer_pid,
94 user,
95 vhost]).
96
22 # before the inclusion of erlang.mk leading to the wrong target becoming
33 # the default.
44 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
526 endif
627
728 # --------------------------------------------------------------------
2041 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
2142 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
2243 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
2345 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
2446 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
2547 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
2648 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
2749 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
2851 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
2952 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
3054 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
3155 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
3256 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
3559 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
3660 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
3761 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
3863 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
3964 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
4065 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
7095
7196 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
7297
73 # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
74 # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
75 # needs to add "ranch" as a BUILD_DEPS. The list of projects needing
76 # this workaround are:
77 # o rabbitmq-web-stomp
78 dep_ranch = git https://github.com/ninenines/ranch 1.2.1
98 # Third-party dependencies version pinning.
99 #
100 # We do that in this file, which is copied in all projects, to ensure
101 # all projects use the same versions. It avoids conflicts and makes it
102 # possible to work with rabbitmq-public-umbrella.
103
104 dep_cowboy_commit = 1.0.4
105 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
106 dep_ranch_commit = 1.3.1
107 dep_webmachine_commit = 1.10.8p2
79108
80109 RABBITMQ_COMPONENTS = amqp_client \
81110 rabbit \
82111 rabbit_common \
83112 rabbitmq_amqp1_0 \
84113 rabbitmq_auth_backend_amqp \
114 rabbitmq_auth_backend_cache \
85115 rabbitmq_auth_backend_http \
86116 rabbitmq_auth_backend_ldap \
87117 rabbitmq_auth_mechanism_ssl \
88118 rabbitmq_boot_steps_visualiser \
89119 rabbitmq_clusterer \
120 rabbitmq_cli \
90121 rabbitmq_codegen \
91122 rabbitmq_consistent_hash_exchange \
123 rabbitmq_ct_client_helpers \
92124 rabbitmq_ct_helpers \
93125 rabbitmq_delayed_message_exchange \
94126 rabbitmq_dotnet_client \
97129 rabbitmq_federation_management \
98130 rabbitmq_java_client \
99131 rabbitmq_jms_client \
132 rabbitmq_jms_cts \
100133 rabbitmq_jms_topic_exchange \
101134 rabbitmq_lvc \
102135 rabbitmq_management \
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mqtt).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mqtt_collector).
1717
1818 -behaviour(gen_server).
1919
20 -export([start_link/0, register/2, unregister/2]).
20 -export([start_link/0, register/2, unregister/2, list/0]).
2121
2222 -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
2323 terminate/2, code_change/3]).
3636
3737 unregister(ClientId, Pid) ->
3838 gen_server:call(rabbit_mqtt_collector, {unregister, ClientId, Pid}, infinity).
39
40 list() ->
41 gen_server:call(rabbit_mqtt_collector, list).
3942
4043 %%----------------------------------------------------------------------------
4144
6568 end,
6669 {reply, Reply, State#state{ client_ids = Ids1 }};
6770
71 handle_call(list, _From, State = #state{client_ids = Ids}) ->
72 {reply, dict:to_list(Ids), State};
73
6874 handle_call(Msg, _From, State) ->
6975 {stop, {unhandled_call, Msg}, State}.
7076
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mqtt_connection_sup).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mqtt_frame).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mqtt_processor).
2020 close_connection/1]).
2121
2222 %% for testing purposes
23 -export([get_vhost_username/1]).
23 -export([get_vhost_username/1, get_vhost/3, get_vhost_from_user_mapping/2]).
2424
2525 -include_lib("amqp_client/include/amqp_client.hrl").
2626 -include("rabbit_mqtt_frame.hrl").
454454 process_login(UserBin, PassBin, ProtoVersion,
455455 #proc_state{ channels = {undefined, undefined},
456456 socket = Sock,
457 adapter_info = AdapterInfo }) ->
458 {VHost, UsernameBin} = get_vhost_username(UserBin),
459 case amqp_connection:start(#amqp_params_direct{
460 username = UsernameBin,
461 password = PassBin,
462 virtual_host = VHost,
463 adapter_info = set_proto_version(AdapterInfo, ProtoVersion)}) of
464 {ok, Connection} ->
465 case rabbit_access_control:check_user_loopback(UsernameBin, Sock) of
466 ok ->
467 [{internal_user, InternalUser}] = amqp_connection:info(
468 Connection, [internal_user]),
469 {?CONNACK_ACCEPT, Connection, VHost,
470 #auth_state{user = InternalUser,
471 username = UsernameBin,
472 vhost = VHost}};
473 not_allowed ->
474 amqp_connection:close(Connection),
475 rabbit_log:warning(
476 "MQTT login failed for ~p access_refused "
477 "(access must be from localhost)~n",
478 [binary_to_list(UsernameBin)]),
457 adapter_info = AdapterInfo,
458 ssl_login_name = SslLoginName}) ->
459 {ok, {_, _, _, ToPort}} = rabbit_net:socket_ends(Sock, inbound),
460 {VHostPickedUsing, {VHost, UsernameBin}} = get_vhost(UserBin, SslLoginName, ToPort),
461 rabbit_log:info(
462 "MQTT vhost picked using ~s~n",
463 [human_readable_vhost_lookup_strategy(VHostPickedUsing)]),
464 case rabbit_vhost:exists(VHost) of
465 true ->
466 case amqp_connection:start(#amqp_params_direct{
467 username = UsernameBin,
468 password = PassBin,
469 virtual_host = VHost,
470 adapter_info = set_proto_version(AdapterInfo, ProtoVersion)}) of
471 {ok, Connection} ->
472 case rabbit_access_control:check_user_loopback(UsernameBin, Sock) of
473 ok ->
474 [{internal_user, InternalUser}] = amqp_connection:info(
475 Connection, [internal_user]),
476 {?CONNACK_ACCEPT, Connection, VHost,
477 #auth_state{user = InternalUser,
478 username = UsernameBin,
479 vhost = VHost}};
480 not_allowed ->
481 amqp_connection:close(Connection),
482 rabbit_log:warning(
483 "MQTT login failed for ~p access_refused "
484 "(access must be from localhost)~n",
485 [binary_to_list(UsernameBin)]),
486 ?CONNACK_AUTH
487 end;
488 {error, {auth_failure, Explanation}} ->
489 rabbit_log:error("MQTT login failed for ~p auth_failure: ~s~n",
490 [binary_to_list(UserBin), Explanation]),
491 ?CONNACK_CREDENTIALS;
492 {error, access_refused} ->
493 rabbit_log:warning("MQTT login failed for ~p access_refused "
494 "(vhost access not allowed)~n",
495 [binary_to_list(UserBin)]),
496 ?CONNACK_AUTH;
497 {error, not_allowed} ->
498 %% when vhost allowed for TLS connection
499 rabbit_log:warning("MQTT login failed for ~p access_refused "
500 "(vhost access not allowed)~n",
501 [binary_to_list(UserBin)]),
479502 ?CONNACK_AUTH
480503 end;
481 {error, {auth_failure, Explanation}} ->
482 rabbit_log:error("MQTT login failed for ~p auth_failure: ~s~n",
483 [binary_to_list(UserBin), Explanation]),
484 ?CONNACK_CREDENTIALS;
485 {error, access_refused} ->
486 rabbit_log:warning("MQTT login failed for ~p access_refused "
487 "(vhost access not allowed)~n",
488 [binary_to_list(UserBin)]),
489 ?CONNACK_AUTH
504 false ->
505 rabbit_log:error("MQTT login failed for ~p auth_failure: vhost ~s does not exist~n",
506 [binary_to_list(UserBin), VHost]),
507 ?CONNACK_CREDENTIALS
508 end.
509
510 get_vhost(UserBin, none, Port) ->
511 get_vhost_no_ssl(UserBin, Port);
512 get_vhost(UserBin, undefined, Port) ->
513 get_vhost_no_ssl(UserBin, Port);
514 get_vhost(UserBin, SslLogin, Port) ->
515 get_vhost_ssl(UserBin, SslLogin, Port).
516
517 get_vhost_no_ssl(UserBin, Port) ->
518 case vhost_in_username(UserBin) of
519 true ->
520 {vhost_in_username_or_default, get_vhost_username(UserBin)};
521 false ->
522 PortVirtualHostMapping = rabbit_runtime_parameters:value_global(
523 mqtt_port_to_vhost_mapping
524 ),
525 case get_vhost_from_port_mapping(Port, PortVirtualHostMapping) of
526 undefined ->
527 {default_vhost, {rabbit_mqtt_util:env(vhost), UserBin}};
528 VHost ->
529 {port_to_vhost_mapping, {VHost, UserBin}}
530 end
531 end.
532
533 get_vhost_ssl(UserBin, SslLoginName, Port) ->
534 UserVirtualHostMapping = rabbit_runtime_parameters:value_global(
535 mqtt_default_vhosts
536 ),
537 case get_vhost_from_user_mapping(SslLoginName, UserVirtualHostMapping) of
538 undefined ->
539 PortVirtualHostMapping = rabbit_runtime_parameters:value_global(
540 mqtt_port_to_vhost_mapping
541 ),
542 case get_vhost_from_port_mapping(Port, PortVirtualHostMapping) of
543 undefined ->
544 {vhost_in_username_or_default, get_vhost_username(UserBin)};
545 VHostFromPortMapping ->
546 {port_to_vhost_mapping, {VHostFromPortMapping, UserBin}}
547 end;
548 VHostFromCertMapping ->
549 {cert_to_vhost_mapping, {VHostFromCertMapping, UserBin}}
550 end.
551
552 vhost_in_username(UserBin) ->
553 case application:get_env(?APP, ignore_colons_in_username) of
554 {ok, true} -> false;
555 _ ->
556 %% split at the last colon, disallowing colons in username
557 case re:split(UserBin, ":(?!.*?:)") of
558 [_, _] -> true;
559 [UserBin] -> false
560 end
490561 end.
491562
492563 get_vhost_username(UserBin) ->
500571 [UserBin] -> Default
501572 end
502573 end.
574
575 get_vhost_from_user_mapping(_User, not_found) ->
576 undefined;
577 get_vhost_from_user_mapping(User, Mapping) ->
578 case rabbit_misc:pget(User, Mapping) of
579 undefined ->
580 undefined;
581 VHost ->
582 VHost
583 end.
584
585 get_vhost_from_port_mapping(_Port, not_found) ->
586 undefined;
587 get_vhost_from_port_mapping(Port, Mapping) ->
588 Res = case rabbit_misc:pget(rabbit_data_coercion:to_binary(Port), Mapping) of
589 undefined ->
590 undefined;
591 VHost ->
592 VHost
593 end,
594 Res.
595
596 human_readable_vhost_lookup_strategy(vhost_in_username_or_default) ->
597 "vhost in username or default";
598 human_readable_vhost_lookup_strategy(port_to_vhost_mapping) ->
599 "MQTT port to vhost mapping";
600 human_readable_vhost_lookup_strategy(cert_to_vhost_mapping) ->
601 "client certificate to vhost mapping";
602 human_readable_vhost_lookup_strategy(default_vhost) ->
603 "plugin configuration or default";
604 human_readable_vhost_lookup_strategy(Val) ->
605 atom_to_list(Val).
503606
504607 creds(User, Pass, SSLLoginName) ->
505608 DefaultUser = rabbit_mqtt_util:env(default_user),
593696 {Q, PState}
594697 end.
595698
596 send_will(PState = #proc_state{ will_msg = WillMsg }) ->
597 amqp_pub(WillMsg, PState).
699 send_will(PState = #proc_state{will_msg = undefined}) ->
700 PState;
701
702 send_will(PState = #proc_state{will_msg = WillMsg = #mqtt_msg{retain = Retain, topic = Topic}, retainer_pid = RPid}) ->
703 amqp_pub(WillMsg, PState),
704 case Retain of
705 false -> ok;
706 true -> hand_off_to_retainer(RPid, Topic, WillMsg)
707 end,
708 PState.
598709
599710 amqp_pub(undefined, PState) ->
600711 PState;
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mqtt_reader).
2323 -export([conserve_resources/3, start_keepalive/2]).
2424
2525 -export([ssl_login_name/1]).
26 -export([info/2]).
2627
2728 -include_lib("amqp_client/include/amqp_client.hrl").
2829 -include("rabbit_mqtt.hrl").
30
31 -define(SIMPLE_METRICS, [pid, recv_oct, send_oct, reductions]).
32 -define(OTHER_METRICS, [recv_cnt, send_cnt, send_pend, garbage_collection, state]).
2933
3034 %%----------------------------------------------------------------------------
3135
4751 conserve_resources(Pid, _, {_, Conserve, _}) ->
4852 Pid ! {conserve_resources, Conserve},
4953 ok.
54
55 info(Pid, InfoItems) ->
56 case InfoItems -- ?INFO_ITEMS of
57 [] -> gen_server2:call(Pid, {info, InfoItems});
58 UnknownItems -> throw({bad_argument, UnknownItems})
59 end.
5060
5161 %%----------------------------------------------------------------------------
5262
8494 terminate({network_error, Reason}, undefined)
8595 end.
8696
97 handle_call({info, InfoItems}, _From, State) ->
98 Infos = lists:map(
99 fun(InfoItem) ->
100 {InfoItem, info_internal(InfoItem, State)}
101 end,
102 InfoItems),
103 {reply, Infos, State};
104
87105 handle_call(Msg, From, State) ->
88106 {stop, {mqtt_unexpected_call, Msg, From}, State}.
89107
241259 State = #state{ parse_state = ParseState,
242260 proc_state = ProcState,
243261 conn_name = ConnStr }) ->
244 case rabbit_mqtt_frame:parse(Bytes, ParseState) of
262 case parse(Bytes, ParseState) of
245263 {more, ParseState1} ->
246264 {noreply,
247265 ensure_stats_timer(control_throttle( State #state{ parse_state = ParseState1 })),
256274 proc_state = ProcState1,
257275 connection = ConnPid });
258276 {error, Reason, ProcState1} ->
259 log(info, "MQTT protocol error ~p for connection ~p~n",
277 log(info, "MQTT protocol error ~p for connection ~s~n",
260278 [Reason, ConnStr]),
261279 {stop, {shutdown, Reason}, pstate(State, ProcState1)};
262280 {error, Error} ->
263 log(error, "MQTT detected framing error '~p' for connection ~p~n",
281 log(error, "MQTT detected framing error '~p' for connection ~s~n",
264282 [Error, ConnStr]),
265283 {stop, {shutdown, Error}, State};
266284 {stop, ProcState1} ->
267285 {stop, normal, pstate(State, ProcState1)}
268286 end;
287 {error, {cannot_parse, Error, Stacktrace}} ->
288 log(error, "MQTT cannot parse frame for connection '~s', unparseable payload: ~p, error: {~p, ~p} ~n",
289 [ConnStr, Bytes, Error, Stacktrace]),
290 {stop, {shutdown, Error}, State};
269291 {error, Error} ->
270 log(error, "MQTT detected framing error '~p' for connection ~p~n",
292 log(error, "MQTT detected framing error '~p' for connection ~s~n",
271293 [ConnStr, Error]),
272294 {stop, {shutdown, Error}, State}
273295 end.
284306 State #state{ proc_state = PState }.
285307
286308 %%----------------------------------------------------------------------------
309 parse(Bytes, ParseState) ->
310 try
311 rabbit_mqtt_frame:parse(Bytes, ParseState)
312 catch
313 _:Reason ->
314 {error, {cannot_parse, Reason, erlang:get_stacktrace()}}
315 end.
287316
288317 log(Level, Fmt, Args) -> rabbit_log:log(connection, Level, Fmt, Args).
289318
342371 handle_info({inet_async, Sock, noref, {ok, Data}},
343372 State#state{ deferred_recv = undefined }).
344373
374 maybe_emit_stats(undefined) ->
375 ok;
345376 maybe_emit_stats(State) ->
346377 rabbit_event:if_enabled(State, #state.stats_timer,
347378 fun() -> emit_stats(State) end).
348379
349 emit_stats(State=#state{socket=Sock, connection_state=ConnState, connection=Conn}) ->
350 SockInfos = case rabbit_net:getstat(Sock,
351 [recv_oct, recv_cnt, send_oct, send_cnt, send_pend]) of
352 {ok, SI} -> SI;
353 {error, _} -> []
354 end,
355 Infos = [{pid, Conn}, {state, ConnState}|SockInfos],
356 rabbit_event:notify(connection_stats, Infos),
380 emit_stats(State=#state{connection = C}) when C == none; C == undefined ->
381 %% Avoid emitting stats on terminate when the connection has not yet been
382 %% established, as this causes orphan entries on the stats database
383 State1 = rabbit_event:reset_stats_timer(State, #state.stats_timer),
384 ensure_stats_timer(State1);
385 emit_stats(State) ->
386 [{_, Pid}, {_, Recv_oct}, {_, Send_oct}, {_, Reductions}] = I
387 = infos(?SIMPLE_METRICS, State),
388 Infos = infos(?OTHER_METRICS, State),
389 rabbit_core_metrics:connection_stats(Pid, Infos),
390 rabbit_core_metrics:connection_stats(Pid, Recv_oct, Send_oct, Reductions),
391 rabbit_event:notify(connection_stats, Infos ++ I),
357392 State1 = rabbit_event:reset_stats_timer(State, #state.stats_timer),
358393 ensure_stats_timer(State1).
359394
360395 ensure_stats_timer(State = #state{}) ->
361396 rabbit_event:ensure_stats_timer(State, #state.stats_timer, emit_stats).
397
398 infos(Items, State) -> [{Item, info_internal(Item, State)} || Item <- Items].
399
400 info_internal(pid, State) -> info_internal(connection, State);
401 info_internal(SockStat, #state{socket = Sock}) when SockStat =:= recv_oct;
402 SockStat =:= recv_cnt;
403 SockStat =:= send_oct;
404 SockStat =:= send_cnt;
405 SockStat =:= send_pend ->
406 case rabbit_net:getstat(Sock, [SockStat]) of
407 {ok, [{_, N}]} when is_number(N) -> N;
408 _ -> 0
409 end;
410 info_internal(state, State) -> info_internal(connection_state, State);
411 info_internal(garbage_collection, _State) ->
412 rabbit_misc:get_gc_info(self());
413 info_internal(reductions, _State) ->
414 {reductions, Reductions} = erlang:process_info(self(), reductions),
415 Reductions;
416 info_internal(conn_name, #state{conn_name = Val}) ->
417 Val;
418 info_internal(connection_state, #state{received_connect_frame = false}) ->
419 starting;
420 info_internal(connection_state, #state{connection_state = Val}) ->
421 Val;
422 info_internal(connection, #state{connection = Val}) ->
423 Val;
424 info_internal(Key, #state{proc_state = ProcState}) ->
425 rabbit_mqtt_processor:info(Key, ProcState).
3838
3939 start_child(RetainStoreMod, VHost) ->
4040 supervisor2:start_child(?MODULE,
41 {binary_to_atom(VHost, ?ENCODING),
41
42 {vhost_to_atom(VHost),
4243 {rabbit_mqtt_retainer, start_link, [RetainStoreMod, VHost]},
4344 permanent, 60, worker, [rabbit_mqtt_retainer]}).
4445
4546 delete_child(VHost) ->
46 Id = binary_to_atom(VHost, ?ENCODING),
47 Id = vhost_to_atom(VHost),
4748 ok = supervisor2:terminate_child(?MODULE, Id),
4849 ok = supervisor2:delete_child(?MODULE, Id).
4950
5455 {ok, {{one_for_one, 5, 5}, child_specs(Mod, rabbit_vhost:list())}}.
5556
5657 child_specs(Mod, VHosts) ->
57 [{binary_to_atom(V, ?ENCODING),
58 %% see start_child/2
59 [{vhost_to_atom(V),
5860 {rabbit_mqtt_retainer, start_link, [Mod, V]},
5961 permanent, infinity, worker, [rabbit_mqtt_retainer]} || V <- VHosts].
62
63 vhost_to_atom(VHost) ->
64 %% we'd like to avoid any conversion here because
65 %% this atom isn't meant to be human-readable, only
66 %% unique. This makes sure we don't get noisy process restarts
67 %% with really unusual vhost names used by various HTTP API test suites
68 rabbit_data_coercion:to_atom(VHost, latin1).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mqtt_sup).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_mqtt_util).
+0
-25
deps/rabbitmq_mqtt/src/rabbitmq_mqtt.app.src less more
0 {application, rabbitmq_mqtt,
1 [{description, "RabbitMQ MQTT Adapter"},
2 {vsn, "3.6.6"},
3 {modules, []},
4 {registered, []},
5 {mod, {rabbit_mqtt, []}},
6 {env, [{default_user, <<"guest">>},
7 {default_pass, <<"guest">>},
8 {ssl_cert_login,false},
9 %% To satisfy an unfortunate expectation from popular MQTT clients.
10 {allow_anonymous, true},
11 {vhost, <<"/">>},
12 {exchange, <<"amq.topic">>},
13 {subscription_ttl, 86400000}, %% 24 hours
14 {retained_message_store, rabbit_mqtt_retained_msg_store_dets},
15 %% only used by DETS store
16 {retained_message_store_dets_sync_interval, 2000},
17 {prefetch, 10},
18 {ssl_listeners, []},
19 {num_ssl_acceptors, 1},
20 {tcp_listeners, [1883]},
21 {num_tcp_acceptors, 10},
22 {tcp_listen_options, [{backlog, 128},
23 {nodelay, true}]}]},
24 {applications, [kernel, stdlib, rabbit_common, rabbit, ranch, amqp_client]}]}.
00 PROJECT = rabbitmq_recent_history_exchange
1 PROJECT_DESCRIPTION = RabbitMQ Recent History Exchange
12
23 DEPS = rabbit_common rabbit
3 TEST_DEPS = rabbitmq_ct_helpers amqp_client
4 TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers amqp_client
45
6 DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
57 DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk
68
79 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
0 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
11 #
22 # Permission to use, copy, modify, and/or distribute this software for any
33 # purpose with or without fee is hereby granted, provided that the above
1111 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1212 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1313
14 .PHONY: all app apps deps search rel docs install-docs check tests clean distclean help erlang-mk
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
1515
1616 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17
18 ERLANG_MK_VERSION = 2.0.0-pre.2-144-g647ffd1
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
1931
2032 # Core configuration.
2133
2436
2537 PROJECT_VERSION ?= rolling
2638 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
2740
2841 # Verbosity.
2942
8497 rel::
8598 $(verbose) :
8699
100 relup:: deps app
101
87102 check:: tests
88103
89104 clean:: clean-crashdump
101116 help::
102117 $(verbose) printf "%s\n" \
103118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
104 "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
105120 "" \
106121 "Usage: [V=1] $(MAKE) [target]..." \
107122 "" \
149164 core_native_path = $1
150165 endif
151166
152 ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
153 define core_http_get
154 wget --no-check-certificate -O $(1) $(2)|| rm $(1)
155 endef
156 else
157 define core_http_get.erl
158 ssl:start(),
159 inets:start(),
160 case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
161 {ok, {{_, 200, _}, _, Body}} ->
162 case file:write_file("$(1)", Body) of
163 ok -> ok;
164 {error, R1} -> halt(R1)
165 end;
166 {error, R2} ->
167 halt(R2)
168 end,
169 halt(0).
170 endef
171
172 define core_http_get
173 $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
174 endef
175 endif
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
176168
177169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
178170
192184 ERLANG_MK_BUILD_CONFIG ?= build.config
193185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
194186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
195188 erlang-mk:
196189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
197190 ifdef ERLANG_MK_COMMIT
198191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
199192 endif
200193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
201 $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
202195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
203196 rm -rf $(ERLANG_MK_BUILD_DIR)
204197
285278 pkg_apns_repo = https://github.com/inaka/apns4erl
286279 pkg_apns_commit = master
287280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
288289 PACKAGES += azdht
289290 pkg_azdht_name = azdht
290291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
405406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
406407 pkg_bootstrap_commit = master
407408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
408417 PACKAGES += boss_db
409418 pkg_boss_db_name = boss_db
410419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
413422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
414423 pkg_boss_db_commit = master
415424
416 PACKAGES += boss
417 pkg_boss_name = boss
418 pkg_boss_description = Erlang web MVC, now featuring Comet
419 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
420 pkg_boss_fetch = git
421 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
422 pkg_boss_commit = master
423
424425 PACKAGES += brod
425426 pkg_brod_name = brod
426427 pkg_brod_description = Kafka client in Erlang
565566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
566567 pkg_cloudi_service_api_requests_commit = master
567568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
568585 PACKAGES += cloudi_service_db_cassandra_cql
569586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
570587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
573590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
574591 pkg_cloudi_service_db_cassandra_cql_commit = master
575592
576 PACKAGES += cloudi_service_db_cassandra
577 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
579 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
580 pkg_cloudi_service_db_cassandra_fetch = git
581 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
582 pkg_cloudi_service_db_cassandra_commit = master
583
584593 PACKAGES += cloudi_service_db_couchdb
585594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
586595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
637646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
638647 pkg_cloudi_service_db_tokyotyrant_commit = master
639648
640 PACKAGES += cloudi_service_db
641 pkg_cloudi_service_db_name = cloudi_service_db
642 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
643 pkg_cloudi_service_db_homepage = http://cloudi.org/
644 pkg_cloudi_service_db_fetch = git
645 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
646 pkg_cloudi_service_db_commit = master
647
648649 PACKAGES += cloudi_service_filesystem
649650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
650651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
10371038 pkg_edown_repo = https://github.com/uwiger/edown
10381039 pkg_edown_commit = master
10391040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
10401049 PACKAGES += eep_app
10411050 pkg_eep_app_name = eep_app
10421051 pkg_eep_app_description = Embedded Event Processing
10451054 pkg_eep_app_repo = https://github.com/darach/eep-erl
10461055 pkg_eep_app_commit = master
10471056
1048 PACKAGES += eep
1049 pkg_eep_name = eep
1050 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1051 pkg_eep_homepage = https://github.com/virtan/eep
1052 pkg_eep_fetch = git
1053 pkg_eep_repo = https://github.com/virtan/eep
1054 pkg_eep_commit = master
1055
10561057 PACKAGES += efene
10571058 pkg_efene_name = efene
10581059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
12371238 pkg_eqm_repo = https://github.com/loucash/eqm
12381239 pkg_eqm_commit = master
12391240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
12401249 PACKAGES += eredis_pool
12411250 pkg_eredis_pool_name = eredis_pool
12421251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
12451254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
12461255 pkg_eredis_pool_commit = master
12471256
1248 PACKAGES += eredis
1249 pkg_eredis_name = eredis
1250 pkg_eredis_description = Erlang Redis client
1251 pkg_eredis_homepage = https://github.com/wooga/eredis
1252 pkg_eredis_fetch = git
1253 pkg_eredis_repo = https://github.com/wooga/eredis
1254 pkg_eredis_commit = master
1255
12561257 PACKAGES += erl_streams
12571258 pkg_erl_streams_name = erl_streams
12581259 pkg_erl_streams_description = Streams in Erlang
15411542 pkg_etap_repo = https://github.com/ngerakines/etap
15421543 pkg_etap_commit = master
15431544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
15441553 PACKAGES += etest_http
15451554 pkg_etest_http_name = etest_http
15461555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
15491558 pkg_etest_http_repo = https://github.com/wooga/etest_http
15501559 pkg_etest_http_commit = master
15511560
1552 PACKAGES += etest
1553 pkg_etest_name = etest
1554 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1555 pkg_etest_homepage = https://github.com/wooga/etest
1556 pkg_etest_fetch = git
1557 pkg_etest_repo = https://github.com/wooga/etest
1558 pkg_etest_commit = master
1559
15601561 PACKAGES += etoml
15611562 pkg_etoml_name = etoml
15621563 pkg_etoml_description = TOML language erlang parser
15651566 pkg_etoml_repo = https://github.com/kalta/etoml
15661567 pkg_etoml_commit = master
15671568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
15681577 PACKAGES += eunit_formatters
15691578 pkg_eunit_formatters_name = eunit_formatters
15701579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
15731582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
15741583 pkg_eunit_formatters_commit = master
15751584
1576 PACKAGES += eunit
1577 pkg_eunit_name = eunit
1578 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1579 pkg_eunit_homepage = https://github.com/richcarl/eunit
1580 pkg_eunit_fetch = git
1581 pkg_eunit_repo = https://github.com/richcarl/eunit
1582 pkg_eunit_commit = master
1583
15841585 PACKAGES += euthanasia
15851586 pkg_euthanasia_name = euthanasia
15861587 pkg_euthanasia_description = Merciful killer for your Erlang processes
15981599 pkg_evum_commit = master
15991600
16001601 PACKAGES += exec
1601 pkg_exec_name = exec
1602 pkg_exec_name = erlexec
16021603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
16031604 pkg_exec_homepage = http://saleyn.github.com/erlexec
16041605 pkg_exec_fetch = git
17171718 pkg_fn_repo = https://github.com/reiddraper/fn
17181719 pkg_fn_commit = master
17191720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
17201729 PACKAGES += folsom_cowboy
17211730 pkg_folsom_cowboy_name = folsom_cowboy
17221731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
17251734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
17261735 pkg_folsom_cowboy_commit = master
17271736
1728 PACKAGES += folsom
1729 pkg_folsom_name = folsom
1730 pkg_folsom_description = Expose Erlang Events and Metrics
1731 pkg_folsom_homepage = https://github.com/boundary/folsom
1732 pkg_folsom_fetch = git
1733 pkg_folsom_repo = https://github.com/boundary/folsom
1734 pkg_folsom_commit = master
1735
17361737 PACKAGES += folsomite
17371738 pkg_folsomite_name = folsomite
17381739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
20932094 pkg_jesse_repo = https://github.com/for-GET/jesse
20942095 pkg_jesse_commit = master
20952096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
20962105 PACKAGES += jiffy_v
20972106 pkg_jiffy_v_name = jiffy_v
20982107 pkg_jiffy_v_description = JSON validation utility
21012110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
21022111 pkg_jiffy_v_commit = master
21032112
2104 PACKAGES += jiffy
2105 pkg_jiffy_name = jiffy
2106 pkg_jiffy_description = JSON NIFs for Erlang.
2107 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2108 pkg_jiffy_fetch = git
2109 pkg_jiffy_repo = https://github.com/davisp/jiffy
2110 pkg_jiffy_commit = master
2111
21122113 PACKAGES += jobs
21132114 pkg_jobs_name = jobs
21142115 pkg_jobs_description = a Job scheduler for load regulation
21252126 pkg_joxa_repo = https://github.com/joxa/joxa
21262127 pkg_joxa_commit = master
21272128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
21282137 PACKAGES += json_rec
21292138 pkg_json_rec_name = json_rec
21302139 pkg_json_rec_description = JSON to erlang record
21332142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
21342143 pkg_json_rec_commit = master
21352144
2136 PACKAGES += json
2137 pkg_json_name = json
2138 pkg_json_description = a high level json library for erlang (17.0+)
2139 pkg_json_homepage = https://github.com/talentdeficit/json
2140 pkg_json_fetch = git
2141 pkg_json_repo = https://github.com/talentdeficit/json
2142 pkg_json_commit = master
2143
21442145 PACKAGES += jsone
21452146 pkg_jsone_name = jsone
21462147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
21812182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
21822183 pkg_jsx_commit = master
21832184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
21842193 PACKAGES += kafka_protocol
21852194 pkg_kafka_protocol_name = kafka_protocol
21862195 pkg_kafka_protocol_description = Kafka protocol Erlang library
21892198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
21902199 pkg_kafka_protocol_commit = master
21912200
2192 PACKAGES += kafka
2193 pkg_kafka_name = kafka
2194 pkg_kafka_description = Kafka consumer and producer in Erlang
2195 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2196 pkg_kafka_fetch = git
2197 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2198 pkg_kafka_commit = master
2199
22002201 PACKAGES += kai
22012202 pkg_kai_name = kai
22022203 pkg_kai_description = DHT storage by Takeshi Inoue
22932294 pkg_kvs_repo = https://github.com/synrc/kvs
22942295 pkg_kvs_commit = master
22952296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
22962305 PACKAGES += lager_amqp_backend
22972306 pkg_lager_amqp_backend_name = lager_amqp_backend
22982307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
23092318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
23102319 pkg_lager_syslog_commit = master
23112320
2312 PACKAGES += lager
2313 pkg_lager_name = lager
2314 pkg_lager_description = A logging framework for Erlang/OTP.
2315 pkg_lager_homepage = https://github.com/basho/lager
2316 pkg_lager_fetch = git
2317 pkg_lager_repo = https://github.com/basho/lager
2318 pkg_lager_commit = master
2319
23202321 PACKAGES += lambdapad
23212322 pkg_lambdapad_name = lambdapad
23222323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
25732574 pkg_mixer_repo = https://github.com/chef/mixer
25742575 pkg_mixer_commit = master
25752576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
25762585 PACKAGES += mochiweb_xpath
25772586 pkg_mochiweb_xpath_name = mochiweb_xpath
25782587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
25812590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
25822591 pkg_mochiweb_xpath_commit = master
25832592
2584 PACKAGES += mochiweb
2585 pkg_mochiweb_name = mochiweb
2586 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2587 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2588 pkg_mochiweb_fetch = git
2589 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2590 pkg_mochiweb_commit = master
2591
25922593 PACKAGES += mockgyver
25932594 pkg_mockgyver_name = mockgyver
25942595 pkg_mockgyver_description = A mocking library for Erlang
30613062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
30623063 pkg_quickrand_commit = master
30633064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
30643073 PACKAGES += rabbit_exchange_type_riak
30653074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
30663075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
30693078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
30703079 pkg_rabbit_exchange_type_riak_commit = master
30713080
3072 PACKAGES += rabbit
3073 pkg_rabbit_name = rabbit
3074 pkg_rabbit_description = RabbitMQ Server
3075 pkg_rabbit_homepage = https://www.rabbitmq.com/
3076 pkg_rabbit_fetch = git
3077 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3078 pkg_rabbit_commit = master
3079
30803081 PACKAGES += rack
30813082 pkg_rack_name = rack
30823083 pkg_rack_description = Rack handler for erlang
34933494 pkg_smother_repo = https://github.com/ramsay-t/Smother
34943495 pkg_smother_commit = master
34953496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
34963505 PACKAGES += social
34973506 pkg_social_name = social
34983507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
35413550 pkg_stable_repo = https://github.com/dvv/stable
35423551 pkg_stable_commit = master
35433552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
35443561 PACKAGES += statebox_riak
35453562 pkg_statebox_riak_name = statebox_riak
35463563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
35493566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
35503567 pkg_statebox_riak_commit = master
35513568
3552 PACKAGES += statebox
3553 pkg_statebox_name = statebox
3554 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3555 pkg_statebox_homepage = https://github.com/mochi/statebox
3556 pkg_statebox_fetch = git
3557 pkg_statebox_repo = https://github.com/mochi/statebox
3558 pkg_statebox_commit = master
3559
35603569 PACKAGES += statman
35613570 pkg_statman_name = statman
35623571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
37253734 pkg_tirerl_repo = https://github.com/inaka/tirerl
37263735 pkg_tirerl_commit = master
37273736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
37283745 PACKAGES += traffic_tools
37293746 pkg_traffic_tools_name = traffic_tools
37303747 pkg_traffic_tools_description = Simple traffic limiting library
40614078 pkg_zucchini_repo = https://github.com/devinus/zucchini
40624079 pkg_zucchini_commit = master
40634080
4064 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
40654082 # This file is part of erlang.mk and subject to the terms of the ISC License.
40664083
40674084 .PHONY: search
40884105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
40894106 endif
40904107
4091 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
40924109 # This file is part of erlang.mk and subject to the terms of the ISC License.
40934110
4094 .PHONY: distclean-deps
4111 .PHONY: distclean-deps clean-tmp-deps.log
40954112
40964113 # Configuration.
40974114
41104127
41114128 REBAR_DEPS_DIR = $(DEPS_DIR)
41124129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
41134150
41144151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
41154152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
41164153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
41174154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
41184155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
41194157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
41204158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
41214159
41384176
41394177 # Core targets.
41404178
4141 ifdef IS_APP
4142 apps::
4143 else
4144 apps:: $(ALL_APPS_DIRS)
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
41454180 ifeq ($(IS_APP)$(IS_DEP),)
41464181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
41474182 endif
41494184 # Create ebin directory for all apps to make sure Erlang recognizes them
41504185 # as proper OTP applications when using -include_lib. This is a temporary
41514186 # fix, a proper fix would be to compile apps/* in the right order.
4152 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4153 mkdir -p $$dep/ebin || exit $$?; \
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
41544190 done
4155 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
41564196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
41574197 :; \
41584198 else \
41594199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4160 $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
4200 $(MAKE) -C $$dep IS_APP=1; \
41614201 fi \
41624202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
41634207 endif
41644208
41654209 ifneq ($(SKIP_DEPS),)
41664210 deps::
41674211 else
4168 deps:: $(ALL_DEPS_DIRS) apps
4169 ifeq ($(IS_APP)$(IS_DEP),)
4170 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4171 endif
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
41724213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4173 $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
41744215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
41754216 :; \
41764217 else \
41774218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
41784219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4179 $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
4220 $(MAKE) -C $$dep IS_DEP=1; \
41804221 else \
4181 echo "Error: No Makefile to build dependency $$dep."; \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
41824223 exit 2; \
41834224 fi \
41844225 fi \
41924233 # in practice only Makefile is needed so far.
41934234 define dep_autopatch
41944235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
41954237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
41964238 $(call dep_autopatch_erlang_mk,$(1)); \
41974239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4198 if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
41994243 $(call dep_autopatch2,$(1)); \
4200 elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
42014245 $(call dep_autopatch2,$(1)); \
4202 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
42034247 $(call dep_autopatch2,$(1)); \
4204 else \
4205 $(call erlang,$(call dep_autopatch_app.erl,$(1))); \
42064248 fi \
42074249 else \
42084250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
42144256 endef
42154257
42164258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
42174262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
42184263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
42194264 fi; \
42204265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4221 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
42224267 $(call dep_autopatch_fetch_rebar); \
42234268 $(call dep_autopatch_rebar,$(1)); \
42244269 else \
42304275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
42314276 endef
42324277
4233 # Overwrite erlang.mk with the current file by default.
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
42344280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
42354281 define dep_autopatch_erlang_mk
4236 echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \
4237 > $(DEPS_DIR)/$1/erlang.mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
42384287 endef
42394288 else
42404289 define dep_autopatch_erlang_mk
42534302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
42544303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
42554304 cd $(ERLANG_MK_TMP)/rebar; \
4256 git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
42574306 $(MAKE); \
42584307 cd -; \
42594308 fi
42704319 define dep_autopatch_rebar.erl
42714320 application:load(rebar),
42724321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
42734323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
42744324 {ok, Conf0} -> Conf0;
42754325 _ -> []
44234473 [] -> ok;
44244474 _ ->
44254475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4426 PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
44274477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4428 PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L \\"~s\\" -lerl_interface -lei\n",
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
44294479 [code:lib_dir(erl_interface, lib)])),
44304480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
44314481 FilterEnv = fun(Env) ->
44644514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44654515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44664516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4467 [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
44684518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
44694519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
44704520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
44764526 end,
44774527 [PortSpec(S) || S <- PortSpecs]
44784528 end,
4479 Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"),
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
44804530 RunPlugin = fun(Plugin, Step) ->
44814531 case erlang:function_exported(Plugin, Step, 2) of
44824532 false -> ok;
45244574 halt()
45254575 endef
45264576
4527 define dep_autopatch_app.erl
4528 UpdateModules = fun(App) ->
4529 case filelib:is_regular(App) of
4530 false -> ok;
4531 true ->
4532 {ok, [{application, '$(1)', L0}]} = file:consult(App),
4533 Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true,
4534 fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
4535 L = lists:keystore(modules, 1, L0, {modules, Mods}),
4536 ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}]))
4537 end
4538 end,
4539 UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"),
4540 halt()
4541 endef
4542
45434577 define dep_autopatch_appsrc_script.erl
45444578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
45454579 AppSrcScript = AppSrc ++ ".script",
45874621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
45884622 endef
45894623
4590 define dep_fetch_hex.erl
4591 ssl:start(),
4592 inets:start(),
4593 {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
4594 {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
4595 [], [{body_format, binary}]),
4596 {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
4597 {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
4598 ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
4599 halt()
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
46004626 endef
46014627
46024628 # Hex only has a package version. No need to look in the Erlang.mk packages.
46034629 define dep_fetch_hex
4604 $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
46054634 endef
46064635
46074636 define dep_fetch_fail
46314660 $(eval DEP_NAME := $(call dep_name,$1))
46324661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
46334662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4634 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
46354664 exit 17; \
46364665 fi
46374666 $(verbose) mkdir -p $(DEPS_DIR)
46734702 clean:: clean-apps
46744703
46754704 clean-apps:
4676 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4677 $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
46784707 done
46794708
46804709 distclean:: distclean-apps
46814710
46824711 distclean-apps:
4683 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4684 $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
46854714 done
46864715 endif
46874716
47014730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
47024731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
47034732
4704 # External plugins.
4705
4706 DEP_PLUGINS ?=
4707
4708 define core_dep_plugin
4709 -include $(DEPS_DIR)/$(1)
4710
4711 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4712 endef
4713
4714 $(foreach p,$(DEP_PLUGINS),\
4715 $(eval $(if $(findstring /,$p),\
4716 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4717 $(call core_dep_plugin,$p/plugins.mk,$p))))
4718
4719 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4720 # This file is part of erlang.mk and subject to the terms of the ISC License.
4721
4722 # Configuration.
4723
4724 DTL_FULL_PATH ?=
4725 DTL_PATH ?= templates/
4726 DTL_SUFFIX ?= _dtl
4727 DTL_OPTS ?=
4728
4729 # Verbosity.
4730
4731 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
4732 dtl_verbose = $(dtl_verbose_$(V))
4733
4734 # Core targets.
4735
4736 DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl))
4737
4738 ifneq ($(DTL_FILES),)
4739
4740 ifdef DTL_FULL_PATH
4741 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
4742 else
4743 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
4744 endif
4745
4746 ifneq ($(words $(DTL_FILES)),0)
4747 # Rebuild templates when the Makefile changes.
4748 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
4749 @mkdir -p $(ERLANG_MK_TMP)
4750 @if test -f $@; then \
4751 touch $(DTL_FILES); \
4752 fi
4753 @touch $@
4754
4755 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
4756 endif
4757
4758 define erlydtl_compile.erl
4759 [begin
4760 Module0 = case "$(strip $(DTL_FULL_PATH))" of
4761 "" ->
4762 filename:basename(F, ".dtl");
4763 _ ->
4764 "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
4765 re:replace(F2, "/", "_", [{return, list}, global])
4766 end,
4767 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
4768 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
4769 ok -> ok;
4770 {ok, _} -> ok
4771 end
4772 end || F <- string:tokens("$(1)", " ")],
4773 halt().
4774 endef
4775
4776 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
4777 $(if $(strip $?),\
4778 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
4779
4780 endif
4781
4782 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
47834734 # This file is part of erlang.mk and subject to the terms of the ISC License.
47844735
47854736 # Verbosity.
47984749
47994750 define compile_proto.erl
48004751 [begin
4801 Dir = filename:dirname(filename:dirname(F)),
48024752 protobuffs_compile:generate_source(F,
4803 [{output_include_dir, Dir ++ "/include"},
4804 {output_src_dir, Dir ++ "/ebin"}])
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
48054755 end || F <- string:tokens("$(1)", " ")],
48064756 halt().
48074757 endef
48114761 $(if $(strip $?),$(call compile_proto,$?))
48124762 endif
48134763
4814 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
48154765 # This file is part of erlang.mk and subject to the terms of the ISC License.
48164766
48174767 .PHONY: clean-app
48254775 ERLC_EXCLUDE ?=
48264776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
48274777
4778 ERLC_ASN1_OPTS ?=
4779
48284780 ERLC_MIB_OPTS ?=
48294781 COMPILE_MIB_FIRST ?=
48304782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
48744826
48754827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
48764828 define app_file
4877 {application, $(PROJECT), [
4829 {application, '$(PROJECT)', [
48784830 {description, "$(PROJECT_DESCRIPTION)"},
48794831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48804832 {id$(comma)$(space)"$(1)"}$(comma))
48814833 {modules, [$(call comma_list,$(2))]},
48824834 {registered, []},
4883 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48844837 ]}.
48854838 endef
48864839 else
48874840 define app_file
4888 {application, $(PROJECT), [
4841 {application, '$(PROJECT)', [
48894842 {description, "$(PROJECT_DESCRIPTION)"},
48904843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48914844 {id$(comma)$(space)"$(1)"}$(comma))
48924845 {modules, [$(call comma_list,$(2))]},
48934846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
48944847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4895 {mod, {$(PROJECT_MOD), []}}
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48964850 ]}.
48974851 endef
48984852 endif
49024856
49034857 # Source files.
49044858
4905 ERL_FILES = $(sort $(call core_find,src/,*.erl))
4906 CORE_FILES = $(sort $(call core_find,src/,*.core))
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
49074863
49084864 # ASN.1 files.
49094865
49134869
49144870 define compile_asn1
49154871 $(verbose) mkdir -p include/
4916 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
49174873 $(verbose) mv asn1/*.erl src/
49184874 $(verbose) mv asn1/*.hrl include/
49194875 $(verbose) mv asn1/*.asn1db include/
49364892
49374893 # Leex and Yecc files.
49384894
4939 XRL_FILES = $(sort $(call core_find,src/,*.xrl))
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
49404896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
49414897 ERL_FILES += $(XRL_ERL_FILES)
49424898
4943 YRL_FILES = $(sort $(call core_find,src/,*.yrl))
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
49444900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
49454901 ERL_FILES += $(YRL_ERL_FILES)
49464902
49474903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4948 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
49494905
49504906 # Erlang and Core Erlang files.
49514907
49954951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49964952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49974953 (F, Mod, import, {Imp, _}) ->
4998 case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
49994960 false -> ok;
50004961 true -> Add(Mod, Imp)
50014962 end;
50194980 end || F <- ErlFiles],
50204981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
50214982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
50224991 ok = file:write_file("$(1)", [
50234992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
5024 "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n"
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
50254994 ]),
50264995 halt()
50274996 endef
50345003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
50355004 # Rebuild everything when the Makefile changes.
50365005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5037 @mkdir -p $(ERLANG_MK_TMP)
5038 @if test -f $@; then \
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
50395008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
50405009 touch -c $(PROJECT).d; \
50415010 fi
5042 @touch $@
5011 $(verbose) touch $@
50435012
50445013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
50455014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
50465015 endif
50475016
5048 -include $(PROJECT).d
5017 include $(wildcard $(PROJECT).d)
50495018
50505019 ebin/$(PROJECT).app:: ebin/
50515020
50645033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
50655034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
50665035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5067 $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
50685037 > ebin/$(PROJECT).app
50695038 else
50705039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
50885057
50895058 endif
50905059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
50915061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
50925062 # This file is part of erlang.mk and subject to the terms of the ISC License.
50935063
51055075 doc-deps:
51065076 else
51075077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5108 $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5109 endif
5110
5111 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51125082 # This file is part of erlang.mk and subject to the terms of the ISC License.
51135083
51145084 .PHONY: rel-deps
51255095 rel-deps:
51265096 else
51275097 rel-deps: $(ALL_REL_DEPS_DIRS)
5128 $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5129 endif
5130
5131 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51325102 # This file is part of erlang.mk and subject to the terms of the ISC License.
51335103
51345104 .PHONY: test-deps test-dir test-build clean-test-dir
51505120 test-deps:
51515121 else
51525122 test-deps: $(ALL_TEST_DEPS_DIRS)
5153 $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
51545124 endif
51555125
51565126 ifneq ($(wildcard $(TEST_DIR)),)
51835153 endif
51845154 endif
51855155
5186 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51875157 # This file is part of erlang.mk and subject to the terms of the ISC License.
51885158
51895159 .PHONY: rebar.config
52195189 rebar.config:
52205190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
52215191
5222 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
52235193 # This file is part of erlang.mk and subject to the terms of the ISC License.
52245194
5225 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
5226
5227 MAN_INSTALL_PATH ?= /usr/local/share/man
5228 MAN_SECTIONS ?= 3 7
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
52295200
52305201 docs:: asciidoc
52315202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
52325207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
52335210
52345211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
52355212 asciidoc-guide:
52365213 else
5237 asciidoc-guide: distclean-asciidoc doc-deps
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
52385215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
52395216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5240 endif
5241
5242 ifeq ($(wildcard doc/src/manual/*.asciidoc),)
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
52435227 asciidoc-manual:
52445228 else
5245 asciidoc-manual: distclean-asciidoc doc-deps
5246 for f in doc/src/manual/*.asciidoc ; do \
5247 a2x -v -f manpage $$f ; \
5248 done
5249 for s in $(MAN_SECTIONS); do \
5250 mkdir -p doc/man$$s/ ; \
5251 mv doc/src/manual/*.$$s doc/man$$s/ ; \
5252 gzip doc/man$$s/*.$$s ; \
5253 done
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
52545262
52555263 install-docs:: install-asciidoc
52565264
52575265 install-asciidoc: asciidoc-manual
5258 for s in $(MAN_SECTIONS); do \
5259 mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
5260 install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
5261 done
5262 endif
5263
5264 distclean:: distclean-asciidoc
5265
5266 distclean-asciidoc:
5267 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
5268
5269 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
52705276 # This file is part of erlang.mk and subject to the terms of the ISC License.
52715277
52725278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
53235329 define bs_Makefile
53245330 PROJECT = $p
53255331 PROJECT_DESCRIPTION = New project
5326 PROJECT_VERSION = 0.0.1
5332 PROJECT_VERSION = 0.1.0
53275333
53285334 # Whitespace to be used when creating files from templates.
53295335 SP = $(SP)
53335339 define bs_Makefile
53345340 PROJECT = $p
53355341 PROJECT_DESCRIPTION = New project
5336 PROJECT_VERSION = 0.0.1
5342 PROJECT_VERSION = 0.1.0
53375343
53385344 endef
53395345 endif
53415347 define bs_apps_Makefile
53425348 PROJECT = $p
53435349 PROJECT_DESCRIPTION = New project
5344 PROJECT_VERSION = 0.0.1
5350 PROJECT_VERSION = 0.1.0
53455351
53465352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
53475353 endef
53615367 endef
53625368
53635369 define bs_relx_config
5364 {release, {$p_release, "1"}, [$p]}.
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
53655371 {extended_start_script, true}.
53665372 {sys_config, "rel/sys.config"}.
53675373 {vm_args, "rel/vm.args"}.
57125718 ifndef t
57135719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57145720 endif
5715 ifndef tpl_$(t)
5716 $(error Unknown template)
5717 endif
57185721 ifndef n
57195722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57205723 endif
57275730 list-templates:
57285731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
57295732
5730 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
57315734 # This file is part of erlang.mk and subject to the terms of the ISC License.
57325735
57335736 .PHONY: clean-c_src distclean-c_src-env
59625965 $(call render_template,bs_erl_nif,src/$n.erl)
59635966 endif
59645967
5965 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
59665969 # This file is part of erlang.mk and subject to the terms of the ISC License.
59675970
5968 .PHONY: ci ci-setup distclean-kerl
5969
5970 KERL ?= $(CURDIR)/kerl
5971 export KERL
5972
5973 KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
5974
5975 OTP_GIT ?= https://github.com/erlang/otp
5976
5977 CI_INSTALL_DIR ?= $(HOME)/erlang
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
59785973 CI_OTP ?=
5979
5980 ifeq ($(strip $(CI_OTP)),)
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
59815986 ci::
59825987 else
5983 ci:: $(addprefix ci-,$(CI_OTP))
5984
5985 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
59866007
59876008 ci-setup::
6009
6010 ci-extra::
59886011
59896012 ci_verbose_0 = @echo " CI " $(1);
59906013 ci_verbose = $(ci_verbose_$(V))
59916014
59926015 define ci_target
5993 ci-$(1): $(CI_INSTALL_DIR)/$(1)
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
59946018 $(ci_verbose) \
5995 PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
5996 CI_OTP_RELEASE="$(1)" \
5997 CT_OPTS="-label $(1)" \
5998 $(MAKE) clean ci-setup tests
5999 endef
6000
6001 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
60026030
60036031 define ci_otp_target
60046032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
60056033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6006 $(KERL) build git $(OTP_GIT) $(1) $(1)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
60076035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
60086036 endif
60096037 endef
60106038
60116039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
60126040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
60136052 $(KERL):
6014 $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
60156056 $(verbose) chmod +x $(KERL)
60166057
60176058 help::
60286069 $(gen_verbose) rm -rf $(KERL)
60296070 endif
60306071
6031 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
60326073 # This file is part of erlang.mk and subject to the terms of the ISC License.
60336074
60346075 .PHONY: ct apps-ct distclean-ct
60366077 # Configuration.
60376078
60386079 CT_OPTS ?=
6080
60396081 ifneq ($(wildcard $(TEST_DIR)),)
6040 CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6041 else
6042 CT_SUITES ?=
6043 endif
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
60446088
60456089 # Core targets.
60466090
60636107 -noinput \
60646108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
60656109 -dir $(TEST_DIR) \
6066 -logdir $(CURDIR)/logs
6110 -logdir $(CT_LOGS_DIR)
60676111
60686112 ifeq ($(CT_SUITES),)
60696113 ct: $(if $(IS_APP),,apps-ct)
60706114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
60716117 ct: test-build $(if $(IS_APP),,apps-ct)
6072 $(verbose) mkdir -p $(CURDIR)/logs/
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
60736119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
60746121 endif
60756122
60766123 ifneq ($(ALL_APPS_DIRS),)
60976144
60986145 define ct_suite_target
60996146 ct-$(1): test-build
6100 $(verbose) mkdir -p $(CURDIR)/logs/
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
61016148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
61026149 endef
61036150
61046151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
61056152
61066153 distclean-ct:
6107 $(gen_verbose) rm -rf $(CURDIR)/logs/
6108
6109 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61106157 # This file is part of erlang.mk and subject to the terms of the ISC License.
61116158
61126159 .PHONY: plt distclean-plt dialyze
61506197 endef
61516198
61526199 $(DIALYZER_PLT): deps app
6153 $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
61546204
61556205 plt: $(DIALYZER_PLT)
61566206
61646214 endif
61656215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
61666216
6167 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61686218 # This file is part of erlang.mk and subject to the terms of the ISC License.
61696219
61706220 .PHONY: distclean-edoc edoc
61726222 # Configuration.
61736223
61746224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
61756235
61766236 # Core targets.
61776237
6178 ifneq ($(wildcard doc/overview.edoc),)
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
61796239 docs:: edoc
61806240 endif
61816241
61846244 # Plugin-specific targets.
61856245
61866246 edoc: distclean-edoc doc-deps
6187 $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
61886248
61896249 distclean-edoc:
61906250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
61916251
6192 # Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61936253 # This file is part of erlang.mk and subject to the terms of the ISC License.
61946254
6195 .PHONY: distclean-escript escript
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
61966320
61976321 # Configuration.
61986322
61996323 ESCRIPT_NAME ?= $(PROJECT)
62006324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
62016325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
62026327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6203
6204 ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
6205 ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
6206 ESCRIPT_EMU_ARGS ?= -pa . \
6207 -sasl errlog_type error \
6208 -escript main $(ESCRIPT_NAME)
6209 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6210 ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
62116332
62126333 # Core targets.
62136334
62206341
62216342 # Plugin-specific targets.
62226343
6223 # Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
6224 # Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
6225 # Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
6226 # Software may only be used for the great good and the true happiness of all
6227 # sentient beings.
6228
6229 define ESCRIPT_RAW
6230 'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
6231 'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
6232 ' [F || F <- A, not filelib:is_dir(F) ] end,'\
6233 'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
6234 'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
6235 'Ez = fun(Escript) ->'\
6236 ' Static = Files([$(ESCRIPT_STATIC)]),'\
6237 ' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
6238 ' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
6239 ' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
6240 ' {archive, Archive, [memory]},'\
6241 ' {shebang, "$(ESCRIPT_SHEBANG)"},'\
6242 ' {comment, "$(ESCRIPT_COMMENT)"},'\
6243 ' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
6244 ' ]),'\
6245 ' file:change_mode(Escript, 8#755)'\
6246 'end,'\
6247 'Ez("$(ESCRIPT_FILE)"),'\
6248 'halt().'
6249 endef
6250
6251 ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
6252
6253 escript:: distclean-escript deps app
6254 $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
62556360
62566361 distclean-escript:
6257 $(gen_verbose) rm -f $(ESCRIPT_NAME)
6258
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
62596365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6260 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
62616366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
62626367
62636368 .PHONY: eunit apps-eunit
63216426
63226427 ifneq ($(ALL_APPS_DIRS),)
63236428 apps-eunit:
6324 $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done
6325 endif
6326 endif
6327
6328 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
63296436 # This file is part of erlang.mk and subject to the terms of the ISC License.
63306437
6331 .PHONY: relx-rel distclean-relx-rel distclean-relx run
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
63326439
63336440 # Configuration.
63346441
6335 RELX ?= $(CURDIR)/relx
6442 RELX ?= $(ERLANG_MK_TMP)/relx
63366443 RELX_CONFIG ?= $(CURDIR)/relx.config
63376444
63386445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
63396446 RELX_OPTS ?=
63406447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
63416454
63426455 ifeq ($(firstword $(RELX_OPTS)),-o)
63436456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
63506463 ifeq ($(IS_DEP),)
63516464 ifneq ($(wildcard $(RELX_CONFIG)),)
63526465 rel:: relx-rel
6353 endif
6354 endif
6355
6356 distclean:: distclean-relx-rel distclean-relx
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
63576472
63586473 # Plugin-specific targets.
63596474
63626477 $(verbose) chmod +x $(RELX)
63636478
63646479 relx-rel: $(RELX) rel-deps app
6365 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
63666484
63676485 distclean-relx-rel:
63686486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6369
6370 distclean-relx:
6371 $(gen_verbose) rm -rf $(RELX)
63726487
63736488 # Run target.
63746489
63776492 else
63786493
63796494 define get_relx_release.erl
6380 {ok, Config} = file:consult("$(RELX_CONFIG)"),
6381 {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
6382 io:format("~s", [Name]),
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
63836504 halt(0).
63846505 endef
63856506
6386 RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
63876514
63886515 run: all
6389 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
63906517
63916518 help::
63926519 $(verbose) printf "%s\n" "" \
63956522
63966523 endif
63976524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
63986526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6399 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
64006527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
64016528
64026529 .PHONY: shell
64216548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
64226549
64236550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6424 $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
64256552
64266553 shell: build-shell-deps
64276554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
64286555
6429 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
64306571 # This file is part of erlang.mk and subject to the terms of the ISC License.
64316572
64326573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
64376578 tests:: triq
64386579
64396580 define triq_check.erl
6440 code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
64416582 try
64426583 case $(1) of
64436584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
64696610 endif
64706611 endif
64716612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
64726614 # Copyright (c) 2015, Erlang Solutions Ltd.
64736615 # This file is part of erlang.mk and subject to the terms of the ISC License.
64746616
64856627 XREFR ?= $(CURDIR)/xrefr
64866628 export XREFR
64876629
6488 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
64896631
64906632 # Core targets.
64916633
64926634 help::
6493 $(verbose) printf "%s\n" "" \
6494 "Xref targets:" \
6495 " xref Run Xrefr using $XREF_CONFIG as config file if defined"
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
64966638
64976639 distclean:: distclean-xref
64986640
65086650 distclean-xref:
65096651 $(gen_verbose) rm -rf $(XREFR)
65106652
6511 # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
65126655 # This file is part of erlang.mk and subject to the terms of the ISC License.
65136656
65146657 COVER_REPORT_DIR = cover
65176660
65186661 ifdef COVER
65196662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
65206664 # All modules in 'ebin'
65216665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
65226666
65316675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
65326676 endif
65336677 endif
6678 endif
65346679
65356680 # Core targets
65366681
65526697 "Cover targets:" \
65536698 " cover-report Generate a HTML coverage report from previously collected" \
65546699 " cover data." \
6555 " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
65566701 "" \
65576702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
65586703 "target tests additionally generates a HTML coverage report from the combined" \
65656710
65666711 .PHONY: coverdata-clean
65676712 coverdata-clean:
6568 $(gen_verbose) rm -f *.coverdata ct.cover.spec
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
65696714
65706715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
65716721 all.coverdata: $(COVERDATA)
6572 $(gen_verbose) $(ERL) -eval ' \
6573 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
6574 cover:export("$@"), halt(0).'
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
65756723
65766724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
65776725 # empty if you want the coverdata files but not the HTML report.
65896737 # Modules which include eunit.hrl always contain one line without coverage
65906738 # because eunit defines test/0 which is never called. We compensate for this.
65916739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6592 grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
65936741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
65946742
65956743 define cover_report.erl
66246772 endef
66256773
66266774 cover-report:
6627 $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
66286776 $(gen_verbose) $(call erlang,$(cover_report.erl))
66296777
66306778 endif
66316779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
66326839
66336840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
66346841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
66976904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
66986905 endif
66996906 ifndef IS_APP
6700 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
67016908 $(MAKE) -C $$dep $@ \
67026909 IS_APP=1 \
6703 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6704 || exit $$?; \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67056911 done
67066912 endif
6707 $(verbose) for dep in $^ ; do \
6913 $(verbose) set -e; for dep in $^ ; do \
67086914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
67096915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6710 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
67116917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
67126918 $(MAKE) -C $$dep fetch-deps \
67136919 IS_DEP=1 \
6714 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6715 || exit $$?; \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67166921 fi \
67176922 fi \
67186923 done
22 # before the inclusion of erlang.mk leading to the wrong target becoming
33 # the default.
44 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
526 endif
627
728 # --------------------------------------------------------------------
2041 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
2142 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
2243 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
2345 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
2446 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
2547 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
2648 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
2749 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
2851 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
2952 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
3054 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
3155 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
3256 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
3559 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
3660 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
3761 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
3863 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
3964 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
4065 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
7095
7196 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
7297
73 # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
74 # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
75 # needs to add "ranch" as a BUILD_DEPS. The list of projects needing
76 # this workaround are:
77 # o rabbitmq-web-stomp
78 dep_ranch = git https://github.com/ninenines/ranch 1.2.1
98 # Third-party dependencies version pinning.
99 #
100 # We do that in this file, which is copied in all projects, to ensure
101 # all projects use the same versions. It avoids conflicts and makes it
102 # possible to work with rabbitmq-public-umbrella.
103
104 dep_cowboy_commit = 1.0.4
105 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
106 dep_ranch_commit = 1.3.1
107 dep_webmachine_commit = 1.10.8p2
79108
80109 RABBITMQ_COMPONENTS = amqp_client \
81110 rabbit \
82111 rabbit_common \
83112 rabbitmq_amqp1_0 \
84113 rabbitmq_auth_backend_amqp \
114 rabbitmq_auth_backend_cache \
85115 rabbitmq_auth_backend_http \
86116 rabbitmq_auth_backend_ldap \
87117 rabbitmq_auth_mechanism_ssl \
88118 rabbitmq_boot_steps_visualiser \
89119 rabbitmq_clusterer \
120 rabbitmq_cli \
90121 rabbitmq_codegen \
91122 rabbitmq_consistent_hash_exchange \
123 rabbitmq_ct_client_helpers \
92124 rabbitmq_ct_helpers \
93125 rabbitmq_delayed_message_exchange \
94126 rabbitmq_dotnet_client \
97129 rabbitmq_federation_management \
98130 rabbitmq_java_client \
99131 rabbitmq_jms_client \
132 rabbitmq_jms_cts \
100133 rabbitmq_jms_topic_exchange \
101134 rabbitmq_lvc \
102135 rabbitmq_management \
1010 %% The Original Code is RabbitMQ Delayed Message
1111 %%
1212 %% The Initial Developer of the Original Code is Pivotal Software, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_exchange_type_recent_history).
+0
-6
deps/rabbitmq_recent_history_exchange/src/rabbitmq_recent_history_exchange.app.src less more
0 {application, rabbitmq_recent_history_exchange,
1 [{description, "RabbitMQ Recent History Exchange"},
2 {vsn, "1.2.1"},
3 {modules, []},
4 {registered, []},
5 {applications, [kernel, stdlib, rabbit_common, rabbit, mnesia]}]}.
00 PROJECT = rabbitmq_sharding
1 PROJECT_DESCRIPTION = RabbitMQ Sharding Plugin
12
23 DEPS = rabbit_common rabbit
3 TEST_DEPS = rabbitmq_ct_helpers amqp_client
4 TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers amqp_client
45
6 DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
57 DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk
68
79 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
0 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
11 #
22 # Permission to use, copy, modify, and/or distribute this software for any
33 # purpose with or without fee is hereby granted, provided that the above
1111 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1212 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1313
14 .PHONY: all app apps deps search rel docs install-docs check tests clean distclean help erlang-mk
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
1515
1616 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17
18 ERLANG_MK_VERSION = 2.0.0-pre.2-144-g647ffd1
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
1931
2032 # Core configuration.
2133
2436
2537 PROJECT_VERSION ?= rolling
2638 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
2740
2841 # Verbosity.
2942
8497 rel::
8598 $(verbose) :
8699
100 relup:: deps app
101
87102 check:: tests
88103
89104 clean:: clean-crashdump
101116 help::
102117 $(verbose) printf "%s\n" \
103118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
104 "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
105120 "" \
106121 "Usage: [V=1] $(MAKE) [target]..." \
107122 "" \
149164 core_native_path = $1
150165 endif
151166
152 ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
153 define core_http_get
154 wget --no-check-certificate -O $(1) $(2)|| rm $(1)
155 endef
156 else
157 define core_http_get.erl
158 ssl:start(),
159 inets:start(),
160 case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
161 {ok, {{_, 200, _}, _, Body}} ->
162 case file:write_file("$(1)", Body) of
163 ok -> ok;
164 {error, R1} -> halt(R1)
165 end;
166 {error, R2} ->
167 halt(R2)
168 end,
169 halt(0).
170 endef
171
172 define core_http_get
173 $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
174 endef
175 endif
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
176168
177169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
178170
192184 ERLANG_MK_BUILD_CONFIG ?= build.config
193185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
194186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
195188 erlang-mk:
196189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
197190 ifdef ERLANG_MK_COMMIT
198191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
199192 endif
200193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
201 $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
202195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
203196 rm -rf $(ERLANG_MK_BUILD_DIR)
204197
285278 pkg_apns_repo = https://github.com/inaka/apns4erl
286279 pkg_apns_commit = master
287280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
288289 PACKAGES += azdht
289290 pkg_azdht_name = azdht
290291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
405406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
406407 pkg_bootstrap_commit = master
407408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
408417 PACKAGES += boss_db
409418 pkg_boss_db_name = boss_db
410419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
413422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
414423 pkg_boss_db_commit = master
415424
416 PACKAGES += boss
417 pkg_boss_name = boss
418 pkg_boss_description = Erlang web MVC, now featuring Comet
419 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
420 pkg_boss_fetch = git
421 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
422 pkg_boss_commit = master
423
424425 PACKAGES += brod
425426 pkg_brod_name = brod
426427 pkg_brod_description = Kafka client in Erlang
565566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
566567 pkg_cloudi_service_api_requests_commit = master
567568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
568585 PACKAGES += cloudi_service_db_cassandra_cql
569586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
570587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
573590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
574591 pkg_cloudi_service_db_cassandra_cql_commit = master
575592
576 PACKAGES += cloudi_service_db_cassandra
577 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
579 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
580 pkg_cloudi_service_db_cassandra_fetch = git
581 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
582 pkg_cloudi_service_db_cassandra_commit = master
583
584593 PACKAGES += cloudi_service_db_couchdb
585594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
586595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
637646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
638647 pkg_cloudi_service_db_tokyotyrant_commit = master
639648
640 PACKAGES += cloudi_service_db
641 pkg_cloudi_service_db_name = cloudi_service_db
642 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
643 pkg_cloudi_service_db_homepage = http://cloudi.org/
644 pkg_cloudi_service_db_fetch = git
645 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
646 pkg_cloudi_service_db_commit = master
647
648649 PACKAGES += cloudi_service_filesystem
649650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
650651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
10371038 pkg_edown_repo = https://github.com/uwiger/edown
10381039 pkg_edown_commit = master
10391040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
10401049 PACKAGES += eep_app
10411050 pkg_eep_app_name = eep_app
10421051 pkg_eep_app_description = Embedded Event Processing
10451054 pkg_eep_app_repo = https://github.com/darach/eep-erl
10461055 pkg_eep_app_commit = master
10471056
1048 PACKAGES += eep
1049 pkg_eep_name = eep
1050 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1051 pkg_eep_homepage = https://github.com/virtan/eep
1052 pkg_eep_fetch = git
1053 pkg_eep_repo = https://github.com/virtan/eep
1054 pkg_eep_commit = master
1055
10561057 PACKAGES += efene
10571058 pkg_efene_name = efene
10581059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
12371238 pkg_eqm_repo = https://github.com/loucash/eqm
12381239 pkg_eqm_commit = master
12391240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
12401249 PACKAGES += eredis_pool
12411250 pkg_eredis_pool_name = eredis_pool
12421251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
12451254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
12461255 pkg_eredis_pool_commit = master
12471256
1248 PACKAGES += eredis
1249 pkg_eredis_name = eredis
1250 pkg_eredis_description = Erlang Redis client
1251 pkg_eredis_homepage = https://github.com/wooga/eredis
1252 pkg_eredis_fetch = git
1253 pkg_eredis_repo = https://github.com/wooga/eredis
1254 pkg_eredis_commit = master
1255
12561257 PACKAGES += erl_streams
12571258 pkg_erl_streams_name = erl_streams
12581259 pkg_erl_streams_description = Streams in Erlang
15411542 pkg_etap_repo = https://github.com/ngerakines/etap
15421543 pkg_etap_commit = master
15431544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
15441553 PACKAGES += etest_http
15451554 pkg_etest_http_name = etest_http
15461555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
15491558 pkg_etest_http_repo = https://github.com/wooga/etest_http
15501559 pkg_etest_http_commit = master
15511560
1552 PACKAGES += etest
1553 pkg_etest_name = etest
1554 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1555 pkg_etest_homepage = https://github.com/wooga/etest
1556 pkg_etest_fetch = git
1557 pkg_etest_repo = https://github.com/wooga/etest
1558 pkg_etest_commit = master
1559
15601561 PACKAGES += etoml
15611562 pkg_etoml_name = etoml
15621563 pkg_etoml_description = TOML language erlang parser
15651566 pkg_etoml_repo = https://github.com/kalta/etoml
15661567 pkg_etoml_commit = master
15671568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
15681577 PACKAGES += eunit_formatters
15691578 pkg_eunit_formatters_name = eunit_formatters
15701579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
15731582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
15741583 pkg_eunit_formatters_commit = master
15751584
1576 PACKAGES += eunit
1577 pkg_eunit_name = eunit
1578 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1579 pkg_eunit_homepage = https://github.com/richcarl/eunit
1580 pkg_eunit_fetch = git
1581 pkg_eunit_repo = https://github.com/richcarl/eunit
1582 pkg_eunit_commit = master
1583
15841585 PACKAGES += euthanasia
15851586 pkg_euthanasia_name = euthanasia
15861587 pkg_euthanasia_description = Merciful killer for your Erlang processes
15981599 pkg_evum_commit = master
15991600
16001601 PACKAGES += exec
1601 pkg_exec_name = exec
1602 pkg_exec_name = erlexec
16021603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
16031604 pkg_exec_homepage = http://saleyn.github.com/erlexec
16041605 pkg_exec_fetch = git
17171718 pkg_fn_repo = https://github.com/reiddraper/fn
17181719 pkg_fn_commit = master
17191720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
17201729 PACKAGES += folsom_cowboy
17211730 pkg_folsom_cowboy_name = folsom_cowboy
17221731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
17251734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
17261735 pkg_folsom_cowboy_commit = master
17271736
1728 PACKAGES += folsom
1729 pkg_folsom_name = folsom
1730 pkg_folsom_description = Expose Erlang Events and Metrics
1731 pkg_folsom_homepage = https://github.com/boundary/folsom
1732 pkg_folsom_fetch = git
1733 pkg_folsom_repo = https://github.com/boundary/folsom
1734 pkg_folsom_commit = master
1735
17361737 PACKAGES += folsomite
17371738 pkg_folsomite_name = folsomite
17381739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
20932094 pkg_jesse_repo = https://github.com/for-GET/jesse
20942095 pkg_jesse_commit = master
20952096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
20962105 PACKAGES += jiffy_v
20972106 pkg_jiffy_v_name = jiffy_v
20982107 pkg_jiffy_v_description = JSON validation utility
21012110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
21022111 pkg_jiffy_v_commit = master
21032112
2104 PACKAGES += jiffy
2105 pkg_jiffy_name = jiffy
2106 pkg_jiffy_description = JSON NIFs for Erlang.
2107 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2108 pkg_jiffy_fetch = git
2109 pkg_jiffy_repo = https://github.com/davisp/jiffy
2110 pkg_jiffy_commit = master
2111
21122113 PACKAGES += jobs
21132114 pkg_jobs_name = jobs
21142115 pkg_jobs_description = a Job scheduler for load regulation
21252126 pkg_joxa_repo = https://github.com/joxa/joxa
21262127 pkg_joxa_commit = master
21272128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
21282137 PACKAGES += json_rec
21292138 pkg_json_rec_name = json_rec
21302139 pkg_json_rec_description = JSON to erlang record
21332142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
21342143 pkg_json_rec_commit = master
21352144
2136 PACKAGES += json
2137 pkg_json_name = json
2138 pkg_json_description = a high level json library for erlang (17.0+)
2139 pkg_json_homepage = https://github.com/talentdeficit/json
2140 pkg_json_fetch = git
2141 pkg_json_repo = https://github.com/talentdeficit/json
2142 pkg_json_commit = master
2143
21442145 PACKAGES += jsone
21452146 pkg_jsone_name = jsone
21462147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
21812182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
21822183 pkg_jsx_commit = master
21832184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
21842193 PACKAGES += kafka_protocol
21852194 pkg_kafka_protocol_name = kafka_protocol
21862195 pkg_kafka_protocol_description = Kafka protocol Erlang library
21892198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
21902199 pkg_kafka_protocol_commit = master
21912200
2192 PACKAGES += kafka
2193 pkg_kafka_name = kafka
2194 pkg_kafka_description = Kafka consumer and producer in Erlang
2195 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2196 pkg_kafka_fetch = git
2197 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2198 pkg_kafka_commit = master
2199
22002201 PACKAGES += kai
22012202 pkg_kai_name = kai
22022203 pkg_kai_description = DHT storage by Takeshi Inoue
22932294 pkg_kvs_repo = https://github.com/synrc/kvs
22942295 pkg_kvs_commit = master
22952296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
22962305 PACKAGES += lager_amqp_backend
22972306 pkg_lager_amqp_backend_name = lager_amqp_backend
22982307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
23092318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
23102319 pkg_lager_syslog_commit = master
23112320
2312 PACKAGES += lager
2313 pkg_lager_name = lager
2314 pkg_lager_description = A logging framework for Erlang/OTP.
2315 pkg_lager_homepage = https://github.com/basho/lager
2316 pkg_lager_fetch = git
2317 pkg_lager_repo = https://github.com/basho/lager
2318 pkg_lager_commit = master
2319
23202321 PACKAGES += lambdapad
23212322 pkg_lambdapad_name = lambdapad
23222323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
25732574 pkg_mixer_repo = https://github.com/chef/mixer
25742575 pkg_mixer_commit = master
25752576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
25762585 PACKAGES += mochiweb_xpath
25772586 pkg_mochiweb_xpath_name = mochiweb_xpath
25782587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
25812590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
25822591 pkg_mochiweb_xpath_commit = master
25832592
2584 PACKAGES += mochiweb
2585 pkg_mochiweb_name = mochiweb
2586 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2587 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2588 pkg_mochiweb_fetch = git
2589 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2590 pkg_mochiweb_commit = master
2591
25922593 PACKAGES += mockgyver
25932594 pkg_mockgyver_name = mockgyver
25942595 pkg_mockgyver_description = A mocking library for Erlang
30613062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
30623063 pkg_quickrand_commit = master
30633064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
30643073 PACKAGES += rabbit_exchange_type_riak
30653074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
30663075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
30693078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
30703079 pkg_rabbit_exchange_type_riak_commit = master
30713080
3072 PACKAGES += rabbit
3073 pkg_rabbit_name = rabbit
3074 pkg_rabbit_description = RabbitMQ Server
3075 pkg_rabbit_homepage = https://www.rabbitmq.com/
3076 pkg_rabbit_fetch = git
3077 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3078 pkg_rabbit_commit = master
3079
30803081 PACKAGES += rack
30813082 pkg_rack_name = rack
30823083 pkg_rack_description = Rack handler for erlang
34933494 pkg_smother_repo = https://github.com/ramsay-t/Smother
34943495 pkg_smother_commit = master
34953496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
34963505 PACKAGES += social
34973506 pkg_social_name = social
34983507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
35413550 pkg_stable_repo = https://github.com/dvv/stable
35423551 pkg_stable_commit = master
35433552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
35443561 PACKAGES += statebox_riak
35453562 pkg_statebox_riak_name = statebox_riak
35463563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
35493566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
35503567 pkg_statebox_riak_commit = master
35513568
3552 PACKAGES += statebox
3553 pkg_statebox_name = statebox
3554 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3555 pkg_statebox_homepage = https://github.com/mochi/statebox
3556 pkg_statebox_fetch = git
3557 pkg_statebox_repo = https://github.com/mochi/statebox
3558 pkg_statebox_commit = master
3559
35603569 PACKAGES += statman
35613570 pkg_statman_name = statman
35623571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
37253734 pkg_tirerl_repo = https://github.com/inaka/tirerl
37263735 pkg_tirerl_commit = master
37273736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
37283745 PACKAGES += traffic_tools
37293746 pkg_traffic_tools_name = traffic_tools
37303747 pkg_traffic_tools_description = Simple traffic limiting library
40614078 pkg_zucchini_repo = https://github.com/devinus/zucchini
40624079 pkg_zucchini_commit = master
40634080
4064 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
40654082 # This file is part of erlang.mk and subject to the terms of the ISC License.
40664083
40674084 .PHONY: search
40884105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
40894106 endif
40904107
4091 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
40924109 # This file is part of erlang.mk and subject to the terms of the ISC License.
40934110
4094 .PHONY: distclean-deps
4111 .PHONY: distclean-deps clean-tmp-deps.log
40954112
40964113 # Configuration.
40974114
41104127
41114128 REBAR_DEPS_DIR = $(DEPS_DIR)
41124129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
41134150
41144151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
41154152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
41164153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
41174154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
41184155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
41194157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
41204158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
41214159
41384176
41394177 # Core targets.
41404178
4141 ifdef IS_APP
4142 apps::
4143 else
4144 apps:: $(ALL_APPS_DIRS)
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
41454180 ifeq ($(IS_APP)$(IS_DEP),)
41464181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
41474182 endif
41494184 # Create ebin directory for all apps to make sure Erlang recognizes them
41504185 # as proper OTP applications when using -include_lib. This is a temporary
41514186 # fix, a proper fix would be to compile apps/* in the right order.
4152 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4153 mkdir -p $$dep/ebin || exit $$?; \
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
41544190 done
4155 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
41564196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
41574197 :; \
41584198 else \
41594199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4160 $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
4200 $(MAKE) -C $$dep IS_APP=1; \
41614201 fi \
41624202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
41634207 endif
41644208
41654209 ifneq ($(SKIP_DEPS),)
41664210 deps::
41674211 else
4168 deps:: $(ALL_DEPS_DIRS) apps
4169 ifeq ($(IS_APP)$(IS_DEP),)
4170 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4171 endif
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
41724213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4173 $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
41744215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
41754216 :; \
41764217 else \
41774218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
41784219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4179 $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
4220 $(MAKE) -C $$dep IS_DEP=1; \
41804221 else \
4181 echo "Error: No Makefile to build dependency $$dep."; \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
41824223 exit 2; \
41834224 fi \
41844225 fi \
41924233 # in practice only Makefile is needed so far.
41934234 define dep_autopatch
41944235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
41954237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
41964238 $(call dep_autopatch_erlang_mk,$(1)); \
41974239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4198 if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
41994243 $(call dep_autopatch2,$(1)); \
4200 elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
42014245 $(call dep_autopatch2,$(1)); \
4202 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
42034247 $(call dep_autopatch2,$(1)); \
4204 else \
4205 $(call erlang,$(call dep_autopatch_app.erl,$(1))); \
42064248 fi \
42074249 else \
42084250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
42144256 endef
42154257
42164258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
42174262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
42184263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
42194264 fi; \
42204265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4221 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
42224267 $(call dep_autopatch_fetch_rebar); \
42234268 $(call dep_autopatch_rebar,$(1)); \
42244269 else \
42304275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
42314276 endef
42324277
4233 # Overwrite erlang.mk with the current file by default.
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
42344280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
42354281 define dep_autopatch_erlang_mk
4236 echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \
4237 > $(DEPS_DIR)/$1/erlang.mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
42384287 endef
42394288 else
42404289 define dep_autopatch_erlang_mk
42534302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
42544303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
42554304 cd $(ERLANG_MK_TMP)/rebar; \
4256 git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
42574306 $(MAKE); \
42584307 cd -; \
42594308 fi
42704319 define dep_autopatch_rebar.erl
42714320 application:load(rebar),
42724321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
42734323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
42744324 {ok, Conf0} -> Conf0;
42754325 _ -> []
44234473 [] -> ok;
44244474 _ ->
44254475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4426 PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
44274477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4428 PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L \\"~s\\" -lerl_interface -lei\n",
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
44294479 [code:lib_dir(erl_interface, lib)])),
44304480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
44314481 FilterEnv = fun(Env) ->
44644514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44654515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44664516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4467 [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
44684518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
44694519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
44704520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
44764526 end,
44774527 [PortSpec(S) || S <- PortSpecs]
44784528 end,
4479 Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"),
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
44804530 RunPlugin = fun(Plugin, Step) ->
44814531 case erlang:function_exported(Plugin, Step, 2) of
44824532 false -> ok;
45244574 halt()
45254575 endef
45264576
4527 define dep_autopatch_app.erl
4528 UpdateModules = fun(App) ->
4529 case filelib:is_regular(App) of
4530 false -> ok;
4531 true ->
4532 {ok, [{application, '$(1)', L0}]} = file:consult(App),
4533 Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true,
4534 fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
4535 L = lists:keystore(modules, 1, L0, {modules, Mods}),
4536 ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}]))
4537 end
4538 end,
4539 UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"),
4540 halt()
4541 endef
4542
45434577 define dep_autopatch_appsrc_script.erl
45444578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
45454579 AppSrcScript = AppSrc ++ ".script",
45874621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
45884622 endef
45894623
4590 define dep_fetch_hex.erl
4591 ssl:start(),
4592 inets:start(),
4593 {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
4594 {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
4595 [], [{body_format, binary}]),
4596 {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
4597 {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
4598 ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
4599 halt()
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
46004626 endef
46014627
46024628 # Hex only has a package version. No need to look in the Erlang.mk packages.
46034629 define dep_fetch_hex
4604 $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
46054634 endef
46064635
46074636 define dep_fetch_fail
46314660 $(eval DEP_NAME := $(call dep_name,$1))
46324661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
46334662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4634 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
46354664 exit 17; \
46364665 fi
46374666 $(verbose) mkdir -p $(DEPS_DIR)
46734702 clean:: clean-apps
46744703
46754704 clean-apps:
4676 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4677 $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
46784707 done
46794708
46804709 distclean:: distclean-apps
46814710
46824711 distclean-apps:
4683 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4684 $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
46854714 done
46864715 endif
46874716
47014730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
47024731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
47034732
4704 # External plugins.
4705
4706 DEP_PLUGINS ?=
4707
4708 define core_dep_plugin
4709 -include $(DEPS_DIR)/$(1)
4710
4711 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4712 endef
4713
4714 $(foreach p,$(DEP_PLUGINS),\
4715 $(eval $(if $(findstring /,$p),\
4716 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4717 $(call core_dep_plugin,$p/plugins.mk,$p))))
4718
4719 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4720 # This file is part of erlang.mk and subject to the terms of the ISC License.
4721
4722 # Configuration.
4723
4724 DTL_FULL_PATH ?=
4725 DTL_PATH ?= templates/
4726 DTL_SUFFIX ?= _dtl
4727 DTL_OPTS ?=
4728
4729 # Verbosity.
4730
4731 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
4732 dtl_verbose = $(dtl_verbose_$(V))
4733
4734 # Core targets.
4735
4736 DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl))
4737
4738 ifneq ($(DTL_FILES),)
4739
4740 ifdef DTL_FULL_PATH
4741 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
4742 else
4743 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
4744 endif
4745
4746 ifneq ($(words $(DTL_FILES)),0)
4747 # Rebuild templates when the Makefile changes.
4748 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
4749 @mkdir -p $(ERLANG_MK_TMP)
4750 @if test -f $@; then \
4751 touch $(DTL_FILES); \
4752 fi
4753 @touch $@
4754
4755 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
4756 endif
4757
4758 define erlydtl_compile.erl
4759 [begin
4760 Module0 = case "$(strip $(DTL_FULL_PATH))" of
4761 "" ->
4762 filename:basename(F, ".dtl");
4763 _ ->
4764 "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
4765 re:replace(F2, "/", "_", [{return, list}, global])
4766 end,
4767 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
4768 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
4769 ok -> ok;
4770 {ok, _} -> ok
4771 end
4772 end || F <- string:tokens("$(1)", " ")],
4773 halt().
4774 endef
4775
4776 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
4777 $(if $(strip $?),\
4778 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
4779
4780 endif
4781
4782 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
47834734 # This file is part of erlang.mk and subject to the terms of the ISC License.
47844735
47854736 # Verbosity.
47984749
47994750 define compile_proto.erl
48004751 [begin
4801 Dir = filename:dirname(filename:dirname(F)),
48024752 protobuffs_compile:generate_source(F,
4803 [{output_include_dir, Dir ++ "/include"},
4804 {output_src_dir, Dir ++ "/ebin"}])
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
48054755 end || F <- string:tokens("$(1)", " ")],
48064756 halt().
48074757 endef
48114761 $(if $(strip $?),$(call compile_proto,$?))
48124762 endif
48134763
4814 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
48154765 # This file is part of erlang.mk and subject to the terms of the ISC License.
48164766
48174767 .PHONY: clean-app
48254775 ERLC_EXCLUDE ?=
48264776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
48274777
4778 ERLC_ASN1_OPTS ?=
4779
48284780 ERLC_MIB_OPTS ?=
48294781 COMPILE_MIB_FIRST ?=
48304782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
48744826
48754827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
48764828 define app_file
4877 {application, $(PROJECT), [
4829 {application, '$(PROJECT)', [
48784830 {description, "$(PROJECT_DESCRIPTION)"},
48794831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48804832 {id$(comma)$(space)"$(1)"}$(comma))
48814833 {modules, [$(call comma_list,$(2))]},
48824834 {registered, []},
4883 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48844837 ]}.
48854838 endef
48864839 else
48874840 define app_file
4888 {application, $(PROJECT), [
4841 {application, '$(PROJECT)', [
48894842 {description, "$(PROJECT_DESCRIPTION)"},
48904843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48914844 {id$(comma)$(space)"$(1)"}$(comma))
48924845 {modules, [$(call comma_list,$(2))]},
48934846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
48944847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4895 {mod, {$(PROJECT_MOD), []}}
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48964850 ]}.
48974851 endef
48984852 endif
49024856
49034857 # Source files.
49044858
4905 ERL_FILES = $(sort $(call core_find,src/,*.erl))
4906 CORE_FILES = $(sort $(call core_find,src/,*.core))
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
49074863
49084864 # ASN.1 files.
49094865
49134869
49144870 define compile_asn1
49154871 $(verbose) mkdir -p include/
4916 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
49174873 $(verbose) mv asn1/*.erl src/
49184874 $(verbose) mv asn1/*.hrl include/
49194875 $(verbose) mv asn1/*.asn1db include/
49364892
49374893 # Leex and Yecc files.
49384894
4939 XRL_FILES = $(sort $(call core_find,src/,*.xrl))
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
49404896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
49414897 ERL_FILES += $(XRL_ERL_FILES)
49424898
4943 YRL_FILES = $(sort $(call core_find,src/,*.yrl))
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
49444900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
49454901 ERL_FILES += $(YRL_ERL_FILES)
49464902
49474903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4948 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
49494905
49504906 # Erlang and Core Erlang files.
49514907
49954951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49964952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49974953 (F, Mod, import, {Imp, _}) ->
4998 case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
49994960 false -> ok;
50004961 true -> Add(Mod, Imp)
50014962 end;
50194980 end || F <- ErlFiles],
50204981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
50214982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
50224991 ok = file:write_file("$(1)", [
50234992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
5024 "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n"
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
50254994 ]),
50264995 halt()
50274996 endef
50345003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
50355004 # Rebuild everything when the Makefile changes.
50365005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5037 @mkdir -p $(ERLANG_MK_TMP)
5038 @if test -f $@; then \
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
50395008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
50405009 touch -c $(PROJECT).d; \
50415010 fi
5042 @touch $@
5011 $(verbose) touch $@
50435012
50445013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
50455014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
50465015 endif
50475016
5048 -include $(PROJECT).d
5017 include $(wildcard $(PROJECT).d)
50495018
50505019 ebin/$(PROJECT).app:: ebin/
50515020
50645033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
50655034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
50665035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5067 $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
50685037 > ebin/$(PROJECT).app
50695038 else
50705039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
50885057
50895058 endif
50905059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
50915061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
50925062 # This file is part of erlang.mk and subject to the terms of the ISC License.
50935063
51055075 doc-deps:
51065076 else
51075077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5108 $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5109 endif
5110
5111 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51125082 # This file is part of erlang.mk and subject to the terms of the ISC License.
51135083
51145084 .PHONY: rel-deps
51255095 rel-deps:
51265096 else
51275097 rel-deps: $(ALL_REL_DEPS_DIRS)
5128 $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5129 endif
5130
5131 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51325102 # This file is part of erlang.mk and subject to the terms of the ISC License.
51335103
51345104 .PHONY: test-deps test-dir test-build clean-test-dir
51505120 test-deps:
51515121 else
51525122 test-deps: $(ALL_TEST_DEPS_DIRS)
5153 $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
51545124 endif
51555125
51565126 ifneq ($(wildcard $(TEST_DIR)),)
51835153 endif
51845154 endif
51855155
5186 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51875157 # This file is part of erlang.mk and subject to the terms of the ISC License.
51885158
51895159 .PHONY: rebar.config
52195189 rebar.config:
52205190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
52215191
5222 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
52235193 # This file is part of erlang.mk and subject to the terms of the ISC License.
52245194
5225 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
5226
5227 MAN_INSTALL_PATH ?= /usr/local/share/man
5228 MAN_SECTIONS ?= 3 7
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
52295200
52305201 docs:: asciidoc
52315202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
52325207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
52335210
52345211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
52355212 asciidoc-guide:
52365213 else
5237 asciidoc-guide: distclean-asciidoc doc-deps
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
52385215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
52395216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5240 endif
5241
5242 ifeq ($(wildcard doc/src/manual/*.asciidoc),)
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
52435227 asciidoc-manual:
52445228 else
5245 asciidoc-manual: distclean-asciidoc doc-deps
5246 for f in doc/src/manual/*.asciidoc ; do \
5247 a2x -v -f manpage $$f ; \
5248 done
5249 for s in $(MAN_SECTIONS); do \
5250 mkdir -p doc/man$$s/ ; \
5251 mv doc/src/manual/*.$$s doc/man$$s/ ; \
5252 gzip doc/man$$s/*.$$s ; \
5253 done
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
52545262
52555263 install-docs:: install-asciidoc
52565264
52575265 install-asciidoc: asciidoc-manual
5258 for s in $(MAN_SECTIONS); do \
5259 mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
5260 install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
5261 done
5262 endif
5263
5264 distclean:: distclean-asciidoc
5265
5266 distclean-asciidoc:
5267 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
5268
5269 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
52705276 # This file is part of erlang.mk and subject to the terms of the ISC License.
52715277
52725278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
53235329 define bs_Makefile
53245330 PROJECT = $p
53255331 PROJECT_DESCRIPTION = New project
5326 PROJECT_VERSION = 0.0.1
5332 PROJECT_VERSION = 0.1.0
53275333
53285334 # Whitespace to be used when creating files from templates.
53295335 SP = $(SP)
53335339 define bs_Makefile
53345340 PROJECT = $p
53355341 PROJECT_DESCRIPTION = New project
5336 PROJECT_VERSION = 0.0.1
5342 PROJECT_VERSION = 0.1.0
53375343
53385344 endef
53395345 endif
53415347 define bs_apps_Makefile
53425348 PROJECT = $p
53435349 PROJECT_DESCRIPTION = New project
5344 PROJECT_VERSION = 0.0.1
5350 PROJECT_VERSION = 0.1.0
53455351
53465352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
53475353 endef
53615367 endef
53625368
53635369 define bs_relx_config
5364 {release, {$p_release, "1"}, [$p]}.
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
53655371 {extended_start_script, true}.
53665372 {sys_config, "rel/sys.config"}.
53675373 {vm_args, "rel/vm.args"}.
57125718 ifndef t
57135719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57145720 endif
5715 ifndef tpl_$(t)
5716 $(error Unknown template)
5717 endif
57185721 ifndef n
57195722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57205723 endif
57275730 list-templates:
57285731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
57295732
5730 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
57315734 # This file is part of erlang.mk and subject to the terms of the ISC License.
57325735
57335736 .PHONY: clean-c_src distclean-c_src-env
59625965 $(call render_template,bs_erl_nif,src/$n.erl)
59635966 endif
59645967
5965 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
59665969 # This file is part of erlang.mk and subject to the terms of the ISC License.
59675970
5968 .PHONY: ci ci-setup distclean-kerl
5969
5970 KERL ?= $(CURDIR)/kerl
5971 export KERL
5972
5973 KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
5974
5975 OTP_GIT ?= https://github.com/erlang/otp
5976
5977 CI_INSTALL_DIR ?= $(HOME)/erlang
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
59785973 CI_OTP ?=
5979
5980 ifeq ($(strip $(CI_OTP)),)
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
59815986 ci::
59825987 else
5983 ci:: $(addprefix ci-,$(CI_OTP))
5984
5985 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
59866007
59876008 ci-setup::
6009
6010 ci-extra::
59886011
59896012 ci_verbose_0 = @echo " CI " $(1);
59906013 ci_verbose = $(ci_verbose_$(V))
59916014
59926015 define ci_target
5993 ci-$(1): $(CI_INSTALL_DIR)/$(1)
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
59946018 $(ci_verbose) \
5995 PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
5996 CI_OTP_RELEASE="$(1)" \
5997 CT_OPTS="-label $(1)" \
5998 $(MAKE) clean ci-setup tests
5999 endef
6000
6001 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
60026030
60036031 define ci_otp_target
60046032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
60056033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6006 $(KERL) build git $(OTP_GIT) $(1) $(1)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
60076035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
60086036 endif
60096037 endef
60106038
60116039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
60126040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
60136052 $(KERL):
6014 $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
60156056 $(verbose) chmod +x $(KERL)
60166057
60176058 help::
60286069 $(gen_verbose) rm -rf $(KERL)
60296070 endif
60306071
6031 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
60326073 # This file is part of erlang.mk and subject to the terms of the ISC License.
60336074
60346075 .PHONY: ct apps-ct distclean-ct
60366077 # Configuration.
60376078
60386079 CT_OPTS ?=
6080
60396081 ifneq ($(wildcard $(TEST_DIR)),)
6040 CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6041 else
6042 CT_SUITES ?=
6043 endif
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
60446088
60456089 # Core targets.
60466090
60636107 -noinput \
60646108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
60656109 -dir $(TEST_DIR) \
6066 -logdir $(CURDIR)/logs
6110 -logdir $(CT_LOGS_DIR)
60676111
60686112 ifeq ($(CT_SUITES),)
60696113 ct: $(if $(IS_APP),,apps-ct)
60706114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
60716117 ct: test-build $(if $(IS_APP),,apps-ct)
6072 $(verbose) mkdir -p $(CURDIR)/logs/
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
60736119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
60746121 endif
60756122
60766123 ifneq ($(ALL_APPS_DIRS),)
60976144
60986145 define ct_suite_target
60996146 ct-$(1): test-build
6100 $(verbose) mkdir -p $(CURDIR)/logs/
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
61016148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
61026149 endef
61036150
61046151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
61056152
61066153 distclean-ct:
6107 $(gen_verbose) rm -rf $(CURDIR)/logs/
6108
6109 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61106157 # This file is part of erlang.mk and subject to the terms of the ISC License.
61116158
61126159 .PHONY: plt distclean-plt dialyze
61506197 endef
61516198
61526199 $(DIALYZER_PLT): deps app
6153 $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
61546204
61556205 plt: $(DIALYZER_PLT)
61566206
61646214 endif
61656215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
61666216
6167 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61686218 # This file is part of erlang.mk and subject to the terms of the ISC License.
61696219
61706220 .PHONY: distclean-edoc edoc
61726222 # Configuration.
61736223
61746224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
61756235
61766236 # Core targets.
61776237
6178 ifneq ($(wildcard doc/overview.edoc),)
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
61796239 docs:: edoc
61806240 endif
61816241
61846244 # Plugin-specific targets.
61856245
61866246 edoc: distclean-edoc doc-deps
6187 $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
61886248
61896249 distclean-edoc:
61906250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
61916251
6192 # Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61936253 # This file is part of erlang.mk and subject to the terms of the ISC License.
61946254
6195 .PHONY: distclean-escript escript
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
61966320
61976321 # Configuration.
61986322
61996323 ESCRIPT_NAME ?= $(PROJECT)
62006324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
62016325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
62026327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6203
6204 ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
6205 ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
6206 ESCRIPT_EMU_ARGS ?= -pa . \
6207 -sasl errlog_type error \
6208 -escript main $(ESCRIPT_NAME)
6209 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6210 ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
62116332
62126333 # Core targets.
62136334
62206341
62216342 # Plugin-specific targets.
62226343
6223 # Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
6224 # Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
6225 # Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
6226 # Software may only be used for the great good and the true happiness of all
6227 # sentient beings.
6228
6229 define ESCRIPT_RAW
6230 'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
6231 'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
6232 ' [F || F <- A, not filelib:is_dir(F) ] end,'\
6233 'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
6234 'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
6235 'Ez = fun(Escript) ->'\
6236 ' Static = Files([$(ESCRIPT_STATIC)]),'\
6237 ' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
6238 ' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
6239 ' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
6240 ' {archive, Archive, [memory]},'\
6241 ' {shebang, "$(ESCRIPT_SHEBANG)"},'\
6242 ' {comment, "$(ESCRIPT_COMMENT)"},'\
6243 ' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
6244 ' ]),'\
6245 ' file:change_mode(Escript, 8#755)'\
6246 'end,'\
6247 'Ez("$(ESCRIPT_FILE)"),'\
6248 'halt().'
6249 endef
6250
6251 ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
6252
6253 escript:: distclean-escript deps app
6254 $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
62556360
62566361 distclean-escript:
6257 $(gen_verbose) rm -f $(ESCRIPT_NAME)
6258
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
62596365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6260 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
62616366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
62626367
62636368 .PHONY: eunit apps-eunit
63216426
63226427 ifneq ($(ALL_APPS_DIRS),)
63236428 apps-eunit:
6324 $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done
6325 endif
6326 endif
6327
6328 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
63296436 # This file is part of erlang.mk and subject to the terms of the ISC License.
63306437
6331 .PHONY: relx-rel distclean-relx-rel distclean-relx run
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
63326439
63336440 # Configuration.
63346441
6335 RELX ?= $(CURDIR)/relx
6442 RELX ?= $(ERLANG_MK_TMP)/relx
63366443 RELX_CONFIG ?= $(CURDIR)/relx.config
63376444
63386445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
63396446 RELX_OPTS ?=
63406447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
63416454
63426455 ifeq ($(firstword $(RELX_OPTS)),-o)
63436456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
63506463 ifeq ($(IS_DEP),)
63516464 ifneq ($(wildcard $(RELX_CONFIG)),)
63526465 rel:: relx-rel
6353 endif
6354 endif
6355
6356 distclean:: distclean-relx-rel distclean-relx
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
63576472
63586473 # Plugin-specific targets.
63596474
63626477 $(verbose) chmod +x $(RELX)
63636478
63646479 relx-rel: $(RELX) rel-deps app
6365 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
63666484
63676485 distclean-relx-rel:
63686486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6369
6370 distclean-relx:
6371 $(gen_verbose) rm -rf $(RELX)
63726487
63736488 # Run target.
63746489
63776492 else
63786493
63796494 define get_relx_release.erl
6380 {ok, Config} = file:consult("$(RELX_CONFIG)"),
6381 {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
6382 io:format("~s", [Name]),
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
63836504 halt(0).
63846505 endef
63856506
6386 RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
63876514
63886515 run: all
6389 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
63906517
63916518 help::
63926519 $(verbose) printf "%s\n" "" \
63956522
63966523 endif
63976524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
63986526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6399 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
64006527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
64016528
64026529 .PHONY: shell
64216548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
64226549
64236550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6424 $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
64256552
64266553 shell: build-shell-deps
64276554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
64286555
6429 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
64306571 # This file is part of erlang.mk and subject to the terms of the ISC License.
64316572
64326573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
64376578 tests:: triq
64386579
64396580 define triq_check.erl
6440 code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
64416582 try
64426583 case $(1) of
64436584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
64696610 endif
64706611 endif
64716612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
64726614 # Copyright (c) 2015, Erlang Solutions Ltd.
64736615 # This file is part of erlang.mk and subject to the terms of the ISC License.
64746616
64856627 XREFR ?= $(CURDIR)/xrefr
64866628 export XREFR
64876629
6488 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
64896631
64906632 # Core targets.
64916633
64926634 help::
6493 $(verbose) printf "%s\n" "" \
6494 "Xref targets:" \
6495 " xref Run Xrefr using $XREF_CONFIG as config file if defined"
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
64966638
64976639 distclean:: distclean-xref
64986640
65086650 distclean-xref:
65096651 $(gen_verbose) rm -rf $(XREFR)
65106652
6511 # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
65126655 # This file is part of erlang.mk and subject to the terms of the ISC License.
65136656
65146657 COVER_REPORT_DIR = cover
65176660
65186661 ifdef COVER
65196662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
65206664 # All modules in 'ebin'
65216665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
65226666
65316675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
65326676 endif
65336677 endif
6678 endif
65346679
65356680 # Core targets
65366681
65526697 "Cover targets:" \
65536698 " cover-report Generate a HTML coverage report from previously collected" \
65546699 " cover data." \
6555 " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
65566701 "" \
65576702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
65586703 "target tests additionally generates a HTML coverage report from the combined" \
65656710
65666711 .PHONY: coverdata-clean
65676712 coverdata-clean:
6568 $(gen_verbose) rm -f *.coverdata ct.cover.spec
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
65696714
65706715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
65716721 all.coverdata: $(COVERDATA)
6572 $(gen_verbose) $(ERL) -eval ' \
6573 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
6574 cover:export("$@"), halt(0).'
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
65756723
65766724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
65776725 # empty if you want the coverdata files but not the HTML report.
65896737 # Modules which include eunit.hrl always contain one line without coverage
65906738 # because eunit defines test/0 which is never called. We compensate for this.
65916739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6592 grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
65936741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
65946742
65956743 define cover_report.erl
66246772 endef
66256773
66266774 cover-report:
6627 $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
66286776 $(gen_verbose) $(call erlang,$(cover_report.erl))
66296777
66306778 endif
66316779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
66326839
66336840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
66346841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
66976904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
66986905 endif
66996906 ifndef IS_APP
6700 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
67016908 $(MAKE) -C $$dep $@ \
67026909 IS_APP=1 \
6703 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6704 || exit $$?; \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67056911 done
67066912 endif
6707 $(verbose) for dep in $^ ; do \
6913 $(verbose) set -e; for dep in $^ ; do \
67086914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
67096915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6710 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
67116917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
67126918 $(MAKE) -C $$dep fetch-deps \
67136919 IS_DEP=1 \
6714 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6715 || exit $$?; \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67166921 fi \
67176922 fi \
67186923 done
22 # before the inclusion of erlang.mk leading to the wrong target becoming
33 # the default.
44 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
526 endif
627
728 # --------------------------------------------------------------------
2041 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
2142 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
2243 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
2345 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
2446 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
2547 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
2648 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
2749 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
2851 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
2952 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
3054 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
3155 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
3256 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
3559 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
3660 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
3761 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
3863 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
3964 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
4065 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
7095
7196 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
7297
73 # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
74 # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
75 # needs to add "ranch" as a BUILD_DEPS. The list of projects needing
76 # this workaround are:
77 # o rabbitmq-web-stomp
78 dep_ranch = git https://github.com/ninenines/ranch 1.2.1
98 # Third-party dependencies version pinning.
99 #
100 # We do that in this file, which is copied in all projects, to ensure
101 # all projects use the same versions. It avoids conflicts and makes it
102 # possible to work with rabbitmq-public-umbrella.
103
104 dep_cowboy_commit = 1.0.4
105 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
106 dep_ranch_commit = 1.3.1
107 dep_webmachine_commit = 1.10.8p2
79108
80109 RABBITMQ_COMPONENTS = amqp_client \
81110 rabbit \
82111 rabbit_common \
83112 rabbitmq_amqp1_0 \
84113 rabbitmq_auth_backend_amqp \
114 rabbitmq_auth_backend_cache \
85115 rabbitmq_auth_backend_http \
86116 rabbitmq_auth_backend_ldap \
87117 rabbitmq_auth_mechanism_ssl \
88118 rabbitmq_boot_steps_visualiser \
89119 rabbitmq_clusterer \
120 rabbitmq_cli \
90121 rabbitmq_codegen \
91122 rabbitmq_consistent_hash_exchange \
123 rabbitmq_ct_client_helpers \
92124 rabbitmq_ct_helpers \
93125 rabbitmq_delayed_message_exchange \
94126 rabbitmq_dotnet_client \
97129 rabbitmq_federation_management \
98130 rabbitmq_java_client \
99131 rabbitmq_jms_client \
132 rabbitmq_jms_cts \
100133 rabbitmq_jms_topic_exchange \
101134 rabbitmq_lvc \
102135 rabbitmq_management \
1010 %% The Original Code is RabbitMQ Sharding Plugin
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_sharding_exchange_decorator).
1010 %% The Original Code is RabbitMQ Sharding Plugin
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_sharding_exchange_type_modulus_hash).
1010 %% The Original Code is RabbitMQ Sharding Plugin
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_sharding_interceptor).
1010 %% The Original Code is RabbitMQ Sharding Plugin
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_sharding_policy_validator).
1010 %% The Original Code is RabbitMQ Sharding Plugin
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_sharding_shard).
1010 %% The Original Code is RabbitMQ Sharding Plugin
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_sharding_util).
+0
-6
deps/rabbitmq_sharding/src/rabbitmq_sharding.app.src less more
0 {application, rabbitmq_sharding,
1 [{description, "RabbitMQ Sharding Plugin"},
2 {vsn, "0.1.0"},
3 {modules, []},
4 {registered, []},
5 {applications, [kernel, stdlib, rabbit_common, rabbit]}]}.
00 PROJECT = rabbitmq_shovel
1 PROJECT_DESCRIPTION = Data Shovel for RabbitMQ
2 PROJECT_MOD = rabbit_shovel
3
4 define PROJECT_ENV
5 [
6 {defaults, [
7 {prefetch_count, 1000},
8 {ack_mode, on_confirm},
9 {publish_fields, []},
10 {publish_properties, []},
11 {reconnect_delay, 5}
12 ]}
13 ]
14 endef
15
16 ifneq ($(RABBITMQ_VERSION),)
17 define PROJECT_APP_EXTRA_KEYS
18 {broker_version_requirements, ["$(RABBITMQ_VERSION)"]}
19 endef
20 endif
121
222 DEPS = rabbit_common rabbit amqp_client
3 TEST_DEPS = rabbitmq_ct_helpers
23 TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers
424
25 DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
526 DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk
627
728 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
1818
1919 Released under [the same license as RabbitMQ](https://www.rabbitmq.com/mpl.html).
2020
21 2007-2016 (c) Pivotal Software Inc.
21 2007-2017 (c) Pivotal Software Inc.
0 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
11 #
22 # Permission to use, copy, modify, and/or distribute this software for any
33 # purpose with or without fee is hereby granted, provided that the above
1111 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1212 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1313
14 .PHONY: all app apps deps search rel docs install-docs check tests clean distclean help erlang-mk
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
1515
1616 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17
18 ERLANG_MK_VERSION = 2.0.0-pre.2-144-g647ffd1
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
1931
2032 # Core configuration.
2133
2436
2537 PROJECT_VERSION ?= rolling
2638 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
2740
2841 # Verbosity.
2942
8497 rel::
8598 $(verbose) :
8699
100 relup:: deps app
101
87102 check:: tests
88103
89104 clean:: clean-crashdump
101116 help::
102117 $(verbose) printf "%s\n" \
103118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
104 "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
105120 "" \
106121 "Usage: [V=1] $(MAKE) [target]..." \
107122 "" \
149164 core_native_path = $1
150165 endif
151166
152 ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
153 define core_http_get
154 wget --no-check-certificate -O $(1) $(2)|| rm $(1)
155 endef
156 else
157 define core_http_get.erl
158 ssl:start(),
159 inets:start(),
160 case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
161 {ok, {{_, 200, _}, _, Body}} ->
162 case file:write_file("$(1)", Body) of
163 ok -> ok;
164 {error, R1} -> halt(R1)
165 end;
166 {error, R2} ->
167 halt(R2)
168 end,
169 halt(0).
170 endef
171
172 define core_http_get
173 $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
174 endef
175 endif
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
176168
177169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
178170
192184 ERLANG_MK_BUILD_CONFIG ?= build.config
193185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
194186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
195188 erlang-mk:
196189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
197190 ifdef ERLANG_MK_COMMIT
198191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
199192 endif
200193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
201 $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
202195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
203196 rm -rf $(ERLANG_MK_BUILD_DIR)
204197
285278 pkg_apns_repo = https://github.com/inaka/apns4erl
286279 pkg_apns_commit = master
287280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
288289 PACKAGES += azdht
289290 pkg_azdht_name = azdht
290291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
405406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
406407 pkg_bootstrap_commit = master
407408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
408417 PACKAGES += boss_db
409418 pkg_boss_db_name = boss_db
410419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
413422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
414423 pkg_boss_db_commit = master
415424
416 PACKAGES += boss
417 pkg_boss_name = boss
418 pkg_boss_description = Erlang web MVC, now featuring Comet
419 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
420 pkg_boss_fetch = git
421 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
422 pkg_boss_commit = master
423
424425 PACKAGES += brod
425426 pkg_brod_name = brod
426427 pkg_brod_description = Kafka client in Erlang
565566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
566567 pkg_cloudi_service_api_requests_commit = master
567568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
568585 PACKAGES += cloudi_service_db_cassandra_cql
569586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
570587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
573590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
574591 pkg_cloudi_service_db_cassandra_cql_commit = master
575592
576 PACKAGES += cloudi_service_db_cassandra
577 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
579 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
580 pkg_cloudi_service_db_cassandra_fetch = git
581 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
582 pkg_cloudi_service_db_cassandra_commit = master
583
584593 PACKAGES += cloudi_service_db_couchdb
585594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
586595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
637646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
638647 pkg_cloudi_service_db_tokyotyrant_commit = master
639648
640 PACKAGES += cloudi_service_db
641 pkg_cloudi_service_db_name = cloudi_service_db
642 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
643 pkg_cloudi_service_db_homepage = http://cloudi.org/
644 pkg_cloudi_service_db_fetch = git
645 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
646 pkg_cloudi_service_db_commit = master
647
648649 PACKAGES += cloudi_service_filesystem
649650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
650651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
10371038 pkg_edown_repo = https://github.com/uwiger/edown
10381039 pkg_edown_commit = master
10391040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
10401049 PACKAGES += eep_app
10411050 pkg_eep_app_name = eep_app
10421051 pkg_eep_app_description = Embedded Event Processing
10451054 pkg_eep_app_repo = https://github.com/darach/eep-erl
10461055 pkg_eep_app_commit = master
10471056
1048 PACKAGES += eep
1049 pkg_eep_name = eep
1050 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1051 pkg_eep_homepage = https://github.com/virtan/eep
1052 pkg_eep_fetch = git
1053 pkg_eep_repo = https://github.com/virtan/eep
1054 pkg_eep_commit = master
1055
10561057 PACKAGES += efene
10571058 pkg_efene_name = efene
10581059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
12371238 pkg_eqm_repo = https://github.com/loucash/eqm
12381239 pkg_eqm_commit = master
12391240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
12401249 PACKAGES += eredis_pool
12411250 pkg_eredis_pool_name = eredis_pool
12421251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
12451254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
12461255 pkg_eredis_pool_commit = master
12471256
1248 PACKAGES += eredis
1249 pkg_eredis_name = eredis
1250 pkg_eredis_description = Erlang Redis client
1251 pkg_eredis_homepage = https://github.com/wooga/eredis
1252 pkg_eredis_fetch = git
1253 pkg_eredis_repo = https://github.com/wooga/eredis
1254 pkg_eredis_commit = master
1255
12561257 PACKAGES += erl_streams
12571258 pkg_erl_streams_name = erl_streams
12581259 pkg_erl_streams_description = Streams in Erlang
15411542 pkg_etap_repo = https://github.com/ngerakines/etap
15421543 pkg_etap_commit = master
15431544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
15441553 PACKAGES += etest_http
15451554 pkg_etest_http_name = etest_http
15461555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
15491558 pkg_etest_http_repo = https://github.com/wooga/etest_http
15501559 pkg_etest_http_commit = master
15511560
1552 PACKAGES += etest
1553 pkg_etest_name = etest
1554 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1555 pkg_etest_homepage = https://github.com/wooga/etest
1556 pkg_etest_fetch = git
1557 pkg_etest_repo = https://github.com/wooga/etest
1558 pkg_etest_commit = master
1559
15601561 PACKAGES += etoml
15611562 pkg_etoml_name = etoml
15621563 pkg_etoml_description = TOML language erlang parser
15651566 pkg_etoml_repo = https://github.com/kalta/etoml
15661567 pkg_etoml_commit = master
15671568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
15681577 PACKAGES += eunit_formatters
15691578 pkg_eunit_formatters_name = eunit_formatters
15701579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
15731582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
15741583 pkg_eunit_formatters_commit = master
15751584
1576 PACKAGES += eunit
1577 pkg_eunit_name = eunit
1578 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1579 pkg_eunit_homepage = https://github.com/richcarl/eunit
1580 pkg_eunit_fetch = git
1581 pkg_eunit_repo = https://github.com/richcarl/eunit
1582 pkg_eunit_commit = master
1583
15841585 PACKAGES += euthanasia
15851586 pkg_euthanasia_name = euthanasia
15861587 pkg_euthanasia_description = Merciful killer for your Erlang processes
15981599 pkg_evum_commit = master
15991600
16001601 PACKAGES += exec
1601 pkg_exec_name = exec
1602 pkg_exec_name = erlexec
16021603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
16031604 pkg_exec_homepage = http://saleyn.github.com/erlexec
16041605 pkg_exec_fetch = git
17171718 pkg_fn_repo = https://github.com/reiddraper/fn
17181719 pkg_fn_commit = master
17191720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
17201729 PACKAGES += folsom_cowboy
17211730 pkg_folsom_cowboy_name = folsom_cowboy
17221731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
17251734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
17261735 pkg_folsom_cowboy_commit = master
17271736
1728 PACKAGES += folsom
1729 pkg_folsom_name = folsom
1730 pkg_folsom_description = Expose Erlang Events and Metrics
1731 pkg_folsom_homepage = https://github.com/boundary/folsom
1732 pkg_folsom_fetch = git
1733 pkg_folsom_repo = https://github.com/boundary/folsom
1734 pkg_folsom_commit = master
1735
17361737 PACKAGES += folsomite
17371738 pkg_folsomite_name = folsomite
17381739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
20932094 pkg_jesse_repo = https://github.com/for-GET/jesse
20942095 pkg_jesse_commit = master
20952096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
20962105 PACKAGES += jiffy_v
20972106 pkg_jiffy_v_name = jiffy_v
20982107 pkg_jiffy_v_description = JSON validation utility
21012110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
21022111 pkg_jiffy_v_commit = master
21032112
2104 PACKAGES += jiffy
2105 pkg_jiffy_name = jiffy
2106 pkg_jiffy_description = JSON NIFs for Erlang.
2107 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2108 pkg_jiffy_fetch = git
2109 pkg_jiffy_repo = https://github.com/davisp/jiffy
2110 pkg_jiffy_commit = master
2111
21122113 PACKAGES += jobs
21132114 pkg_jobs_name = jobs
21142115 pkg_jobs_description = a Job scheduler for load regulation
21252126 pkg_joxa_repo = https://github.com/joxa/joxa
21262127 pkg_joxa_commit = master
21272128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
21282137 PACKAGES += json_rec
21292138 pkg_json_rec_name = json_rec
21302139 pkg_json_rec_description = JSON to erlang record
21332142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
21342143 pkg_json_rec_commit = master
21352144
2136 PACKAGES += json
2137 pkg_json_name = json
2138 pkg_json_description = a high level json library for erlang (17.0+)
2139 pkg_json_homepage = https://github.com/talentdeficit/json
2140 pkg_json_fetch = git
2141 pkg_json_repo = https://github.com/talentdeficit/json
2142 pkg_json_commit = master
2143
21442145 PACKAGES += jsone
21452146 pkg_jsone_name = jsone
21462147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
21812182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
21822183 pkg_jsx_commit = master
21832184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
21842193 PACKAGES += kafka_protocol
21852194 pkg_kafka_protocol_name = kafka_protocol
21862195 pkg_kafka_protocol_description = Kafka protocol Erlang library
21892198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
21902199 pkg_kafka_protocol_commit = master
21912200
2192 PACKAGES += kafka
2193 pkg_kafka_name = kafka
2194 pkg_kafka_description = Kafka consumer and producer in Erlang
2195 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2196 pkg_kafka_fetch = git
2197 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2198 pkg_kafka_commit = master
2199
22002201 PACKAGES += kai
22012202 pkg_kai_name = kai
22022203 pkg_kai_description = DHT storage by Takeshi Inoue
22932294 pkg_kvs_repo = https://github.com/synrc/kvs
22942295 pkg_kvs_commit = master
22952296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
22962305 PACKAGES += lager_amqp_backend
22972306 pkg_lager_amqp_backend_name = lager_amqp_backend
22982307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
23092318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
23102319 pkg_lager_syslog_commit = master
23112320
2312 PACKAGES += lager
2313 pkg_lager_name = lager
2314 pkg_lager_description = A logging framework for Erlang/OTP.
2315 pkg_lager_homepage = https://github.com/basho/lager
2316 pkg_lager_fetch = git
2317 pkg_lager_repo = https://github.com/basho/lager
2318 pkg_lager_commit = master
2319
23202321 PACKAGES += lambdapad
23212322 pkg_lambdapad_name = lambdapad
23222323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
25732574 pkg_mixer_repo = https://github.com/chef/mixer
25742575 pkg_mixer_commit = master
25752576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
25762585 PACKAGES += mochiweb_xpath
25772586 pkg_mochiweb_xpath_name = mochiweb_xpath
25782587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
25812590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
25822591 pkg_mochiweb_xpath_commit = master
25832592
2584 PACKAGES += mochiweb
2585 pkg_mochiweb_name = mochiweb
2586 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2587 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2588 pkg_mochiweb_fetch = git
2589 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2590 pkg_mochiweb_commit = master
2591
25922593 PACKAGES += mockgyver
25932594 pkg_mockgyver_name = mockgyver
25942595 pkg_mockgyver_description = A mocking library for Erlang
30613062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
30623063 pkg_quickrand_commit = master
30633064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
30643073 PACKAGES += rabbit_exchange_type_riak
30653074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
30663075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
30693078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
30703079 pkg_rabbit_exchange_type_riak_commit = master
30713080
3072 PACKAGES += rabbit
3073 pkg_rabbit_name = rabbit
3074 pkg_rabbit_description = RabbitMQ Server
3075 pkg_rabbit_homepage = https://www.rabbitmq.com/
3076 pkg_rabbit_fetch = git
3077 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3078 pkg_rabbit_commit = master
3079
30803081 PACKAGES += rack
30813082 pkg_rack_name = rack
30823083 pkg_rack_description = Rack handler for erlang
34933494 pkg_smother_repo = https://github.com/ramsay-t/Smother
34943495 pkg_smother_commit = master
34953496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
34963505 PACKAGES += social
34973506 pkg_social_name = social
34983507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
35413550 pkg_stable_repo = https://github.com/dvv/stable
35423551 pkg_stable_commit = master
35433552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
35443561 PACKAGES += statebox_riak
35453562 pkg_statebox_riak_name = statebox_riak
35463563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
35493566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
35503567 pkg_statebox_riak_commit = master
35513568
3552 PACKAGES += statebox
3553 pkg_statebox_name = statebox
3554 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3555 pkg_statebox_homepage = https://github.com/mochi/statebox
3556 pkg_statebox_fetch = git
3557 pkg_statebox_repo = https://github.com/mochi/statebox
3558 pkg_statebox_commit = master
3559
35603569 PACKAGES += statman
35613570 pkg_statman_name = statman
35623571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
37253734 pkg_tirerl_repo = https://github.com/inaka/tirerl
37263735 pkg_tirerl_commit = master
37273736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
37283745 PACKAGES += traffic_tools
37293746 pkg_traffic_tools_name = traffic_tools
37303747 pkg_traffic_tools_description = Simple traffic limiting library
40614078 pkg_zucchini_repo = https://github.com/devinus/zucchini
40624079 pkg_zucchini_commit = master
40634080
4064 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
40654082 # This file is part of erlang.mk and subject to the terms of the ISC License.
40664083
40674084 .PHONY: search
40884105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
40894106 endif
40904107
4091 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
40924109 # This file is part of erlang.mk and subject to the terms of the ISC License.
40934110
4094 .PHONY: distclean-deps
4111 .PHONY: distclean-deps clean-tmp-deps.log
40954112
40964113 # Configuration.
40974114
41104127
41114128 REBAR_DEPS_DIR = $(DEPS_DIR)
41124129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
41134150
41144151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
41154152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
41164153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
41174154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
41184155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
41194157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
41204158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
41214159
41384176
41394177 # Core targets.
41404178
4141 ifdef IS_APP
4142 apps::
4143 else
4144 apps:: $(ALL_APPS_DIRS)
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
41454180 ifeq ($(IS_APP)$(IS_DEP),)
41464181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
41474182 endif
41494184 # Create ebin directory for all apps to make sure Erlang recognizes them
41504185 # as proper OTP applications when using -include_lib. This is a temporary
41514186 # fix, a proper fix would be to compile apps/* in the right order.
4152 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4153 mkdir -p $$dep/ebin || exit $$?; \
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
41544190 done
4155 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
41564196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
41574197 :; \
41584198 else \
41594199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4160 $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
4200 $(MAKE) -C $$dep IS_APP=1; \
41614201 fi \
41624202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
41634207 endif
41644208
41654209 ifneq ($(SKIP_DEPS),)
41664210 deps::
41674211 else
4168 deps:: $(ALL_DEPS_DIRS) apps
4169 ifeq ($(IS_APP)$(IS_DEP),)
4170 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4171 endif
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
41724213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4173 $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
41744215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
41754216 :; \
41764217 else \
41774218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
41784219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4179 $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
4220 $(MAKE) -C $$dep IS_DEP=1; \
41804221 else \
4181 echo "Error: No Makefile to build dependency $$dep."; \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
41824223 exit 2; \
41834224 fi \
41844225 fi \
41924233 # in practice only Makefile is needed so far.
41934234 define dep_autopatch
41944235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
41954237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
41964238 $(call dep_autopatch_erlang_mk,$(1)); \
41974239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4198 if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
41994243 $(call dep_autopatch2,$(1)); \
4200 elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
42014245 $(call dep_autopatch2,$(1)); \
4202 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
42034247 $(call dep_autopatch2,$(1)); \
4204 else \
4205 $(call erlang,$(call dep_autopatch_app.erl,$(1))); \
42064248 fi \
42074249 else \
42084250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
42144256 endef
42154257
42164258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
42174262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
42184263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
42194264 fi; \
42204265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4221 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
42224267 $(call dep_autopatch_fetch_rebar); \
42234268 $(call dep_autopatch_rebar,$(1)); \
42244269 else \
42304275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
42314276 endef
42324277
4233 # Overwrite erlang.mk with the current file by default.
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
42344280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
42354281 define dep_autopatch_erlang_mk
4236 echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \
4237 > $(DEPS_DIR)/$1/erlang.mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
42384287 endef
42394288 else
42404289 define dep_autopatch_erlang_mk
42534302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
42544303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
42554304 cd $(ERLANG_MK_TMP)/rebar; \
4256 git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
42574306 $(MAKE); \
42584307 cd -; \
42594308 fi
42704319 define dep_autopatch_rebar.erl
42714320 application:load(rebar),
42724321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
42734323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
42744324 {ok, Conf0} -> Conf0;
42754325 _ -> []
44234473 [] -> ok;
44244474 _ ->
44254475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4426 PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
44274477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4428 PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L \\"~s\\" -lerl_interface -lei\n",
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
44294479 [code:lib_dir(erl_interface, lib)])),
44304480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
44314481 FilterEnv = fun(Env) ->
44644514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44654515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44664516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4467 [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
44684518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
44694519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
44704520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
44764526 end,
44774527 [PortSpec(S) || S <- PortSpecs]
44784528 end,
4479 Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"),
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
44804530 RunPlugin = fun(Plugin, Step) ->
44814531 case erlang:function_exported(Plugin, Step, 2) of
44824532 false -> ok;
45244574 halt()
45254575 endef
45264576
4527 define dep_autopatch_app.erl
4528 UpdateModules = fun(App) ->
4529 case filelib:is_regular(App) of
4530 false -> ok;
4531 true ->
4532 {ok, [{application, '$(1)', L0}]} = file:consult(App),
4533 Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true,
4534 fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
4535 L = lists:keystore(modules, 1, L0, {modules, Mods}),
4536 ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}]))
4537 end
4538 end,
4539 UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"),
4540 halt()
4541 endef
4542
45434577 define dep_autopatch_appsrc_script.erl
45444578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
45454579 AppSrcScript = AppSrc ++ ".script",
45874621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
45884622 endef
45894623
4590 define dep_fetch_hex.erl
4591 ssl:start(),
4592 inets:start(),
4593 {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
4594 {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
4595 [], [{body_format, binary}]),
4596 {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
4597 {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
4598 ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
4599 halt()
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
46004626 endef
46014627
46024628 # Hex only has a package version. No need to look in the Erlang.mk packages.
46034629 define dep_fetch_hex
4604 $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
46054634 endef
46064635
46074636 define dep_fetch_fail
46314660 $(eval DEP_NAME := $(call dep_name,$1))
46324661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
46334662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4634 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
46354664 exit 17; \
46364665 fi
46374666 $(verbose) mkdir -p $(DEPS_DIR)
46734702 clean:: clean-apps
46744703
46754704 clean-apps:
4676 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4677 $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
46784707 done
46794708
46804709 distclean:: distclean-apps
46814710
46824711 distclean-apps:
4683 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4684 $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
46854714 done
46864715 endif
46874716
47014730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
47024731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
47034732
4704 # External plugins.
4705
4706 DEP_PLUGINS ?=
4707
4708 define core_dep_plugin
4709 -include $(DEPS_DIR)/$(1)
4710
4711 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4712 endef
4713
4714 $(foreach p,$(DEP_PLUGINS),\
4715 $(eval $(if $(findstring /,$p),\
4716 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4717 $(call core_dep_plugin,$p/plugins.mk,$p))))
4718
4719 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4720 # This file is part of erlang.mk and subject to the terms of the ISC License.
4721
4722 # Configuration.
4723
4724 DTL_FULL_PATH ?=
4725 DTL_PATH ?= templates/
4726 DTL_SUFFIX ?= _dtl
4727 DTL_OPTS ?=
4728
4729 # Verbosity.
4730
4731 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
4732 dtl_verbose = $(dtl_verbose_$(V))
4733
4734 # Core targets.
4735
4736 DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl))
4737
4738 ifneq ($(DTL_FILES),)
4739
4740 ifdef DTL_FULL_PATH
4741 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
4742 else
4743 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
4744 endif
4745
4746 ifneq ($(words $(DTL_FILES)),0)
4747 # Rebuild templates when the Makefile changes.
4748 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
4749 @mkdir -p $(ERLANG_MK_TMP)
4750 @if test -f $@; then \
4751 touch $(DTL_FILES); \
4752 fi
4753 @touch $@
4754
4755 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
4756 endif
4757
4758 define erlydtl_compile.erl
4759 [begin
4760 Module0 = case "$(strip $(DTL_FULL_PATH))" of
4761 "" ->
4762 filename:basename(F, ".dtl");
4763 _ ->
4764 "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
4765 re:replace(F2, "/", "_", [{return, list}, global])
4766 end,
4767 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
4768 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
4769 ok -> ok;
4770 {ok, _} -> ok
4771 end
4772 end || F <- string:tokens("$(1)", " ")],
4773 halt().
4774 endef
4775
4776 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
4777 $(if $(strip $?),\
4778 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
4779
4780 endif
4781
4782 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
47834734 # This file is part of erlang.mk and subject to the terms of the ISC License.
47844735
47854736 # Verbosity.
47984749
47994750 define compile_proto.erl
48004751 [begin
4801 Dir = filename:dirname(filename:dirname(F)),
48024752 protobuffs_compile:generate_source(F,
4803 [{output_include_dir, Dir ++ "/include"},
4804 {output_src_dir, Dir ++ "/ebin"}])
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
48054755 end || F <- string:tokens("$(1)", " ")],
48064756 halt().
48074757 endef
48114761 $(if $(strip $?),$(call compile_proto,$?))
48124762 endif
48134763
4814 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
48154765 # This file is part of erlang.mk and subject to the terms of the ISC License.
48164766
48174767 .PHONY: clean-app
48254775 ERLC_EXCLUDE ?=
48264776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
48274777
4778 ERLC_ASN1_OPTS ?=
4779
48284780 ERLC_MIB_OPTS ?=
48294781 COMPILE_MIB_FIRST ?=
48304782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
48744826
48754827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
48764828 define app_file
4877 {application, $(PROJECT), [
4829 {application, '$(PROJECT)', [
48784830 {description, "$(PROJECT_DESCRIPTION)"},
48794831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48804832 {id$(comma)$(space)"$(1)"}$(comma))
48814833 {modules, [$(call comma_list,$(2))]},
48824834 {registered, []},
4883 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48844837 ]}.
48854838 endef
48864839 else
48874840 define app_file
4888 {application, $(PROJECT), [
4841 {application, '$(PROJECT)', [
48894842 {description, "$(PROJECT_DESCRIPTION)"},
48904843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48914844 {id$(comma)$(space)"$(1)"}$(comma))
48924845 {modules, [$(call comma_list,$(2))]},
48934846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
48944847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4895 {mod, {$(PROJECT_MOD), []}}
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48964850 ]}.
48974851 endef
48984852 endif
49024856
49034857 # Source files.
49044858
4905 ERL_FILES = $(sort $(call core_find,src/,*.erl))
4906 CORE_FILES = $(sort $(call core_find,src/,*.core))
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
49074863
49084864 # ASN.1 files.
49094865
49134869
49144870 define compile_asn1
49154871 $(verbose) mkdir -p include/
4916 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
49174873 $(verbose) mv asn1/*.erl src/
49184874 $(verbose) mv asn1/*.hrl include/
49194875 $(verbose) mv asn1/*.asn1db include/
49364892
49374893 # Leex and Yecc files.
49384894
4939 XRL_FILES = $(sort $(call core_find,src/,*.xrl))
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
49404896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
49414897 ERL_FILES += $(XRL_ERL_FILES)
49424898
4943 YRL_FILES = $(sort $(call core_find,src/,*.yrl))
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
49444900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
49454901 ERL_FILES += $(YRL_ERL_FILES)
49464902
49474903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4948 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
49494905
49504906 # Erlang and Core Erlang files.
49514907
49954951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49964952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49974953 (F, Mod, import, {Imp, _}) ->
4998 case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
49994960 false -> ok;
50004961 true -> Add(Mod, Imp)
50014962 end;
50194980 end || F <- ErlFiles],
50204981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
50214982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
50224991 ok = file:write_file("$(1)", [
50234992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
5024 "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n"
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
50254994 ]),
50264995 halt()
50274996 endef
50345003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
50355004 # Rebuild everything when the Makefile changes.
50365005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5037 @mkdir -p $(ERLANG_MK_TMP)
5038 @if test -f $@; then \
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
50395008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
50405009 touch -c $(PROJECT).d; \
50415010 fi
5042 @touch $@
5011 $(verbose) touch $@
50435012
50445013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
50455014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
50465015 endif
50475016
5048 -include $(PROJECT).d
5017 include $(wildcard $(PROJECT).d)
50495018
50505019 ebin/$(PROJECT).app:: ebin/
50515020
50645033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
50655034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
50665035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5067 $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
50685037 > ebin/$(PROJECT).app
50695038 else
50705039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
50885057
50895058 endif
50905059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
50915061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
50925062 # This file is part of erlang.mk and subject to the terms of the ISC License.
50935063
51055075 doc-deps:
51065076 else
51075077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5108 $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5109 endif
5110
5111 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51125082 # This file is part of erlang.mk and subject to the terms of the ISC License.
51135083
51145084 .PHONY: rel-deps
51255095 rel-deps:
51265096 else
51275097 rel-deps: $(ALL_REL_DEPS_DIRS)
5128 $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5129 endif
5130
5131 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51325102 # This file is part of erlang.mk and subject to the terms of the ISC License.
51335103
51345104 .PHONY: test-deps test-dir test-build clean-test-dir
51505120 test-deps:
51515121 else
51525122 test-deps: $(ALL_TEST_DEPS_DIRS)
5153 $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
51545124 endif
51555125
51565126 ifneq ($(wildcard $(TEST_DIR)),)
51835153 endif
51845154 endif
51855155
5186 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51875157 # This file is part of erlang.mk and subject to the terms of the ISC License.
51885158
51895159 .PHONY: rebar.config
52195189 rebar.config:
52205190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
52215191
5222 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
52235193 # This file is part of erlang.mk and subject to the terms of the ISC License.
52245194
5225 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
5226
5227 MAN_INSTALL_PATH ?= /usr/local/share/man
5228 MAN_SECTIONS ?= 3 7
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
52295200
52305201 docs:: asciidoc
52315202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
52325207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
52335210
52345211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
52355212 asciidoc-guide:
52365213 else
5237 asciidoc-guide: distclean-asciidoc doc-deps
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
52385215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
52395216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5240 endif
5241
5242 ifeq ($(wildcard doc/src/manual/*.asciidoc),)
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
52435227 asciidoc-manual:
52445228 else
5245 asciidoc-manual: distclean-asciidoc doc-deps
5246 for f in doc/src/manual/*.asciidoc ; do \
5247 a2x -v -f manpage $$f ; \
5248 done
5249 for s in $(MAN_SECTIONS); do \
5250 mkdir -p doc/man$$s/ ; \
5251 mv doc/src/manual/*.$$s doc/man$$s/ ; \
5252 gzip doc/man$$s/*.$$s ; \
5253 done
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
52545262
52555263 install-docs:: install-asciidoc
52565264
52575265 install-asciidoc: asciidoc-manual
5258 for s in $(MAN_SECTIONS); do \
5259 mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
5260 install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
5261 done
5262 endif
5263
5264 distclean:: distclean-asciidoc
5265
5266 distclean-asciidoc:
5267 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
5268
5269 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
52705276 # This file is part of erlang.mk and subject to the terms of the ISC License.
52715277
52725278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
53235329 define bs_Makefile
53245330 PROJECT = $p
53255331 PROJECT_DESCRIPTION = New project
5326 PROJECT_VERSION = 0.0.1
5332 PROJECT_VERSION = 0.1.0
53275333
53285334 # Whitespace to be used when creating files from templates.
53295335 SP = $(SP)
53335339 define bs_Makefile
53345340 PROJECT = $p
53355341 PROJECT_DESCRIPTION = New project
5336 PROJECT_VERSION = 0.0.1
5342 PROJECT_VERSION = 0.1.0
53375343
53385344 endef
53395345 endif
53415347 define bs_apps_Makefile
53425348 PROJECT = $p
53435349 PROJECT_DESCRIPTION = New project
5344 PROJECT_VERSION = 0.0.1
5350 PROJECT_VERSION = 0.1.0
53455351
53465352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
53475353 endef
53615367 endef
53625368
53635369 define bs_relx_config
5364 {release, {$p_release, "1"}, [$p]}.
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
53655371 {extended_start_script, true}.
53665372 {sys_config, "rel/sys.config"}.
53675373 {vm_args, "rel/vm.args"}.
57125718 ifndef t
57135719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57145720 endif
5715 ifndef tpl_$(t)
5716 $(error Unknown template)
5717 endif
57185721 ifndef n
57195722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57205723 endif
57275730 list-templates:
57285731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
57295732
5730 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
57315734 # This file is part of erlang.mk and subject to the terms of the ISC License.
57325735
57335736 .PHONY: clean-c_src distclean-c_src-env
59625965 $(call render_template,bs_erl_nif,src/$n.erl)
59635966 endif
59645967
5965 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
59665969 # This file is part of erlang.mk and subject to the terms of the ISC License.
59675970
5968 .PHONY: ci ci-setup distclean-kerl
5969
5970 KERL ?= $(CURDIR)/kerl
5971 export KERL
5972
5973 KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
5974
5975 OTP_GIT ?= https://github.com/erlang/otp
5976
5977 CI_INSTALL_DIR ?= $(HOME)/erlang
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
59785973 CI_OTP ?=
5979
5980 ifeq ($(strip $(CI_OTP)),)
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
59815986 ci::
59825987 else
5983 ci:: $(addprefix ci-,$(CI_OTP))
5984
5985 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
59866007
59876008 ci-setup::
6009
6010 ci-extra::
59886011
59896012 ci_verbose_0 = @echo " CI " $(1);
59906013 ci_verbose = $(ci_verbose_$(V))
59916014
59926015 define ci_target
5993 ci-$(1): $(CI_INSTALL_DIR)/$(1)
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
59946018 $(ci_verbose) \
5995 PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
5996 CI_OTP_RELEASE="$(1)" \
5997 CT_OPTS="-label $(1)" \
5998 $(MAKE) clean ci-setup tests
5999 endef
6000
6001 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
60026030
60036031 define ci_otp_target
60046032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
60056033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6006 $(KERL) build git $(OTP_GIT) $(1) $(1)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
60076035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
60086036 endif
60096037 endef
60106038
60116039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
60126040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
60136052 $(KERL):
6014 $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
60156056 $(verbose) chmod +x $(KERL)
60166057
60176058 help::
60286069 $(gen_verbose) rm -rf $(KERL)
60296070 endif
60306071
6031 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
60326073 # This file is part of erlang.mk and subject to the terms of the ISC License.
60336074
60346075 .PHONY: ct apps-ct distclean-ct
60366077 # Configuration.
60376078
60386079 CT_OPTS ?=
6080
60396081 ifneq ($(wildcard $(TEST_DIR)),)
6040 CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6041 else
6042 CT_SUITES ?=
6043 endif
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
60446088
60456089 # Core targets.
60466090
60636107 -noinput \
60646108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
60656109 -dir $(TEST_DIR) \
6066 -logdir $(CURDIR)/logs
6110 -logdir $(CT_LOGS_DIR)
60676111
60686112 ifeq ($(CT_SUITES),)
60696113 ct: $(if $(IS_APP),,apps-ct)
60706114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
60716117 ct: test-build $(if $(IS_APP),,apps-ct)
6072 $(verbose) mkdir -p $(CURDIR)/logs/
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
60736119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
60746121 endif
60756122
60766123 ifneq ($(ALL_APPS_DIRS),)
60976144
60986145 define ct_suite_target
60996146 ct-$(1): test-build
6100 $(verbose) mkdir -p $(CURDIR)/logs/
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
61016148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
61026149 endef
61036150
61046151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
61056152
61066153 distclean-ct:
6107 $(gen_verbose) rm -rf $(CURDIR)/logs/
6108
6109 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61106157 # This file is part of erlang.mk and subject to the terms of the ISC License.
61116158
61126159 .PHONY: plt distclean-plt dialyze
61506197 endef
61516198
61526199 $(DIALYZER_PLT): deps app
6153 $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
61546204
61556205 plt: $(DIALYZER_PLT)
61566206
61646214 endif
61656215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
61666216
6167 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61686218 # This file is part of erlang.mk and subject to the terms of the ISC License.
61696219
61706220 .PHONY: distclean-edoc edoc
61726222 # Configuration.
61736223
61746224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
61756235
61766236 # Core targets.
61776237
6178 ifneq ($(wildcard doc/overview.edoc),)
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
61796239 docs:: edoc
61806240 endif
61816241
61846244 # Plugin-specific targets.
61856245
61866246 edoc: distclean-edoc doc-deps
6187 $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
61886248
61896249 distclean-edoc:
61906250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
61916251
6192 # Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61936253 # This file is part of erlang.mk and subject to the terms of the ISC License.
61946254
6195 .PHONY: distclean-escript escript
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
61966320
61976321 # Configuration.
61986322
61996323 ESCRIPT_NAME ?= $(PROJECT)
62006324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
62016325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
62026327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6203
6204 ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
6205 ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
6206 ESCRIPT_EMU_ARGS ?= -pa . \
6207 -sasl errlog_type error \
6208 -escript main $(ESCRIPT_NAME)
6209 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6210 ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
62116332
62126333 # Core targets.
62136334
62206341
62216342 # Plugin-specific targets.
62226343
6223 # Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
6224 # Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
6225 # Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
6226 # Software may only be used for the great good and the true happiness of all
6227 # sentient beings.
6228
6229 define ESCRIPT_RAW
6230 'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
6231 'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
6232 ' [F || F <- A, not filelib:is_dir(F) ] end,'\
6233 'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
6234 'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
6235 'Ez = fun(Escript) ->'\
6236 ' Static = Files([$(ESCRIPT_STATIC)]),'\
6237 ' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
6238 ' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
6239 ' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
6240 ' {archive, Archive, [memory]},'\
6241 ' {shebang, "$(ESCRIPT_SHEBANG)"},'\
6242 ' {comment, "$(ESCRIPT_COMMENT)"},'\
6243 ' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
6244 ' ]),'\
6245 ' file:change_mode(Escript, 8#755)'\
6246 'end,'\
6247 'Ez("$(ESCRIPT_FILE)"),'\
6248 'halt().'
6249 endef
6250
6251 ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
6252
6253 escript:: distclean-escript deps app
6254 $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
62556360
62566361 distclean-escript:
6257 $(gen_verbose) rm -f $(ESCRIPT_NAME)
6258
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
62596365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6260 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
62616366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
62626367
62636368 .PHONY: eunit apps-eunit
63216426
63226427 ifneq ($(ALL_APPS_DIRS),)
63236428 apps-eunit:
6324 $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done
6325 endif
6326 endif
6327
6328 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
63296436 # This file is part of erlang.mk and subject to the terms of the ISC License.
63306437
6331 .PHONY: relx-rel distclean-relx-rel distclean-relx run
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
63326439
63336440 # Configuration.
63346441
6335 RELX ?= $(CURDIR)/relx
6442 RELX ?= $(ERLANG_MK_TMP)/relx
63366443 RELX_CONFIG ?= $(CURDIR)/relx.config
63376444
63386445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
63396446 RELX_OPTS ?=
63406447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
63416454
63426455 ifeq ($(firstword $(RELX_OPTS)),-o)
63436456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
63506463 ifeq ($(IS_DEP),)
63516464 ifneq ($(wildcard $(RELX_CONFIG)),)
63526465 rel:: relx-rel
6353 endif
6354 endif
6355
6356 distclean:: distclean-relx-rel distclean-relx
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
63576472
63586473 # Plugin-specific targets.
63596474
63626477 $(verbose) chmod +x $(RELX)
63636478
63646479 relx-rel: $(RELX) rel-deps app
6365 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
63666484
63676485 distclean-relx-rel:
63686486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6369
6370 distclean-relx:
6371 $(gen_verbose) rm -rf $(RELX)
63726487
63736488 # Run target.
63746489
63776492 else
63786493
63796494 define get_relx_release.erl
6380 {ok, Config} = file:consult("$(RELX_CONFIG)"),
6381 {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
6382 io:format("~s", [Name]),
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
63836504 halt(0).
63846505 endef
63856506
6386 RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
63876514
63886515 run: all
6389 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
63906517
63916518 help::
63926519 $(verbose) printf "%s\n" "" \
63956522
63966523 endif
63976524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
63986526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6399 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
64006527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
64016528
64026529 .PHONY: shell
64216548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
64226549
64236550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6424 $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
64256552
64266553 shell: build-shell-deps
64276554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
64286555
6429 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
64306571 # This file is part of erlang.mk and subject to the terms of the ISC License.
64316572
64326573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
64376578 tests:: triq
64386579
64396580 define triq_check.erl
6440 code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
64416582 try
64426583 case $(1) of
64436584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
64696610 endif
64706611 endif
64716612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
64726614 # Copyright (c) 2015, Erlang Solutions Ltd.
64736615 # This file is part of erlang.mk and subject to the terms of the ISC License.
64746616
64856627 XREFR ?= $(CURDIR)/xrefr
64866628 export XREFR
64876629
6488 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
64896631
64906632 # Core targets.
64916633
64926634 help::
6493 $(verbose) printf "%s\n" "" \
6494 "Xref targets:" \
6495 " xref Run Xrefr using $XREF_CONFIG as config file if defined"
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
64966638
64976639 distclean:: distclean-xref
64986640
65086650 distclean-xref:
65096651 $(gen_verbose) rm -rf $(XREFR)
65106652
6511 # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
65126655 # This file is part of erlang.mk and subject to the terms of the ISC License.
65136656
65146657 COVER_REPORT_DIR = cover
65176660
65186661 ifdef COVER
65196662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
65206664 # All modules in 'ebin'
65216665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
65226666
65316675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
65326676 endif
65336677 endif
6678 endif
65346679
65356680 # Core targets
65366681
65526697 "Cover targets:" \
65536698 " cover-report Generate a HTML coverage report from previously collected" \
65546699 " cover data." \
6555 " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
65566701 "" \
65576702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
65586703 "target tests additionally generates a HTML coverage report from the combined" \
65656710
65666711 .PHONY: coverdata-clean
65676712 coverdata-clean:
6568 $(gen_verbose) rm -f *.coverdata ct.cover.spec
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
65696714
65706715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
65716721 all.coverdata: $(COVERDATA)
6572 $(gen_verbose) $(ERL) -eval ' \
6573 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
6574 cover:export("$@"), halt(0).'
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
65756723
65766724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
65776725 # empty if you want the coverdata files but not the HTML report.
65896737 # Modules which include eunit.hrl always contain one line without coverage
65906738 # because eunit defines test/0 which is never called. We compensate for this.
65916739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6592 grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
65936741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
65946742
65956743 define cover_report.erl
66246772 endef
66256773
66266774 cover-report:
6627 $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
66286776 $(gen_verbose) $(call erlang,$(cover_report.erl))
66296777
66306778 endif
66316779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
66326839
66336840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
66346841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
66976904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
66986905 endif
66996906 ifndef IS_APP
6700 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
67016908 $(MAKE) -C $$dep $@ \
67026909 IS_APP=1 \
6703 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6704 || exit $$?; \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67056911 done
67066912 endif
6707 $(verbose) for dep in $^ ; do \
6913 $(verbose) set -e; for dep in $^ ; do \
67086914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
67096915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6710 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
67116917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
67126918 $(MAKE) -C $$dep fetch-deps \
67136919 IS_DEP=1 \
6714 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6715 || exit $$?; \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67166921 fi \
67176922 fi \
67186923 done
22 # before the inclusion of erlang.mk leading to the wrong target becoming
33 # the default.
44 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
526 endif
627
728 # --------------------------------------------------------------------
2041 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
2142 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
2243 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
2345 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
2446 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
2547 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
2648 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
2749 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
2851 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
2952 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
3054 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
3155 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
3256 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
3559 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
3660 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
3761 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
3863 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
3964 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
4065 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
7095
7196 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
7297
73 # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
74 # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
75 # needs to add "ranch" as a BUILD_DEPS. The list of projects needing
76 # this workaround are:
77 # o rabbitmq-web-stomp
78 dep_ranch = git https://github.com/ninenines/ranch 1.2.1
98 # Third-party dependencies version pinning.
99 #
100 # We do that in this file, which is copied in all projects, to ensure
101 # all projects use the same versions. It avoids conflicts and makes it
102 # possible to work with rabbitmq-public-umbrella.
103
104 dep_cowboy_commit = 1.0.4
105 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
106 dep_ranch_commit = 1.3.1
107 dep_webmachine_commit = 1.10.8p2
79108
80109 RABBITMQ_COMPONENTS = amqp_client \
81110 rabbit \
82111 rabbit_common \
83112 rabbitmq_amqp1_0 \
84113 rabbitmq_auth_backend_amqp \
114 rabbitmq_auth_backend_cache \
85115 rabbitmq_auth_backend_http \
86116 rabbitmq_auth_backend_ldap \
87117 rabbitmq_auth_mechanism_ssl \
88118 rabbitmq_boot_steps_visualiser \
89119 rabbitmq_clusterer \
120 rabbitmq_cli \
90121 rabbitmq_codegen \
91122 rabbitmq_consistent_hash_exchange \
123 rabbitmq_ct_client_helpers \
92124 rabbitmq_ct_helpers \
93125 rabbitmq_delayed_message_exchange \
94126 rabbitmq_dotnet_client \
97129 rabbitmq_federation_management \
98130 rabbitmq_java_client \
99131 rabbitmq_jms_client \
132 rabbitmq_jms_cts \
100133 rabbitmq_jms_topic_exchange \
101134 rabbitmq_lvc \
102135 rabbitmq_management \
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_shovel).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_shovel_config).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_shovel_dyn_worker_sup).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_shovel_dyn_worker_sup_sup).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_shovel_parameters).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_shovel_status).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_shovel_sup).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_shovel_util).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_shovel_worker).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_shovel_worker_sup).
+0
-13
deps/rabbitmq_shovel/src/rabbitmq_shovel.app.src less more
0 {application, rabbitmq_shovel,
1 [{description, "Data Shovel for RabbitMQ"},
2 {vsn, "3.6.6"},
3 {modules, []},
4 {registered, []},
5 {env, [{defaults, [{prefetch_count, 1000},
6 {ack_mode, on_confirm},
7 {publish_fields, []},
8 {publish_properties, []},
9 {reconnect_delay, 5}]
10 }]},
11 {mod, {rabbit_shovel, []}},
12 {applications, [kernel, stdlib, rabbit_common, rabbit, amqp_client]}]}.
00 PROJECT = rabbitmq_shovel_management
1 PROJECT_DESCRIPTION = Management extension for the Shovel plugin
12
2 DEPS = rabbit_common rabbit rabbitmq_management rabbitmq_shovel webmachine
3 TEST_DEPS = rabbitmq_ct_helpers
3 DEPS = rabbit_common rabbit rabbitmq_management rabbitmq_shovel
4 TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers
45
6 DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
57 DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk
68
79 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
3030
3131 Released under [the same license as RabbitMQ](https://www.rabbitmq.com/mpl.html).
3232
33 2007-2016 (c) Pivotal Software Inc.
33 2007-2017 (c) Pivotal Software Inc.
0 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
11 #
22 # Permission to use, copy, modify, and/or distribute this software for any
33 # purpose with or without fee is hereby granted, provided that the above
1111 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1212 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1313
14 .PHONY: all app apps deps search rel docs install-docs check tests clean distclean help erlang-mk
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
1515
1616 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17
18 ERLANG_MK_VERSION = 2.0.0-pre.2-144-g647ffd1
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
1931
2032 # Core configuration.
2133
2436
2537 PROJECT_VERSION ?= rolling
2638 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
2740
2841 # Verbosity.
2942
8497 rel::
8598 $(verbose) :
8699
100 relup:: deps app
101
87102 check:: tests
88103
89104 clean:: clean-crashdump
101116 help::
102117 $(verbose) printf "%s\n" \
103118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
104 "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
105120 "" \
106121 "Usage: [V=1] $(MAKE) [target]..." \
107122 "" \
149164 core_native_path = $1
150165 endif
151166
152 ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
153 define core_http_get
154 wget --no-check-certificate -O $(1) $(2)|| rm $(1)
155 endef
156 else
157 define core_http_get.erl
158 ssl:start(),
159 inets:start(),
160 case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
161 {ok, {{_, 200, _}, _, Body}} ->
162 case file:write_file("$(1)", Body) of
163 ok -> ok;
164 {error, R1} -> halt(R1)
165 end;
166 {error, R2} ->
167 halt(R2)
168 end,
169 halt(0).
170 endef
171
172 define core_http_get
173 $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
174 endef
175 endif
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
176168
177169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
178170
192184 ERLANG_MK_BUILD_CONFIG ?= build.config
193185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
194186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
195188 erlang-mk:
196189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
197190 ifdef ERLANG_MK_COMMIT
198191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
199192 endif
200193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
201 $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
202195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
203196 rm -rf $(ERLANG_MK_BUILD_DIR)
204197
285278 pkg_apns_repo = https://github.com/inaka/apns4erl
286279 pkg_apns_commit = master
287280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
288289 PACKAGES += azdht
289290 pkg_azdht_name = azdht
290291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
405406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
406407 pkg_bootstrap_commit = master
407408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
408417 PACKAGES += boss_db
409418 pkg_boss_db_name = boss_db
410419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
413422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
414423 pkg_boss_db_commit = master
415424
416 PACKAGES += boss
417 pkg_boss_name = boss
418 pkg_boss_description = Erlang web MVC, now featuring Comet
419 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
420 pkg_boss_fetch = git
421 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
422 pkg_boss_commit = master
423
424425 PACKAGES += brod
425426 pkg_brod_name = brod
426427 pkg_brod_description = Kafka client in Erlang
565566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
566567 pkg_cloudi_service_api_requests_commit = master
567568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
568585 PACKAGES += cloudi_service_db_cassandra_cql
569586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
570587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
573590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
574591 pkg_cloudi_service_db_cassandra_cql_commit = master
575592
576 PACKAGES += cloudi_service_db_cassandra
577 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
579 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
580 pkg_cloudi_service_db_cassandra_fetch = git
581 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
582 pkg_cloudi_service_db_cassandra_commit = master
583
584593 PACKAGES += cloudi_service_db_couchdb
585594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
586595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
637646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
638647 pkg_cloudi_service_db_tokyotyrant_commit = master
639648
640 PACKAGES += cloudi_service_db
641 pkg_cloudi_service_db_name = cloudi_service_db
642 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
643 pkg_cloudi_service_db_homepage = http://cloudi.org/
644 pkg_cloudi_service_db_fetch = git
645 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
646 pkg_cloudi_service_db_commit = master
647
648649 PACKAGES += cloudi_service_filesystem
649650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
650651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
10371038 pkg_edown_repo = https://github.com/uwiger/edown
10381039 pkg_edown_commit = master
10391040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
10401049 PACKAGES += eep_app
10411050 pkg_eep_app_name = eep_app
10421051 pkg_eep_app_description = Embedded Event Processing
10451054 pkg_eep_app_repo = https://github.com/darach/eep-erl
10461055 pkg_eep_app_commit = master
10471056
1048 PACKAGES += eep
1049 pkg_eep_name = eep
1050 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1051 pkg_eep_homepage = https://github.com/virtan/eep
1052 pkg_eep_fetch = git
1053 pkg_eep_repo = https://github.com/virtan/eep
1054 pkg_eep_commit = master
1055
10561057 PACKAGES += efene
10571058 pkg_efene_name = efene
10581059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
12371238 pkg_eqm_repo = https://github.com/loucash/eqm
12381239 pkg_eqm_commit = master
12391240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
12401249 PACKAGES += eredis_pool
12411250 pkg_eredis_pool_name = eredis_pool
12421251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
12451254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
12461255 pkg_eredis_pool_commit = master
12471256
1248 PACKAGES += eredis
1249 pkg_eredis_name = eredis
1250 pkg_eredis_description = Erlang Redis client
1251 pkg_eredis_homepage = https://github.com/wooga/eredis
1252 pkg_eredis_fetch = git
1253 pkg_eredis_repo = https://github.com/wooga/eredis
1254 pkg_eredis_commit = master
1255
12561257 PACKAGES += erl_streams
12571258 pkg_erl_streams_name = erl_streams
12581259 pkg_erl_streams_description = Streams in Erlang
15411542 pkg_etap_repo = https://github.com/ngerakines/etap
15421543 pkg_etap_commit = master
15431544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
15441553 PACKAGES += etest_http
15451554 pkg_etest_http_name = etest_http
15461555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
15491558 pkg_etest_http_repo = https://github.com/wooga/etest_http
15501559 pkg_etest_http_commit = master
15511560
1552 PACKAGES += etest
1553 pkg_etest_name = etest
1554 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1555 pkg_etest_homepage = https://github.com/wooga/etest
1556 pkg_etest_fetch = git
1557 pkg_etest_repo = https://github.com/wooga/etest
1558 pkg_etest_commit = master
1559
15601561 PACKAGES += etoml
15611562 pkg_etoml_name = etoml
15621563 pkg_etoml_description = TOML language erlang parser
15651566 pkg_etoml_repo = https://github.com/kalta/etoml
15661567 pkg_etoml_commit = master
15671568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
15681577 PACKAGES += eunit_formatters
15691578 pkg_eunit_formatters_name = eunit_formatters
15701579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
15731582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
15741583 pkg_eunit_formatters_commit = master
15751584
1576 PACKAGES += eunit
1577 pkg_eunit_name = eunit
1578 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1579 pkg_eunit_homepage = https://github.com/richcarl/eunit
1580 pkg_eunit_fetch = git
1581 pkg_eunit_repo = https://github.com/richcarl/eunit
1582 pkg_eunit_commit = master
1583
15841585 PACKAGES += euthanasia
15851586 pkg_euthanasia_name = euthanasia
15861587 pkg_euthanasia_description = Merciful killer for your Erlang processes
15981599 pkg_evum_commit = master
15991600
16001601 PACKAGES += exec
1601 pkg_exec_name = exec
1602 pkg_exec_name = erlexec
16021603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
16031604 pkg_exec_homepage = http://saleyn.github.com/erlexec
16041605 pkg_exec_fetch = git
17171718 pkg_fn_repo = https://github.com/reiddraper/fn
17181719 pkg_fn_commit = master
17191720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
17201729 PACKAGES += folsom_cowboy
17211730 pkg_folsom_cowboy_name = folsom_cowboy
17221731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
17251734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
17261735 pkg_folsom_cowboy_commit = master
17271736
1728 PACKAGES += folsom
1729 pkg_folsom_name = folsom
1730 pkg_folsom_description = Expose Erlang Events and Metrics
1731 pkg_folsom_homepage = https://github.com/boundary/folsom
1732 pkg_folsom_fetch = git
1733 pkg_folsom_repo = https://github.com/boundary/folsom
1734 pkg_folsom_commit = master
1735
17361737 PACKAGES += folsomite
17371738 pkg_folsomite_name = folsomite
17381739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
20932094 pkg_jesse_repo = https://github.com/for-GET/jesse
20942095 pkg_jesse_commit = master
20952096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
20962105 PACKAGES += jiffy_v
20972106 pkg_jiffy_v_name = jiffy_v
20982107 pkg_jiffy_v_description = JSON validation utility
21012110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
21022111 pkg_jiffy_v_commit = master
21032112
2104 PACKAGES += jiffy
2105 pkg_jiffy_name = jiffy
2106 pkg_jiffy_description = JSON NIFs for Erlang.
2107 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2108 pkg_jiffy_fetch = git
2109 pkg_jiffy_repo = https://github.com/davisp/jiffy
2110 pkg_jiffy_commit = master
2111
21122113 PACKAGES += jobs
21132114 pkg_jobs_name = jobs
21142115 pkg_jobs_description = a Job scheduler for load regulation
21252126 pkg_joxa_repo = https://github.com/joxa/joxa
21262127 pkg_joxa_commit = master
21272128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
21282137 PACKAGES += json_rec
21292138 pkg_json_rec_name = json_rec
21302139 pkg_json_rec_description = JSON to erlang record
21332142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
21342143 pkg_json_rec_commit = master
21352144
2136 PACKAGES += json
2137 pkg_json_name = json
2138 pkg_json_description = a high level json library for erlang (17.0+)
2139 pkg_json_homepage = https://github.com/talentdeficit/json
2140 pkg_json_fetch = git
2141 pkg_json_repo = https://github.com/talentdeficit/json
2142 pkg_json_commit = master
2143
21442145 PACKAGES += jsone
21452146 pkg_jsone_name = jsone
21462147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
21812182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
21822183 pkg_jsx_commit = master
21832184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
21842193 PACKAGES += kafka_protocol
21852194 pkg_kafka_protocol_name = kafka_protocol
21862195 pkg_kafka_protocol_description = Kafka protocol Erlang library
21892198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
21902199 pkg_kafka_protocol_commit = master
21912200
2192 PACKAGES += kafka
2193 pkg_kafka_name = kafka
2194 pkg_kafka_description = Kafka consumer and producer in Erlang
2195 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2196 pkg_kafka_fetch = git
2197 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2198 pkg_kafka_commit = master
2199
22002201 PACKAGES += kai
22012202 pkg_kai_name = kai
22022203 pkg_kai_description = DHT storage by Takeshi Inoue
22932294 pkg_kvs_repo = https://github.com/synrc/kvs
22942295 pkg_kvs_commit = master
22952296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
22962305 PACKAGES += lager_amqp_backend
22972306 pkg_lager_amqp_backend_name = lager_amqp_backend
22982307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
23092318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
23102319 pkg_lager_syslog_commit = master
23112320
2312 PACKAGES += lager
2313 pkg_lager_name = lager
2314 pkg_lager_description = A logging framework for Erlang/OTP.
2315 pkg_lager_homepage = https://github.com/basho/lager
2316 pkg_lager_fetch = git
2317 pkg_lager_repo = https://github.com/basho/lager
2318 pkg_lager_commit = master
2319
23202321 PACKAGES += lambdapad
23212322 pkg_lambdapad_name = lambdapad
23222323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
25732574 pkg_mixer_repo = https://github.com/chef/mixer
25742575 pkg_mixer_commit = master
25752576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
25762585 PACKAGES += mochiweb_xpath
25772586 pkg_mochiweb_xpath_name = mochiweb_xpath
25782587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
25812590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
25822591 pkg_mochiweb_xpath_commit = master
25832592
2584 PACKAGES += mochiweb
2585 pkg_mochiweb_name = mochiweb
2586 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2587 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2588 pkg_mochiweb_fetch = git
2589 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2590 pkg_mochiweb_commit = master
2591
25922593 PACKAGES += mockgyver
25932594 pkg_mockgyver_name = mockgyver
25942595 pkg_mockgyver_description = A mocking library for Erlang
30613062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
30623063 pkg_quickrand_commit = master
30633064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
30643073 PACKAGES += rabbit_exchange_type_riak
30653074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
30663075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
30693078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
30703079 pkg_rabbit_exchange_type_riak_commit = master
30713080
3072 PACKAGES += rabbit
3073 pkg_rabbit_name = rabbit
3074 pkg_rabbit_description = RabbitMQ Server
3075 pkg_rabbit_homepage = https://www.rabbitmq.com/
3076 pkg_rabbit_fetch = git
3077 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3078 pkg_rabbit_commit = master
3079
30803081 PACKAGES += rack
30813082 pkg_rack_name = rack
30823083 pkg_rack_description = Rack handler for erlang
34933494 pkg_smother_repo = https://github.com/ramsay-t/Smother
34943495 pkg_smother_commit = master
34953496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
34963505 PACKAGES += social
34973506 pkg_social_name = social
34983507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
35413550 pkg_stable_repo = https://github.com/dvv/stable
35423551 pkg_stable_commit = master
35433552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
35443561 PACKAGES += statebox_riak
35453562 pkg_statebox_riak_name = statebox_riak
35463563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
35493566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
35503567 pkg_statebox_riak_commit = master
35513568
3552 PACKAGES += statebox
3553 pkg_statebox_name = statebox
3554 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3555 pkg_statebox_homepage = https://github.com/mochi/statebox
3556 pkg_statebox_fetch = git
3557 pkg_statebox_repo = https://github.com/mochi/statebox
3558 pkg_statebox_commit = master
3559
35603569 PACKAGES += statman
35613570 pkg_statman_name = statman
35623571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
37253734 pkg_tirerl_repo = https://github.com/inaka/tirerl
37263735 pkg_tirerl_commit = master
37273736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
37283745 PACKAGES += traffic_tools
37293746 pkg_traffic_tools_name = traffic_tools
37303747 pkg_traffic_tools_description = Simple traffic limiting library
40614078 pkg_zucchini_repo = https://github.com/devinus/zucchini
40624079 pkg_zucchini_commit = master
40634080
4064 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
40654082 # This file is part of erlang.mk and subject to the terms of the ISC License.
40664083
40674084 .PHONY: search
40884105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
40894106 endif
40904107
4091 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
40924109 # This file is part of erlang.mk and subject to the terms of the ISC License.
40934110
4094 .PHONY: distclean-deps
4111 .PHONY: distclean-deps clean-tmp-deps.log
40954112
40964113 # Configuration.
40974114
41104127
41114128 REBAR_DEPS_DIR = $(DEPS_DIR)
41124129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
41134150
41144151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
41154152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
41164153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
41174154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
41184155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
41194157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
41204158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
41214159
41384176
41394177 # Core targets.
41404178
4141 ifdef IS_APP
4142 apps::
4143 else
4144 apps:: $(ALL_APPS_DIRS)
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
41454180 ifeq ($(IS_APP)$(IS_DEP),)
41464181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
41474182 endif
41494184 # Create ebin directory for all apps to make sure Erlang recognizes them
41504185 # as proper OTP applications when using -include_lib. This is a temporary
41514186 # fix, a proper fix would be to compile apps/* in the right order.
4152 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4153 mkdir -p $$dep/ebin || exit $$?; \
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
41544190 done
4155 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
41564196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
41574197 :; \
41584198 else \
41594199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4160 $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
4200 $(MAKE) -C $$dep IS_APP=1; \
41614201 fi \
41624202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
41634207 endif
41644208
41654209 ifneq ($(SKIP_DEPS),)
41664210 deps::
41674211 else
4168 deps:: $(ALL_DEPS_DIRS) apps
4169 ifeq ($(IS_APP)$(IS_DEP),)
4170 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4171 endif
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
41724213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4173 $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
41744215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
41754216 :; \
41764217 else \
41774218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
41784219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4179 $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
4220 $(MAKE) -C $$dep IS_DEP=1; \
41804221 else \
4181 echo "Error: No Makefile to build dependency $$dep."; \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
41824223 exit 2; \
41834224 fi \
41844225 fi \
41924233 # in practice only Makefile is needed so far.
41934234 define dep_autopatch
41944235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
41954237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
41964238 $(call dep_autopatch_erlang_mk,$(1)); \
41974239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4198 if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
41994243 $(call dep_autopatch2,$(1)); \
4200 elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
42014245 $(call dep_autopatch2,$(1)); \
4202 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
42034247 $(call dep_autopatch2,$(1)); \
4204 else \
4205 $(call erlang,$(call dep_autopatch_app.erl,$(1))); \
42064248 fi \
42074249 else \
42084250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
42144256 endef
42154257
42164258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
42174262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
42184263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
42194264 fi; \
42204265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4221 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
42224267 $(call dep_autopatch_fetch_rebar); \
42234268 $(call dep_autopatch_rebar,$(1)); \
42244269 else \
42304275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
42314276 endef
42324277
4233 # Overwrite erlang.mk with the current file by default.
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
42344280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
42354281 define dep_autopatch_erlang_mk
4236 echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \
4237 > $(DEPS_DIR)/$1/erlang.mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
42384287 endef
42394288 else
42404289 define dep_autopatch_erlang_mk
42534302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
42544303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
42554304 cd $(ERLANG_MK_TMP)/rebar; \
4256 git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
42574306 $(MAKE); \
42584307 cd -; \
42594308 fi
42704319 define dep_autopatch_rebar.erl
42714320 application:load(rebar),
42724321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
42734323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
42744324 {ok, Conf0} -> Conf0;
42754325 _ -> []
44234473 [] -> ok;
44244474 _ ->
44254475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4426 PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
44274477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4428 PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L \\"~s\\" -lerl_interface -lei\n",
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
44294479 [code:lib_dir(erl_interface, lib)])),
44304480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
44314481 FilterEnv = fun(Env) ->
44644514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44654515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44664516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4467 [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
44684518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
44694519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
44704520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
44764526 end,
44774527 [PortSpec(S) || S <- PortSpecs]
44784528 end,
4479 Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"),
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
44804530 RunPlugin = fun(Plugin, Step) ->
44814531 case erlang:function_exported(Plugin, Step, 2) of
44824532 false -> ok;
45244574 halt()
45254575 endef
45264576
4527 define dep_autopatch_app.erl
4528 UpdateModules = fun(App) ->
4529 case filelib:is_regular(App) of
4530 false -> ok;
4531 true ->
4532 {ok, [{application, '$(1)', L0}]} = file:consult(App),
4533 Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true,
4534 fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
4535 L = lists:keystore(modules, 1, L0, {modules, Mods}),
4536 ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}]))
4537 end
4538 end,
4539 UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"),
4540 halt()
4541 endef
4542
45434577 define dep_autopatch_appsrc_script.erl
45444578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
45454579 AppSrcScript = AppSrc ++ ".script",
45874621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
45884622 endef
45894623
4590 define dep_fetch_hex.erl
4591 ssl:start(),
4592 inets:start(),
4593 {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
4594 {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
4595 [], [{body_format, binary}]),
4596 {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
4597 {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
4598 ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
4599 halt()
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
46004626 endef
46014627
46024628 # Hex only has a package version. No need to look in the Erlang.mk packages.
46034629 define dep_fetch_hex
4604 $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
46054634 endef
46064635
46074636 define dep_fetch_fail
46314660 $(eval DEP_NAME := $(call dep_name,$1))
46324661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
46334662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4634 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
46354664 exit 17; \
46364665 fi
46374666 $(verbose) mkdir -p $(DEPS_DIR)
46734702 clean:: clean-apps
46744703
46754704 clean-apps:
4676 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4677 $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
46784707 done
46794708
46804709 distclean:: distclean-apps
46814710
46824711 distclean-apps:
4683 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4684 $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
46854714 done
46864715 endif
46874716
47014730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
47024731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
47034732
4704 # External plugins.
4705
4706 DEP_PLUGINS ?=
4707
4708 define core_dep_plugin
4709 -include $(DEPS_DIR)/$(1)
4710
4711 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4712 endef
4713
4714 $(foreach p,$(DEP_PLUGINS),\
4715 $(eval $(if $(findstring /,$p),\
4716 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4717 $(call core_dep_plugin,$p/plugins.mk,$p))))
4718
4719 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4720 # This file is part of erlang.mk and subject to the terms of the ISC License.
4721
4722 # Configuration.
4723
4724 DTL_FULL_PATH ?=
4725 DTL_PATH ?= templates/
4726 DTL_SUFFIX ?= _dtl
4727 DTL_OPTS ?=
4728
4729 # Verbosity.
4730
4731 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
4732 dtl_verbose = $(dtl_verbose_$(V))
4733
4734 # Core targets.
4735
4736 DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl))
4737
4738 ifneq ($(DTL_FILES),)
4739
4740 ifdef DTL_FULL_PATH
4741 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
4742 else
4743 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
4744 endif
4745
4746 ifneq ($(words $(DTL_FILES)),0)
4747 # Rebuild templates when the Makefile changes.
4748 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
4749 @mkdir -p $(ERLANG_MK_TMP)
4750 @if test -f $@; then \
4751 touch $(DTL_FILES); \
4752 fi
4753 @touch $@
4754
4755 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
4756 endif
4757
4758 define erlydtl_compile.erl
4759 [begin
4760 Module0 = case "$(strip $(DTL_FULL_PATH))" of
4761 "" ->
4762 filename:basename(F, ".dtl");
4763 _ ->
4764 "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
4765 re:replace(F2, "/", "_", [{return, list}, global])
4766 end,
4767 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
4768 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
4769 ok -> ok;
4770 {ok, _} -> ok
4771 end
4772 end || F <- string:tokens("$(1)", " ")],
4773 halt().
4774 endef
4775
4776 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
4777 $(if $(strip $?),\
4778 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
4779
4780 endif
4781
4782 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
47834734 # This file is part of erlang.mk and subject to the terms of the ISC License.
47844735
47854736 # Verbosity.
47984749
47994750 define compile_proto.erl
48004751 [begin
4801 Dir = filename:dirname(filename:dirname(F)),
48024752 protobuffs_compile:generate_source(F,
4803 [{output_include_dir, Dir ++ "/include"},
4804 {output_src_dir, Dir ++ "/ebin"}])
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
48054755 end || F <- string:tokens("$(1)", " ")],
48064756 halt().
48074757 endef
48114761 $(if $(strip $?),$(call compile_proto,$?))
48124762 endif
48134763
4814 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
48154765 # This file is part of erlang.mk and subject to the terms of the ISC License.
48164766
48174767 .PHONY: clean-app
48254775 ERLC_EXCLUDE ?=
48264776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
48274777
4778 ERLC_ASN1_OPTS ?=
4779
48284780 ERLC_MIB_OPTS ?=
48294781 COMPILE_MIB_FIRST ?=
48304782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
48744826
48754827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
48764828 define app_file
4877 {application, $(PROJECT), [
4829 {application, '$(PROJECT)', [
48784830 {description, "$(PROJECT_DESCRIPTION)"},
48794831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48804832 {id$(comma)$(space)"$(1)"}$(comma))
48814833 {modules, [$(call comma_list,$(2))]},
48824834 {registered, []},
4883 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48844837 ]}.
48854838 endef
48864839 else
48874840 define app_file
4888 {application, $(PROJECT), [
4841 {application, '$(PROJECT)', [
48894842 {description, "$(PROJECT_DESCRIPTION)"},
48904843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48914844 {id$(comma)$(space)"$(1)"}$(comma))
48924845 {modules, [$(call comma_list,$(2))]},
48934846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
48944847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4895 {mod, {$(PROJECT_MOD), []}}
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48964850 ]}.
48974851 endef
48984852 endif
49024856
49034857 # Source files.
49044858
4905 ERL_FILES = $(sort $(call core_find,src/,*.erl))
4906 CORE_FILES = $(sort $(call core_find,src/,*.core))
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
49074863
49084864 # ASN.1 files.
49094865
49134869
49144870 define compile_asn1
49154871 $(verbose) mkdir -p include/
4916 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
49174873 $(verbose) mv asn1/*.erl src/
49184874 $(verbose) mv asn1/*.hrl include/
49194875 $(verbose) mv asn1/*.asn1db include/
49364892
49374893 # Leex and Yecc files.
49384894
4939 XRL_FILES = $(sort $(call core_find,src/,*.xrl))
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
49404896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
49414897 ERL_FILES += $(XRL_ERL_FILES)
49424898
4943 YRL_FILES = $(sort $(call core_find,src/,*.yrl))
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
49444900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
49454901 ERL_FILES += $(YRL_ERL_FILES)
49464902
49474903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4948 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
49494905
49504906 # Erlang and Core Erlang files.
49514907
49954951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49964952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49974953 (F, Mod, import, {Imp, _}) ->
4998 case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
49994960 false -> ok;
50004961 true -> Add(Mod, Imp)
50014962 end;
50194980 end || F <- ErlFiles],
50204981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
50214982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
50224991 ok = file:write_file("$(1)", [
50234992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
5024 "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n"
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
50254994 ]),
50264995 halt()
50274996 endef
50345003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
50355004 # Rebuild everything when the Makefile changes.
50365005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5037 @mkdir -p $(ERLANG_MK_TMP)
5038 @if test -f $@; then \
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
50395008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
50405009 touch -c $(PROJECT).d; \
50415010 fi
5042 @touch $@
5011 $(verbose) touch $@
50435012
50445013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
50455014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
50465015 endif
50475016
5048 -include $(PROJECT).d
5017 include $(wildcard $(PROJECT).d)
50495018
50505019 ebin/$(PROJECT).app:: ebin/
50515020
50645033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
50655034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
50665035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5067 $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
50685037 > ebin/$(PROJECT).app
50695038 else
50705039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
50885057
50895058 endif
50905059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
50915061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
50925062 # This file is part of erlang.mk and subject to the terms of the ISC License.
50935063
51055075 doc-deps:
51065076 else
51075077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5108 $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5109 endif
5110
5111 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51125082 # This file is part of erlang.mk and subject to the terms of the ISC License.
51135083
51145084 .PHONY: rel-deps
51255095 rel-deps:
51265096 else
51275097 rel-deps: $(ALL_REL_DEPS_DIRS)
5128 $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5129 endif
5130
5131 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51325102 # This file is part of erlang.mk and subject to the terms of the ISC License.
51335103
51345104 .PHONY: test-deps test-dir test-build clean-test-dir
51505120 test-deps:
51515121 else
51525122 test-deps: $(ALL_TEST_DEPS_DIRS)
5153 $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
51545124 endif
51555125
51565126 ifneq ($(wildcard $(TEST_DIR)),)
51835153 endif
51845154 endif
51855155
5186 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51875157 # This file is part of erlang.mk and subject to the terms of the ISC License.
51885158
51895159 .PHONY: rebar.config
52195189 rebar.config:
52205190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
52215191
5222 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
52235193 # This file is part of erlang.mk and subject to the terms of the ISC License.
52245194
5225 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
5226
5227 MAN_INSTALL_PATH ?= /usr/local/share/man
5228 MAN_SECTIONS ?= 3 7
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
52295200
52305201 docs:: asciidoc
52315202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
52325207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
52335210
52345211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
52355212 asciidoc-guide:
52365213 else
5237 asciidoc-guide: distclean-asciidoc doc-deps
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
52385215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
52395216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5240 endif
5241
5242 ifeq ($(wildcard doc/src/manual/*.asciidoc),)
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
52435227 asciidoc-manual:
52445228 else
5245 asciidoc-manual: distclean-asciidoc doc-deps
5246 for f in doc/src/manual/*.asciidoc ; do \
5247 a2x -v -f manpage $$f ; \
5248 done
5249 for s in $(MAN_SECTIONS); do \
5250 mkdir -p doc/man$$s/ ; \
5251 mv doc/src/manual/*.$$s doc/man$$s/ ; \
5252 gzip doc/man$$s/*.$$s ; \
5253 done
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
52545262
52555263 install-docs:: install-asciidoc
52565264
52575265 install-asciidoc: asciidoc-manual
5258 for s in $(MAN_SECTIONS); do \
5259 mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
5260 install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
5261 done
5262 endif
5263
5264 distclean:: distclean-asciidoc
5265
5266 distclean-asciidoc:
5267 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
5268
5269 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
52705276 # This file is part of erlang.mk and subject to the terms of the ISC License.
52715277
52725278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
53235329 define bs_Makefile
53245330 PROJECT = $p
53255331 PROJECT_DESCRIPTION = New project
5326 PROJECT_VERSION = 0.0.1
5332 PROJECT_VERSION = 0.1.0
53275333
53285334 # Whitespace to be used when creating files from templates.
53295335 SP = $(SP)
53335339 define bs_Makefile
53345340 PROJECT = $p
53355341 PROJECT_DESCRIPTION = New project
5336 PROJECT_VERSION = 0.0.1
5342 PROJECT_VERSION = 0.1.0
53375343
53385344 endef
53395345 endif
53415347 define bs_apps_Makefile
53425348 PROJECT = $p
53435349 PROJECT_DESCRIPTION = New project
5344 PROJECT_VERSION = 0.0.1
5350 PROJECT_VERSION = 0.1.0
53455351
53465352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
53475353 endef
53615367 endef
53625368
53635369 define bs_relx_config
5364 {release, {$p_release, "1"}, [$p]}.
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
53655371 {extended_start_script, true}.
53665372 {sys_config, "rel/sys.config"}.
53675373 {vm_args, "rel/vm.args"}.
57125718 ifndef t
57135719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57145720 endif
5715 ifndef tpl_$(t)
5716 $(error Unknown template)
5717 endif
57185721 ifndef n
57195722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57205723 endif
57275730 list-templates:
57285731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
57295732
5730 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
57315734 # This file is part of erlang.mk and subject to the terms of the ISC License.
57325735
57335736 .PHONY: clean-c_src distclean-c_src-env
59625965 $(call render_template,bs_erl_nif,src/$n.erl)
59635966 endif
59645967
5965 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
59665969 # This file is part of erlang.mk and subject to the terms of the ISC License.
59675970
5968 .PHONY: ci ci-setup distclean-kerl
5969
5970 KERL ?= $(CURDIR)/kerl
5971 export KERL
5972
5973 KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
5974
5975 OTP_GIT ?= https://github.com/erlang/otp
5976
5977 CI_INSTALL_DIR ?= $(HOME)/erlang
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
59785973 CI_OTP ?=
5979
5980 ifeq ($(strip $(CI_OTP)),)
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
59815986 ci::
59825987 else
5983 ci:: $(addprefix ci-,$(CI_OTP))
5984
5985 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
59866007
59876008 ci-setup::
6009
6010 ci-extra::
59886011
59896012 ci_verbose_0 = @echo " CI " $(1);
59906013 ci_verbose = $(ci_verbose_$(V))
59916014
59926015 define ci_target
5993 ci-$(1): $(CI_INSTALL_DIR)/$(1)
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
59946018 $(ci_verbose) \
5995 PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
5996 CI_OTP_RELEASE="$(1)" \
5997 CT_OPTS="-label $(1)" \
5998 $(MAKE) clean ci-setup tests
5999 endef
6000
6001 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
60026030
60036031 define ci_otp_target
60046032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
60056033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6006 $(KERL) build git $(OTP_GIT) $(1) $(1)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
60076035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
60086036 endif
60096037 endef
60106038
60116039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
60126040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
60136052 $(KERL):
6014 $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
60156056 $(verbose) chmod +x $(KERL)
60166057
60176058 help::
60286069 $(gen_verbose) rm -rf $(KERL)
60296070 endif
60306071
6031 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
60326073 # This file is part of erlang.mk and subject to the terms of the ISC License.
60336074
60346075 .PHONY: ct apps-ct distclean-ct
60366077 # Configuration.
60376078
60386079 CT_OPTS ?=
6080
60396081 ifneq ($(wildcard $(TEST_DIR)),)
6040 CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6041 else
6042 CT_SUITES ?=
6043 endif
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
60446088
60456089 # Core targets.
60466090
60636107 -noinput \
60646108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
60656109 -dir $(TEST_DIR) \
6066 -logdir $(CURDIR)/logs
6110 -logdir $(CT_LOGS_DIR)
60676111
60686112 ifeq ($(CT_SUITES),)
60696113 ct: $(if $(IS_APP),,apps-ct)
60706114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
60716117 ct: test-build $(if $(IS_APP),,apps-ct)
6072 $(verbose) mkdir -p $(CURDIR)/logs/
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
60736119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
60746121 endif
60756122
60766123 ifneq ($(ALL_APPS_DIRS),)
60976144
60986145 define ct_suite_target
60996146 ct-$(1): test-build
6100 $(verbose) mkdir -p $(CURDIR)/logs/
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
61016148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
61026149 endef
61036150
61046151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
61056152
61066153 distclean-ct:
6107 $(gen_verbose) rm -rf $(CURDIR)/logs/
6108
6109 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61106157 # This file is part of erlang.mk and subject to the terms of the ISC License.
61116158
61126159 .PHONY: plt distclean-plt dialyze
61506197 endef
61516198
61526199 $(DIALYZER_PLT): deps app
6153 $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
61546204
61556205 plt: $(DIALYZER_PLT)
61566206
61646214 endif
61656215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
61666216
6167 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61686218 # This file is part of erlang.mk and subject to the terms of the ISC License.
61696219
61706220 .PHONY: distclean-edoc edoc
61726222 # Configuration.
61736223
61746224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
61756235
61766236 # Core targets.
61776237
6178 ifneq ($(wildcard doc/overview.edoc),)
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
61796239 docs:: edoc
61806240 endif
61816241
61846244 # Plugin-specific targets.
61856245
61866246 edoc: distclean-edoc doc-deps
6187 $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
61886248
61896249 distclean-edoc:
61906250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
61916251
6192 # Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61936253 # This file is part of erlang.mk and subject to the terms of the ISC License.
61946254
6195 .PHONY: distclean-escript escript
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
61966320
61976321 # Configuration.
61986322
61996323 ESCRIPT_NAME ?= $(PROJECT)
62006324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
62016325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
62026327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6203
6204 ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
6205 ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
6206 ESCRIPT_EMU_ARGS ?= -pa . \
6207 -sasl errlog_type error \
6208 -escript main $(ESCRIPT_NAME)
6209 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6210 ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
62116332
62126333 # Core targets.
62136334
62206341
62216342 # Plugin-specific targets.
62226343
6223 # Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
6224 # Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
6225 # Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
6226 # Software may only be used for the great good and the true happiness of all
6227 # sentient beings.
6228
6229 define ESCRIPT_RAW
6230 'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
6231 'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
6232 ' [F || F <- A, not filelib:is_dir(F) ] end,'\
6233 'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
6234 'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
6235 'Ez = fun(Escript) ->'\
6236 ' Static = Files([$(ESCRIPT_STATIC)]),'\
6237 ' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
6238 ' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
6239 ' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
6240 ' {archive, Archive, [memory]},'\
6241 ' {shebang, "$(ESCRIPT_SHEBANG)"},'\
6242 ' {comment, "$(ESCRIPT_COMMENT)"},'\
6243 ' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
6244 ' ]),'\
6245 ' file:change_mode(Escript, 8#755)'\
6246 'end,'\
6247 'Ez("$(ESCRIPT_FILE)"),'\
6248 'halt().'
6249 endef
6250
6251 ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
6252
6253 escript:: distclean-escript deps app
6254 $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
62556360
62566361 distclean-escript:
6257 $(gen_verbose) rm -f $(ESCRIPT_NAME)
6258
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
62596365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6260 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
62616366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
62626367
62636368 .PHONY: eunit apps-eunit
63216426
63226427 ifneq ($(ALL_APPS_DIRS),)
63236428 apps-eunit:
6324 $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done
6325 endif
6326 endif
6327
6328 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
63296436 # This file is part of erlang.mk and subject to the terms of the ISC License.
63306437
6331 .PHONY: relx-rel distclean-relx-rel distclean-relx run
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
63326439
63336440 # Configuration.
63346441
6335 RELX ?= $(CURDIR)/relx
6442 RELX ?= $(ERLANG_MK_TMP)/relx
63366443 RELX_CONFIG ?= $(CURDIR)/relx.config
63376444
63386445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
63396446 RELX_OPTS ?=
63406447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
63416454
63426455 ifeq ($(firstword $(RELX_OPTS)),-o)
63436456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
63506463 ifeq ($(IS_DEP),)
63516464 ifneq ($(wildcard $(RELX_CONFIG)),)
63526465 rel:: relx-rel
6353 endif
6354 endif
6355
6356 distclean:: distclean-relx-rel distclean-relx
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
63576472
63586473 # Plugin-specific targets.
63596474
63626477 $(verbose) chmod +x $(RELX)
63636478
63646479 relx-rel: $(RELX) rel-deps app
6365 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
63666484
63676485 distclean-relx-rel:
63686486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6369
6370 distclean-relx:
6371 $(gen_verbose) rm -rf $(RELX)
63726487
63736488 # Run target.
63746489
63776492 else
63786493
63796494 define get_relx_release.erl
6380 {ok, Config} = file:consult("$(RELX_CONFIG)"),
6381 {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
6382 io:format("~s", [Name]),
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
63836504 halt(0).
63846505 endef
63856506
6386 RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
63876514
63886515 run: all
6389 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
63906517
63916518 help::
63926519 $(verbose) printf "%s\n" "" \
63956522
63966523 endif
63976524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
63986526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6399 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
64006527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
64016528
64026529 .PHONY: shell
64216548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
64226549
64236550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6424 $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
64256552
64266553 shell: build-shell-deps
64276554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
64286555
6429 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
64306571 # This file is part of erlang.mk and subject to the terms of the ISC License.
64316572
64326573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
64376578 tests:: triq
64386579
64396580 define triq_check.erl
6440 code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
64416582 try
64426583 case $(1) of
64436584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
64696610 endif
64706611 endif
64716612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
64726614 # Copyright (c) 2015, Erlang Solutions Ltd.
64736615 # This file is part of erlang.mk and subject to the terms of the ISC License.
64746616
64856627 XREFR ?= $(CURDIR)/xrefr
64866628 export XREFR
64876629
6488 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
64896631
64906632 # Core targets.
64916633
64926634 help::
6493 $(verbose) printf "%s\n" "" \
6494 "Xref targets:" \
6495 " xref Run Xrefr using $XREF_CONFIG as config file if defined"
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
64966638
64976639 distclean:: distclean-xref
64986640
65086650 distclean-xref:
65096651 $(gen_verbose) rm -rf $(XREFR)
65106652
6511 # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
65126655 # This file is part of erlang.mk and subject to the terms of the ISC License.
65136656
65146657 COVER_REPORT_DIR = cover
65176660
65186661 ifdef COVER
65196662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
65206664 # All modules in 'ebin'
65216665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
65226666
65316675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
65326676 endif
65336677 endif
6678 endif
65346679
65356680 # Core targets
65366681
65526697 "Cover targets:" \
65536698 " cover-report Generate a HTML coverage report from previously collected" \
65546699 " cover data." \
6555 " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
65566701 "" \
65576702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
65586703 "target tests additionally generates a HTML coverage report from the combined" \
65656710
65666711 .PHONY: coverdata-clean
65676712 coverdata-clean:
6568 $(gen_verbose) rm -f *.coverdata ct.cover.spec
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
65696714
65706715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
65716721 all.coverdata: $(COVERDATA)
6572 $(gen_verbose) $(ERL) -eval ' \
6573 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
6574 cover:export("$@"), halt(0).'
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
65756723
65766724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
65776725 # empty if you want the coverdata files but not the HTML report.
65896737 # Modules which include eunit.hrl always contain one line without coverage
65906738 # because eunit defines test/0 which is never called. We compensate for this.
65916739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6592 grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
65936741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
65946742
65956743 define cover_report.erl
66246772 endef
66256773
66266774 cover-report:
6627 $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
66286776 $(gen_verbose) $(call erlang,$(cover_report.erl))
66296777
66306778 endif
66316779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
66326839
66336840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
66346841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
66976904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
66986905 endif
66996906 ifndef IS_APP
6700 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
67016908 $(MAKE) -C $$dep $@ \
67026909 IS_APP=1 \
6703 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6704 || exit $$?; \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67056911 done
67066912 endif
6707 $(verbose) for dep in $^ ; do \
6913 $(verbose) set -e; for dep in $^ ; do \
67086914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
67096915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6710 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
67116917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
67126918 $(MAKE) -C $$dep fetch-deps \
67136919 IS_DEP=1 \
6714 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6715 || exit $$?; \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67166921 fi \
67176922 fi \
67186923 done
22 # before the inclusion of erlang.mk leading to the wrong target becoming
33 # the default.
44 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
526 endif
627
728 # --------------------------------------------------------------------
2041 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
2142 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
2243 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
2345 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
2446 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
2547 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
2648 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
2749 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
2851 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
2952 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
3054 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
3155 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
3256 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
3559 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
3660 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
3761 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
3863 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
3964 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
4065 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
7095
7196 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
7297
73 # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
74 # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
75 # needs to add "ranch" as a BUILD_DEPS. The list of projects needing
76 # this workaround are:
77 # o rabbitmq-web-stomp
78 dep_ranch = git https://github.com/ninenines/ranch 1.2.1
98 # Third-party dependencies version pinning.
99 #
100 # We do that in this file, which is copied in all projects, to ensure
101 # all projects use the same versions. It avoids conflicts and makes it
102 # possible to work with rabbitmq-public-umbrella.
103
104 dep_cowboy_commit = 1.0.4
105 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
106 dep_ranch_commit = 1.3.1
107 dep_webmachine_commit = 1.10.8p2
79108
80109 RABBITMQ_COMPONENTS = amqp_client \
81110 rabbit \
82111 rabbit_common \
83112 rabbitmq_amqp1_0 \
84113 rabbitmq_auth_backend_amqp \
114 rabbitmq_auth_backend_cache \
85115 rabbitmq_auth_backend_http \
86116 rabbitmq_auth_backend_ldap \
87117 rabbitmq_auth_mechanism_ssl \
88118 rabbitmq_boot_steps_visualiser \
89119 rabbitmq_clusterer \
120 rabbitmq_cli \
90121 rabbitmq_codegen \
91122 rabbitmq_consistent_hash_exchange \
123 rabbitmq_ct_client_helpers \
92124 rabbitmq_ct_helpers \
93125 rabbitmq_delayed_message_exchange \
94126 rabbitmq_dotnet_client \
97129 rabbitmq_federation_management \
98130 rabbitmq_java_client \
99131 rabbitmq_jms_client \
132 rabbitmq_jms_cts \
100133 rabbitmq_jms_topic_exchange \
101134 rabbitmq_lvc \
102135 rabbitmq_management \
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_shovel_mgmt).
1818 -behaviour(rabbit_mgmt_extension).
1919
2020 -export([dispatcher/0, web_ui/0]).
21 -export([init/1, to_json/2, resource_exists/2, content_types_provided/2,
21 -export([init/3, rest_init/2, to_json/2, resource_exists/2, content_types_provided/2,
2222 is_authorized/2]).
2323
2424 -import(rabbit_misc, [pget/2]).
2525
26 -include_lib("rabbitmq_management/include/rabbit_mgmt.hrl").
26 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2727 -include_lib("amqp_client/include/amqp_client.hrl").
28 -include_lib("webmachine/include/webmachine.hrl").
2928
30 dispatcher() -> [{["shovels"], ?MODULE, []},
31 {["shovels", vhost], ?MODULE, []}].
29 dispatcher() -> [{"/shovels", ?MODULE, []},
30 {"/shovels/:vhost", ?MODULE, []}].
3231 web_ui() -> [{javascript, <<"shovel.js">>}].
3332
3433 %%--------------------------------------------------------------------
3534
36 init(_Config) -> {ok, #context{}}.
35 init(_, _, _) ->
36 {upgrade, protocol, cowboy_rest}.
37
38 rest_init(Req, _Opts) ->
39 {ok, Req, #context{}}.
3740
3841 content_types_provided(ReqData, Context) ->
39 {[{"application/json", to_json}], ReqData, Context}.
42 {[{<<"application/json">>, to_json}], ReqData, Context}.
4043
4144 resource_exists(ReqData, Context) ->
4245 {case rabbit_mgmt_util:vhost(ReqData) of
+0
-6
deps/rabbitmq_shovel_management/src/rabbitmq_shovel_management.app.src less more
0 {application, rabbitmq_shovel_management,
1 [{description, "Shovel Status"},
2 {vsn, "3.6.6"},
3 {modules, []},
4 {registered, []},
5 {applications, [kernel, stdlib, rabbit_common, rabbit, rabbitmq_management]}]}.
00 PROJECT = rabbitmq_stomp
1 PROJECT_DESCRIPTION = RabbitMQ STOMP plugin
2 PROJECT_MOD = rabbit_stomp
3
4 define PROJECT_ENV
5 [
6 {default_user,
7 [{login, <<"guest">>},
8 {passcode, <<"guest">>}]},
9 {default_vhost, <<"/">>},
10 {ssl_cert_login, false},
11 {implicit_connect, false},
12 {tcp_listeners, [61613]},
13 {num_tcp_acceptors, 10},
14 {ssl_listeners, []},
15 {num_ssl_acceptors, 1},
16 {tcp_listen_options, [{backlog, 128},
17 {nodelay, true}]},
18 %% see rabbitmq/rabbitmq-stomp#39
19 {trailing_lf, true},
20 %% see rabbitmq/rabbitmq-stomp#57
21 {hide_server_info, false}
22 ]
23 endef
124
225 DEPS = ranch rabbit_common rabbit amqp_client
3 TEST_DEPS = rabbitmq_ct_helpers
26 TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers
427
28 DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
529 DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk
630
731 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
0 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
11 #
22 # Permission to use, copy, modify, and/or distribute this software for any
33 # purpose with or without fee is hereby granted, provided that the above
1111 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1212 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1313
14 .PHONY: all app apps deps search rel docs install-docs check tests clean distclean help erlang-mk
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
1515
1616 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17
18 ERLANG_MK_VERSION = 2.0.0-pre.2-144-g647ffd1
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
1931
2032 # Core configuration.
2133
2436
2537 PROJECT_VERSION ?= rolling
2638 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
2740
2841 # Verbosity.
2942
8497 rel::
8598 $(verbose) :
8699
100 relup:: deps app
101
87102 check:: tests
88103
89104 clean:: clean-crashdump
101116 help::
102117 $(verbose) printf "%s\n" \
103118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
104 "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
105120 "" \
106121 "Usage: [V=1] $(MAKE) [target]..." \
107122 "" \
149164 core_native_path = $1
150165 endif
151166
152 ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
153 define core_http_get
154 wget --no-check-certificate -O $(1) $(2)|| rm $(1)
155 endef
156 else
157 define core_http_get.erl
158 ssl:start(),
159 inets:start(),
160 case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
161 {ok, {{_, 200, _}, _, Body}} ->
162 case file:write_file("$(1)", Body) of
163 ok -> ok;
164 {error, R1} -> halt(R1)
165 end;
166 {error, R2} ->
167 halt(R2)
168 end,
169 halt(0).
170 endef
171
172 define core_http_get
173 $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
174 endef
175 endif
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
176168
177169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
178170
192184 ERLANG_MK_BUILD_CONFIG ?= build.config
193185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
194186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
195188 erlang-mk:
196189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
197190 ifdef ERLANG_MK_COMMIT
198191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
199192 endif
200193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
201 $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
202195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
203196 rm -rf $(ERLANG_MK_BUILD_DIR)
204197
285278 pkg_apns_repo = https://github.com/inaka/apns4erl
286279 pkg_apns_commit = master
287280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
288289 PACKAGES += azdht
289290 pkg_azdht_name = azdht
290291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
405406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
406407 pkg_bootstrap_commit = master
407408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
408417 PACKAGES += boss_db
409418 pkg_boss_db_name = boss_db
410419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
413422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
414423 pkg_boss_db_commit = master
415424
416 PACKAGES += boss
417 pkg_boss_name = boss
418 pkg_boss_description = Erlang web MVC, now featuring Comet
419 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
420 pkg_boss_fetch = git
421 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
422 pkg_boss_commit = master
423
424425 PACKAGES += brod
425426 pkg_brod_name = brod
426427 pkg_brod_description = Kafka client in Erlang
565566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
566567 pkg_cloudi_service_api_requests_commit = master
567568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
568585 PACKAGES += cloudi_service_db_cassandra_cql
569586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
570587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
573590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
574591 pkg_cloudi_service_db_cassandra_cql_commit = master
575592
576 PACKAGES += cloudi_service_db_cassandra
577 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
579 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
580 pkg_cloudi_service_db_cassandra_fetch = git
581 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
582 pkg_cloudi_service_db_cassandra_commit = master
583
584593 PACKAGES += cloudi_service_db_couchdb
585594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
586595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
637646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
638647 pkg_cloudi_service_db_tokyotyrant_commit = master
639648
640 PACKAGES += cloudi_service_db
641 pkg_cloudi_service_db_name = cloudi_service_db
642 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
643 pkg_cloudi_service_db_homepage = http://cloudi.org/
644 pkg_cloudi_service_db_fetch = git
645 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
646 pkg_cloudi_service_db_commit = master
647
648649 PACKAGES += cloudi_service_filesystem
649650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
650651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
10371038 pkg_edown_repo = https://github.com/uwiger/edown
10381039 pkg_edown_commit = master
10391040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
10401049 PACKAGES += eep_app
10411050 pkg_eep_app_name = eep_app
10421051 pkg_eep_app_description = Embedded Event Processing
10451054 pkg_eep_app_repo = https://github.com/darach/eep-erl
10461055 pkg_eep_app_commit = master
10471056
1048 PACKAGES += eep
1049 pkg_eep_name = eep
1050 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1051 pkg_eep_homepage = https://github.com/virtan/eep
1052 pkg_eep_fetch = git
1053 pkg_eep_repo = https://github.com/virtan/eep
1054 pkg_eep_commit = master
1055
10561057 PACKAGES += efene
10571058 pkg_efene_name = efene
10581059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
12371238 pkg_eqm_repo = https://github.com/loucash/eqm
12381239 pkg_eqm_commit = master
12391240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
12401249 PACKAGES += eredis_pool
12411250 pkg_eredis_pool_name = eredis_pool
12421251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
12451254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
12461255 pkg_eredis_pool_commit = master
12471256
1248 PACKAGES += eredis
1249 pkg_eredis_name = eredis
1250 pkg_eredis_description = Erlang Redis client
1251 pkg_eredis_homepage = https://github.com/wooga/eredis
1252 pkg_eredis_fetch = git
1253 pkg_eredis_repo = https://github.com/wooga/eredis
1254 pkg_eredis_commit = master
1255
12561257 PACKAGES += erl_streams
12571258 pkg_erl_streams_name = erl_streams
12581259 pkg_erl_streams_description = Streams in Erlang
15411542 pkg_etap_repo = https://github.com/ngerakines/etap
15421543 pkg_etap_commit = master
15431544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
15441553 PACKAGES += etest_http
15451554 pkg_etest_http_name = etest_http
15461555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
15491558 pkg_etest_http_repo = https://github.com/wooga/etest_http
15501559 pkg_etest_http_commit = master
15511560
1552 PACKAGES += etest
1553 pkg_etest_name = etest
1554 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1555 pkg_etest_homepage = https://github.com/wooga/etest
1556 pkg_etest_fetch = git
1557 pkg_etest_repo = https://github.com/wooga/etest
1558 pkg_etest_commit = master
1559
15601561 PACKAGES += etoml
15611562 pkg_etoml_name = etoml
15621563 pkg_etoml_description = TOML language erlang parser
15651566 pkg_etoml_repo = https://github.com/kalta/etoml
15661567 pkg_etoml_commit = master
15671568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
15681577 PACKAGES += eunit_formatters
15691578 pkg_eunit_formatters_name = eunit_formatters
15701579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
15731582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
15741583 pkg_eunit_formatters_commit = master
15751584
1576 PACKAGES += eunit
1577 pkg_eunit_name = eunit
1578 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1579 pkg_eunit_homepage = https://github.com/richcarl/eunit
1580 pkg_eunit_fetch = git
1581 pkg_eunit_repo = https://github.com/richcarl/eunit
1582 pkg_eunit_commit = master
1583
15841585 PACKAGES += euthanasia
15851586 pkg_euthanasia_name = euthanasia
15861587 pkg_euthanasia_description = Merciful killer for your Erlang processes
15981599 pkg_evum_commit = master
15991600
16001601 PACKAGES += exec
1601 pkg_exec_name = exec
1602 pkg_exec_name = erlexec
16021603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
16031604 pkg_exec_homepage = http://saleyn.github.com/erlexec
16041605 pkg_exec_fetch = git
17171718 pkg_fn_repo = https://github.com/reiddraper/fn
17181719 pkg_fn_commit = master
17191720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
17201729 PACKAGES += folsom_cowboy
17211730 pkg_folsom_cowboy_name = folsom_cowboy
17221731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
17251734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
17261735 pkg_folsom_cowboy_commit = master
17271736
1728 PACKAGES += folsom
1729 pkg_folsom_name = folsom
1730 pkg_folsom_description = Expose Erlang Events and Metrics
1731 pkg_folsom_homepage = https://github.com/boundary/folsom
1732 pkg_folsom_fetch = git
1733 pkg_folsom_repo = https://github.com/boundary/folsom
1734 pkg_folsom_commit = master
1735
17361737 PACKAGES += folsomite
17371738 pkg_folsomite_name = folsomite
17381739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
20932094 pkg_jesse_repo = https://github.com/for-GET/jesse
20942095 pkg_jesse_commit = master
20952096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
20962105 PACKAGES += jiffy_v
20972106 pkg_jiffy_v_name = jiffy_v
20982107 pkg_jiffy_v_description = JSON validation utility
21012110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
21022111 pkg_jiffy_v_commit = master
21032112
2104 PACKAGES += jiffy
2105 pkg_jiffy_name = jiffy
2106 pkg_jiffy_description = JSON NIFs for Erlang.
2107 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2108 pkg_jiffy_fetch = git
2109 pkg_jiffy_repo = https://github.com/davisp/jiffy
2110 pkg_jiffy_commit = master
2111
21122113 PACKAGES += jobs
21132114 pkg_jobs_name = jobs
21142115 pkg_jobs_description = a Job scheduler for load regulation
21252126 pkg_joxa_repo = https://github.com/joxa/joxa
21262127 pkg_joxa_commit = master
21272128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
21282137 PACKAGES += json_rec
21292138 pkg_json_rec_name = json_rec
21302139 pkg_json_rec_description = JSON to erlang record
21332142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
21342143 pkg_json_rec_commit = master
21352144
2136 PACKAGES += json
2137 pkg_json_name = json
2138 pkg_json_description = a high level json library for erlang (17.0+)
2139 pkg_json_homepage = https://github.com/talentdeficit/json
2140 pkg_json_fetch = git
2141 pkg_json_repo = https://github.com/talentdeficit/json
2142 pkg_json_commit = master
2143
21442145 PACKAGES += jsone
21452146 pkg_jsone_name = jsone
21462147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
21812182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
21822183 pkg_jsx_commit = master
21832184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
21842193 PACKAGES += kafka_protocol
21852194 pkg_kafka_protocol_name = kafka_protocol
21862195 pkg_kafka_protocol_description = Kafka protocol Erlang library
21892198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
21902199 pkg_kafka_protocol_commit = master
21912200
2192 PACKAGES += kafka
2193 pkg_kafka_name = kafka
2194 pkg_kafka_description = Kafka consumer and producer in Erlang
2195 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2196 pkg_kafka_fetch = git
2197 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2198 pkg_kafka_commit = master
2199
22002201 PACKAGES += kai
22012202 pkg_kai_name = kai
22022203 pkg_kai_description = DHT storage by Takeshi Inoue
22932294 pkg_kvs_repo = https://github.com/synrc/kvs
22942295 pkg_kvs_commit = master
22952296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
22962305 PACKAGES += lager_amqp_backend
22972306 pkg_lager_amqp_backend_name = lager_amqp_backend
22982307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
23092318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
23102319 pkg_lager_syslog_commit = master
23112320
2312 PACKAGES += lager
2313 pkg_lager_name = lager
2314 pkg_lager_description = A logging framework for Erlang/OTP.
2315 pkg_lager_homepage = https://github.com/basho/lager
2316 pkg_lager_fetch = git
2317 pkg_lager_repo = https://github.com/basho/lager
2318 pkg_lager_commit = master
2319
23202321 PACKAGES += lambdapad
23212322 pkg_lambdapad_name = lambdapad
23222323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
25732574 pkg_mixer_repo = https://github.com/chef/mixer
25742575 pkg_mixer_commit = master
25752576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
25762585 PACKAGES += mochiweb_xpath
25772586 pkg_mochiweb_xpath_name = mochiweb_xpath
25782587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
25812590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
25822591 pkg_mochiweb_xpath_commit = master
25832592
2584 PACKAGES += mochiweb
2585 pkg_mochiweb_name = mochiweb
2586 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2587 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2588 pkg_mochiweb_fetch = git
2589 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2590 pkg_mochiweb_commit = master
2591
25922593 PACKAGES += mockgyver
25932594 pkg_mockgyver_name = mockgyver
25942595 pkg_mockgyver_description = A mocking library for Erlang
30613062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
30623063 pkg_quickrand_commit = master
30633064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
30643073 PACKAGES += rabbit_exchange_type_riak
30653074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
30663075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
30693078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
30703079 pkg_rabbit_exchange_type_riak_commit = master
30713080
3072 PACKAGES += rabbit
3073 pkg_rabbit_name = rabbit
3074 pkg_rabbit_description = RabbitMQ Server
3075 pkg_rabbit_homepage = https://www.rabbitmq.com/
3076 pkg_rabbit_fetch = git
3077 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3078 pkg_rabbit_commit = master
3079
30803081 PACKAGES += rack
30813082 pkg_rack_name = rack
30823083 pkg_rack_description = Rack handler for erlang
34933494 pkg_smother_repo = https://github.com/ramsay-t/Smother
34943495 pkg_smother_commit = master
34953496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
34963505 PACKAGES += social
34973506 pkg_social_name = social
34983507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
35413550 pkg_stable_repo = https://github.com/dvv/stable
35423551 pkg_stable_commit = master
35433552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
35443561 PACKAGES += statebox_riak
35453562 pkg_statebox_riak_name = statebox_riak
35463563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
35493566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
35503567 pkg_statebox_riak_commit = master
35513568
3552 PACKAGES += statebox
3553 pkg_statebox_name = statebox
3554 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3555 pkg_statebox_homepage = https://github.com/mochi/statebox
3556 pkg_statebox_fetch = git
3557 pkg_statebox_repo = https://github.com/mochi/statebox
3558 pkg_statebox_commit = master
3559
35603569 PACKAGES += statman
35613570 pkg_statman_name = statman
35623571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
37253734 pkg_tirerl_repo = https://github.com/inaka/tirerl
37263735 pkg_tirerl_commit = master
37273736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
37283745 PACKAGES += traffic_tools
37293746 pkg_traffic_tools_name = traffic_tools
37303747 pkg_traffic_tools_description = Simple traffic limiting library
40614078 pkg_zucchini_repo = https://github.com/devinus/zucchini
40624079 pkg_zucchini_commit = master
40634080
4064 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
40654082 # This file is part of erlang.mk and subject to the terms of the ISC License.
40664083
40674084 .PHONY: search
40884105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
40894106 endif
40904107
4091 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
40924109 # This file is part of erlang.mk and subject to the terms of the ISC License.
40934110
4094 .PHONY: distclean-deps
4111 .PHONY: distclean-deps clean-tmp-deps.log
40954112
40964113 # Configuration.
40974114
41104127
41114128 REBAR_DEPS_DIR = $(DEPS_DIR)
41124129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
41134150
41144151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
41154152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
41164153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
41174154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
41184155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
41194157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
41204158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
41214159
41384176
41394177 # Core targets.
41404178
4141 ifdef IS_APP
4142 apps::
4143 else
4144 apps:: $(ALL_APPS_DIRS)
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
41454180 ifeq ($(IS_APP)$(IS_DEP),)
41464181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
41474182 endif
41494184 # Create ebin directory for all apps to make sure Erlang recognizes them
41504185 # as proper OTP applications when using -include_lib. This is a temporary
41514186 # fix, a proper fix would be to compile apps/* in the right order.
4152 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4153 mkdir -p $$dep/ebin || exit $$?; \
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
41544190 done
4155 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
41564196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
41574197 :; \
41584198 else \
41594199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4160 $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
4200 $(MAKE) -C $$dep IS_APP=1; \
41614201 fi \
41624202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
41634207 endif
41644208
41654209 ifneq ($(SKIP_DEPS),)
41664210 deps::
41674211 else
4168 deps:: $(ALL_DEPS_DIRS) apps
4169 ifeq ($(IS_APP)$(IS_DEP),)
4170 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4171 endif
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
41724213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4173 $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
41744215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
41754216 :; \
41764217 else \
41774218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
41784219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4179 $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
4220 $(MAKE) -C $$dep IS_DEP=1; \
41804221 else \
4181 echo "Error: No Makefile to build dependency $$dep."; \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
41824223 exit 2; \
41834224 fi \
41844225 fi \
41924233 # in practice only Makefile is needed so far.
41934234 define dep_autopatch
41944235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
41954237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
41964238 $(call dep_autopatch_erlang_mk,$(1)); \
41974239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4198 if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
41994243 $(call dep_autopatch2,$(1)); \
4200 elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
42014245 $(call dep_autopatch2,$(1)); \
4202 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
42034247 $(call dep_autopatch2,$(1)); \
4204 else \
4205 $(call erlang,$(call dep_autopatch_app.erl,$(1))); \
42064248 fi \
42074249 else \
42084250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
42144256 endef
42154257
42164258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
42174262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
42184263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
42194264 fi; \
42204265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4221 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
42224267 $(call dep_autopatch_fetch_rebar); \
42234268 $(call dep_autopatch_rebar,$(1)); \
42244269 else \
42304275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
42314276 endef
42324277
4233 # Overwrite erlang.mk with the current file by default.
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
42344280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
42354281 define dep_autopatch_erlang_mk
4236 echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \
4237 > $(DEPS_DIR)/$1/erlang.mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
42384287 endef
42394288 else
42404289 define dep_autopatch_erlang_mk
42534302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
42544303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
42554304 cd $(ERLANG_MK_TMP)/rebar; \
4256 git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
42574306 $(MAKE); \
42584307 cd -; \
42594308 fi
42704319 define dep_autopatch_rebar.erl
42714320 application:load(rebar),
42724321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
42734323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
42744324 {ok, Conf0} -> Conf0;
42754325 _ -> []
44234473 [] -> ok;
44244474 _ ->
44254475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4426 PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
44274477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4428 PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L \\"~s\\" -lerl_interface -lei\n",
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
44294479 [code:lib_dir(erl_interface, lib)])),
44304480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
44314481 FilterEnv = fun(Env) ->
44644514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44654515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44664516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4467 [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
44684518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
44694519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
44704520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
44764526 end,
44774527 [PortSpec(S) || S <- PortSpecs]
44784528 end,
4479 Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"),
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
44804530 RunPlugin = fun(Plugin, Step) ->
44814531 case erlang:function_exported(Plugin, Step, 2) of
44824532 false -> ok;
45244574 halt()
45254575 endef
45264576
4527 define dep_autopatch_app.erl
4528 UpdateModules = fun(App) ->
4529 case filelib:is_regular(App) of
4530 false -> ok;
4531 true ->
4532 {ok, [{application, '$(1)', L0}]} = file:consult(App),
4533 Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true,
4534 fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
4535 L = lists:keystore(modules, 1, L0, {modules, Mods}),
4536 ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}]))
4537 end
4538 end,
4539 UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"),
4540 halt()
4541 endef
4542
45434577 define dep_autopatch_appsrc_script.erl
45444578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
45454579 AppSrcScript = AppSrc ++ ".script",
45874621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
45884622 endef
45894623
4590 define dep_fetch_hex.erl
4591 ssl:start(),
4592 inets:start(),
4593 {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
4594 {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
4595 [], [{body_format, binary}]),
4596 {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
4597 {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
4598 ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
4599 halt()
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
46004626 endef
46014627
46024628 # Hex only has a package version. No need to look in the Erlang.mk packages.
46034629 define dep_fetch_hex
4604 $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
46054634 endef
46064635
46074636 define dep_fetch_fail
46314660 $(eval DEP_NAME := $(call dep_name,$1))
46324661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
46334662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4634 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
46354664 exit 17; \
46364665 fi
46374666 $(verbose) mkdir -p $(DEPS_DIR)
46734702 clean:: clean-apps
46744703
46754704 clean-apps:
4676 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4677 $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
46784707 done
46794708
46804709 distclean:: distclean-apps
46814710
46824711 distclean-apps:
4683 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4684 $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
46854714 done
46864715 endif
46874716
47014730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
47024731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
47034732
4704 # External plugins.
4705
4706 DEP_PLUGINS ?=
4707
4708 define core_dep_plugin
4709 -include $(DEPS_DIR)/$(1)
4710
4711 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4712 endef
4713
4714 $(foreach p,$(DEP_PLUGINS),\
4715 $(eval $(if $(findstring /,$p),\
4716 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4717 $(call core_dep_plugin,$p/plugins.mk,$p))))
4718
4719 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4720 # This file is part of erlang.mk and subject to the terms of the ISC License.
4721
4722 # Configuration.
4723
4724 DTL_FULL_PATH ?=
4725 DTL_PATH ?= templates/
4726 DTL_SUFFIX ?= _dtl
4727 DTL_OPTS ?=
4728
4729 # Verbosity.
4730
4731 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
4732 dtl_verbose = $(dtl_verbose_$(V))
4733
4734 # Core targets.
4735
4736 DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl))
4737
4738 ifneq ($(DTL_FILES),)
4739
4740 ifdef DTL_FULL_PATH
4741 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
4742 else
4743 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
4744 endif
4745
4746 ifneq ($(words $(DTL_FILES)),0)
4747 # Rebuild templates when the Makefile changes.
4748 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
4749 @mkdir -p $(ERLANG_MK_TMP)
4750 @if test -f $@; then \
4751 touch $(DTL_FILES); \
4752 fi
4753 @touch $@
4754
4755 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
4756 endif
4757
4758 define erlydtl_compile.erl
4759 [begin
4760 Module0 = case "$(strip $(DTL_FULL_PATH))" of
4761 "" ->
4762 filename:basename(F, ".dtl");
4763 _ ->
4764 "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
4765 re:replace(F2, "/", "_", [{return, list}, global])
4766 end,
4767 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
4768 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
4769 ok -> ok;
4770 {ok, _} -> ok
4771 end
4772 end || F <- string:tokens("$(1)", " ")],
4773 halt().
4774 endef
4775
4776 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
4777 $(if $(strip $?),\
4778 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
4779
4780 endif
4781
4782 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
47834734 # This file is part of erlang.mk and subject to the terms of the ISC License.
47844735
47854736 # Verbosity.
47984749
47994750 define compile_proto.erl
48004751 [begin
4801 Dir = filename:dirname(filename:dirname(F)),
48024752 protobuffs_compile:generate_source(F,
4803 [{output_include_dir, Dir ++ "/include"},
4804 {output_src_dir, Dir ++ "/ebin"}])
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
48054755 end || F <- string:tokens("$(1)", " ")],
48064756 halt().
48074757 endef
48114761 $(if $(strip $?),$(call compile_proto,$?))
48124762 endif
48134763
4814 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
48154765 # This file is part of erlang.mk and subject to the terms of the ISC License.
48164766
48174767 .PHONY: clean-app
48254775 ERLC_EXCLUDE ?=
48264776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
48274777
4778 ERLC_ASN1_OPTS ?=
4779
48284780 ERLC_MIB_OPTS ?=
48294781 COMPILE_MIB_FIRST ?=
48304782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
48744826
48754827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
48764828 define app_file
4877 {application, $(PROJECT), [
4829 {application, '$(PROJECT)', [
48784830 {description, "$(PROJECT_DESCRIPTION)"},
48794831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48804832 {id$(comma)$(space)"$(1)"}$(comma))
48814833 {modules, [$(call comma_list,$(2))]},
48824834 {registered, []},
4883 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48844837 ]}.
48854838 endef
48864839 else
48874840 define app_file
4888 {application, $(PROJECT), [
4841 {application, '$(PROJECT)', [
48894842 {description, "$(PROJECT_DESCRIPTION)"},
48904843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48914844 {id$(comma)$(space)"$(1)"}$(comma))
48924845 {modules, [$(call comma_list,$(2))]},
48934846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
48944847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4895 {mod, {$(PROJECT_MOD), []}}
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48964850 ]}.
48974851 endef
48984852 endif
49024856
49034857 # Source files.
49044858
4905 ERL_FILES = $(sort $(call core_find,src/,*.erl))
4906 CORE_FILES = $(sort $(call core_find,src/,*.core))
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
49074863
49084864 # ASN.1 files.
49094865
49134869
49144870 define compile_asn1
49154871 $(verbose) mkdir -p include/
4916 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
49174873 $(verbose) mv asn1/*.erl src/
49184874 $(verbose) mv asn1/*.hrl include/
49194875 $(verbose) mv asn1/*.asn1db include/
49364892
49374893 # Leex and Yecc files.
49384894
4939 XRL_FILES = $(sort $(call core_find,src/,*.xrl))
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
49404896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
49414897 ERL_FILES += $(XRL_ERL_FILES)
49424898
4943 YRL_FILES = $(sort $(call core_find,src/,*.yrl))
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
49444900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
49454901 ERL_FILES += $(YRL_ERL_FILES)
49464902
49474903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4948 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
49494905
49504906 # Erlang and Core Erlang files.
49514907
49954951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49964952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49974953 (F, Mod, import, {Imp, _}) ->
4998 case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
49994960 false -> ok;
50004961 true -> Add(Mod, Imp)
50014962 end;
50194980 end || F <- ErlFiles],
50204981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
50214982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
50224991 ok = file:write_file("$(1)", [
50234992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
5024 "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n"
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
50254994 ]),
50264995 halt()
50274996 endef
50345003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
50355004 # Rebuild everything when the Makefile changes.
50365005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5037 @mkdir -p $(ERLANG_MK_TMP)
5038 @if test -f $@; then \
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
50395008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
50405009 touch -c $(PROJECT).d; \
50415010 fi
5042 @touch $@
5011 $(verbose) touch $@
50435012
50445013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
50455014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
50465015 endif
50475016
5048 -include $(PROJECT).d
5017 include $(wildcard $(PROJECT).d)
50495018
50505019 ebin/$(PROJECT).app:: ebin/
50515020
50645033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
50655034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
50665035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5067 $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
50685037 > ebin/$(PROJECT).app
50695038 else
50705039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
50885057
50895058 endif
50905059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
50915061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
50925062 # This file is part of erlang.mk and subject to the terms of the ISC License.
50935063
51055075 doc-deps:
51065076 else
51075077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5108 $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5109 endif
5110
5111 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51125082 # This file is part of erlang.mk and subject to the terms of the ISC License.
51135083
51145084 .PHONY: rel-deps
51255095 rel-deps:
51265096 else
51275097 rel-deps: $(ALL_REL_DEPS_DIRS)
5128 $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5129 endif
5130
5131 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51325102 # This file is part of erlang.mk and subject to the terms of the ISC License.
51335103
51345104 .PHONY: test-deps test-dir test-build clean-test-dir
51505120 test-deps:
51515121 else
51525122 test-deps: $(ALL_TEST_DEPS_DIRS)
5153 $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
51545124 endif
51555125
51565126 ifneq ($(wildcard $(TEST_DIR)),)
51835153 endif
51845154 endif
51855155
5186 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51875157 # This file is part of erlang.mk and subject to the terms of the ISC License.
51885158
51895159 .PHONY: rebar.config
52195189 rebar.config:
52205190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
52215191
5222 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
52235193 # This file is part of erlang.mk and subject to the terms of the ISC License.
52245194
5225 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
5226
5227 MAN_INSTALL_PATH ?= /usr/local/share/man
5228 MAN_SECTIONS ?= 3 7
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
52295200
52305201 docs:: asciidoc
52315202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
52325207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
52335210
52345211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
52355212 asciidoc-guide:
52365213 else
5237 asciidoc-guide: distclean-asciidoc doc-deps
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
52385215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
52395216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5240 endif
5241
5242 ifeq ($(wildcard doc/src/manual/*.asciidoc),)
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
52435227 asciidoc-manual:
52445228 else
5245 asciidoc-manual: distclean-asciidoc doc-deps
5246 for f in doc/src/manual/*.asciidoc ; do \
5247 a2x -v -f manpage $$f ; \
5248 done
5249 for s in $(MAN_SECTIONS); do \
5250 mkdir -p doc/man$$s/ ; \
5251 mv doc/src/manual/*.$$s doc/man$$s/ ; \
5252 gzip doc/man$$s/*.$$s ; \
5253 done
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
52545262
52555263 install-docs:: install-asciidoc
52565264
52575265 install-asciidoc: asciidoc-manual
5258 for s in $(MAN_SECTIONS); do \
5259 mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
5260 install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
5261 done
5262 endif
5263
5264 distclean:: distclean-asciidoc
5265
5266 distclean-asciidoc:
5267 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
5268
5269 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
52705276 # This file is part of erlang.mk and subject to the terms of the ISC License.
52715277
52725278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
53235329 define bs_Makefile
53245330 PROJECT = $p
53255331 PROJECT_DESCRIPTION = New project
5326 PROJECT_VERSION = 0.0.1
5332 PROJECT_VERSION = 0.1.0
53275333
53285334 # Whitespace to be used when creating files from templates.
53295335 SP = $(SP)
53335339 define bs_Makefile
53345340 PROJECT = $p
53355341 PROJECT_DESCRIPTION = New project
5336 PROJECT_VERSION = 0.0.1
5342 PROJECT_VERSION = 0.1.0
53375343
53385344 endef
53395345 endif
53415347 define bs_apps_Makefile
53425348 PROJECT = $p
53435349 PROJECT_DESCRIPTION = New project
5344 PROJECT_VERSION = 0.0.1
5350 PROJECT_VERSION = 0.1.0
53455351
53465352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
53475353 endef
53615367 endef
53625368
53635369 define bs_relx_config
5364 {release, {$p_release, "1"}, [$p]}.
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
53655371 {extended_start_script, true}.
53665372 {sys_config, "rel/sys.config"}.
53675373 {vm_args, "rel/vm.args"}.
57125718 ifndef t
57135719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57145720 endif
5715 ifndef tpl_$(t)
5716 $(error Unknown template)
5717 endif
57185721 ifndef n
57195722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57205723 endif
57275730 list-templates:
57285731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
57295732
5730 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
57315734 # This file is part of erlang.mk and subject to the terms of the ISC License.
57325735
57335736 .PHONY: clean-c_src distclean-c_src-env
59625965 $(call render_template,bs_erl_nif,src/$n.erl)
59635966 endif
59645967
5965 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
59665969 # This file is part of erlang.mk and subject to the terms of the ISC License.
59675970
5968 .PHONY: ci ci-setup distclean-kerl
5969
5970 KERL ?= $(CURDIR)/kerl
5971 export KERL
5972
5973 KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
5974
5975 OTP_GIT ?= https://github.com/erlang/otp
5976
5977 CI_INSTALL_DIR ?= $(HOME)/erlang
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
59785973 CI_OTP ?=
5979
5980 ifeq ($(strip $(CI_OTP)),)
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
59815986 ci::
59825987 else
5983 ci:: $(addprefix ci-,$(CI_OTP))
5984
5985 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
59866007
59876008 ci-setup::
6009
6010 ci-extra::
59886011
59896012 ci_verbose_0 = @echo " CI " $(1);
59906013 ci_verbose = $(ci_verbose_$(V))
59916014
59926015 define ci_target
5993 ci-$(1): $(CI_INSTALL_DIR)/$(1)
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
59946018 $(ci_verbose) \
5995 PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
5996 CI_OTP_RELEASE="$(1)" \
5997 CT_OPTS="-label $(1)" \
5998 $(MAKE) clean ci-setup tests
5999 endef
6000
6001 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
60026030
60036031 define ci_otp_target
60046032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
60056033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6006 $(KERL) build git $(OTP_GIT) $(1) $(1)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
60076035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
60086036 endif
60096037 endef
60106038
60116039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
60126040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
60136052 $(KERL):
6014 $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
60156056 $(verbose) chmod +x $(KERL)
60166057
60176058 help::
60286069 $(gen_verbose) rm -rf $(KERL)
60296070 endif
60306071
6031 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
60326073 # This file is part of erlang.mk and subject to the terms of the ISC License.
60336074
60346075 .PHONY: ct apps-ct distclean-ct
60366077 # Configuration.
60376078
60386079 CT_OPTS ?=
6080
60396081 ifneq ($(wildcard $(TEST_DIR)),)
6040 CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6041 else
6042 CT_SUITES ?=
6043 endif
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
60446088
60456089 # Core targets.
60466090
60636107 -noinput \
60646108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
60656109 -dir $(TEST_DIR) \
6066 -logdir $(CURDIR)/logs
6110 -logdir $(CT_LOGS_DIR)
60676111
60686112 ifeq ($(CT_SUITES),)
60696113 ct: $(if $(IS_APP),,apps-ct)
60706114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
60716117 ct: test-build $(if $(IS_APP),,apps-ct)
6072 $(verbose) mkdir -p $(CURDIR)/logs/
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
60736119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
60746121 endif
60756122
60766123 ifneq ($(ALL_APPS_DIRS),)
60976144
60986145 define ct_suite_target
60996146 ct-$(1): test-build
6100 $(verbose) mkdir -p $(CURDIR)/logs/
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
61016148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
61026149 endef
61036150
61046151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
61056152
61066153 distclean-ct:
6107 $(gen_verbose) rm -rf $(CURDIR)/logs/
6108
6109 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61106157 # This file is part of erlang.mk and subject to the terms of the ISC License.
61116158
61126159 .PHONY: plt distclean-plt dialyze
61506197 endef
61516198
61526199 $(DIALYZER_PLT): deps app
6153 $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
61546204
61556205 plt: $(DIALYZER_PLT)
61566206
61646214 endif
61656215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
61666216
6167 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61686218 # This file is part of erlang.mk and subject to the terms of the ISC License.
61696219
61706220 .PHONY: distclean-edoc edoc
61726222 # Configuration.
61736223
61746224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
61756235
61766236 # Core targets.
61776237
6178 ifneq ($(wildcard doc/overview.edoc),)
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
61796239 docs:: edoc
61806240 endif
61816241
61846244 # Plugin-specific targets.
61856245
61866246 edoc: distclean-edoc doc-deps
6187 $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
61886248
61896249 distclean-edoc:
61906250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
61916251
6192 # Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61936253 # This file is part of erlang.mk and subject to the terms of the ISC License.
61946254
6195 .PHONY: distclean-escript escript
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
61966320
61976321 # Configuration.
61986322
61996323 ESCRIPT_NAME ?= $(PROJECT)
62006324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
62016325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
62026327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6203
6204 ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
6205 ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
6206 ESCRIPT_EMU_ARGS ?= -pa . \
6207 -sasl errlog_type error \
6208 -escript main $(ESCRIPT_NAME)
6209 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6210 ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
62116332
62126333 # Core targets.
62136334
62206341
62216342 # Plugin-specific targets.
62226343
6223 # Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
6224 # Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
6225 # Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
6226 # Software may only be used for the great good and the true happiness of all
6227 # sentient beings.
6228
6229 define ESCRIPT_RAW
6230 'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
6231 'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
6232 ' [F || F <- A, not filelib:is_dir(F) ] end,'\
6233 'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
6234 'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
6235 'Ez = fun(Escript) ->'\
6236 ' Static = Files([$(ESCRIPT_STATIC)]),'\
6237 ' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
6238 ' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
6239 ' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
6240 ' {archive, Archive, [memory]},'\
6241 ' {shebang, "$(ESCRIPT_SHEBANG)"},'\
6242 ' {comment, "$(ESCRIPT_COMMENT)"},'\
6243 ' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
6244 ' ]),'\
6245 ' file:change_mode(Escript, 8#755)'\
6246 'end,'\
6247 'Ez("$(ESCRIPT_FILE)"),'\
6248 'halt().'
6249 endef
6250
6251 ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
6252
6253 escript:: distclean-escript deps app
6254 $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
62556360
62566361 distclean-escript:
6257 $(gen_verbose) rm -f $(ESCRIPT_NAME)
6258
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
62596365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6260 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
62616366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
62626367
62636368 .PHONY: eunit apps-eunit
63216426
63226427 ifneq ($(ALL_APPS_DIRS),)
63236428 apps-eunit:
6324 $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done
6325 endif
6326 endif
6327
6328 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
63296436 # This file is part of erlang.mk and subject to the terms of the ISC License.
63306437
6331 .PHONY: relx-rel distclean-relx-rel distclean-relx run
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
63326439
63336440 # Configuration.
63346441
6335 RELX ?= $(CURDIR)/relx
6442 RELX ?= $(ERLANG_MK_TMP)/relx
63366443 RELX_CONFIG ?= $(CURDIR)/relx.config
63376444
63386445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
63396446 RELX_OPTS ?=
63406447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
63416454
63426455 ifeq ($(firstword $(RELX_OPTS)),-o)
63436456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
63506463 ifeq ($(IS_DEP),)
63516464 ifneq ($(wildcard $(RELX_CONFIG)),)
63526465 rel:: relx-rel
6353 endif
6354 endif
6355
6356 distclean:: distclean-relx-rel distclean-relx
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
63576472
63586473 # Plugin-specific targets.
63596474
63626477 $(verbose) chmod +x $(RELX)
63636478
63646479 relx-rel: $(RELX) rel-deps app
6365 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
63666484
63676485 distclean-relx-rel:
63686486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6369
6370 distclean-relx:
6371 $(gen_verbose) rm -rf $(RELX)
63726487
63736488 # Run target.
63746489
63776492 else
63786493
63796494 define get_relx_release.erl
6380 {ok, Config} = file:consult("$(RELX_CONFIG)"),
6381 {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
6382 io:format("~s", [Name]),
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
63836504 halt(0).
63846505 endef
63856506
6386 RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
63876514
63886515 run: all
6389 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
63906517
63916518 help::
63926519 $(verbose) printf "%s\n" "" \
63956522
63966523 endif
63976524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
63986526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6399 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
64006527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
64016528
64026529 .PHONY: shell
64216548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
64226549
64236550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6424 $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
64256552
64266553 shell: build-shell-deps
64276554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
64286555
6429 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
64306571 # This file is part of erlang.mk and subject to the terms of the ISC License.
64316572
64326573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
64376578 tests:: triq
64386579
64396580 define triq_check.erl
6440 code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
64416582 try
64426583 case $(1) of
64436584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
64696610 endif
64706611 endif
64716612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
64726614 # Copyright (c) 2015, Erlang Solutions Ltd.
64736615 # This file is part of erlang.mk and subject to the terms of the ISC License.
64746616
64856627 XREFR ?= $(CURDIR)/xrefr
64866628 export XREFR
64876629
6488 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
64896631
64906632 # Core targets.
64916633
64926634 help::
6493 $(verbose) printf "%s\n" "" \
6494 "Xref targets:" \
6495 " xref Run Xrefr using $XREF_CONFIG as config file if defined"
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
64966638
64976639 distclean:: distclean-xref
64986640
65086650 distclean-xref:
65096651 $(gen_verbose) rm -rf $(XREFR)
65106652
6511 # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
65126655 # This file is part of erlang.mk and subject to the terms of the ISC License.
65136656
65146657 COVER_REPORT_DIR = cover
65176660
65186661 ifdef COVER
65196662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
65206664 # All modules in 'ebin'
65216665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
65226666
65316675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
65326676 endif
65336677 endif
6678 endif
65346679
65356680 # Core targets
65366681
65526697 "Cover targets:" \
65536698 " cover-report Generate a HTML coverage report from previously collected" \
65546699 " cover data." \
6555 " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
65566701 "" \
65576702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
65586703 "target tests additionally generates a HTML coverage report from the combined" \
65656710
65666711 .PHONY: coverdata-clean
65676712 coverdata-clean:
6568 $(gen_verbose) rm -f *.coverdata ct.cover.spec
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
65696714
65706715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
65716721 all.coverdata: $(COVERDATA)
6572 $(gen_verbose) $(ERL) -eval ' \
6573 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
6574 cover:export("$@"), halt(0).'
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
65756723
65766724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
65776725 # empty if you want the coverdata files but not the HTML report.
65896737 # Modules which include eunit.hrl always contain one line without coverage
65906738 # because eunit defines test/0 which is never called. We compensate for this.
65916739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6592 grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
65936741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
65946742
65956743 define cover_report.erl
66246772 endef
66256773
66266774 cover-report:
6627 $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
66286776 $(gen_verbose) $(call erlang,$(cover_report.erl))
66296777
66306778 endif
66316779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
66326839
66336840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
66346841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
66976904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
66986905 endif
66996906 ifndef IS_APP
6700 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
67016908 $(MAKE) -C $$dep $@ \
67026909 IS_APP=1 \
6703 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6704 || exit $$?; \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67056911 done
67066912 endif
6707 $(verbose) for dep in $^ ; do \
6913 $(verbose) set -e; for dep in $^ ; do \
67086914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
67096915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6710 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
67116917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
67126918 $(MAKE) -C $$dep fetch-deps \
67136919 IS_DEP=1 \
6714 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6715 || exit $$?; \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67166921 fi \
67176922 fi \
67186923 done
2020 ssl_cert_login}).
2121
2222 -define(SUPPORTED_VERSIONS, ["1.0", "1.1", "1.2"]).
23
24 -define(INFO_ITEMS,
25 [conn_name,
26 connection,
27 connection_state,
28 session_id,
29 channel,
30 version,
31 ssl_cert_login,
32 implicit_connect,
33 default_login,
34 default_passcode,
35 ssl_login_name,
36 peer_addr,
37 host,
38 port,
39 peer_host,
40 peer_port,
41 protocol,
42 channels,
43 channel_max,
44 frame_max,
45 client_properties]).
22 # before the inclusion of erlang.mk leading to the wrong target becoming
33 # the default.
44 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
526 endif
627
728 # --------------------------------------------------------------------
2041 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
2142 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
2243 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
2345 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
2446 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
2547 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
2648 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
2749 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
2851 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
2952 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
3054 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
3155 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
3256 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
3559 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
3660 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
3761 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
3863 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
3964 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
4065 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
7095
7196 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
7297
73 # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
74 # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
75 # needs to add "ranch" as a BUILD_DEPS. The list of projects needing
76 # this workaround are:
77 # o rabbitmq-web-stomp
78 dep_ranch = git https://github.com/ninenines/ranch 1.2.1
98 # Third-party dependencies version pinning.
99 #
100 # We do that in this file, which is copied in all projects, to ensure
101 # all projects use the same versions. It avoids conflicts and makes it
102 # possible to work with rabbitmq-public-umbrella.
103
104 dep_cowboy_commit = 1.0.4
105 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
106 dep_ranch_commit = 1.3.1
107 dep_webmachine_commit = 1.10.8p2
79108
80109 RABBITMQ_COMPONENTS = amqp_client \
81110 rabbit \
82111 rabbit_common \
83112 rabbitmq_amqp1_0 \
84113 rabbitmq_auth_backend_amqp \
114 rabbitmq_auth_backend_cache \
85115 rabbitmq_auth_backend_http \
86116 rabbitmq_auth_backend_ldap \
87117 rabbitmq_auth_mechanism_ssl \
88118 rabbitmq_boot_steps_visualiser \
89119 rabbitmq_clusterer \
120 rabbitmq_cli \
90121 rabbitmq_codegen \
91122 rabbitmq_consistent_hash_exchange \
123 rabbitmq_ct_client_helpers \
92124 rabbitmq_ct_helpers \
93125 rabbitmq_delayed_message_exchange \
94126 rabbitmq_dotnet_client \
97129 rabbitmq_federation_management \
98130 rabbitmq_java_client \
99131 rabbitmq_jms_client \
132 rabbitmq_jms_cts \
100133 rabbitmq_jms_topic_exchange \
101134 rabbitmq_lvc \
102135 rabbitmq_management \
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_stomp).
1919
2020 -behaviour(application).
2121 -export([start/2, stop/1]).
22 -export([parse_default_user/2]).
23 -export([list/0]).
2224
2325 -define(DEFAULT_CONFIGURATION,
2426 #stomp_configuration{
8486 end,
8587
8688 ok.
89
90 list() ->
91 [Client
92 || {_, ListSupPid, _, _} <- supervisor2:which_children(rabbit_stomp_sup),
93 {_, RanchSup, supervisor, _} <- supervisor2:which_children(ListSupPid),
94 {ranch_conns_sup, ConnSup, _, _} <- supervisor:which_children(RanchSup),
95 {_, CliSup, _, _} <- supervisor:which_children(ConnSup),
96 {rabbit_stomp_reader, Client, _, _} <- supervisor:which_children(CliSup)].
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_stomp_client_sup).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 %% stomp_frame implements the STOMP framing protocol "version 1.0", as
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_stomp_processor).
2222 send_delivery/5]).
2323
2424 -export([adapter_name/1]).
25 -export([info/2]).
2526
2627 -include_lib("amqp_client/include/amqp_client.hrl").
2728 -include_lib("amqp_client/include/rabbit_routing_prefixes.hrl").
5657 PeerAddr :: inet:ip_address().
5758
5859 -type process_frame_result() ::
59 {ok, #proc_state{}} |
60 {ok, term(), #proc_state{}} |
6061 {stop, term(), #proc_state{}}.
6162
6263 -spec process_frame(#stomp_frame{}, #proc_state{}) ->
6364 process_frame_result().
6465
65 -spec flush_and_die(#proc_state{}) -> ok.
66 -spec flush_and_die(#proc_state{}) -> #proc_state{}.
6667
6768 -spec command({Command, Frame}, State) -> process_frame_result()
6869 when Command :: string(),
103104
104105 flush_and_die(State) ->
105106 close_connection(State).
107
108 info(session_id, #proc_state{session_id = Val}) ->
109 Val;
110 info(channel, #proc_state{channel = Val}) -> Val;
111 info(version, #proc_state{version = Val}) -> Val;
112 info(ssl_cert_login, #proc_state{config = #stomp_configuration{ssl_cert_login = Val}}) -> Val;
113 info(implicit_connect, #proc_state{config = #stomp_configuration{implicit_connect = Val}}) -> Val;
114 info(default_login, #proc_state{config = #stomp_configuration{default_login = Val}}) -> Val;
115 info(default_passcode, #proc_state{config = #stomp_configuration{default_passcode = Val}}) -> Val;
116 info(ssl_login_name, #proc_state{ssl_login_name = Val}) -> Val;
117 info(peer_addr, #proc_state{peer_addr = Val}) -> Val;
118 info(host, #proc_state{adapter_info = #amqp_adapter_info{host = Val}}) -> Val;
119 info(port, #proc_state{adapter_info = #amqp_adapter_info{port = Val}}) -> Val;
120 info(peer_host, #proc_state{adapter_info = #amqp_adapter_info{peer_host = Val}}) -> Val;
121 info(peer_port, #proc_state{adapter_info = #amqp_adapter_info{peer_port = Val}}) -> Val;
122 info(protocol, #proc_state{adapter_info = #amqp_adapter_info{protocol = Val}}) ->
123 case Val of
124 {Proto, Version} -> {Proto, rabbit_data_coercion:to_binary(Version)};
125 Other -> Other
126 end;
127 info(channels, PState) -> additional_info(channels, PState);
128 info(channel_max, PState) -> additional_info(channel_max, PState);
129 info(frame_max, PState) -> additional_info(frame_max, PState);
130 info(client_properties, PState) -> additional_info(client_properties, PState).
106131
107132 initial_state(Configuration,
108133 {SendFun, AdapterInfo0 = #amqp_adapter_info{additional_info = Extra},
183208 State = #proc_state{connection = Conn}) ->
184209 amqp_death(Code, Explanation, State);
185210 handle_exit(Conn, Reason, State = #proc_state{connection = Conn}) ->
186 send_error("AMQP connection died", "Reason: ~p", [Reason], State),
211 _ = send_error("AMQP connection died", "Reason: ~p", [Reason], State),
187212 {stop, {conn_died, Reason}, State};
188213
189214 handle_exit(Ch, {shutdown, {server_initiated_close, Code, Explanation}},
191216 amqp_death(Code, Explanation, State);
192217
193218 handle_exit(Ch, Reason, State = #proc_state{channel = Ch}) ->
194 send_error("AMQP channel died", "Reason: ~p", [Reason], State),
219 _ = send_error("AMQP channel died", "Reason: ~p", [Reason], State),
195220 {stop, {channel_died, Reason}, State};
196221 handle_exit(Ch, {shutdown, {server_initiated_close, Code, Explanation}},
197222 State = #proc_state{channel = Ch}) ->
203228 process_request(ProcessFun, fun (StateM) -> StateM end, State).
204229
205230
206 process_request(ProcessFun, SuccessFun, State=#proc_state{connection=Conn}) ->
231 process_request(ProcessFun, SuccessFun, State) ->
207232 Res = case catch ProcessFun(State) of
208233 {'EXIT',
209234 {{shutdown,
216241 Result
217242 end,
218243 case Res of
219 {ok, Frame, NewState} ->
220 case Frame of
221 none -> ok;
222 _ -> send_frame(Frame, NewState)
223 end,
244 {ok, Frame, NewState = #proc_state{connection = Conn}} ->
245 _ = case Frame of
246 none -> ok;
247 _ -> send_frame(Frame, NewState)
248 end,
224249 {ok, SuccessFun(NewState), Conn};
225 {error, Message, Detail, NewState} ->
250 {error, Message, Detail, NewState = #proc_state{connection = Conn}} ->
226251 {ok, send_error(Message, Detail, NewState), Conn};
227252 {stop, normal, NewState} ->
228253 {stop, normal, SuccessFun(NewState)};
268293 creds(_, _, #stomp_configuration{default_login = DefLogin,
269294 default_passcode = DefPasscode,
270295 force_default_creds = true}) ->
271 {DefLogin, DefPasscode};
296 {iolist_to_binary(DefLogin), iolist_to_binary(DefPasscode)};
272297 creds(Frame, SSLLoginName,
273298 #stomp_configuration{default_login = DefLogin,
274299 default_passcode = DefPasscode}) ->
416441 {ok, {_, Id1}} -> Id1;
417442 {error, {_, Id1}} -> "Unknown[" ++ Id1 ++ "]"
418443 end,
419 send_error_frame("Server cancelled subscription",
420 [{?HEADER_SUBSCRIPTION, Id}],
421 "The server has canceled a subscription.~n"
422 "No more messages will be delivered for ~p.~n",
423 [Description],
424 State),
444 _ = send_error_frame("Server cancelled subscription",
445 [{?HEADER_SUBSCRIPTION, Id}],
446 "The server has canceled a subscription.~n"
447 "No more messages will be delivered for ~p.~n",
448 [Description],
449 State),
425450 tidy_canceled_subscription(ConsumerTag, Subscription,
426 #stomp_frame{}, State)
451 undefined, State)
427452 end.
428453
429454 cancel_subscription({error, invalid_prefix}, _Frame, State) ->
462487 end
463488 end.
464489
490 %% Server-initiated cancelations will pass an undefined instead of a
491 %% STOMP frame. In this case we know that the queue was deleted and
492 %% thus we don't have to clean it up.
493 tidy_canceled_subscription(ConsumerTag, _Subscription,
494 undefined, State = #proc_state{subscriptions = Subs}) ->
495 Subs1 = dict:erase(ConsumerTag, Subs),
496 ok(State#proc_state{subscriptions = Subs1});
497
498 %% Client-initiated cancelations will pass an actual frame
465499 tidy_canceled_subscription(ConsumerTag, #subscription{dest_hdr = DestHdr},
466500 Frame, State = #proc_state{subscriptions = Subs}) ->
467501 Subs1 = dict:erase(ConsumerTag, Subs),
604638 end.
605639
606640 server_header() ->
607 {ok, Product} = application:get_key(rabbit, id),
641 {ok, Product} = application:get_key(rabbit, description),
608642 {ok, Version} = application:get_key(rabbit, vsn),
609643 rabbit_misc:format("~s/~s", [Product, Version]).
610644
629663 {ok, _} ->
630664 Message = "Duplicated subscription identifier",
631665 Detail = "A subscription identified by '~s' alredy exists.",
632 error(Message, Detail, [ConsumerTag], State),
633 send_error(Message, Detail, [ConsumerTag], State),
666 _ = error(Message, Detail, [ConsumerTag], State),
667 _ = send_error(Message, Detail, [ConsumerTag], State),
634668 {stop, normal, close_connection(State)};
635669 error ->
636670 ExchangeAndKey =
9991033 {SendTimeout, ReceiveTimeout} =
10001034 {millis_to_seconds(CY), millis_to_seconds(CX)},
10011035
1002 rabbit_stomp_reader:start_heartbeats(self(), {SendTimeout, ReceiveTimeout}),
1036 _ = rabbit_stomp_reader:start_heartbeats(self(), {SendTimeout, ReceiveTimeout}),
10031037 {SendTimeout * 1000 , ReceiveTimeout * 1000}.
10041038
10051039 millis_to_seconds(M) when M =< 0 -> 0;
11111145 send_error(Message, Format, Args, State) ->
11121146 send_error(Message, rabbit_misc:format(Format, Args), State).
11131147
1148 additional_info(Key,
1149 #proc_state{adapter_info =
1150 #amqp_adapter_info{additional_info = AddInfo}}) ->
1151 proplists:get_value(Key, AddInfo).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_stomp_reader).
2121 -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
2222 code_change/3, terminate/2]).
2323 -export([start_heartbeats/2]).
24 -export([info/2]).
2425
2526 -include("rabbit_stomp.hrl").
2627 -include("rabbit_stomp_frame.hrl").
2728 -include_lib("amqp_client/include/amqp_client.hrl").
2829
30 -define(SIMPLE_METRICS, [pid, recv_oct, send_oct, reductions]).
31 -define(OTHER_METRICS, [recv_cnt, send_cnt, send_pend, garbage_collection, state,
32 timeout]).
33
2934 -record(reader_state, {socket, conn_name, parse_state, processor_state, state,
3035 conserve_resources, recv_outstanding, stats_timer,
31 parent, connection, heartbeat_sup, heartbeat}).
36 parent, connection, heartbeat_sup, heartbeat,
37 timeout_sec %% heartbeat timeout value used, 0 means
38 %% heartbeats are disabled
39 }).
3240
3341 %%----------------------------------------------------------------------------
3442
4351 %% meaningless synchronous call to the underlying gen_event
4452 %% mechanism. When it returns the mailbox is drained, and we
4553 %% return to our caller to accept more connections.
46 gen_event:which_handlers(error_logger),
54 _ = gen_event:which_handlers(error_logger),
4755
4856 {ok, Pid}.
4957
5058 log(Level, Fmt, Args) -> rabbit_log:log(connection, Level, Fmt, Args).
59
60 info(Pid, InfoItems) ->
61 case InfoItems -- ?INFO_ITEMS of
62 [] ->
63 gen_server2:call(Pid, {info, InfoItems});
64 UnknownItems -> throw({bad_argument, UnknownItems})
65 end.
5166
5267 init([SupHelperPid, Ref, Sock, Configuration]) ->
5368 process_flag(trap_exit, true),
6378 [self(), ConnStr]),
6479
6580 ParseState = rabbit_stomp_frame:initial_state(),
66 register_resource_alarm(),
81 _ = register_resource_alarm(),
6782 gen_server2:enter_loop(?MODULE, [],
6883 rabbit_event:init_stats_timer(
6984 run_socket(control_throttle(
7792 conserve_resources = false,
7893 recv_outstanding = false})), #reader_state.stats_timer),
7994 {backoff, 1000, 1000, 10000});
80 {network_error, Reason} ->
81 rabbit_net:fast_close(Sock),
82 terminate({shutdown, Reason}, undefined);
8395 {error, enotconn} ->
8496 rabbit_net:fast_close(Sock),
8597 terminate(shutdown, undefined);
89101 end.
90102
91103
104 handle_call({info, InfoItems}, _From, State) ->
105 Infos = lists:map(
106 fun(InfoItem) ->
107 {InfoItem, info_internal(InfoItem, State)}
108 end,
109 InfoItems),
110 {reply, Infos, State};
92111 handle_call(Msg, From, State) ->
93112 {stop, {stomp_unexpected_call, Msg, From}, State}.
94113
162181 end;
163182
164183 handle_info({start_heartbeats, {0, 0}}, State) ->
165 {noreply, State};
184 {noreply, State#reader_state{timeout_sec = {0, 0}}};
166185
167186 handle_info({start_heartbeats, {SendTimeout, ReceiveTimeout}},
168187 State = #reader_state{heartbeat_sup = SupPid, socket = Sock}) ->
172191 ReceiveFun = fun() -> gen_server2:cast(Pid, client_timeout) end,
173192 Heartbeat = rabbit_heartbeat:start(SupPid, Sock, SendTimeout,
174193 SendFun, ReceiveTimeout, ReceiveFun),
175 {noreply, State#reader_state{heartbeat = Heartbeat}};
194 {noreply, State#reader_state{heartbeat = Heartbeat,
195 timeout_sec = {SendTimeout, ReceiveTimeout}}};
176196
177197
178198 %%----------------------------------------------------------------------------
250270 run_socket(State = #reader_state{recv_outstanding = true}) ->
251271 State;
252272 run_socket(State = #reader_state{socket = Sock}) ->
253 rabbit_net:async_recv(Sock, 0, infinity),
273 _ = rabbit_net:async_recv(Sock, 0, infinity),
254274 State#reader_state{recv_outstanding = true}.
255275
256276
277 terminate(Reason, undefined) ->
278 log_reason(Reason, undefined),
279 {stop, Reason};
257280 terminate(Reason, State = #reader_state{ processor_state = ProcState }) ->
258281 maybe_emit_stats(State),
259282 log_reason(Reason, State),
260 rabbit_stomp_processor:flush_and_die(ProcState),
261 ok.
283 _ = rabbit_stomp_processor:flush_and_die(ProcState),
284 {stop, Reason}.
262285
263286 code_change(_OldVsn, State, _Extra) ->
264287 {ok, State}.
306329 log_reason(normal, #reader_state{ conn_name = ConnName}) ->
307330 log(info, "closing STOMP connection ~p (~s)~n", [self(), ConnName]);
308331
332 log_reason(shutdown, undefined) ->
333 log(error, "closing STOMP connection that never completed connection handshake (negotiation)~n", []);
334
309335 log_reason(Reason, #reader_state{ processor_state = ProcState }) ->
310336 AdapterName = rabbit_stomp_processor:adapter_name(ProcState),
311337 rabbit_log:warning("STOMP connection ~s terminated"
312338 " with reason ~p, closing it~n", [AdapterName, Reason]).
313
314339
315340 %%----------------------------------------------------------------------------
316341
355380 rabbit_event:if_enabled(State, #reader_state.stats_timer,
356381 fun() -> emit_stats(State) end).
357382
358 emit_stats(State=#reader_state{socket = Sock, state = ConnState, connection = Conn}) ->
359 SockInfos = case rabbit_net:getstat(Sock,
360 [recv_oct, recv_cnt, send_oct, send_cnt, send_pend]) of
361 {ok, SI} -> SI;
362 {error, _} -> []
363 end,
364 Infos = [{pid, Conn}, {state, ConnState} | SockInfos],
365 rabbit_event:notify(connection_stats, Infos),
383 emit_stats(State=#reader_state{connection = C}) when C == none; C == undefined ->
384 %% Avoid emitting stats on terminate when the connection has not yet been
385 %% established, as this causes orphan entries on the stats database
386 State1 = rabbit_event:reset_stats_timer(State, #reader_state.stats_timer),
387 ensure_stats_timer(State1);
388 emit_stats(State) ->
389 [{_, Pid}, {_, Recv_oct}, {_, Send_oct}, {_, Reductions}] = I
390 = infos(?SIMPLE_METRICS, State),
391 Infos = infos(?OTHER_METRICS, State),
392 rabbit_core_metrics:connection_stats(Pid, Infos),
393 rabbit_core_metrics:connection_stats(Pid, Recv_oct, Send_oct, Reductions),
394 rabbit_event:notify(connection_stats, Infos ++ I),
366395 State1 = rabbit_event:reset_stats_timer(State, #reader_state.stats_timer),
367396 ensure_stats_timer(State1).
368397
375404 processor_state(#reader_state{ processor_state = ProcState }) -> ProcState.
376405 processor_state(ProcState, #reader_state{} = State) ->
377406 State#reader_state{ processor_state = ProcState}.
407
408 %%----------------------------------------------------------------------------
409
410 infos(Items, State) -> [{Item, info_internal(Item, State)} || Item <- Items].
411
412 info_internal(pid, State) -> info_internal(connection, State);
413 info_internal(SockStat, #reader_state{socket = Sock}) when SockStat =:= recv_oct;
414 SockStat =:= recv_cnt;
415 SockStat =:= send_oct;
416 SockStat =:= send_cnt;
417 SockStat =:= send_pend ->
418 case rabbit_net:getstat(Sock, [SockStat]) of
419 {ok, [{_, N}]} when is_number(N) -> N;
420 _ -> 0
421 end;
422 info_internal(state, State) -> info_internal(connection_state, State);
423 info_internal(garbage_collection, _State) ->
424 rabbit_misc:get_gc_info(self());
425 info_internal(reductions, _State) ->
426 {reductions, Reductions} = erlang:process_info(self(), reductions),
427 Reductions;
428 info_internal(timeout, #reader_state{timeout_sec = {_, Receive}}) ->
429 Receive;
430 info_internal(timeout, #reader_state{timeout_sec = undefined}) ->
431 0;
432 info_internal(conn_name, #reader_state{conn_name = Val}) ->
433 rabbit_data_coercion:to_binary(Val);
434 info_internal(connection, #reader_state{connection = Val}) ->
435 Val;
436 info_internal(connection_state, #reader_state{state = Val}) ->
437 Val;
438 info_internal(Key, #reader_state{processor_state = ProcState}) ->
439 rabbit_stomp_processor:info(Key, ProcState).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_stomp_sup).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_stomp_util).
382382 %% ---- Helpers ----
383383
384384 split([], _Splitter) -> [];
385 split(Content, []) -> Content;
386385 split(Content, Splitter) -> split(Content, [], [], Splitter).
387386
388387 split([], RPart, RParts, _Splitter) ->
+0
-23
deps/rabbitmq_stomp/src/rabbitmq_stomp.app.src less more
0 {application, rabbitmq_stomp,
1 [{description, "RabbitMQ STOMP plugin"},
2 {vsn, "3.6.6"},
3 {modules, []},
4 {registered, []},
5 {mod, {rabbit_stomp, []}},
6 {env, [{default_user,
7 [{login, <<"guest">>},
8 {passcode, <<"guest">>}]},
9 {default_vhost, <<"/">>},
10 {ssl_cert_login, false},
11 {implicit_connect, false},
12 {tcp_listeners, [61613]},
13 {num_tcp_acceptors, 10},
14 {ssl_listeners, []},
15 {num_ssl_acceptors, 1},
16 {tcp_listen_options, [{backlog, 128},
17 {nodelay, true}]},
18 %% see rabbitmq/rabbitmq-stomp#39
19 {trailing_lf, true},
20 %% see rabbitmq/rabbitmq-stomp#57
21 {hide_server_info, false}]},
22 {applications, [kernel, stdlib, rabbit_common, rabbit, amqp_client]}]}.
00 PROJECT = rabbitmq_top
1 PROJECT_DESCRIPTION = RabbitMQ Top
2 PROJECT_MOD = rabbit_top_app
13
24 DEPS = rabbit_common rabbit amqp_client rabbitmq_management
35
6 DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
47 DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk
58
69 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
0 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
11 #
22 # Permission to use, copy, modify, and/or distribute this software for any
33 # purpose with or without fee is hereby granted, provided that the above
1111 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1212 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1313
14 .PHONY: all app apps deps search rel docs install-docs check tests clean distclean help erlang-mk
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
1515
1616 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17
18 ERLANG_MK_VERSION = 2.0.0-pre.2-144-g647ffd1
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
1931
2032 # Core configuration.
2133
2436
2537 PROJECT_VERSION ?= rolling
2638 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
2740
2841 # Verbosity.
2942
8497 rel::
8598 $(verbose) :
8699
100 relup:: deps app
101
87102 check:: tests
88103
89104 clean:: clean-crashdump
101116 help::
102117 $(verbose) printf "%s\n" \
103118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
104 "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
105120 "" \
106121 "Usage: [V=1] $(MAKE) [target]..." \
107122 "" \
149164 core_native_path = $1
150165 endif
151166
152 ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
153 define core_http_get
154 wget --no-check-certificate -O $(1) $(2)|| rm $(1)
155 endef
156 else
157 define core_http_get.erl
158 ssl:start(),
159 inets:start(),
160 case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
161 {ok, {{_, 200, _}, _, Body}} ->
162 case file:write_file("$(1)", Body) of
163 ok -> ok;
164 {error, R1} -> halt(R1)
165 end;
166 {error, R2} ->
167 halt(R2)
168 end,
169 halt(0).
170 endef
171
172 define core_http_get
173 $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
174 endef
175 endif
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
176168
177169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
178170
192184 ERLANG_MK_BUILD_CONFIG ?= build.config
193185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
194186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
195188 erlang-mk:
196189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
197190 ifdef ERLANG_MK_COMMIT
198191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
199192 endif
200193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
201 $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
202195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
203196 rm -rf $(ERLANG_MK_BUILD_DIR)
204197
285278 pkg_apns_repo = https://github.com/inaka/apns4erl
286279 pkg_apns_commit = master
287280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
288289 PACKAGES += azdht
289290 pkg_azdht_name = azdht
290291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
405406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
406407 pkg_bootstrap_commit = master
407408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
408417 PACKAGES += boss_db
409418 pkg_boss_db_name = boss_db
410419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
413422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
414423 pkg_boss_db_commit = master
415424
416 PACKAGES += boss
417 pkg_boss_name = boss
418 pkg_boss_description = Erlang web MVC, now featuring Comet
419 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
420 pkg_boss_fetch = git
421 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
422 pkg_boss_commit = master
423
424425 PACKAGES += brod
425426 pkg_brod_name = brod
426427 pkg_brod_description = Kafka client in Erlang
565566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
566567 pkg_cloudi_service_api_requests_commit = master
567568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
568585 PACKAGES += cloudi_service_db_cassandra_cql
569586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
570587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
573590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
574591 pkg_cloudi_service_db_cassandra_cql_commit = master
575592
576 PACKAGES += cloudi_service_db_cassandra
577 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
579 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
580 pkg_cloudi_service_db_cassandra_fetch = git
581 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
582 pkg_cloudi_service_db_cassandra_commit = master
583
584593 PACKAGES += cloudi_service_db_couchdb
585594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
586595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
637646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
638647 pkg_cloudi_service_db_tokyotyrant_commit = master
639648
640 PACKAGES += cloudi_service_db
641 pkg_cloudi_service_db_name = cloudi_service_db
642 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
643 pkg_cloudi_service_db_homepage = http://cloudi.org/
644 pkg_cloudi_service_db_fetch = git
645 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
646 pkg_cloudi_service_db_commit = master
647
648649 PACKAGES += cloudi_service_filesystem
649650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
650651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
10371038 pkg_edown_repo = https://github.com/uwiger/edown
10381039 pkg_edown_commit = master
10391040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
10401049 PACKAGES += eep_app
10411050 pkg_eep_app_name = eep_app
10421051 pkg_eep_app_description = Embedded Event Processing
10451054 pkg_eep_app_repo = https://github.com/darach/eep-erl
10461055 pkg_eep_app_commit = master
10471056
1048 PACKAGES += eep
1049 pkg_eep_name = eep
1050 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1051 pkg_eep_homepage = https://github.com/virtan/eep
1052 pkg_eep_fetch = git
1053 pkg_eep_repo = https://github.com/virtan/eep
1054 pkg_eep_commit = master
1055
10561057 PACKAGES += efene
10571058 pkg_efene_name = efene
10581059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
12371238 pkg_eqm_repo = https://github.com/loucash/eqm
12381239 pkg_eqm_commit = master
12391240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
12401249 PACKAGES += eredis_pool
12411250 pkg_eredis_pool_name = eredis_pool
12421251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
12451254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
12461255 pkg_eredis_pool_commit = master
12471256
1248 PACKAGES += eredis
1249 pkg_eredis_name = eredis
1250 pkg_eredis_description = Erlang Redis client
1251 pkg_eredis_homepage = https://github.com/wooga/eredis
1252 pkg_eredis_fetch = git
1253 pkg_eredis_repo = https://github.com/wooga/eredis
1254 pkg_eredis_commit = master
1255
12561257 PACKAGES += erl_streams
12571258 pkg_erl_streams_name = erl_streams
12581259 pkg_erl_streams_description = Streams in Erlang
15411542 pkg_etap_repo = https://github.com/ngerakines/etap
15421543 pkg_etap_commit = master
15431544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
15441553 PACKAGES += etest_http
15451554 pkg_etest_http_name = etest_http
15461555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
15491558 pkg_etest_http_repo = https://github.com/wooga/etest_http
15501559 pkg_etest_http_commit = master
15511560
1552 PACKAGES += etest
1553 pkg_etest_name = etest
1554 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1555 pkg_etest_homepage = https://github.com/wooga/etest
1556 pkg_etest_fetch = git
1557 pkg_etest_repo = https://github.com/wooga/etest
1558 pkg_etest_commit = master
1559
15601561 PACKAGES += etoml
15611562 pkg_etoml_name = etoml
15621563 pkg_etoml_description = TOML language erlang parser
15651566 pkg_etoml_repo = https://github.com/kalta/etoml
15661567 pkg_etoml_commit = master
15671568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
15681577 PACKAGES += eunit_formatters
15691578 pkg_eunit_formatters_name = eunit_formatters
15701579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
15731582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
15741583 pkg_eunit_formatters_commit = master
15751584
1576 PACKAGES += eunit
1577 pkg_eunit_name = eunit
1578 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1579 pkg_eunit_homepage = https://github.com/richcarl/eunit
1580 pkg_eunit_fetch = git
1581 pkg_eunit_repo = https://github.com/richcarl/eunit
1582 pkg_eunit_commit = master
1583
15841585 PACKAGES += euthanasia
15851586 pkg_euthanasia_name = euthanasia
15861587 pkg_euthanasia_description = Merciful killer for your Erlang processes
15981599 pkg_evum_commit = master
15991600
16001601 PACKAGES += exec
1601 pkg_exec_name = exec
1602 pkg_exec_name = erlexec
16021603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
16031604 pkg_exec_homepage = http://saleyn.github.com/erlexec
16041605 pkg_exec_fetch = git
17171718 pkg_fn_repo = https://github.com/reiddraper/fn
17181719 pkg_fn_commit = master
17191720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
17201729 PACKAGES += folsom_cowboy
17211730 pkg_folsom_cowboy_name = folsom_cowboy
17221731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
17251734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
17261735 pkg_folsom_cowboy_commit = master
17271736
1728 PACKAGES += folsom
1729 pkg_folsom_name = folsom
1730 pkg_folsom_description = Expose Erlang Events and Metrics
1731 pkg_folsom_homepage = https://github.com/boundary/folsom
1732 pkg_folsom_fetch = git
1733 pkg_folsom_repo = https://github.com/boundary/folsom
1734 pkg_folsom_commit = master
1735
17361737 PACKAGES += folsomite
17371738 pkg_folsomite_name = folsomite
17381739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
20932094 pkg_jesse_repo = https://github.com/for-GET/jesse
20942095 pkg_jesse_commit = master
20952096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
20962105 PACKAGES += jiffy_v
20972106 pkg_jiffy_v_name = jiffy_v
20982107 pkg_jiffy_v_description = JSON validation utility
21012110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
21022111 pkg_jiffy_v_commit = master
21032112
2104 PACKAGES += jiffy
2105 pkg_jiffy_name = jiffy
2106 pkg_jiffy_description = JSON NIFs for Erlang.
2107 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2108 pkg_jiffy_fetch = git
2109 pkg_jiffy_repo = https://github.com/davisp/jiffy
2110 pkg_jiffy_commit = master
2111
21122113 PACKAGES += jobs
21132114 pkg_jobs_name = jobs
21142115 pkg_jobs_description = a Job scheduler for load regulation
21252126 pkg_joxa_repo = https://github.com/joxa/joxa
21262127 pkg_joxa_commit = master
21272128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
21282137 PACKAGES += json_rec
21292138 pkg_json_rec_name = json_rec
21302139 pkg_json_rec_description = JSON to erlang record
21332142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
21342143 pkg_json_rec_commit = master
21352144
2136 PACKAGES += json
2137 pkg_json_name = json
2138 pkg_json_description = a high level json library for erlang (17.0+)
2139 pkg_json_homepage = https://github.com/talentdeficit/json
2140 pkg_json_fetch = git
2141 pkg_json_repo = https://github.com/talentdeficit/json
2142 pkg_json_commit = master
2143
21442145 PACKAGES += jsone
21452146 pkg_jsone_name = jsone
21462147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
21812182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
21822183 pkg_jsx_commit = master
21832184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
21842193 PACKAGES += kafka_protocol
21852194 pkg_kafka_protocol_name = kafka_protocol
21862195 pkg_kafka_protocol_description = Kafka protocol Erlang library
21892198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
21902199 pkg_kafka_protocol_commit = master
21912200
2192 PACKAGES += kafka
2193 pkg_kafka_name = kafka
2194 pkg_kafka_description = Kafka consumer and producer in Erlang
2195 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2196 pkg_kafka_fetch = git
2197 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2198 pkg_kafka_commit = master
2199
22002201 PACKAGES += kai
22012202 pkg_kai_name = kai
22022203 pkg_kai_description = DHT storage by Takeshi Inoue
22932294 pkg_kvs_repo = https://github.com/synrc/kvs
22942295 pkg_kvs_commit = master
22952296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
22962305 PACKAGES += lager_amqp_backend
22972306 pkg_lager_amqp_backend_name = lager_amqp_backend
22982307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
23092318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
23102319 pkg_lager_syslog_commit = master
23112320
2312 PACKAGES += lager
2313 pkg_lager_name = lager
2314 pkg_lager_description = A logging framework for Erlang/OTP.
2315 pkg_lager_homepage = https://github.com/basho/lager
2316 pkg_lager_fetch = git
2317 pkg_lager_repo = https://github.com/basho/lager
2318 pkg_lager_commit = master
2319
23202321 PACKAGES += lambdapad
23212322 pkg_lambdapad_name = lambdapad
23222323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
25732574 pkg_mixer_repo = https://github.com/chef/mixer
25742575 pkg_mixer_commit = master
25752576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
25762585 PACKAGES += mochiweb_xpath
25772586 pkg_mochiweb_xpath_name = mochiweb_xpath
25782587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
25812590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
25822591 pkg_mochiweb_xpath_commit = master
25832592
2584 PACKAGES += mochiweb
2585 pkg_mochiweb_name = mochiweb
2586 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2587 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2588 pkg_mochiweb_fetch = git
2589 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2590 pkg_mochiweb_commit = master
2591
25922593 PACKAGES += mockgyver
25932594 pkg_mockgyver_name = mockgyver
25942595 pkg_mockgyver_description = A mocking library for Erlang
30613062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
30623063 pkg_quickrand_commit = master
30633064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
30643073 PACKAGES += rabbit_exchange_type_riak
30653074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
30663075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
30693078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
30703079 pkg_rabbit_exchange_type_riak_commit = master
30713080
3072 PACKAGES += rabbit
3073 pkg_rabbit_name = rabbit
3074 pkg_rabbit_description = RabbitMQ Server
3075 pkg_rabbit_homepage = https://www.rabbitmq.com/
3076 pkg_rabbit_fetch = git
3077 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3078 pkg_rabbit_commit = master
3079
30803081 PACKAGES += rack
30813082 pkg_rack_name = rack
30823083 pkg_rack_description = Rack handler for erlang
34933494 pkg_smother_repo = https://github.com/ramsay-t/Smother
34943495 pkg_smother_commit = master
34953496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
34963505 PACKAGES += social
34973506 pkg_social_name = social
34983507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
35413550 pkg_stable_repo = https://github.com/dvv/stable
35423551 pkg_stable_commit = master
35433552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
35443561 PACKAGES += statebox_riak
35453562 pkg_statebox_riak_name = statebox_riak
35463563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
35493566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
35503567 pkg_statebox_riak_commit = master
35513568
3552 PACKAGES += statebox
3553 pkg_statebox_name = statebox
3554 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3555 pkg_statebox_homepage = https://github.com/mochi/statebox
3556 pkg_statebox_fetch = git
3557 pkg_statebox_repo = https://github.com/mochi/statebox
3558 pkg_statebox_commit = master
3559
35603569 PACKAGES += statman
35613570 pkg_statman_name = statman
35623571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
37253734 pkg_tirerl_repo = https://github.com/inaka/tirerl
37263735 pkg_tirerl_commit = master
37273736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
37283745 PACKAGES += traffic_tools
37293746 pkg_traffic_tools_name = traffic_tools
37303747 pkg_traffic_tools_description = Simple traffic limiting library
40614078 pkg_zucchini_repo = https://github.com/devinus/zucchini
40624079 pkg_zucchini_commit = master
40634080
4064 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
40654082 # This file is part of erlang.mk and subject to the terms of the ISC License.
40664083
40674084 .PHONY: search
40884105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
40894106 endif
40904107
4091 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
40924109 # This file is part of erlang.mk and subject to the terms of the ISC License.
40934110
4094 .PHONY: distclean-deps
4111 .PHONY: distclean-deps clean-tmp-deps.log
40954112
40964113 # Configuration.
40974114
41104127
41114128 REBAR_DEPS_DIR = $(DEPS_DIR)
41124129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
41134150
41144151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
41154152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
41164153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
41174154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
41184155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
41194157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
41204158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
41214159
41384176
41394177 # Core targets.
41404178
4141 ifdef IS_APP
4142 apps::
4143 else
4144 apps:: $(ALL_APPS_DIRS)
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
41454180 ifeq ($(IS_APP)$(IS_DEP),)
41464181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
41474182 endif
41494184 # Create ebin directory for all apps to make sure Erlang recognizes them
41504185 # as proper OTP applications when using -include_lib. This is a temporary
41514186 # fix, a proper fix would be to compile apps/* in the right order.
4152 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4153 mkdir -p $$dep/ebin || exit $$?; \
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
41544190 done
4155 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
41564196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
41574197 :; \
41584198 else \
41594199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4160 $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
4200 $(MAKE) -C $$dep IS_APP=1; \
41614201 fi \
41624202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
41634207 endif
41644208
41654209 ifneq ($(SKIP_DEPS),)
41664210 deps::
41674211 else
4168 deps:: $(ALL_DEPS_DIRS) apps
4169 ifeq ($(IS_APP)$(IS_DEP),)
4170 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4171 endif
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
41724213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4173 $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
41744215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
41754216 :; \
41764217 else \
41774218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
41784219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4179 $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
4220 $(MAKE) -C $$dep IS_DEP=1; \
41804221 else \
4181 echo "Error: No Makefile to build dependency $$dep."; \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
41824223 exit 2; \
41834224 fi \
41844225 fi \
41924233 # in practice only Makefile is needed so far.
41934234 define dep_autopatch
41944235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
41954237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
41964238 $(call dep_autopatch_erlang_mk,$(1)); \
41974239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4198 if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
41994243 $(call dep_autopatch2,$(1)); \
4200 elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
42014245 $(call dep_autopatch2,$(1)); \
4202 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
42034247 $(call dep_autopatch2,$(1)); \
4204 else \
4205 $(call erlang,$(call dep_autopatch_app.erl,$(1))); \
42064248 fi \
42074249 else \
42084250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
42144256 endef
42154257
42164258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
42174262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
42184263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
42194264 fi; \
42204265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4221 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
42224267 $(call dep_autopatch_fetch_rebar); \
42234268 $(call dep_autopatch_rebar,$(1)); \
42244269 else \
42304275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
42314276 endef
42324277
4233 # Overwrite erlang.mk with the current file by default.
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
42344280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
42354281 define dep_autopatch_erlang_mk
4236 echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \
4237 > $(DEPS_DIR)/$1/erlang.mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
42384287 endef
42394288 else
42404289 define dep_autopatch_erlang_mk
42534302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
42544303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
42554304 cd $(ERLANG_MK_TMP)/rebar; \
4256 git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
42574306 $(MAKE); \
42584307 cd -; \
42594308 fi
42704319 define dep_autopatch_rebar.erl
42714320 application:load(rebar),
42724321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
42734323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
42744324 {ok, Conf0} -> Conf0;
42754325 _ -> []
44234473 [] -> ok;
44244474 _ ->
44254475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4426 PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
44274477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4428 PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L \\"~s\\" -lerl_interface -lei\n",
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
44294479 [code:lib_dir(erl_interface, lib)])),
44304480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
44314481 FilterEnv = fun(Env) ->
44644514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44654515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44664516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4467 [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
44684518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
44694519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
44704520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
44764526 end,
44774527 [PortSpec(S) || S <- PortSpecs]
44784528 end,
4479 Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"),
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
44804530 RunPlugin = fun(Plugin, Step) ->
44814531 case erlang:function_exported(Plugin, Step, 2) of
44824532 false -> ok;
45244574 halt()
45254575 endef
45264576
4527 define dep_autopatch_app.erl
4528 UpdateModules = fun(App) ->
4529 case filelib:is_regular(App) of
4530 false -> ok;
4531 true ->
4532 {ok, [{application, '$(1)', L0}]} = file:consult(App),
4533 Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true,
4534 fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
4535 L = lists:keystore(modules, 1, L0, {modules, Mods}),
4536 ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}]))
4537 end
4538 end,
4539 UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"),
4540 halt()
4541 endef
4542
45434577 define dep_autopatch_appsrc_script.erl
45444578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
45454579 AppSrcScript = AppSrc ++ ".script",
45874621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
45884622 endef
45894623
4590 define dep_fetch_hex.erl
4591 ssl:start(),
4592 inets:start(),
4593 {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
4594 {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
4595 [], [{body_format, binary}]),
4596 {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
4597 {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
4598 ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
4599 halt()
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
46004626 endef
46014627
46024628 # Hex only has a package version. No need to look in the Erlang.mk packages.
46034629 define dep_fetch_hex
4604 $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
46054634 endef
46064635
46074636 define dep_fetch_fail
46314660 $(eval DEP_NAME := $(call dep_name,$1))
46324661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
46334662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4634 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
46354664 exit 17; \
46364665 fi
46374666 $(verbose) mkdir -p $(DEPS_DIR)
46734702 clean:: clean-apps
46744703
46754704 clean-apps:
4676 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4677 $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
46784707 done
46794708
46804709 distclean:: distclean-apps
46814710
46824711 distclean-apps:
4683 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4684 $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
46854714 done
46864715 endif
46874716
47014730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
47024731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
47034732
4704 # External plugins.
4705
4706 DEP_PLUGINS ?=
4707
4708 define core_dep_plugin
4709 -include $(DEPS_DIR)/$(1)
4710
4711 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4712 endef
4713
4714 $(foreach p,$(DEP_PLUGINS),\
4715 $(eval $(if $(findstring /,$p),\
4716 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4717 $(call core_dep_plugin,$p/plugins.mk,$p))))
4718
4719 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4720 # This file is part of erlang.mk and subject to the terms of the ISC License.
4721
4722 # Configuration.
4723
4724 DTL_FULL_PATH ?=
4725 DTL_PATH ?= templates/
4726 DTL_SUFFIX ?= _dtl
4727 DTL_OPTS ?=
4728
4729 # Verbosity.
4730
4731 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
4732 dtl_verbose = $(dtl_verbose_$(V))
4733
4734 # Core targets.
4735
4736 DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl))
4737
4738 ifneq ($(DTL_FILES),)
4739
4740 ifdef DTL_FULL_PATH
4741 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
4742 else
4743 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
4744 endif
4745
4746 ifneq ($(words $(DTL_FILES)),0)
4747 # Rebuild templates when the Makefile changes.
4748 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
4749 @mkdir -p $(ERLANG_MK_TMP)
4750 @if test -f $@; then \
4751 touch $(DTL_FILES); \
4752 fi
4753 @touch $@
4754
4755 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
4756 endif
4757
4758 define erlydtl_compile.erl
4759 [begin
4760 Module0 = case "$(strip $(DTL_FULL_PATH))" of
4761 "" ->
4762 filename:basename(F, ".dtl");
4763 _ ->
4764 "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
4765 re:replace(F2, "/", "_", [{return, list}, global])
4766 end,
4767 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
4768 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
4769 ok -> ok;
4770 {ok, _} -> ok
4771 end
4772 end || F <- string:tokens("$(1)", " ")],
4773 halt().
4774 endef
4775
4776 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
4777 $(if $(strip $?),\
4778 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
4779
4780 endif
4781
4782 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
47834734 # This file is part of erlang.mk and subject to the terms of the ISC License.
47844735
47854736 # Verbosity.
47984749
47994750 define compile_proto.erl
48004751 [begin
4801 Dir = filename:dirname(filename:dirname(F)),
48024752 protobuffs_compile:generate_source(F,
4803 [{output_include_dir, Dir ++ "/include"},
4804 {output_src_dir, Dir ++ "/ebin"}])
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
48054755 end || F <- string:tokens("$(1)", " ")],
48064756 halt().
48074757 endef
48114761 $(if $(strip $?),$(call compile_proto,$?))
48124762 endif
48134763
4814 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
48154765 # This file is part of erlang.mk and subject to the terms of the ISC License.
48164766
48174767 .PHONY: clean-app
48254775 ERLC_EXCLUDE ?=
48264776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
48274777
4778 ERLC_ASN1_OPTS ?=
4779
48284780 ERLC_MIB_OPTS ?=
48294781 COMPILE_MIB_FIRST ?=
48304782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
48744826
48754827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
48764828 define app_file
4877 {application, $(PROJECT), [
4829 {application, '$(PROJECT)', [
48784830 {description, "$(PROJECT_DESCRIPTION)"},
48794831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48804832 {id$(comma)$(space)"$(1)"}$(comma))
48814833 {modules, [$(call comma_list,$(2))]},
48824834 {registered, []},
4883 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48844837 ]}.
48854838 endef
48864839 else
48874840 define app_file
4888 {application, $(PROJECT), [
4841 {application, '$(PROJECT)', [
48894842 {description, "$(PROJECT_DESCRIPTION)"},
48904843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48914844 {id$(comma)$(space)"$(1)"}$(comma))
48924845 {modules, [$(call comma_list,$(2))]},
48934846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
48944847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4895 {mod, {$(PROJECT_MOD), []}}
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48964850 ]}.
48974851 endef
48984852 endif
49024856
49034857 # Source files.
49044858
4905 ERL_FILES = $(sort $(call core_find,src/,*.erl))
4906 CORE_FILES = $(sort $(call core_find,src/,*.core))
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
49074863
49084864 # ASN.1 files.
49094865
49134869
49144870 define compile_asn1
49154871 $(verbose) mkdir -p include/
4916 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
49174873 $(verbose) mv asn1/*.erl src/
49184874 $(verbose) mv asn1/*.hrl include/
49194875 $(verbose) mv asn1/*.asn1db include/
49364892
49374893 # Leex and Yecc files.
49384894
4939 XRL_FILES = $(sort $(call core_find,src/,*.xrl))
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
49404896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
49414897 ERL_FILES += $(XRL_ERL_FILES)
49424898
4943 YRL_FILES = $(sort $(call core_find,src/,*.yrl))
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
49444900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
49454901 ERL_FILES += $(YRL_ERL_FILES)
49464902
49474903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4948 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
49494905
49504906 # Erlang and Core Erlang files.
49514907
49954951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49964952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49974953 (F, Mod, import, {Imp, _}) ->
4998 case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
49994960 false -> ok;
50004961 true -> Add(Mod, Imp)
50014962 end;
50194980 end || F <- ErlFiles],
50204981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
50214982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
50224991 ok = file:write_file("$(1)", [
50234992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
5024 "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n"
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
50254994 ]),
50264995 halt()
50274996 endef
50345003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
50355004 # Rebuild everything when the Makefile changes.
50365005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5037 @mkdir -p $(ERLANG_MK_TMP)
5038 @if test -f $@; then \
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
50395008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
50405009 touch -c $(PROJECT).d; \
50415010 fi
5042 @touch $@
5011 $(verbose) touch $@
50435012
50445013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
50455014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
50465015 endif
50475016
5048 -include $(PROJECT).d
5017 include $(wildcard $(PROJECT).d)
50495018
50505019 ebin/$(PROJECT).app:: ebin/
50515020
50645033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
50655034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
50665035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5067 $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
50685037 > ebin/$(PROJECT).app
50695038 else
50705039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
50885057
50895058 endif
50905059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
50915061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
50925062 # This file is part of erlang.mk and subject to the terms of the ISC License.
50935063
51055075 doc-deps:
51065076 else
51075077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5108 $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5109 endif
5110
5111 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51125082 # This file is part of erlang.mk and subject to the terms of the ISC License.
51135083
51145084 .PHONY: rel-deps
51255095 rel-deps:
51265096 else
51275097 rel-deps: $(ALL_REL_DEPS_DIRS)
5128 $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5129 endif
5130
5131 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51325102 # This file is part of erlang.mk and subject to the terms of the ISC License.
51335103
51345104 .PHONY: test-deps test-dir test-build clean-test-dir
51505120 test-deps:
51515121 else
51525122 test-deps: $(ALL_TEST_DEPS_DIRS)
5153 $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
51545124 endif
51555125
51565126 ifneq ($(wildcard $(TEST_DIR)),)
51835153 endif
51845154 endif
51855155
5186 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51875157 # This file is part of erlang.mk and subject to the terms of the ISC License.
51885158
51895159 .PHONY: rebar.config
52195189 rebar.config:
52205190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
52215191
5222 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
52235193 # This file is part of erlang.mk and subject to the terms of the ISC License.
52245194
5225 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
5226
5227 MAN_INSTALL_PATH ?= /usr/local/share/man
5228 MAN_SECTIONS ?= 3 7
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
52295200
52305201 docs:: asciidoc
52315202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
52325207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
52335210
52345211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
52355212 asciidoc-guide:
52365213 else
5237 asciidoc-guide: distclean-asciidoc doc-deps
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
52385215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
52395216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5240 endif
5241
5242 ifeq ($(wildcard doc/src/manual/*.asciidoc),)
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
52435227 asciidoc-manual:
52445228 else
5245 asciidoc-manual: distclean-asciidoc doc-deps
5246 for f in doc/src/manual/*.asciidoc ; do \
5247 a2x -v -f manpage $$f ; \
5248 done
5249 for s in $(MAN_SECTIONS); do \
5250 mkdir -p doc/man$$s/ ; \
5251 mv doc/src/manual/*.$$s doc/man$$s/ ; \
5252 gzip doc/man$$s/*.$$s ; \
5253 done
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
52545262
52555263 install-docs:: install-asciidoc
52565264
52575265 install-asciidoc: asciidoc-manual
5258 for s in $(MAN_SECTIONS); do \
5259 mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
5260 install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
5261 done
5262 endif
5263
5264 distclean:: distclean-asciidoc
5265
5266 distclean-asciidoc:
5267 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
5268
5269 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
52705276 # This file is part of erlang.mk and subject to the terms of the ISC License.
52715277
52725278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
53235329 define bs_Makefile
53245330 PROJECT = $p
53255331 PROJECT_DESCRIPTION = New project
5326 PROJECT_VERSION = 0.0.1
5332 PROJECT_VERSION = 0.1.0
53275333
53285334 # Whitespace to be used when creating files from templates.
53295335 SP = $(SP)
53335339 define bs_Makefile
53345340 PROJECT = $p
53355341 PROJECT_DESCRIPTION = New project
5336 PROJECT_VERSION = 0.0.1
5342 PROJECT_VERSION = 0.1.0
53375343
53385344 endef
53395345 endif
53415347 define bs_apps_Makefile
53425348 PROJECT = $p
53435349 PROJECT_DESCRIPTION = New project
5344 PROJECT_VERSION = 0.0.1
5350 PROJECT_VERSION = 0.1.0
53455351
53465352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
53475353 endef
53615367 endef
53625368
53635369 define bs_relx_config
5364 {release, {$p_release, "1"}, [$p]}.
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
53655371 {extended_start_script, true}.
53665372 {sys_config, "rel/sys.config"}.
53675373 {vm_args, "rel/vm.args"}.
57125718 ifndef t
57135719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57145720 endif
5715 ifndef tpl_$(t)
5716 $(error Unknown template)
5717 endif
57185721 ifndef n
57195722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57205723 endif
57275730 list-templates:
57285731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
57295732
5730 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
57315734 # This file is part of erlang.mk and subject to the terms of the ISC License.
57325735
57335736 .PHONY: clean-c_src distclean-c_src-env
59625965 $(call render_template,bs_erl_nif,src/$n.erl)
59635966 endif
59645967
5965 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
59665969 # This file is part of erlang.mk and subject to the terms of the ISC License.
59675970
5968 .PHONY: ci ci-setup distclean-kerl
5969
5970 KERL ?= $(CURDIR)/kerl
5971 export KERL
5972
5973 KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
5974
5975 OTP_GIT ?= https://github.com/erlang/otp
5976
5977 CI_INSTALL_DIR ?= $(HOME)/erlang
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
59785973 CI_OTP ?=
5979
5980 ifeq ($(strip $(CI_OTP)),)
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
59815986 ci::
59825987 else
5983 ci:: $(addprefix ci-,$(CI_OTP))
5984
5985 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
59866007
59876008 ci-setup::
6009
6010 ci-extra::
59886011
59896012 ci_verbose_0 = @echo " CI " $(1);
59906013 ci_verbose = $(ci_verbose_$(V))
59916014
59926015 define ci_target
5993 ci-$(1): $(CI_INSTALL_DIR)/$(1)
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
59946018 $(ci_verbose) \
5995 PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
5996 CI_OTP_RELEASE="$(1)" \
5997 CT_OPTS="-label $(1)" \
5998 $(MAKE) clean ci-setup tests
5999 endef
6000
6001 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
60026030
60036031 define ci_otp_target
60046032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
60056033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6006 $(KERL) build git $(OTP_GIT) $(1) $(1)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
60076035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
60086036 endif
60096037 endef
60106038
60116039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
60126040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
60136052 $(KERL):
6014 $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
60156056 $(verbose) chmod +x $(KERL)
60166057
60176058 help::
60286069 $(gen_verbose) rm -rf $(KERL)
60296070 endif
60306071
6031 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
60326073 # This file is part of erlang.mk and subject to the terms of the ISC License.
60336074
60346075 .PHONY: ct apps-ct distclean-ct
60366077 # Configuration.
60376078
60386079 CT_OPTS ?=
6080
60396081 ifneq ($(wildcard $(TEST_DIR)),)
6040 CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6041 else
6042 CT_SUITES ?=
6043 endif
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
60446088
60456089 # Core targets.
60466090
60636107 -noinput \
60646108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
60656109 -dir $(TEST_DIR) \
6066 -logdir $(CURDIR)/logs
6110 -logdir $(CT_LOGS_DIR)
60676111
60686112 ifeq ($(CT_SUITES),)
60696113 ct: $(if $(IS_APP),,apps-ct)
60706114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
60716117 ct: test-build $(if $(IS_APP),,apps-ct)
6072 $(verbose) mkdir -p $(CURDIR)/logs/
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
60736119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
60746121 endif
60756122
60766123 ifneq ($(ALL_APPS_DIRS),)
60976144
60986145 define ct_suite_target
60996146 ct-$(1): test-build
6100 $(verbose) mkdir -p $(CURDIR)/logs/
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
61016148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
61026149 endef
61036150
61046151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
61056152
61066153 distclean-ct:
6107 $(gen_verbose) rm -rf $(CURDIR)/logs/
6108
6109 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61106157 # This file is part of erlang.mk and subject to the terms of the ISC License.
61116158
61126159 .PHONY: plt distclean-plt dialyze
61506197 endef
61516198
61526199 $(DIALYZER_PLT): deps app
6153 $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
61546204
61556205 plt: $(DIALYZER_PLT)
61566206
61646214 endif
61656215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
61666216
6167 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61686218 # This file is part of erlang.mk and subject to the terms of the ISC License.
61696219
61706220 .PHONY: distclean-edoc edoc
61726222 # Configuration.
61736223
61746224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
61756235
61766236 # Core targets.
61776237
6178 ifneq ($(wildcard doc/overview.edoc),)
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
61796239 docs:: edoc
61806240 endif
61816241
61846244 # Plugin-specific targets.
61856245
61866246 edoc: distclean-edoc doc-deps
6187 $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
61886248
61896249 distclean-edoc:
61906250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
61916251
6192 # Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61936253 # This file is part of erlang.mk and subject to the terms of the ISC License.
61946254
6195 .PHONY: distclean-escript escript
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
61966320
61976321 # Configuration.
61986322
61996323 ESCRIPT_NAME ?= $(PROJECT)
62006324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
62016325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
62026327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6203
6204 ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
6205 ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
6206 ESCRIPT_EMU_ARGS ?= -pa . \
6207 -sasl errlog_type error \
6208 -escript main $(ESCRIPT_NAME)
6209 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6210 ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
62116332
62126333 # Core targets.
62136334
62206341
62216342 # Plugin-specific targets.
62226343
6223 # Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
6224 # Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
6225 # Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
6226 # Software may only be used for the great good and the true happiness of all
6227 # sentient beings.
6228
6229 define ESCRIPT_RAW
6230 'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
6231 'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
6232 ' [F || F <- A, not filelib:is_dir(F) ] end,'\
6233 'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
6234 'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
6235 'Ez = fun(Escript) ->'\
6236 ' Static = Files([$(ESCRIPT_STATIC)]),'\
6237 ' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
6238 ' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
6239 ' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
6240 ' {archive, Archive, [memory]},'\
6241 ' {shebang, "$(ESCRIPT_SHEBANG)"},'\
6242 ' {comment, "$(ESCRIPT_COMMENT)"},'\
6243 ' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
6244 ' ]),'\
6245 ' file:change_mode(Escript, 8#755)'\
6246 'end,'\
6247 'Ez("$(ESCRIPT_FILE)"),'\
6248 'halt().'
6249 endef
6250
6251 ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
6252
6253 escript:: distclean-escript deps app
6254 $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
62556360
62566361 distclean-escript:
6257 $(gen_verbose) rm -f $(ESCRIPT_NAME)
6258
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
62596365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6260 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
62616366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
62626367
62636368 .PHONY: eunit apps-eunit
63216426
63226427 ifneq ($(ALL_APPS_DIRS),)
63236428 apps-eunit:
6324 $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done
6325 endif
6326 endif
6327
6328 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
63296436 # This file is part of erlang.mk and subject to the terms of the ISC License.
63306437
6331 .PHONY: relx-rel distclean-relx-rel distclean-relx run
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
63326439
63336440 # Configuration.
63346441
6335 RELX ?= $(CURDIR)/relx
6442 RELX ?= $(ERLANG_MK_TMP)/relx
63366443 RELX_CONFIG ?= $(CURDIR)/relx.config
63376444
63386445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
63396446 RELX_OPTS ?=
63406447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
63416454
63426455 ifeq ($(firstword $(RELX_OPTS)),-o)
63436456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
63506463 ifeq ($(IS_DEP),)
63516464 ifneq ($(wildcard $(RELX_CONFIG)),)
63526465 rel:: relx-rel
6353 endif
6354 endif
6355
6356 distclean:: distclean-relx-rel distclean-relx
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
63576472
63586473 # Plugin-specific targets.
63596474
63626477 $(verbose) chmod +x $(RELX)
63636478
63646479 relx-rel: $(RELX) rel-deps app
6365 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
63666484
63676485 distclean-relx-rel:
63686486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6369
6370 distclean-relx:
6371 $(gen_verbose) rm -rf $(RELX)
63726487
63736488 # Run target.
63746489
63776492 else
63786493
63796494 define get_relx_release.erl
6380 {ok, Config} = file:consult("$(RELX_CONFIG)"),
6381 {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
6382 io:format("~s", [Name]),
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
63836504 halt(0).
63846505 endef
63856506
6386 RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
63876514
63886515 run: all
6389 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
63906517
63916518 help::
63926519 $(verbose) printf "%s\n" "" \
63956522
63966523 endif
63976524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
63986526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6399 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
64006527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
64016528
64026529 .PHONY: shell
64216548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
64226549
64236550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6424 $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
64256552
64266553 shell: build-shell-deps
64276554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
64286555
6429 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
64306571 # This file is part of erlang.mk and subject to the terms of the ISC License.
64316572
64326573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
64376578 tests:: triq
64386579
64396580 define triq_check.erl
6440 code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
64416582 try
64426583 case $(1) of
64436584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
64696610 endif
64706611 endif
64716612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
64726614 # Copyright (c) 2015, Erlang Solutions Ltd.
64736615 # This file is part of erlang.mk and subject to the terms of the ISC License.
64746616
64856627 XREFR ?= $(CURDIR)/xrefr
64866628 export XREFR
64876629
6488 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
64896631
64906632 # Core targets.
64916633
64926634 help::
6493 $(verbose) printf "%s\n" "" \
6494 "Xref targets:" \
6495 " xref Run Xrefr using $XREF_CONFIG as config file if defined"
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
64966638
64976639 distclean:: distclean-xref
64986640
65086650 distclean-xref:
65096651 $(gen_verbose) rm -rf $(XREFR)
65106652
6511 # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
65126655 # This file is part of erlang.mk and subject to the terms of the ISC License.
65136656
65146657 COVER_REPORT_DIR = cover
65176660
65186661 ifdef COVER
65196662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
65206664 # All modules in 'ebin'
65216665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
65226666
65316675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
65326676 endif
65336677 endif
6678 endif
65346679
65356680 # Core targets
65366681
65526697 "Cover targets:" \
65536698 " cover-report Generate a HTML coverage report from previously collected" \
65546699 " cover data." \
6555 " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
65566701 "" \
65576702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
65586703 "target tests additionally generates a HTML coverage report from the combined" \
65656710
65666711 .PHONY: coverdata-clean
65676712 coverdata-clean:
6568 $(gen_verbose) rm -f *.coverdata ct.cover.spec
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
65696714
65706715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
65716721 all.coverdata: $(COVERDATA)
6572 $(gen_verbose) $(ERL) -eval ' \
6573 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
6574 cover:export("$@"), halt(0).'
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
65756723
65766724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
65776725 # empty if you want the coverdata files but not the HTML report.
65896737 # Modules which include eunit.hrl always contain one line without coverage
65906738 # because eunit defines test/0 which is never called. We compensate for this.
65916739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6592 grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
65936741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
65946742
65956743 define cover_report.erl
66246772 endef
66256773
66266774 cover-report:
6627 $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
66286776 $(gen_verbose) $(call erlang,$(cover_report.erl))
66296777
66306778 endif
66316779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
66326839
66336840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
66346841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
66976904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
66986905 endif
66996906 ifndef IS_APP
6700 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
67016908 $(MAKE) -C $$dep $@ \
67026909 IS_APP=1 \
6703 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6704 || exit $$?; \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67056911 done
67066912 endif
6707 $(verbose) for dep in $^ ; do \
6913 $(verbose) set -e; for dep in $^ ; do \
67086914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
67096915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6710 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
67116917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
67126918 $(MAKE) -C $$dep fetch-deps \
67136919 IS_DEP=1 \
6714 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6715 || exit $$?; \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67166921 fi \
67176922 fi \
67186923 done
1616 <tr>
1717 <th>Message queue length</th>
1818 <td><%= process.message_queue_len %></td>
19 </tr>
20 <tr>
21 <th>Internal buffer <span class="help" id="process-internal-buffer"></span></th>
22 <td><pre><%= process.buffer_len %></pre></td>
1923 </tr>
2024 <tr>
2125 <th>Reductions / sec</th>
2929 <th><%= fmt_sort('Memory', 'memory') %></th>
3030 <th><%= fmt_sort('Reductions / sec', 'reduction_delta') %></th>
3131 <th>Message queue</th>
32 <th>Internal buffer <span class="help" id="process-internal-buffer"></span></th>
3233 <th>Status</th>
3334 </tr>
3435 </thead>
4445 <td><%= fmt_bytes(process.memory * 1.0) %></td>
4546 <td class="r"><%= fmt_reduction_delta(process.reduction_delta) %></td>
4647 <td class="r"><%= process.message_queue_len %></td>
48 <td class="r"><%= process.buffer_len %></td>
4749 <td><%= process.status %></td>
4850 </tr>
4951 <% } %>
2929 NAVIGATION['Admin'][0]['Top Processes'] = ['#/top', 'administrator'];
3030 NAVIGATION['Admin'][0]['Top ETS Tables'] = ['#/top/ets', 'administrator'];
3131
32 HELP['process-internal-buffer'] = "Some processes drain their Erlang process mailbox (Erlang message queue) into a separate priority queue. \"Queue\" here refers to a data structure and should not be confused with RabbitMQ queues.";
33
3234 $('select#top-node').live('change', function() {
33 go_to('#/top/' + $(this).val());
35 var url='#/top/' + $(this).val() + "/" + $('select#row-count').val();
36 go_to(url);
3437 });
3538
3639 $('select#top-node-ets').live('change', function() {
37 go_to('#/top/ets' + $(this).val());
40 var url='#/top/' + $(this).val() + "/" + $('select#row-count-ets').val();
41 go_to(url);
3842 });
3943
4044 $('select#row-count').live('change', function() {
22 # before the inclusion of erlang.mk leading to the wrong target becoming
33 # the default.
44 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
526 endif
627
728 # --------------------------------------------------------------------
2041 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
2142 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
2243 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
2345 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
2446 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
2547 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
2648 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
2749 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
2851 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
2952 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
3054 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
3155 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
3256 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
3559 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
3660 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
3761 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
3863 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
3964 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
4065 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
7095
7196 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
7297
73 # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
74 # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
75 # needs to add "ranch" as a BUILD_DEPS. The list of projects needing
76 # this workaround are:
77 # o rabbitmq-web-stomp
78 dep_ranch = git https://github.com/ninenines/ranch 1.2.1
98 # Third-party dependencies version pinning.
99 #
100 # We do that in this file, which is copied in all projects, to ensure
101 # all projects use the same versions. It avoids conflicts and makes it
102 # possible to work with rabbitmq-public-umbrella.
103
104 dep_cowboy_commit = 1.0.4
105 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
106 dep_ranch_commit = 1.3.1
107 dep_webmachine_commit = 1.10.8p2
79108
80109 RABBITMQ_COMPONENTS = amqp_client \
81110 rabbit \
82111 rabbit_common \
83112 rabbitmq_amqp1_0 \
84113 rabbitmq_auth_backend_amqp \
114 rabbitmq_auth_backend_cache \
85115 rabbitmq_auth_backend_http \
86116 rabbitmq_auth_backend_ldap \
87117 rabbitmq_auth_mechanism_ssl \
88118 rabbitmq_boot_steps_visualiser \
89119 rabbitmq_clusterer \
120 rabbitmq_cli \
90121 rabbitmq_codegen \
91122 rabbitmq_consistent_hash_exchange \
123 rabbitmq_ct_client_helpers \
92124 rabbitmq_ct_helpers \
93125 rabbitmq_delayed_message_exchange \
94126 rabbitmq_dotnet_client \
97129 rabbitmq_federation_management \
98130 rabbitmq_java_client \
99131 rabbitmq_jms_client \
132 rabbitmq_jms_cts \
100133 rabbitmq_jms_topic_exchange \
101134 rabbitmq_lvc \
102135 rabbitmq_management \
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is VMware, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_top_app).
1919
2020 -export([dispatcher/0, web_ui/0]).
2121
22 dispatcher() -> [{["top", node], rabbit_top_wm_processes, []},
23 {["top", "ets", node], rabbit_top_wm_ets_tables, []},
24 {["process", pid], rabbit_top_wm_process, []}].
22 dispatcher() -> [{"/top/:node", rabbit_top_wm_processes, []},
23 {"/top/ets/:node", rabbit_top_wm_ets_tables, []},
24 {"/process/:pid", rabbit_top_wm_process, []}].
2525 web_ui() -> [{javascript, <<"top.js">>}].
1818 -include_lib("rabbit_common/include/rabbit.hrl").
1919
2020 -export([toplist/3, fmt_all/1, fmt/1, obtain_name/1, safe_process_info/2]).
21 -export([sort_by_param/2, sort_order_param/1, row_count_param/2]).
2122
2223 toplist(Key, Count, List) ->
2324 Sorted = lists:sublist(
2829 toplist(Key, Info) ->
2930 {Key, Val} = lists:keyfind(Key, 1, Info),
3031 {Val, Info}.
32
33 sort_by_param(ReqData, Default) ->
34 case cowboy_req:qs_val(<<"sort">>, ReqData) of
35 {undefined, _} -> Default;
36 {Bin, _} -> rabbit_data_coercion:to_atom(Bin)
37 end.
38
39 sort_order_param(ReqData) ->
40 case cowboy_req:qs_val(<<"sort_reverse">>, ReqData) of
41 {<<"true">>, _} -> asc;
42 _ -> desc
43 end.
44
45 row_count_param(ReqData, Default) ->
46 case cowboy_req:qs_val(<<"row_count">>, ReqData) of
47 {undefined, _} -> Default;
48 {Bin, _} -> rabbit_data_coercion:to_integer(Bin)
49 end.
3150
3251 add_name(Info) ->
3352 {pid, Pid} = lists:keyfind(pid, 1, Info),
121140 fail
122141 end.
123142
124 guess_initial_call({supervisor, _F, _A}) -> supervisor;
125 guess_initial_call({supervisor2, _F, _A}) -> supervisor;
126 guess_initial_call({mochiweb_acceptor, _F, _A}) -> mochiweb_http;
127 guess_initial_call(_MFA) -> fail.
143 guess_initial_call({supervisor, _F, _A}) -> supervisor;
144 guess_initial_call({supervisor2, _F, _A}) -> supervisor;
145 guess_initial_call({cowboy_protocol, _F, _A}) -> cowboy_protocol;
146 guess_initial_call(_MFA) -> fail.
128147
129148
130149 safe_process_info(Pid, Info) ->
1515
1616 -module(rabbit_top_wm_ets_tables).
1717
18 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2]).
18 -export([init/3]).
19 -export([rest_init/2, to_json/2, content_types_provided/2, is_authorized/2]).
1920
20 -include_lib("rabbitmq_management/include/rabbit_mgmt.hrl").
21 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2122 -include_lib("amqp_client/include/amqp_client.hrl").
22 -include_lib("webmachine/include/webmachine.hrl").
2323
2424 %%--------------------------------------------------------------------
2525
26 init(_Config) -> {ok, #context{}}.
26 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
27
28 rest_init(ReqData, _) -> {ok, ReqData, #context{}}.
2729
2830 content_types_provided(ReqData, Context) ->
29 {[{"application/json", to_json}], ReqData, Context}.
31 {[{<<"application/json">>, to_json}], ReqData, Context}.
3032
3133 to_json(ReqData, Context) ->
32 Sort = case wrq:get_qs_value("sort", ReqData) of
33 undefined -> memory;
34 Str -> list_to_atom(Str)
35 end,
36 Node = b2a(rabbit_mgmt_util:id(node, ReqData)),
37 Order = case wrq:get_qs_value("sort_reverse", ReqData) of
38 "true" -> asc;
39 _ -> desc
40 end,
41 RowCount = case wrq:get_qs_value("row_count", ReqData) of
42 undefined -> 20;
43 List when is_list(List) -> list_to_integer(List)
44 end,
34 Sort = rabbit_top_util:sort_by_param(ReqData, memory),
35 Node = rabbit_data_coercion:to_atom(rabbit_mgmt_util:id(node, ReqData)),
36 Order = rabbit_top_util:sort_order_param(ReqData),
37 RowCount = rabbit_top_util:row_count_param(ReqData, 20),
38
4539 rabbit_mgmt_util:reply([{node, Node},
4640 {row_count, RowCount},
4741 {ets_tables, ets_tables(Node, Sort, Order, RowCount)}],
5246
5347 %%--------------------------------------------------------------------
5448
55 b2a(B) -> list_to_atom(binary_to_list(B)).
56
5749 ets_tables(Node, Sort, Order, RowCount) ->
5850 [fmt(P) || P <- rabbit_top_worker:ets_tables(Node, Sort, Order, RowCount)].
5951
1515
1616 -module(rabbit_top_wm_process).
1717
18 -export([init/1, to_json/2, resource_exists/2, content_types_provided/2,
18 -export([init/3]).
19 -export([rest_init/2, to_json/2, resource_exists/2, content_types_provided/2,
1920 is_authorized/2]).
2021
2122 -define(ADDITIONAL_INFO,
2223 [current_stacktrace, trap_exit, links, monitors, monitored_by]).
2324
24 -include_lib("rabbitmq_management/include/rabbit_mgmt.hrl").
25 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2526 -include_lib("amqp_client/include/amqp_client.hrl").
26 -include_lib("webmachine/include/webmachine.hrl").
2727
2828 %%--------------------------------------------------------------------
2929
30 init(_Config) -> {ok, #context{}}.
30 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
31
32 rest_init(ReqData, _) -> {ok, ReqData, #context{}}.
3133
3234 content_types_provided(ReqData, Context) ->
33 {[{"application/json", to_json}], ReqData, Context}.
35 {[{<<"application/json">>, to_json}], ReqData, Context}.
3436
3537 to_json(ReqData, Context) ->
3638 rabbit_mgmt_util:reply(proc(ReqData), ReqData, Context).
1515
1616 -module(rabbit_top_wm_processes).
1717
18 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2]).
18 -export([init/3]).
19 -export([rest_init/2, to_json/2, content_types_provided/2, is_authorized/2]).
1920
20 -include_lib("rabbitmq_management/include/rabbit_mgmt.hrl").
21 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2122 -include_lib("amqp_client/include/amqp_client.hrl").
22 -include_lib("webmachine/include/webmachine.hrl").
2323
2424 %%--------------------------------------------------------------------
2525
26 init(_Config) -> {ok, #context{}}.
26 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
27
28 rest_init(ReqData, _) -> {ok, ReqData, #context{}}.
2729
2830 content_types_provided(ReqData, Context) ->
29 {[{"application/json", to_json}], ReqData, Context}.
31 {[{<<"application/json">>, to_json}], ReqData, Context}.
3032
3133 to_json(ReqData, Context) ->
32 Sort = case wrq:get_qs_value("sort", ReqData) of
33 undefined -> reduction_delta;
34 Str -> list_to_atom(Str)
35 end,
36 Node = b2a(rabbit_mgmt_util:id(node, ReqData)),
37 Order = case wrq:get_qs_value("sort_reverse", ReqData) of
38 "true" -> asc;
39 _ -> desc
40 end,
41 RowCount = case wrq:get_qs_value("row_count", ReqData) of
42 undefined -> 20;
43 List when is_list(List) -> list_to_integer(List)
44 end,
34 Sort = rabbit_top_util:sort_by_param(ReqData, reduction_delta),
35 Node = rabbit_data_coercion:to_atom(rabbit_mgmt_util:id(node, ReqData)),
36 Order = rabbit_top_util:sort_order_param(ReqData),
37 RowCount = rabbit_top_util:row_count_param(ReqData, 20),
38
4539 rabbit_mgmt_util:reply([{node, Node},
4640 {row_count, RowCount},
4741 {processes, procs(Node, Sort, Order, RowCount)}],
5246
5347 %%--------------------------------------------------------------------
5448
55 b2a(B) -> list_to_atom(binary_to_list(B)).
56
5749 procs(Node, Sort, Order, RowCount) ->
5850 [fmt(P) || P <- rabbit_top_worker:procs(Node, Sort, Order, RowCount)].
5951
101101 {ok, OldProps} -> reductions(OldProps);
102102 error -> 0
103103 end) div ?EVERY,
104 dict:store(
105 Pid, [{reduction_delta, Delta} | Props], Procs)
104 NewProps = expand_gen_server2_info(
105 Pid, [{reduction_delta, Delta} | Props]),
106 dict:store(Pid, NewProps, Procs)
106107 end
107108 end, dict:new(), processes()).
108109
160161 Words * erlang:system_info(wordsize)
161162 catch
162163 _:_ -> 0
163 end.
164 end.
165
166 expand_gen_server2_info(Pid, Props) ->
167 case rabbit_core_metrics:get_gen_server2_stats(Pid) of
168 not_found ->
169 Props;
170 BufferLength ->
171 [{buffer_len, BufferLength} | Props]
172 end.
+0
-7
deps/rabbitmq_top/src/rabbitmq_top.app.src less more
0 {application, rabbitmq_top,
1 [{description, "RabbitMQ Top"},
2 {vsn, "3.6.6"},
3 {modules, []},
4 {registered, []},
5 {mod, {rabbit_top_app, []}},
6 {applications, [kernel, stdlib, rabbit_common, rabbit, rabbitmq_management]}]}.
00 PROJECT = rabbitmq_tracing
1 PROJECT_DESCRIPTION = RabbitMQ message logging / tracing
2 PROJECT_MOD = rabbit_tracing_app
13
2 DEPS = rabbit_common rabbit rabbitmq_management webmachine
3 TEST_DEPS = rabbitmq_ct_helpers
4 define PROJECT_ENV
5 [
6 {directory, "/var/tmp/rabbitmq-tracing"},
7 {username, <<"guest">>},
8 {password, <<"guest">>}
9 ]
10 endef
411
12 DEPS = rabbit_common rabbit rabbitmq_management
13 TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers
14
15 DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
516 DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk
617
718 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
0 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
11 #
22 # Permission to use, copy, modify, and/or distribute this software for any
33 # purpose with or without fee is hereby granted, provided that the above
1111 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1212 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1313
14 .PHONY: all app apps deps search rel docs install-docs check tests clean distclean help erlang-mk
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
1515
1616 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17
18 ERLANG_MK_VERSION = 2.0.0-pre.2-144-g647ffd1
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
1931
2032 # Core configuration.
2133
2436
2537 PROJECT_VERSION ?= rolling
2638 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
2740
2841 # Verbosity.
2942
8497 rel::
8598 $(verbose) :
8699
100 relup:: deps app
101
87102 check:: tests
88103
89104 clean:: clean-crashdump
101116 help::
102117 $(verbose) printf "%s\n" \
103118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
104 "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
105120 "" \
106121 "Usage: [V=1] $(MAKE) [target]..." \
107122 "" \
149164 core_native_path = $1
150165 endif
151166
152 ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
153 define core_http_get
154 wget --no-check-certificate -O $(1) $(2)|| rm $(1)
155 endef
156 else
157 define core_http_get.erl
158 ssl:start(),
159 inets:start(),
160 case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
161 {ok, {{_, 200, _}, _, Body}} ->
162 case file:write_file("$(1)", Body) of
163 ok -> ok;
164 {error, R1} -> halt(R1)
165 end;
166 {error, R2} ->
167 halt(R2)
168 end,
169 halt(0).
170 endef
171
172 define core_http_get
173 $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
174 endef
175 endif
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
176168
177169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
178170
192184 ERLANG_MK_BUILD_CONFIG ?= build.config
193185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
194186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
195188 erlang-mk:
196189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
197190 ifdef ERLANG_MK_COMMIT
198191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
199192 endif
200193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
201 $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
202195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
203196 rm -rf $(ERLANG_MK_BUILD_DIR)
204197
285278 pkg_apns_repo = https://github.com/inaka/apns4erl
286279 pkg_apns_commit = master
287280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
288289 PACKAGES += azdht
289290 pkg_azdht_name = azdht
290291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
405406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
406407 pkg_bootstrap_commit = master
407408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
408417 PACKAGES += boss_db
409418 pkg_boss_db_name = boss_db
410419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
413422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
414423 pkg_boss_db_commit = master
415424
416 PACKAGES += boss
417 pkg_boss_name = boss
418 pkg_boss_description = Erlang web MVC, now featuring Comet
419 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
420 pkg_boss_fetch = git
421 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
422 pkg_boss_commit = master
423
424425 PACKAGES += brod
425426 pkg_brod_name = brod
426427 pkg_brod_description = Kafka client in Erlang
565566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
566567 pkg_cloudi_service_api_requests_commit = master
567568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
568585 PACKAGES += cloudi_service_db_cassandra_cql
569586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
570587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
573590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
574591 pkg_cloudi_service_db_cassandra_cql_commit = master
575592
576 PACKAGES += cloudi_service_db_cassandra
577 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
579 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
580 pkg_cloudi_service_db_cassandra_fetch = git
581 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
582 pkg_cloudi_service_db_cassandra_commit = master
583
584593 PACKAGES += cloudi_service_db_couchdb
585594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
586595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
637646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
638647 pkg_cloudi_service_db_tokyotyrant_commit = master
639648
640 PACKAGES += cloudi_service_db
641 pkg_cloudi_service_db_name = cloudi_service_db
642 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
643 pkg_cloudi_service_db_homepage = http://cloudi.org/
644 pkg_cloudi_service_db_fetch = git
645 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
646 pkg_cloudi_service_db_commit = master
647
648649 PACKAGES += cloudi_service_filesystem
649650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
650651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
10371038 pkg_edown_repo = https://github.com/uwiger/edown
10381039 pkg_edown_commit = master
10391040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
10401049 PACKAGES += eep_app
10411050 pkg_eep_app_name = eep_app
10421051 pkg_eep_app_description = Embedded Event Processing
10451054 pkg_eep_app_repo = https://github.com/darach/eep-erl
10461055 pkg_eep_app_commit = master
10471056
1048 PACKAGES += eep
1049 pkg_eep_name = eep
1050 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1051 pkg_eep_homepage = https://github.com/virtan/eep
1052 pkg_eep_fetch = git
1053 pkg_eep_repo = https://github.com/virtan/eep
1054 pkg_eep_commit = master
1055
10561057 PACKAGES += efene
10571058 pkg_efene_name = efene
10581059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
12371238 pkg_eqm_repo = https://github.com/loucash/eqm
12381239 pkg_eqm_commit = master
12391240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
12401249 PACKAGES += eredis_pool
12411250 pkg_eredis_pool_name = eredis_pool
12421251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
12451254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
12461255 pkg_eredis_pool_commit = master
12471256
1248 PACKAGES += eredis
1249 pkg_eredis_name = eredis
1250 pkg_eredis_description = Erlang Redis client
1251 pkg_eredis_homepage = https://github.com/wooga/eredis
1252 pkg_eredis_fetch = git
1253 pkg_eredis_repo = https://github.com/wooga/eredis
1254 pkg_eredis_commit = master
1255
12561257 PACKAGES += erl_streams
12571258 pkg_erl_streams_name = erl_streams
12581259 pkg_erl_streams_description = Streams in Erlang
15411542 pkg_etap_repo = https://github.com/ngerakines/etap
15421543 pkg_etap_commit = master
15431544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
15441553 PACKAGES += etest_http
15451554 pkg_etest_http_name = etest_http
15461555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
15491558 pkg_etest_http_repo = https://github.com/wooga/etest_http
15501559 pkg_etest_http_commit = master
15511560
1552 PACKAGES += etest
1553 pkg_etest_name = etest
1554 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1555 pkg_etest_homepage = https://github.com/wooga/etest
1556 pkg_etest_fetch = git
1557 pkg_etest_repo = https://github.com/wooga/etest
1558 pkg_etest_commit = master
1559
15601561 PACKAGES += etoml
15611562 pkg_etoml_name = etoml
15621563 pkg_etoml_description = TOML language erlang parser
15651566 pkg_etoml_repo = https://github.com/kalta/etoml
15661567 pkg_etoml_commit = master
15671568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
15681577 PACKAGES += eunit_formatters
15691578 pkg_eunit_formatters_name = eunit_formatters
15701579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
15731582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
15741583 pkg_eunit_formatters_commit = master
15751584
1576 PACKAGES += eunit
1577 pkg_eunit_name = eunit
1578 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1579 pkg_eunit_homepage = https://github.com/richcarl/eunit
1580 pkg_eunit_fetch = git
1581 pkg_eunit_repo = https://github.com/richcarl/eunit
1582 pkg_eunit_commit = master
1583
15841585 PACKAGES += euthanasia
15851586 pkg_euthanasia_name = euthanasia
15861587 pkg_euthanasia_description = Merciful killer for your Erlang processes
15981599 pkg_evum_commit = master
15991600
16001601 PACKAGES += exec
1601 pkg_exec_name = exec
1602 pkg_exec_name = erlexec
16021603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
16031604 pkg_exec_homepage = http://saleyn.github.com/erlexec
16041605 pkg_exec_fetch = git
17171718 pkg_fn_repo = https://github.com/reiddraper/fn
17181719 pkg_fn_commit = master
17191720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
17201729 PACKAGES += folsom_cowboy
17211730 pkg_folsom_cowboy_name = folsom_cowboy
17221731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
17251734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
17261735 pkg_folsom_cowboy_commit = master
17271736
1728 PACKAGES += folsom
1729 pkg_folsom_name = folsom
1730 pkg_folsom_description = Expose Erlang Events and Metrics
1731 pkg_folsom_homepage = https://github.com/boundary/folsom
1732 pkg_folsom_fetch = git
1733 pkg_folsom_repo = https://github.com/boundary/folsom
1734 pkg_folsom_commit = master
1735
17361737 PACKAGES += folsomite
17371738 pkg_folsomite_name = folsomite
17381739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
20932094 pkg_jesse_repo = https://github.com/for-GET/jesse
20942095 pkg_jesse_commit = master
20952096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
20962105 PACKAGES += jiffy_v
20972106 pkg_jiffy_v_name = jiffy_v
20982107 pkg_jiffy_v_description = JSON validation utility
21012110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
21022111 pkg_jiffy_v_commit = master
21032112
2104 PACKAGES += jiffy
2105 pkg_jiffy_name = jiffy
2106 pkg_jiffy_description = JSON NIFs for Erlang.
2107 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2108 pkg_jiffy_fetch = git
2109 pkg_jiffy_repo = https://github.com/davisp/jiffy
2110 pkg_jiffy_commit = master
2111
21122113 PACKAGES += jobs
21132114 pkg_jobs_name = jobs
21142115 pkg_jobs_description = a Job scheduler for load regulation
21252126 pkg_joxa_repo = https://github.com/joxa/joxa
21262127 pkg_joxa_commit = master
21272128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
21282137 PACKAGES += json_rec
21292138 pkg_json_rec_name = json_rec
21302139 pkg_json_rec_description = JSON to erlang record
21332142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
21342143 pkg_json_rec_commit = master
21352144
2136 PACKAGES += json
2137 pkg_json_name = json
2138 pkg_json_description = a high level json library for erlang (17.0+)
2139 pkg_json_homepage = https://github.com/talentdeficit/json
2140 pkg_json_fetch = git
2141 pkg_json_repo = https://github.com/talentdeficit/json
2142 pkg_json_commit = master
2143
21442145 PACKAGES += jsone
21452146 pkg_jsone_name = jsone
21462147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
21812182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
21822183 pkg_jsx_commit = master
21832184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
21842193 PACKAGES += kafka_protocol
21852194 pkg_kafka_protocol_name = kafka_protocol
21862195 pkg_kafka_protocol_description = Kafka protocol Erlang library
21892198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
21902199 pkg_kafka_protocol_commit = master
21912200
2192 PACKAGES += kafka
2193 pkg_kafka_name = kafka
2194 pkg_kafka_description = Kafka consumer and producer in Erlang
2195 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2196 pkg_kafka_fetch = git
2197 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2198 pkg_kafka_commit = master
2199
22002201 PACKAGES += kai
22012202 pkg_kai_name = kai
22022203 pkg_kai_description = DHT storage by Takeshi Inoue
22932294 pkg_kvs_repo = https://github.com/synrc/kvs
22942295 pkg_kvs_commit = master
22952296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
22962305 PACKAGES += lager_amqp_backend
22972306 pkg_lager_amqp_backend_name = lager_amqp_backend
22982307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
23092318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
23102319 pkg_lager_syslog_commit = master
23112320
2312 PACKAGES += lager
2313 pkg_lager_name = lager
2314 pkg_lager_description = A logging framework for Erlang/OTP.
2315 pkg_lager_homepage = https://github.com/basho/lager
2316 pkg_lager_fetch = git
2317 pkg_lager_repo = https://github.com/basho/lager
2318 pkg_lager_commit = master
2319
23202321 PACKAGES += lambdapad
23212322 pkg_lambdapad_name = lambdapad
23222323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
25732574 pkg_mixer_repo = https://github.com/chef/mixer
25742575 pkg_mixer_commit = master
25752576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
25762585 PACKAGES += mochiweb_xpath
25772586 pkg_mochiweb_xpath_name = mochiweb_xpath
25782587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
25812590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
25822591 pkg_mochiweb_xpath_commit = master
25832592
2584 PACKAGES += mochiweb
2585 pkg_mochiweb_name = mochiweb
2586 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2587 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2588 pkg_mochiweb_fetch = git
2589 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2590 pkg_mochiweb_commit = master
2591
25922593 PACKAGES += mockgyver
25932594 pkg_mockgyver_name = mockgyver
25942595 pkg_mockgyver_description = A mocking library for Erlang
30613062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
30623063 pkg_quickrand_commit = master
30633064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
30643073 PACKAGES += rabbit_exchange_type_riak
30653074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
30663075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
30693078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
30703079 pkg_rabbit_exchange_type_riak_commit = master
30713080
3072 PACKAGES += rabbit
3073 pkg_rabbit_name = rabbit
3074 pkg_rabbit_description = RabbitMQ Server
3075 pkg_rabbit_homepage = https://www.rabbitmq.com/
3076 pkg_rabbit_fetch = git
3077 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3078 pkg_rabbit_commit = master
3079
30803081 PACKAGES += rack
30813082 pkg_rack_name = rack
30823083 pkg_rack_description = Rack handler for erlang
34933494 pkg_smother_repo = https://github.com/ramsay-t/Smother
34943495 pkg_smother_commit = master
34953496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
34963505 PACKAGES += social
34973506 pkg_social_name = social
34983507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
35413550 pkg_stable_repo = https://github.com/dvv/stable
35423551 pkg_stable_commit = master
35433552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
35443561 PACKAGES += statebox_riak
35453562 pkg_statebox_riak_name = statebox_riak
35463563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
35493566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
35503567 pkg_statebox_riak_commit = master
35513568
3552 PACKAGES += statebox
3553 pkg_statebox_name = statebox
3554 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3555 pkg_statebox_homepage = https://github.com/mochi/statebox
3556 pkg_statebox_fetch = git
3557 pkg_statebox_repo = https://github.com/mochi/statebox
3558 pkg_statebox_commit = master
3559
35603569 PACKAGES += statman
35613570 pkg_statman_name = statman
35623571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
37253734 pkg_tirerl_repo = https://github.com/inaka/tirerl
37263735 pkg_tirerl_commit = master
37273736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
37283745 PACKAGES += traffic_tools
37293746 pkg_traffic_tools_name = traffic_tools
37303747 pkg_traffic_tools_description = Simple traffic limiting library
40614078 pkg_zucchini_repo = https://github.com/devinus/zucchini
40624079 pkg_zucchini_commit = master
40634080
4064 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
40654082 # This file is part of erlang.mk and subject to the terms of the ISC License.
40664083
40674084 .PHONY: search
40884105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
40894106 endif
40904107
4091 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
40924109 # This file is part of erlang.mk and subject to the terms of the ISC License.
40934110
4094 .PHONY: distclean-deps
4111 .PHONY: distclean-deps clean-tmp-deps.log
40954112
40964113 # Configuration.
40974114
41104127
41114128 REBAR_DEPS_DIR = $(DEPS_DIR)
41124129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
41134150
41144151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
41154152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
41164153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
41174154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
41184155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
41194157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
41204158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
41214159
41384176
41394177 # Core targets.
41404178
4141 ifdef IS_APP
4142 apps::
4143 else
4144 apps:: $(ALL_APPS_DIRS)
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
41454180 ifeq ($(IS_APP)$(IS_DEP),)
41464181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
41474182 endif
41494184 # Create ebin directory for all apps to make sure Erlang recognizes them
41504185 # as proper OTP applications when using -include_lib. This is a temporary
41514186 # fix, a proper fix would be to compile apps/* in the right order.
4152 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4153 mkdir -p $$dep/ebin || exit $$?; \
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
41544190 done
4155 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
41564196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
41574197 :; \
41584198 else \
41594199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4160 $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
4200 $(MAKE) -C $$dep IS_APP=1; \
41614201 fi \
41624202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
41634207 endif
41644208
41654209 ifneq ($(SKIP_DEPS),)
41664210 deps::
41674211 else
4168 deps:: $(ALL_DEPS_DIRS) apps
4169 ifeq ($(IS_APP)$(IS_DEP),)
4170 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4171 endif
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
41724213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4173 $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
41744215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
41754216 :; \
41764217 else \
41774218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
41784219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4179 $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
4220 $(MAKE) -C $$dep IS_DEP=1; \
41804221 else \
4181 echo "Error: No Makefile to build dependency $$dep."; \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
41824223 exit 2; \
41834224 fi \
41844225 fi \
41924233 # in practice only Makefile is needed so far.
41934234 define dep_autopatch
41944235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
41954237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
41964238 $(call dep_autopatch_erlang_mk,$(1)); \
41974239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4198 if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
41994243 $(call dep_autopatch2,$(1)); \
4200 elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
42014245 $(call dep_autopatch2,$(1)); \
4202 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
42034247 $(call dep_autopatch2,$(1)); \
4204 else \
4205 $(call erlang,$(call dep_autopatch_app.erl,$(1))); \
42064248 fi \
42074249 else \
42084250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
42144256 endef
42154257
42164258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
42174262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
42184263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
42194264 fi; \
42204265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4221 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
42224267 $(call dep_autopatch_fetch_rebar); \
42234268 $(call dep_autopatch_rebar,$(1)); \
42244269 else \
42304275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
42314276 endef
42324277
4233 # Overwrite erlang.mk with the current file by default.
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
42344280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
42354281 define dep_autopatch_erlang_mk
4236 echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \
4237 > $(DEPS_DIR)/$1/erlang.mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
42384287 endef
42394288 else
42404289 define dep_autopatch_erlang_mk
42534302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
42544303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
42554304 cd $(ERLANG_MK_TMP)/rebar; \
4256 git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
42574306 $(MAKE); \
42584307 cd -; \
42594308 fi
42704319 define dep_autopatch_rebar.erl
42714320 application:load(rebar),
42724321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
42734323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
42744324 {ok, Conf0} -> Conf0;
42754325 _ -> []
44234473 [] -> ok;
44244474 _ ->
44254475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4426 PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
44274477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4428 PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L \\"~s\\" -lerl_interface -lei\n",
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
44294479 [code:lib_dir(erl_interface, lib)])),
44304480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
44314481 FilterEnv = fun(Env) ->
44644514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44654515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44664516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4467 [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
44684518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
44694519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
44704520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
44764526 end,
44774527 [PortSpec(S) || S <- PortSpecs]
44784528 end,
4479 Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"),
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
44804530 RunPlugin = fun(Plugin, Step) ->
44814531 case erlang:function_exported(Plugin, Step, 2) of
44824532 false -> ok;
45244574 halt()
45254575 endef
45264576
4527 define dep_autopatch_app.erl
4528 UpdateModules = fun(App) ->
4529 case filelib:is_regular(App) of
4530 false -> ok;
4531 true ->
4532 {ok, [{application, '$(1)', L0}]} = file:consult(App),
4533 Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true,
4534 fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
4535 L = lists:keystore(modules, 1, L0, {modules, Mods}),
4536 ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}]))
4537 end
4538 end,
4539 UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"),
4540 halt()
4541 endef
4542
45434577 define dep_autopatch_appsrc_script.erl
45444578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
45454579 AppSrcScript = AppSrc ++ ".script",
45874621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
45884622 endef
45894623
4590 define dep_fetch_hex.erl
4591 ssl:start(),
4592 inets:start(),
4593 {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
4594 {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
4595 [], [{body_format, binary}]),
4596 {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
4597 {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
4598 ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
4599 halt()
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
46004626 endef
46014627
46024628 # Hex only has a package version. No need to look in the Erlang.mk packages.
46034629 define dep_fetch_hex
4604 $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
46054634 endef
46064635
46074636 define dep_fetch_fail
46314660 $(eval DEP_NAME := $(call dep_name,$1))
46324661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
46334662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4634 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
46354664 exit 17; \
46364665 fi
46374666 $(verbose) mkdir -p $(DEPS_DIR)
46734702 clean:: clean-apps
46744703
46754704 clean-apps:
4676 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4677 $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
46784707 done
46794708
46804709 distclean:: distclean-apps
46814710
46824711 distclean-apps:
4683 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4684 $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
46854714 done
46864715 endif
46874716
47014730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
47024731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
47034732
4704 # External plugins.
4705
4706 DEP_PLUGINS ?=
4707
4708 define core_dep_plugin
4709 -include $(DEPS_DIR)/$(1)
4710
4711 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4712 endef
4713
4714 $(foreach p,$(DEP_PLUGINS),\
4715 $(eval $(if $(findstring /,$p),\
4716 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4717 $(call core_dep_plugin,$p/plugins.mk,$p))))
4718
4719 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4720 # This file is part of erlang.mk and subject to the terms of the ISC License.
4721
4722 # Configuration.
4723
4724 DTL_FULL_PATH ?=
4725 DTL_PATH ?= templates/
4726 DTL_SUFFIX ?= _dtl
4727 DTL_OPTS ?=
4728
4729 # Verbosity.
4730
4731 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
4732 dtl_verbose = $(dtl_verbose_$(V))
4733
4734 # Core targets.
4735
4736 DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl))
4737
4738 ifneq ($(DTL_FILES),)
4739
4740 ifdef DTL_FULL_PATH
4741 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
4742 else
4743 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
4744 endif
4745
4746 ifneq ($(words $(DTL_FILES)),0)
4747 # Rebuild templates when the Makefile changes.
4748 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
4749 @mkdir -p $(ERLANG_MK_TMP)
4750 @if test -f $@; then \
4751 touch $(DTL_FILES); \
4752 fi
4753 @touch $@
4754
4755 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
4756 endif
4757
4758 define erlydtl_compile.erl
4759 [begin
4760 Module0 = case "$(strip $(DTL_FULL_PATH))" of
4761 "" ->
4762 filename:basename(F, ".dtl");
4763 _ ->
4764 "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
4765 re:replace(F2, "/", "_", [{return, list}, global])
4766 end,
4767 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
4768 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
4769 ok -> ok;
4770 {ok, _} -> ok
4771 end
4772 end || F <- string:tokens("$(1)", " ")],
4773 halt().
4774 endef
4775
4776 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
4777 $(if $(strip $?),\
4778 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
4779
4780 endif
4781
4782 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
47834734 # This file is part of erlang.mk and subject to the terms of the ISC License.
47844735
47854736 # Verbosity.
47984749
47994750 define compile_proto.erl
48004751 [begin
4801 Dir = filename:dirname(filename:dirname(F)),
48024752 protobuffs_compile:generate_source(F,
4803 [{output_include_dir, Dir ++ "/include"},
4804 {output_src_dir, Dir ++ "/ebin"}])
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
48054755 end || F <- string:tokens("$(1)", " ")],
48064756 halt().
48074757 endef
48114761 $(if $(strip $?),$(call compile_proto,$?))
48124762 endif
48134763
4814 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
48154765 # This file is part of erlang.mk and subject to the terms of the ISC License.
48164766
48174767 .PHONY: clean-app
48254775 ERLC_EXCLUDE ?=
48264776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
48274777
4778 ERLC_ASN1_OPTS ?=
4779
48284780 ERLC_MIB_OPTS ?=
48294781 COMPILE_MIB_FIRST ?=
48304782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
48744826
48754827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
48764828 define app_file
4877 {application, $(PROJECT), [
4829 {application, '$(PROJECT)', [
48784830 {description, "$(PROJECT_DESCRIPTION)"},
48794831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48804832 {id$(comma)$(space)"$(1)"}$(comma))
48814833 {modules, [$(call comma_list,$(2))]},
48824834 {registered, []},
4883 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48844837 ]}.
48854838 endef
48864839 else
48874840 define app_file
4888 {application, $(PROJECT), [
4841 {application, '$(PROJECT)', [
48894842 {description, "$(PROJECT_DESCRIPTION)"},
48904843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48914844 {id$(comma)$(space)"$(1)"}$(comma))
48924845 {modules, [$(call comma_list,$(2))]},
48934846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
48944847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4895 {mod, {$(PROJECT_MOD), []}}
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48964850 ]}.
48974851 endef
48984852 endif
49024856
49034857 # Source files.
49044858
4905 ERL_FILES = $(sort $(call core_find,src/,*.erl))
4906 CORE_FILES = $(sort $(call core_find,src/,*.core))
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
49074863
49084864 # ASN.1 files.
49094865
49134869
49144870 define compile_asn1
49154871 $(verbose) mkdir -p include/
4916 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
49174873 $(verbose) mv asn1/*.erl src/
49184874 $(verbose) mv asn1/*.hrl include/
49194875 $(verbose) mv asn1/*.asn1db include/
49364892
49374893 # Leex and Yecc files.
49384894
4939 XRL_FILES = $(sort $(call core_find,src/,*.xrl))
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
49404896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
49414897 ERL_FILES += $(XRL_ERL_FILES)
49424898
4943 YRL_FILES = $(sort $(call core_find,src/,*.yrl))
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
49444900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
49454901 ERL_FILES += $(YRL_ERL_FILES)
49464902
49474903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4948 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
49494905
49504906 # Erlang and Core Erlang files.
49514907
49954951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49964952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49974953 (F, Mod, import, {Imp, _}) ->
4998 case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
49994960 false -> ok;
50004961 true -> Add(Mod, Imp)
50014962 end;
50194980 end || F <- ErlFiles],
50204981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
50214982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
50224991 ok = file:write_file("$(1)", [
50234992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
5024 "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n"
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
50254994 ]),
50264995 halt()
50274996 endef
50345003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
50355004 # Rebuild everything when the Makefile changes.
50365005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5037 @mkdir -p $(ERLANG_MK_TMP)
5038 @if test -f $@; then \
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
50395008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
50405009 touch -c $(PROJECT).d; \
50415010 fi
5042 @touch $@
5011 $(verbose) touch $@
50435012
50445013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
50455014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
50465015 endif
50475016
5048 -include $(PROJECT).d
5017 include $(wildcard $(PROJECT).d)
50495018
50505019 ebin/$(PROJECT).app:: ebin/
50515020
50645033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
50655034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
50665035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5067 $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
50685037 > ebin/$(PROJECT).app
50695038 else
50705039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
50885057
50895058 endif
50905059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
50915061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
50925062 # This file is part of erlang.mk and subject to the terms of the ISC License.
50935063
51055075 doc-deps:
51065076 else
51075077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5108 $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5109 endif
5110
5111 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51125082 # This file is part of erlang.mk and subject to the terms of the ISC License.
51135083
51145084 .PHONY: rel-deps
51255095 rel-deps:
51265096 else
51275097 rel-deps: $(ALL_REL_DEPS_DIRS)
5128 $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5129 endif
5130
5131 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51325102 # This file is part of erlang.mk and subject to the terms of the ISC License.
51335103
51345104 .PHONY: test-deps test-dir test-build clean-test-dir
51505120 test-deps:
51515121 else
51525122 test-deps: $(ALL_TEST_DEPS_DIRS)
5153 $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
51545124 endif
51555125
51565126 ifneq ($(wildcard $(TEST_DIR)),)
51835153 endif
51845154 endif
51855155
5186 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51875157 # This file is part of erlang.mk and subject to the terms of the ISC License.
51885158
51895159 .PHONY: rebar.config
52195189 rebar.config:
52205190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
52215191
5222 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
52235193 # This file is part of erlang.mk and subject to the terms of the ISC License.
52245194
5225 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
5226
5227 MAN_INSTALL_PATH ?= /usr/local/share/man
5228 MAN_SECTIONS ?= 3 7
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
52295200
52305201 docs:: asciidoc
52315202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
52325207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
52335210
52345211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
52355212 asciidoc-guide:
52365213 else
5237 asciidoc-guide: distclean-asciidoc doc-deps
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
52385215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
52395216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5240 endif
5241
5242 ifeq ($(wildcard doc/src/manual/*.asciidoc),)
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
52435227 asciidoc-manual:
52445228 else
5245 asciidoc-manual: distclean-asciidoc doc-deps
5246 for f in doc/src/manual/*.asciidoc ; do \
5247 a2x -v -f manpage $$f ; \
5248 done
5249 for s in $(MAN_SECTIONS); do \
5250 mkdir -p doc/man$$s/ ; \
5251 mv doc/src/manual/*.$$s doc/man$$s/ ; \
5252 gzip doc/man$$s/*.$$s ; \
5253 done
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
52545262
52555263 install-docs:: install-asciidoc
52565264
52575265 install-asciidoc: asciidoc-manual
5258 for s in $(MAN_SECTIONS); do \
5259 mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
5260 install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
5261 done
5262 endif
5263
5264 distclean:: distclean-asciidoc
5265
5266 distclean-asciidoc:
5267 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
5268
5269 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
52705276 # This file is part of erlang.mk and subject to the terms of the ISC License.
52715277
52725278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
53235329 define bs_Makefile
53245330 PROJECT = $p
53255331 PROJECT_DESCRIPTION = New project
5326 PROJECT_VERSION = 0.0.1
5332 PROJECT_VERSION = 0.1.0
53275333
53285334 # Whitespace to be used when creating files from templates.
53295335 SP = $(SP)
53335339 define bs_Makefile
53345340 PROJECT = $p
53355341 PROJECT_DESCRIPTION = New project
5336 PROJECT_VERSION = 0.0.1
5342 PROJECT_VERSION = 0.1.0
53375343
53385344 endef
53395345 endif
53415347 define bs_apps_Makefile
53425348 PROJECT = $p
53435349 PROJECT_DESCRIPTION = New project
5344 PROJECT_VERSION = 0.0.1
5350 PROJECT_VERSION = 0.1.0
53455351
53465352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
53475353 endef
53615367 endef
53625368
53635369 define bs_relx_config
5364 {release, {$p_release, "1"}, [$p]}.
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
53655371 {extended_start_script, true}.
53665372 {sys_config, "rel/sys.config"}.
53675373 {vm_args, "rel/vm.args"}.
57125718 ifndef t
57135719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57145720 endif
5715 ifndef tpl_$(t)
5716 $(error Unknown template)
5717 endif
57185721 ifndef n
57195722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57205723 endif
57275730 list-templates:
57285731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
57295732
5730 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
57315734 # This file is part of erlang.mk and subject to the terms of the ISC License.
57325735
57335736 .PHONY: clean-c_src distclean-c_src-env
59625965 $(call render_template,bs_erl_nif,src/$n.erl)
59635966 endif
59645967
5965 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
59665969 # This file is part of erlang.mk and subject to the terms of the ISC License.
59675970
5968 .PHONY: ci ci-setup distclean-kerl
5969
5970 KERL ?= $(CURDIR)/kerl
5971 export KERL
5972
5973 KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
5974
5975 OTP_GIT ?= https://github.com/erlang/otp
5976
5977 CI_INSTALL_DIR ?= $(HOME)/erlang
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
59785973 CI_OTP ?=
5979
5980 ifeq ($(strip $(CI_OTP)),)
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
59815986 ci::
59825987 else
5983 ci:: $(addprefix ci-,$(CI_OTP))
5984
5985 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
59866007
59876008 ci-setup::
6009
6010 ci-extra::
59886011
59896012 ci_verbose_0 = @echo " CI " $(1);
59906013 ci_verbose = $(ci_verbose_$(V))
59916014
59926015 define ci_target
5993 ci-$(1): $(CI_INSTALL_DIR)/$(1)
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
59946018 $(ci_verbose) \
5995 PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
5996 CI_OTP_RELEASE="$(1)" \
5997 CT_OPTS="-label $(1)" \
5998 $(MAKE) clean ci-setup tests
5999 endef
6000
6001 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
60026030
60036031 define ci_otp_target
60046032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
60056033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6006 $(KERL) build git $(OTP_GIT) $(1) $(1)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
60076035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
60086036 endif
60096037 endef
60106038
60116039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
60126040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
60136052 $(KERL):
6014 $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
60156056 $(verbose) chmod +x $(KERL)
60166057
60176058 help::
60286069 $(gen_verbose) rm -rf $(KERL)
60296070 endif
60306071
6031 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
60326073 # This file is part of erlang.mk and subject to the terms of the ISC License.
60336074
60346075 .PHONY: ct apps-ct distclean-ct
60366077 # Configuration.
60376078
60386079 CT_OPTS ?=
6080
60396081 ifneq ($(wildcard $(TEST_DIR)),)
6040 CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6041 else
6042 CT_SUITES ?=
6043 endif
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
60446088
60456089 # Core targets.
60466090
60636107 -noinput \
60646108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
60656109 -dir $(TEST_DIR) \
6066 -logdir $(CURDIR)/logs
6110 -logdir $(CT_LOGS_DIR)
60676111
60686112 ifeq ($(CT_SUITES),)
60696113 ct: $(if $(IS_APP),,apps-ct)
60706114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
60716117 ct: test-build $(if $(IS_APP),,apps-ct)
6072 $(verbose) mkdir -p $(CURDIR)/logs/
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
60736119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
60746121 endif
60756122
60766123 ifneq ($(ALL_APPS_DIRS),)
60976144
60986145 define ct_suite_target
60996146 ct-$(1): test-build
6100 $(verbose) mkdir -p $(CURDIR)/logs/
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
61016148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
61026149 endef
61036150
61046151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
61056152
61066153 distclean-ct:
6107 $(gen_verbose) rm -rf $(CURDIR)/logs/
6108
6109 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61106157 # This file is part of erlang.mk and subject to the terms of the ISC License.
61116158
61126159 .PHONY: plt distclean-plt dialyze
61506197 endef
61516198
61526199 $(DIALYZER_PLT): deps app
6153 $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
61546204
61556205 plt: $(DIALYZER_PLT)
61566206
61646214 endif
61656215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
61666216
6167 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61686218 # This file is part of erlang.mk and subject to the terms of the ISC License.
61696219
61706220 .PHONY: distclean-edoc edoc
61726222 # Configuration.
61736223
61746224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
61756235
61766236 # Core targets.
61776237
6178 ifneq ($(wildcard doc/overview.edoc),)
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
61796239 docs:: edoc
61806240 endif
61816241
61846244 # Plugin-specific targets.
61856245
61866246 edoc: distclean-edoc doc-deps
6187 $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
61886248
61896249 distclean-edoc:
61906250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
61916251
6192 # Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61936253 # This file is part of erlang.mk and subject to the terms of the ISC License.
61946254
6195 .PHONY: distclean-escript escript
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
61966320
61976321 # Configuration.
61986322
61996323 ESCRIPT_NAME ?= $(PROJECT)
62006324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
62016325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
62026327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6203
6204 ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
6205 ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
6206 ESCRIPT_EMU_ARGS ?= -pa . \
6207 -sasl errlog_type error \
6208 -escript main $(ESCRIPT_NAME)
6209 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6210 ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
62116332
62126333 # Core targets.
62136334
62206341
62216342 # Plugin-specific targets.
62226343
6223 # Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
6224 # Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
6225 # Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
6226 # Software may only be used for the great good and the true happiness of all
6227 # sentient beings.
6228
6229 define ESCRIPT_RAW
6230 'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
6231 'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
6232 ' [F || F <- A, not filelib:is_dir(F) ] end,'\
6233 'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
6234 'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
6235 'Ez = fun(Escript) ->'\
6236 ' Static = Files([$(ESCRIPT_STATIC)]),'\
6237 ' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
6238 ' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
6239 ' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
6240 ' {archive, Archive, [memory]},'\
6241 ' {shebang, "$(ESCRIPT_SHEBANG)"},'\
6242 ' {comment, "$(ESCRIPT_COMMENT)"},'\
6243 ' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
6244 ' ]),'\
6245 ' file:change_mode(Escript, 8#755)'\
6246 'end,'\
6247 'Ez("$(ESCRIPT_FILE)"),'\
6248 'halt().'
6249 endef
6250
6251 ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
6252
6253 escript:: distclean-escript deps app
6254 $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
62556360
62566361 distclean-escript:
6257 $(gen_verbose) rm -f $(ESCRIPT_NAME)
6258
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
62596365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6260 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
62616366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
62626367
62636368 .PHONY: eunit apps-eunit
63216426
63226427 ifneq ($(ALL_APPS_DIRS),)
63236428 apps-eunit:
6324 $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done
6325 endif
6326 endif
6327
6328 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
63296436 # This file is part of erlang.mk and subject to the terms of the ISC License.
63306437
6331 .PHONY: relx-rel distclean-relx-rel distclean-relx run
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
63326439
63336440 # Configuration.
63346441
6335 RELX ?= $(CURDIR)/relx
6442 RELX ?= $(ERLANG_MK_TMP)/relx
63366443 RELX_CONFIG ?= $(CURDIR)/relx.config
63376444
63386445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
63396446 RELX_OPTS ?=
63406447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
63416454
63426455 ifeq ($(firstword $(RELX_OPTS)),-o)
63436456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
63506463 ifeq ($(IS_DEP),)
63516464 ifneq ($(wildcard $(RELX_CONFIG)),)
63526465 rel:: relx-rel
6353 endif
6354 endif
6355
6356 distclean:: distclean-relx-rel distclean-relx
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
63576472
63586473 # Plugin-specific targets.
63596474
63626477 $(verbose) chmod +x $(RELX)
63636478
63646479 relx-rel: $(RELX) rel-deps app
6365 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
63666484
63676485 distclean-relx-rel:
63686486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6369
6370 distclean-relx:
6371 $(gen_verbose) rm -rf $(RELX)
63726487
63736488 # Run target.
63746489
63776492 else
63786493
63796494 define get_relx_release.erl
6380 {ok, Config} = file:consult("$(RELX_CONFIG)"),
6381 {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
6382 io:format("~s", [Name]),
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
63836504 halt(0).
63846505 endef
63856506
6386 RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
63876514
63886515 run: all
6389 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
63906517
63916518 help::
63926519 $(verbose) printf "%s\n" "" \
63956522
63966523 endif
63976524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
63986526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6399 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
64006527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
64016528
64026529 .PHONY: shell
64216548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
64226549
64236550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6424 $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
64256552
64266553 shell: build-shell-deps
64276554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
64286555
6429 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
64306571 # This file is part of erlang.mk and subject to the terms of the ISC License.
64316572
64326573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
64376578 tests:: triq
64386579
64396580 define triq_check.erl
6440 code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
64416582 try
64426583 case $(1) of
64436584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
64696610 endif
64706611 endif
64716612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
64726614 # Copyright (c) 2015, Erlang Solutions Ltd.
64736615 # This file is part of erlang.mk and subject to the terms of the ISC License.
64746616
64856627 XREFR ?= $(CURDIR)/xrefr
64866628 export XREFR
64876629
6488 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
64896631
64906632 # Core targets.
64916633
64926634 help::
6493 $(verbose) printf "%s\n" "" \
6494 "Xref targets:" \
6495 " xref Run Xrefr using $XREF_CONFIG as config file if defined"
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
64966638
64976639 distclean:: distclean-xref
64986640
65086650 distclean-xref:
65096651 $(gen_verbose) rm -rf $(XREFR)
65106652
6511 # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
65126655 # This file is part of erlang.mk and subject to the terms of the ISC License.
65136656
65146657 COVER_REPORT_DIR = cover
65176660
65186661 ifdef COVER
65196662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
65206664 # All modules in 'ebin'
65216665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
65226666
65316675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
65326676 endif
65336677 endif
6678 endif
65346679
65356680 # Core targets
65366681
65526697 "Cover targets:" \
65536698 " cover-report Generate a HTML coverage report from previously collected" \
65546699 " cover data." \
6555 " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
65566701 "" \
65576702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
65586703 "target tests additionally generates a HTML coverage report from the combined" \
65656710
65666711 .PHONY: coverdata-clean
65676712 coverdata-clean:
6568 $(gen_verbose) rm -f *.coverdata ct.cover.spec
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
65696714
65706715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
65716721 all.coverdata: $(COVERDATA)
6572 $(gen_verbose) $(ERL) -eval ' \
6573 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
6574 cover:export("$@"), halt(0).'
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
65756723
65766724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
65776725 # empty if you want the coverdata files but not the HTML report.
65896737 # Modules which include eunit.hrl always contain one line without coverage
65906738 # because eunit defines test/0 which is never called. We compensate for this.
65916739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6592 grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
65936741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
65946742
65956743 define cover_report.erl
66246772 endef
66256773
66266774 cover-report:
6627 $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
66286776 $(gen_verbose) $(call erlang,$(cover_report.erl))
66296777
66306778 endif
66316779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
66326839
66336840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
66346841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
66976904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
66986905 endif
66996906 ifndef IS_APP
6700 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
67016908 $(MAKE) -C $$dep $@ \
67026909 IS_APP=1 \
6703 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6704 || exit $$?; \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67056911 done
67066912 endif
6707 $(verbose) for dep in $^ ; do \
6913 $(verbose) set -e; for dep in $^ ; do \
67086914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
67096915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6710 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
67116917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
67126918 $(MAKE) -C $$dep fetch-deps \
67136919 IS_DEP=1 \
6714 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6715 || exit $$?; \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67166921 fi \
67176922 fi \
67186923 done
22 # before the inclusion of erlang.mk leading to the wrong target becoming
33 # the default.
44 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
526 endif
627
728 # --------------------------------------------------------------------
2041 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
2142 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
2243 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
2345 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
2446 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
2547 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
2648 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
2749 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
2851 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
2952 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
3054 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
3155 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
3256 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
3559 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
3660 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
3761 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
3863 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
3964 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
4065 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
7095
7196 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
7297
73 # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
74 # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
75 # needs to add "ranch" as a BUILD_DEPS. The list of projects needing
76 # this workaround are:
77 # o rabbitmq-web-stomp
78 dep_ranch = git https://github.com/ninenines/ranch 1.2.1
98 # Third-party dependencies version pinning.
99 #
100 # We do that in this file, which is copied in all projects, to ensure
101 # all projects use the same versions. It avoids conflicts and makes it
102 # possible to work with rabbitmq-public-umbrella.
103
104 dep_cowboy_commit = 1.0.4
105 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
106 dep_ranch_commit = 1.3.1
107 dep_webmachine_commit = 1.10.8p2
79108
80109 RABBITMQ_COMPONENTS = amqp_client \
81110 rabbit \
82111 rabbit_common \
83112 rabbitmq_amqp1_0 \
84113 rabbitmq_auth_backend_amqp \
114 rabbitmq_auth_backend_cache \
85115 rabbitmq_auth_backend_http \
86116 rabbitmq_auth_backend_ldap \
87117 rabbitmq_auth_mechanism_ssl \
88118 rabbitmq_boot_steps_visualiser \
89119 rabbitmq_clusterer \
120 rabbitmq_cli \
90121 rabbitmq_codegen \
91122 rabbitmq_consistent_hash_exchange \
123 rabbitmq_ct_client_helpers \
92124 rabbitmq_ct_helpers \
93125 rabbitmq_delayed_message_exchange \
94126 rabbitmq_dotnet_client \
97129 rabbitmq_federation_management \
98130 rabbitmq_java_client \
99131 rabbitmq_jms_client \
132 rabbitmq_jms_cts \
100133 rabbitmq_jms_topic_exchange \
101134 rabbitmq_lvc \
102135 rabbitmq_management \
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_tracing_app).
1010 %% The Original Code is RabbitMQ Federation.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_tracing_consumer).
1010 %% The Original Code is RabbitMQ Federation.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_tracing_consumer_sup).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_tracing_files).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_tracing_mgmt).
1919
2020 -export([dispatcher/0, web_ui/0]).
2121
22 dispatcher() -> [{["traces"], rabbit_tracing_wm_traces, []},
23 {["traces", vhost], rabbit_tracing_wm_traces, []},
24 {["traces", vhost, name], rabbit_tracing_wm_trace, []},
25 {["trace-files"], rabbit_tracing_wm_files, []},
26 {["trace-files", name], rabbit_tracing_wm_file, []}].
22 dispatcher() -> [{"/traces", rabbit_tracing_wm_traces, []},
23 {"/traces/:vhost", rabbit_tracing_wm_traces, []},
24 {"/traces/:vhost/:name", rabbit_tracing_wm_trace, []},
25 {"/trace-files", rabbit_tracing_wm_files, []},
26 {"/trace-files/:name", rabbit_tracing_wm_file, []}].
2727
2828 web_ui() -> [{javascript, <<"tracing.js">>}].
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_tracing_sup).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_tracing_traces).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414
1515 -module(rabbit_tracing_wm_file).
1616
17 -export([init/1, resource_exists/2, serve/2, content_types_provided/2,
17 -export([init/3]).
18 -export([rest_init/2, resource_exists/2, serve/2, content_types_provided/2,
1819 is_authorized/2, allowed_methods/2, delete_resource/2]).
1920
20 -include_lib("rabbitmq_management/include/rabbit_mgmt.hrl").
21 -include_lib("webmachine/include/webmachine.hrl").
21 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2222
2323 %%--------------------------------------------------------------------
24 init(_Config) -> {ok, #context{}}.
24 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
25
26 rest_init(ReqData, _) -> {ok, ReqData, #context{}}.
2527
2628 content_types_provided(ReqData, Context) ->
27 {[{"text/plain", serve}], ReqData, Context}.
29 {[{<<"text/plain">>, serve}], ReqData, Context}.
2830
2931 allowed_methods(ReqData, Context) ->
30 {['HEAD', 'GET', 'DELETE'], ReqData, Context}.
32 {[<<"HEAD">>, <<"GET">>, <<"DELETE">>], ReqData, Context}.
3133
3234 resource_exists(ReqData, Context) ->
3335 Name = rabbit_mgmt_util:id(name, ReqData),
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_tracing_wm_files).
1717
18 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2]).
18 -export([init/3]).
19 -export([rest_init/2, to_json/2, content_types_provided/2, is_authorized/2]).
1920
20 -include_lib("rabbitmq_management/include/rabbit_mgmt.hrl").
21 -include_lib("webmachine/include/webmachine.hrl").
21 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2222
2323 %%--------------------------------------------------------------------
24 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
2425
25 init(_Config) -> {ok, #context{}}.
26 rest_init(ReqData, _) -> {ok, ReqData, #context{}}.
2627
2728 content_types_provided(ReqData, Context) ->
28 {[{"application/json", to_json}], ReqData, Context}.
29 {[{<<"application/json">>, to_json}], ReqData, Context}.
2930
3031 to_json(ReqData, Context) ->
3132 rabbit_mgmt_util:reply(rabbit_tracing_files:list(), ReqData, Context).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414
1515 -module(rabbit_tracing_wm_trace).
1616
17 -export([init/1, resource_exists/2, to_json/2,
17 -export([init/3]).
18 -export([rest_init/2, resource_exists/2, to_json/2,
1819 content_types_provided/2, content_types_accepted/2,
1920 is_authorized/2, allowed_methods/2, accept_content/2,
2021 delete_resource/2]).
2425
2526 -import(rabbit_misc, [pget/2, pget/3]).
2627
27 -include_lib("rabbitmq_management/include/rabbit_mgmt.hrl").
28 -include_lib("webmachine/include/webmachine.hrl").
28 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2929
3030 %%--------------------------------------------------------------------
31 init(_Config) -> {ok, #context{}}.
31 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
32
33 rest_init(ReqData, _) -> {ok, ReqData, #context{}}.
3234
3335 content_types_provided(ReqData, Context) ->
34 {[{"application/json", to_json}], ReqData, Context}.
36 {[{<<"application/json">>, to_json}], ReqData, Context}.
3537
3638 content_types_accepted(ReqData, Context) ->
37 {[{"application/json", accept_content}], ReqData, Context}.
39 {[{<<"application/json">>, accept_content}], ReqData, Context}.
3840
3941 allowed_methods(ReqData, Context) ->
40 {['HEAD', 'GET', 'PUT', 'DELETE'], ReqData, Context}.
42 {[<<"HEAD">>, <<"GET">>, <<"PUT">>, <<"DELETE">>], ReqData, Context}.
4143
4244 resource_exists(ReqData, Context) ->
4345 {case trace(ReqData) of
4850 to_json(ReqData, Context) ->
4951 rabbit_mgmt_util:reply(trace(ReqData), ReqData, Context).
5052
51 accept_content(RD, Ctx) ->
52 case rabbit_mgmt_util:vhost(RD) of
53 accept_content(ReqData0, Ctx) ->
54 case rabbit_mgmt_util:vhost(ReqData0) of
5355 not_found ->
5456 not_found;
5557 VHost ->
56 Name = rabbit_mgmt_util:id(name, RD),
58 Name = rabbit_mgmt_util:id(name, ReqData0),
5759 rabbit_mgmt_util:with_decode(
58 [format, pattern], RD, Ctx,
59 fun([_, _], Trace) ->
60 [format, pattern], ReqData0, Ctx,
61 fun([_, _], Trace, ReqData) ->
6062 Fs = [fun val_payload_bytes/3, fun val_format/3,
6163 fun val_create/3],
6264 case lists:foldl(fun (F, ok) -> F(VHost, Name, Trace);
6365 (_F, Err) -> Err
6466 end, ok, Fs) of
65 ok -> {true, RD, Ctx};
66 Err -> rabbit_mgmt_util:bad_request(Err, RD, Ctx)
67 ok -> {true, ReqData, Ctx};
68 Err -> rabbit_mgmt_util:bad_request(Err,
69 ReqData,
70 Ctx)
6771 end
6872 end)
6973 end.
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_tracing_wm_traces).
1717
18 -export([init/1, to_json/2, content_types_provided/2, is_authorized/2]).
18 -export([init/3]).
19 -export([rest_init/2, to_json/2, content_types_provided/2, is_authorized/2]).
1920
20 -include_lib("rabbitmq_management/include/rabbit_mgmt.hrl").
21 -include_lib("webmachine/include/webmachine.hrl").
21 -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2222
2323 %%--------------------------------------------------------------------
24 init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
2425
25 init(_Config) -> {ok, #context{}}.
26 rest_init(ReqData, _) -> {ok, ReqData, #context{}}.
2627
2728 content_types_provided(ReqData, Context) ->
28 {[{"application/json", to_json}], ReqData, Context}.
29 {[{<<"application/json">>, to_json}], ReqData, Context}.
2930
3031 to_json(ReqData, Context) ->
3132 rabbit_mgmt_util:reply(rabbit_tracing_traces:list(), ReqData, Context).
+0
-10
deps/rabbitmq_tracing/src/rabbitmq_tracing.app.src less more
0 {application, rabbitmq_tracing,
1 [{description, "RabbitMQ message logging / tracing"},
2 {vsn, "3.6.6"},
3 {modules, []},
4 {registered, []},
5 {mod, {rabbit_tracing_app, []}},
6 {env, [{directory, "/var/tmp/rabbitmq-tracing"},
7 {username, <<"guest">>},
8 {password, <<"guest">>}]},
9 {applications, [kernel, stdlib, rabbit_common, rabbit, rabbitmq_management]}]}.
00 PROJECT = rabbitmq_trust_store
1 PROJECT_DESCRIPTION = Client X.509 certificates trust store
2 PROJECT_MOD = rabbit_trust_store_app
3
4 define PROJECT_ENV
5 [
6 {default_refresh_interval, 30},
7 {providers, [rabbit_trust_store_file_provider]}
8 ]
9 endef
110
211 DEPS = rabbit_common rabbit
12 LOCAL_DEPS += ssl crypto public_key
313 ## We need the Cowboy's test utilities
4 TEST_DEPS = rabbitmq_ct_helpers amqp_client ct_helper
14 TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers amqp_client ct_helper trust_store_http
515 dep_ct_helper = git https://github.com/extend/ct_helper.git master
16 dep_trust_store_http = git https://github.com/rabbitmq/trust_store_http.git
617
18 DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
719 DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk
820
921 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
1111 There is no convenient means with which to change it in realtime, that
1212 is, without making configuration changes to TLS listening sockets.
1313
14 This plugin maintains a list of trusted .PEM formatted TLS (x509) certificates in a given
15 directory, refreshing at configurable intervals, or when `rabbitmqctl
14 This plugin maintains a list of trusted .PEM formatted TLS (x509) certificates,
15 refreshing at configurable intervals, or when `rabbitmqctl
1616 eval 'rabbit_trust_store:refresh().'` is invoked. Said certificates are then used
1717 to verify inbound TLS connections for the entire RabbitMQ node (all plugins and protocols).
1818 The list is node-local.
1919
20 Certificates can be loaded from different sources (e.g. filesystem, HTTP server)
21 Sources are loaded using "providers" - erlang modules, implementing `rabbit_trust_store_certificate_provider`
22 behaviour.
23
24 The default provider is `rabbit_trust_store_file_provider`, which will load certificates
25 from a configured local filesystem directory.
26
2027 ## RabbitMQ Version Requirements
2128
2229 This plugin requires RabbitMQ `3.6.1` or later.
30
31 ## Erlang Version Requirements
32
33 This plugin requires Erlang version 17.3 or later.
2334
2435 ## Installation and Binary Builds
2536
2738 Please consult the docs on [how to install RabbitMQ plugins](http://www.rabbitmq.com/plugins.html#installing-plugins).
2839
2940 ## Usage
41
42 ### Filesystem provider
3043
3144 Configure the trust store with a directory of whitelisted certificates
3245 and a refresh interval:
4053
4154 Setting `refresh_interval` to `0` seconds will disable automatic refresh.
4255
43 Certificates are distinguished by their **filenames** and file modification time.
56 Certificates are distinguished by their **filenames**, file modification time and
57 the hash of file contents.
4458
45 ### Installing a Certificate
59 #### Installing a Certificate
4660
4761 Write a `PEM` formatted certificate file to the configured directory
4862 to whitelist it. This contains all the necessary information to
4963 authorize a client which presents the very same certificate to the
5064 server.
5165
52 ### Removing a Certificate
66 #### Removing a Certificate
5367
5468 Delete the certificate file from the configured directory to remove it
5569 from the whitelist.
5872 make it seem as if a removed certificate is still active. Disabling session caching
5973 in the broker by setting the `reuse_sessions` ssl option to `false` can be done if
6074 timely certificate removal is important.
75
76 ### HTTP provider
77
78 HTTP provider loads certificates via HTTP(S) from remote server.
79
80 The server should have following API:
81
82 - `GET <root>` - list certificates in JSON format: `{"certificates": [{"id": <id>, "url": <url>}, ...]}`
83 - `GET <root>/<url>` - download PEM encoded certificate.
84
85 Where `<root>` is a configured certificate path, `<id>` - unique certificate identifier,
86 `<url>` - relative certificate path to load it from server.
87
88 Configuration of the HTTP provider:
89
90 ```
91 {rabbitmq_trust_store,
92 [{providers, [rabbit_trust_store_http_provider]},
93 {url, "http://example.cert.url/path"},
94 {refresh_interval, {seconds, 30}}
95 ]}.
96 ```
97
98 You can specify TLS options if you use HTTPS:
99
100 ```
101 {rabbitmq_trust_store,
102 [{providers, [rabbit_trust_store_http_provider]},
103 {url, "https://example.secure.cert.url/path"},
104 {refresh_interval, {seconds, 30}},
105 {ssl_options, [{certfile, "/client/cert.pem"},
106 {keyfile, "/client/key.pem"},
107 {cacertfile, "/ca/cert.pem"}
108 ]}
109 ]}.
110 ```
111
112 HTTP provider uses `If-Modified-Since` during list request header to avoid updating
113 unchanged list of certificates.
114
115 #### Example
116
117 `examples/rabbitmq_trust_store_django` is an example Django application, which serves
118 certificates from a directory.
61119
62120
63121 ### Listing certificates
0 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
11 #
22 # Permission to use, copy, modify, and/or distribute this software for any
33 # purpose with or without fee is hereby granted, provided that the above
1111 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1212 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1313
14 .PHONY: all app apps deps search rel docs install-docs check tests clean distclean help erlang-mk
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
1515
1616 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17
18 ERLANG_MK_VERSION = 2.0.0-pre.2-144-g647ffd1
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
1931
2032 # Core configuration.
2133
2436
2537 PROJECT_VERSION ?= rolling
2638 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
2740
2841 # Verbosity.
2942
8497 rel::
8598 $(verbose) :
8699
100 relup:: deps app
101
87102 check:: tests
88103
89104 clean:: clean-crashdump
101116 help::
102117 $(verbose) printf "%s\n" \
103118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
104 "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
105120 "" \
106121 "Usage: [V=1] $(MAKE) [target]..." \
107122 "" \
149164 core_native_path = $1
150165 endif
151166
152 ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
153 define core_http_get
154 wget --no-check-certificate -O $(1) $(2)|| rm $(1)
155 endef
156 else
157 define core_http_get.erl
158 ssl:start(),
159 inets:start(),
160 case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
161 {ok, {{_, 200, _}, _, Body}} ->
162 case file:write_file("$(1)", Body) of
163 ok -> ok;
164 {error, R1} -> halt(R1)
165 end;
166 {error, R2} ->
167 halt(R2)
168 end,
169 halt(0).
170 endef
171
172 define core_http_get
173 $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
174 endef
175 endif
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
176168
177169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
178170
192184 ERLANG_MK_BUILD_CONFIG ?= build.config
193185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
194186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
195188 erlang-mk:
196189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
197190 ifdef ERLANG_MK_COMMIT
198191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
199192 endif
200193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
201 $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
202195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
203196 rm -rf $(ERLANG_MK_BUILD_DIR)
204197
285278 pkg_apns_repo = https://github.com/inaka/apns4erl
286279 pkg_apns_commit = master
287280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
288289 PACKAGES += azdht
289290 pkg_azdht_name = azdht
290291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
405406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
406407 pkg_bootstrap_commit = master
407408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
408417 PACKAGES += boss_db
409418 pkg_boss_db_name = boss_db
410419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
413422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
414423 pkg_boss_db_commit = master
415424
416 PACKAGES += boss
417 pkg_boss_name = boss
418 pkg_boss_description = Erlang web MVC, now featuring Comet
419 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
420 pkg_boss_fetch = git
421 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
422 pkg_boss_commit = master
423
424425 PACKAGES += brod
425426 pkg_brod_name = brod
426427 pkg_brod_description = Kafka client in Erlang
565566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
566567 pkg_cloudi_service_api_requests_commit = master
567568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
568585 PACKAGES += cloudi_service_db_cassandra_cql
569586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
570587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
573590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
574591 pkg_cloudi_service_db_cassandra_cql_commit = master
575592
576 PACKAGES += cloudi_service_db_cassandra
577 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
579 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
580 pkg_cloudi_service_db_cassandra_fetch = git
581 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
582 pkg_cloudi_service_db_cassandra_commit = master
583
584593 PACKAGES += cloudi_service_db_couchdb
585594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
586595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
637646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
638647 pkg_cloudi_service_db_tokyotyrant_commit = master
639648
640 PACKAGES += cloudi_service_db
641 pkg_cloudi_service_db_name = cloudi_service_db
642 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
643 pkg_cloudi_service_db_homepage = http://cloudi.org/
644 pkg_cloudi_service_db_fetch = git
645 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
646 pkg_cloudi_service_db_commit = master
647
648649 PACKAGES += cloudi_service_filesystem
649650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
650651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
10371038 pkg_edown_repo = https://github.com/uwiger/edown
10381039 pkg_edown_commit = master
10391040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
10401049 PACKAGES += eep_app
10411050 pkg_eep_app_name = eep_app
10421051 pkg_eep_app_description = Embedded Event Processing
10451054 pkg_eep_app_repo = https://github.com/darach/eep-erl
10461055 pkg_eep_app_commit = master
10471056
1048 PACKAGES += eep
1049 pkg_eep_name = eep
1050 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1051 pkg_eep_homepage = https://github.com/virtan/eep
1052 pkg_eep_fetch = git
1053 pkg_eep_repo = https://github.com/virtan/eep
1054 pkg_eep_commit = master
1055
10561057 PACKAGES += efene
10571058 pkg_efene_name = efene
10581059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
12371238 pkg_eqm_repo = https://github.com/loucash/eqm
12381239 pkg_eqm_commit = master
12391240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
12401249 PACKAGES += eredis_pool
12411250 pkg_eredis_pool_name = eredis_pool
12421251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
12451254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
12461255 pkg_eredis_pool_commit = master
12471256
1248 PACKAGES += eredis
1249 pkg_eredis_name = eredis
1250 pkg_eredis_description = Erlang Redis client
1251 pkg_eredis_homepage = https://github.com/wooga/eredis
1252 pkg_eredis_fetch = git
1253 pkg_eredis_repo = https://github.com/wooga/eredis
1254 pkg_eredis_commit = master
1255
12561257 PACKAGES += erl_streams
12571258 pkg_erl_streams_name = erl_streams
12581259 pkg_erl_streams_description = Streams in Erlang
15411542 pkg_etap_repo = https://github.com/ngerakines/etap
15421543 pkg_etap_commit = master
15431544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
15441553 PACKAGES += etest_http
15451554 pkg_etest_http_name = etest_http
15461555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
15491558 pkg_etest_http_repo = https://github.com/wooga/etest_http
15501559 pkg_etest_http_commit = master
15511560
1552 PACKAGES += etest
1553 pkg_etest_name = etest
1554 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1555 pkg_etest_homepage = https://github.com/wooga/etest
1556 pkg_etest_fetch = git
1557 pkg_etest_repo = https://github.com/wooga/etest
1558 pkg_etest_commit = master
1559
15601561 PACKAGES += etoml
15611562 pkg_etoml_name = etoml
15621563 pkg_etoml_description = TOML language erlang parser
15651566 pkg_etoml_repo = https://github.com/kalta/etoml
15661567 pkg_etoml_commit = master
15671568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
15681577 PACKAGES += eunit_formatters
15691578 pkg_eunit_formatters_name = eunit_formatters
15701579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
15731582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
15741583 pkg_eunit_formatters_commit = master
15751584
1576 PACKAGES += eunit
1577 pkg_eunit_name = eunit
1578 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1579 pkg_eunit_homepage = https://github.com/richcarl/eunit
1580 pkg_eunit_fetch = git
1581 pkg_eunit_repo = https://github.com/richcarl/eunit
1582 pkg_eunit_commit = master
1583
15841585 PACKAGES += euthanasia
15851586 pkg_euthanasia_name = euthanasia
15861587 pkg_euthanasia_description = Merciful killer for your Erlang processes
15981599 pkg_evum_commit = master
15991600
16001601 PACKAGES += exec
1601 pkg_exec_name = exec
1602 pkg_exec_name = erlexec
16021603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
16031604 pkg_exec_homepage = http://saleyn.github.com/erlexec
16041605 pkg_exec_fetch = git
17171718 pkg_fn_repo = https://github.com/reiddraper/fn
17181719 pkg_fn_commit = master
17191720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
17201729 PACKAGES += folsom_cowboy
17211730 pkg_folsom_cowboy_name = folsom_cowboy
17221731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
17251734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
17261735 pkg_folsom_cowboy_commit = master
17271736
1728 PACKAGES += folsom
1729 pkg_folsom_name = folsom
1730 pkg_folsom_description = Expose Erlang Events and Metrics
1731 pkg_folsom_homepage = https://github.com/boundary/folsom
1732 pkg_folsom_fetch = git
1733 pkg_folsom_repo = https://github.com/boundary/folsom
1734 pkg_folsom_commit = master
1735
17361737 PACKAGES += folsomite
17371738 pkg_folsomite_name = folsomite
17381739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
20932094 pkg_jesse_repo = https://github.com/for-GET/jesse
20942095 pkg_jesse_commit = master
20952096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
20962105 PACKAGES += jiffy_v
20972106 pkg_jiffy_v_name = jiffy_v
20982107 pkg_jiffy_v_description = JSON validation utility
21012110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
21022111 pkg_jiffy_v_commit = master
21032112
2104 PACKAGES += jiffy
2105 pkg_jiffy_name = jiffy
2106 pkg_jiffy_description = JSON NIFs for Erlang.
2107 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2108 pkg_jiffy_fetch = git
2109 pkg_jiffy_repo = https://github.com/davisp/jiffy
2110 pkg_jiffy_commit = master
2111
21122113 PACKAGES += jobs
21132114 pkg_jobs_name = jobs
21142115 pkg_jobs_description = a Job scheduler for load regulation
21252126 pkg_joxa_repo = https://github.com/joxa/joxa
21262127 pkg_joxa_commit = master
21272128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
21282137 PACKAGES += json_rec
21292138 pkg_json_rec_name = json_rec
21302139 pkg_json_rec_description = JSON to erlang record
21332142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
21342143 pkg_json_rec_commit = master
21352144
2136 PACKAGES += json
2137 pkg_json_name = json
2138 pkg_json_description = a high level json library for erlang (17.0+)
2139 pkg_json_homepage = https://github.com/talentdeficit/json
2140 pkg_json_fetch = git
2141 pkg_json_repo = https://github.com/talentdeficit/json
2142 pkg_json_commit = master
2143
21442145 PACKAGES += jsone
21452146 pkg_jsone_name = jsone
21462147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
21812182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
21822183 pkg_jsx_commit = master
21832184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
21842193 PACKAGES += kafka_protocol
21852194 pkg_kafka_protocol_name = kafka_protocol
21862195 pkg_kafka_protocol_description = Kafka protocol Erlang library
21892198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
21902199 pkg_kafka_protocol_commit = master
21912200
2192 PACKAGES += kafka
2193 pkg_kafka_name = kafka
2194 pkg_kafka_description = Kafka consumer and producer in Erlang
2195 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2196 pkg_kafka_fetch = git
2197 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2198 pkg_kafka_commit = master
2199
22002201 PACKAGES += kai
22012202 pkg_kai_name = kai
22022203 pkg_kai_description = DHT storage by Takeshi Inoue
22932294 pkg_kvs_repo = https://github.com/synrc/kvs
22942295 pkg_kvs_commit = master
22952296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
22962305 PACKAGES += lager_amqp_backend
22972306 pkg_lager_amqp_backend_name = lager_amqp_backend
22982307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
23092318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
23102319 pkg_lager_syslog_commit = master
23112320
2312 PACKAGES += lager
2313 pkg_lager_name = lager
2314 pkg_lager_description = A logging framework for Erlang/OTP.
2315 pkg_lager_homepage = https://github.com/basho/lager
2316 pkg_lager_fetch = git
2317 pkg_lager_repo = https://github.com/basho/lager
2318 pkg_lager_commit = master
2319
23202321 PACKAGES += lambdapad
23212322 pkg_lambdapad_name = lambdapad
23222323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
25732574 pkg_mixer_repo = https://github.com/chef/mixer
25742575 pkg_mixer_commit = master
25752576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
25762585 PACKAGES += mochiweb_xpath
25772586 pkg_mochiweb_xpath_name = mochiweb_xpath
25782587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
25812590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
25822591 pkg_mochiweb_xpath_commit = master
25832592
2584 PACKAGES += mochiweb
2585 pkg_mochiweb_name = mochiweb
2586 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2587 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2588 pkg_mochiweb_fetch = git
2589 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2590 pkg_mochiweb_commit = master
2591
25922593 PACKAGES += mockgyver
25932594 pkg_mockgyver_name = mockgyver
25942595 pkg_mockgyver_description = A mocking library for Erlang
30613062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
30623063 pkg_quickrand_commit = master
30633064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
30643073 PACKAGES += rabbit_exchange_type_riak
30653074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
30663075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
30693078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
30703079 pkg_rabbit_exchange_type_riak_commit = master
30713080
3072 PACKAGES += rabbit
3073 pkg_rabbit_name = rabbit
3074 pkg_rabbit_description = RabbitMQ Server
3075 pkg_rabbit_homepage = https://www.rabbitmq.com/
3076 pkg_rabbit_fetch = git
3077 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3078 pkg_rabbit_commit = master
3079
30803081 PACKAGES += rack
30813082 pkg_rack_name = rack
30823083 pkg_rack_description = Rack handler for erlang
34933494 pkg_smother_repo = https://github.com/ramsay-t/Smother
34943495 pkg_smother_commit = master
34953496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
34963505 PACKAGES += social
34973506 pkg_social_name = social
34983507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
35413550 pkg_stable_repo = https://github.com/dvv/stable
35423551 pkg_stable_commit = master
35433552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
35443561 PACKAGES += statebox_riak
35453562 pkg_statebox_riak_name = statebox_riak
35463563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
35493566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
35503567 pkg_statebox_riak_commit = master
35513568
3552 PACKAGES += statebox
3553 pkg_statebox_name = statebox
3554 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3555 pkg_statebox_homepage = https://github.com/mochi/statebox
3556 pkg_statebox_fetch = git
3557 pkg_statebox_repo = https://github.com/mochi/statebox
3558 pkg_statebox_commit = master
3559
35603569 PACKAGES += statman
35613570 pkg_statman_name = statman
35623571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
37253734 pkg_tirerl_repo = https://github.com/inaka/tirerl
37263735 pkg_tirerl_commit = master
37273736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
37283745 PACKAGES += traffic_tools
37293746 pkg_traffic_tools_name = traffic_tools
37303747 pkg_traffic_tools_description = Simple traffic limiting library
40614078 pkg_zucchini_repo = https://github.com/devinus/zucchini
40624079 pkg_zucchini_commit = master
40634080
4064 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
40654082 # This file is part of erlang.mk and subject to the terms of the ISC License.
40664083
40674084 .PHONY: search
40884105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
40894106 endif
40904107
4091 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
40924109 # This file is part of erlang.mk and subject to the terms of the ISC License.
40934110
4094 .PHONY: distclean-deps
4111 .PHONY: distclean-deps clean-tmp-deps.log
40954112
40964113 # Configuration.
40974114
41104127
41114128 REBAR_DEPS_DIR = $(DEPS_DIR)
41124129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
41134150
41144151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
41154152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
41164153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
41174154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
41184155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
41194157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
41204158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
41214159
41384176
41394177 # Core targets.
41404178
4141 ifdef IS_APP
4142 apps::
4143 else
4144 apps:: $(ALL_APPS_DIRS)
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
41454180 ifeq ($(IS_APP)$(IS_DEP),)
41464181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
41474182 endif
41494184 # Create ebin directory for all apps to make sure Erlang recognizes them
41504185 # as proper OTP applications when using -include_lib. This is a temporary
41514186 # fix, a proper fix would be to compile apps/* in the right order.
4152 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4153 mkdir -p $$dep/ebin || exit $$?; \
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
41544190 done
4155 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
41564196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
41574197 :; \
41584198 else \
41594199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4160 $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
4200 $(MAKE) -C $$dep IS_APP=1; \
41614201 fi \
41624202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
41634207 endif
41644208
41654209 ifneq ($(SKIP_DEPS),)
41664210 deps::
41674211 else
4168 deps:: $(ALL_DEPS_DIRS) apps
4169 ifeq ($(IS_APP)$(IS_DEP),)
4170 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4171 endif
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
41724213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4173 $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
41744215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
41754216 :; \
41764217 else \
41774218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
41784219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4179 $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
4220 $(MAKE) -C $$dep IS_DEP=1; \
41804221 else \
4181 echo "Error: No Makefile to build dependency $$dep."; \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
41824223 exit 2; \
41834224 fi \
41844225 fi \
41924233 # in practice only Makefile is needed so far.
41934234 define dep_autopatch
41944235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
41954237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
41964238 $(call dep_autopatch_erlang_mk,$(1)); \
41974239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4198 if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
41994243 $(call dep_autopatch2,$(1)); \
4200 elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
42014245 $(call dep_autopatch2,$(1)); \
4202 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
42034247 $(call dep_autopatch2,$(1)); \
4204 else \
4205 $(call erlang,$(call dep_autopatch_app.erl,$(1))); \
42064248 fi \
42074249 else \
42084250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
42144256 endef
42154257
42164258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
42174262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
42184263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
42194264 fi; \
42204265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4221 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
42224267 $(call dep_autopatch_fetch_rebar); \
42234268 $(call dep_autopatch_rebar,$(1)); \
42244269 else \
42304275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
42314276 endef
42324277
4233 # Overwrite erlang.mk with the current file by default.
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
42344280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
42354281 define dep_autopatch_erlang_mk
4236 echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \
4237 > $(DEPS_DIR)/$1/erlang.mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
42384287 endef
42394288 else
42404289 define dep_autopatch_erlang_mk
42534302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
42544303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
42554304 cd $(ERLANG_MK_TMP)/rebar; \
4256 git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
42574306 $(MAKE); \
42584307 cd -; \
42594308 fi
42704319 define dep_autopatch_rebar.erl
42714320 application:load(rebar),
42724321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
42734323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
42744324 {ok, Conf0} -> Conf0;
42754325 _ -> []
44234473 [] -> ok;
44244474 _ ->
44254475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4426 PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
44274477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4428 PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L \\"~s\\" -lerl_interface -lei\n",
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
44294479 [code:lib_dir(erl_interface, lib)])),
44304480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
44314481 FilterEnv = fun(Env) ->
44644514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44654515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44664516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4467 [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
44684518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
44694519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
44704520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
44764526 end,
44774527 [PortSpec(S) || S <- PortSpecs]
44784528 end,
4479 Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"),
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
44804530 RunPlugin = fun(Plugin, Step) ->
44814531 case erlang:function_exported(Plugin, Step, 2) of
44824532 false -> ok;
45244574 halt()
45254575 endef
45264576
4527 define dep_autopatch_app.erl
4528 UpdateModules = fun(App) ->
4529 case filelib:is_regular(App) of
4530 false -> ok;
4531 true ->
4532 {ok, [{application, '$(1)', L0}]} = file:consult(App),
4533 Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true,
4534 fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
4535 L = lists:keystore(modules, 1, L0, {modules, Mods}),
4536 ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}]))
4537 end
4538 end,
4539 UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"),
4540 halt()
4541 endef
4542
45434577 define dep_autopatch_appsrc_script.erl
45444578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
45454579 AppSrcScript = AppSrc ++ ".script",
45874621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
45884622 endef
45894623
4590 define dep_fetch_hex.erl
4591 ssl:start(),
4592 inets:start(),
4593 {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
4594 {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
4595 [], [{body_format, binary}]),
4596 {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
4597 {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
4598 ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
4599 halt()
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
46004626 endef
46014627
46024628 # Hex only has a package version. No need to look in the Erlang.mk packages.
46034629 define dep_fetch_hex
4604 $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
46054634 endef
46064635
46074636 define dep_fetch_fail
46314660 $(eval DEP_NAME := $(call dep_name,$1))
46324661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
46334662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4634 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
46354664 exit 17; \
46364665 fi
46374666 $(verbose) mkdir -p $(DEPS_DIR)
46734702 clean:: clean-apps
46744703
46754704 clean-apps:
4676 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4677 $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
46784707 done
46794708
46804709 distclean:: distclean-apps
46814710
46824711 distclean-apps:
4683 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4684 $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
46854714 done
46864715 endif
46874716
47014730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
47024731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
47034732
4704 # External plugins.
4705
4706 DEP_PLUGINS ?=
4707
4708 define core_dep_plugin
4709 -include $(DEPS_DIR)/$(1)
4710
4711 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4712 endef
4713
4714 $(foreach p,$(DEP_PLUGINS),\
4715 $(eval $(if $(findstring /,$p),\
4716 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4717 $(call core_dep_plugin,$p/plugins.mk,$p))))
4718
4719 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4720 # This file is part of erlang.mk and subject to the terms of the ISC License.
4721
4722 # Configuration.
4723
4724 DTL_FULL_PATH ?=
4725 DTL_PATH ?= templates/
4726 DTL_SUFFIX ?= _dtl
4727 DTL_OPTS ?=
4728
4729 # Verbosity.
4730
4731 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
4732 dtl_verbose = $(dtl_verbose_$(V))
4733
4734 # Core targets.
4735
4736 DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl))
4737
4738 ifneq ($(DTL_FILES),)
4739
4740 ifdef DTL_FULL_PATH
4741 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
4742 else
4743 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
4744 endif
4745
4746 ifneq ($(words $(DTL_FILES)),0)
4747 # Rebuild templates when the Makefile changes.
4748 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
4749 @mkdir -p $(ERLANG_MK_TMP)
4750 @if test -f $@; then \
4751 touch $(DTL_FILES); \
4752 fi
4753 @touch $@
4754
4755 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
4756 endif
4757
4758 define erlydtl_compile.erl
4759 [begin
4760 Module0 = case "$(strip $(DTL_FULL_PATH))" of
4761 "" ->
4762 filename:basename(F, ".dtl");
4763 _ ->
4764 "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
4765 re:replace(F2, "/", "_", [{return, list}, global])
4766 end,
4767 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
4768 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
4769 ok -> ok;
4770 {ok, _} -> ok
4771 end
4772 end || F <- string:tokens("$(1)", " ")],
4773 halt().
4774 endef
4775
4776 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
4777 $(if $(strip $?),\
4778 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
4779
4780 endif
4781
4782 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
47834734 # This file is part of erlang.mk and subject to the terms of the ISC License.
47844735
47854736 # Verbosity.
47984749
47994750 define compile_proto.erl
48004751 [begin
4801 Dir = filename:dirname(filename:dirname(F)),
48024752 protobuffs_compile:generate_source(F,
4803 [{output_include_dir, Dir ++ "/include"},
4804 {output_src_dir, Dir ++ "/ebin"}])
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
48054755 end || F <- string:tokens("$(1)", " ")],
48064756 halt().
48074757 endef
48114761 $(if $(strip $?),$(call compile_proto,$?))
48124762 endif
48134763
4814 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
48154765 # This file is part of erlang.mk and subject to the terms of the ISC License.
48164766
48174767 .PHONY: clean-app
48254775 ERLC_EXCLUDE ?=
48264776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
48274777
4778 ERLC_ASN1_OPTS ?=
4779
48284780 ERLC_MIB_OPTS ?=
48294781 COMPILE_MIB_FIRST ?=
48304782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
48744826
48754827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
48764828 define app_file
4877 {application, $(PROJECT), [
4829 {application, '$(PROJECT)', [
48784830 {description, "$(PROJECT_DESCRIPTION)"},
48794831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48804832 {id$(comma)$(space)"$(1)"}$(comma))
48814833 {modules, [$(call comma_list,$(2))]},
48824834 {registered, []},
4883 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48844837 ]}.
48854838 endef
48864839 else
48874840 define app_file
4888 {application, $(PROJECT), [
4841 {application, '$(PROJECT)', [
48894842 {description, "$(PROJECT_DESCRIPTION)"},
48904843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48914844 {id$(comma)$(space)"$(1)"}$(comma))
48924845 {modules, [$(call comma_list,$(2))]},
48934846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
48944847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4895 {mod, {$(PROJECT_MOD), []}}
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48964850 ]}.
48974851 endef
48984852 endif
49024856
49034857 # Source files.
49044858
4905 ERL_FILES = $(sort $(call core_find,src/,*.erl))
4906 CORE_FILES = $(sort $(call core_find,src/,*.core))
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
49074863
49084864 # ASN.1 files.
49094865
49134869
49144870 define compile_asn1
49154871 $(verbose) mkdir -p include/
4916 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
49174873 $(verbose) mv asn1/*.erl src/
49184874 $(verbose) mv asn1/*.hrl include/
49194875 $(verbose) mv asn1/*.asn1db include/
49364892
49374893 # Leex and Yecc files.
49384894
4939 XRL_FILES = $(sort $(call core_find,src/,*.xrl))
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
49404896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
49414897 ERL_FILES += $(XRL_ERL_FILES)
49424898
4943 YRL_FILES = $(sort $(call core_find,src/,*.yrl))
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
49444900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
49454901 ERL_FILES += $(YRL_ERL_FILES)
49464902
49474903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4948 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
49494905
49504906 # Erlang and Core Erlang files.
49514907
49954951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49964952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49974953 (F, Mod, import, {Imp, _}) ->
4998 case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
49994960 false -> ok;
50004961 true -> Add(Mod, Imp)
50014962 end;
50194980 end || F <- ErlFiles],
50204981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
50214982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
50224991 ok = file:write_file("$(1)", [
50234992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
5024 "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n"
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
50254994 ]),
50264995 halt()
50274996 endef
50345003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
50355004 # Rebuild everything when the Makefile changes.
50365005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5037 @mkdir -p $(ERLANG_MK_TMP)
5038 @if test -f $@; then \
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
50395008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
50405009 touch -c $(PROJECT).d; \
50415010 fi
5042 @touch $@
5011 $(verbose) touch $@
50435012
50445013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
50455014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
50465015 endif
50475016
5048 -include $(PROJECT).d
5017 include $(wildcard $(PROJECT).d)
50495018
50505019 ebin/$(PROJECT).app:: ebin/
50515020
50645033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
50655034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
50665035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5067 $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
50685037 > ebin/$(PROJECT).app
50695038 else
50705039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
50885057
50895058 endif
50905059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
50915061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
50925062 # This file is part of erlang.mk and subject to the terms of the ISC License.
50935063
51055075 doc-deps:
51065076 else
51075077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5108 $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5109 endif
5110
5111 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51125082 # This file is part of erlang.mk and subject to the terms of the ISC License.
51135083
51145084 .PHONY: rel-deps
51255095 rel-deps:
51265096 else
51275097 rel-deps: $(ALL_REL_DEPS_DIRS)
5128 $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5129 endif
5130
5131 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51325102 # This file is part of erlang.mk and subject to the terms of the ISC License.
51335103
51345104 .PHONY: test-deps test-dir test-build clean-test-dir
51505120 test-deps:
51515121 else
51525122 test-deps: $(ALL_TEST_DEPS_DIRS)
5153 $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
51545124 endif
51555125
51565126 ifneq ($(wildcard $(TEST_DIR)),)
51835153 endif
51845154 endif
51855155
5186 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51875157 # This file is part of erlang.mk and subject to the terms of the ISC License.
51885158
51895159 .PHONY: rebar.config
52195189 rebar.config:
52205190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
52215191
5222 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
52235193 # This file is part of erlang.mk and subject to the terms of the ISC License.
52245194
5225 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
5226
5227 MAN_INSTALL_PATH ?= /usr/local/share/man
5228 MAN_SECTIONS ?= 3 7
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
52295200
52305201 docs:: asciidoc
52315202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
52325207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
52335210
52345211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
52355212 asciidoc-guide:
52365213 else
5237 asciidoc-guide: distclean-asciidoc doc-deps
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
52385215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
52395216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5240 endif
5241
5242 ifeq ($(wildcard doc/src/manual/*.asciidoc),)
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
52435227 asciidoc-manual:
52445228 else
5245 asciidoc-manual: distclean-asciidoc doc-deps
5246 for f in doc/src/manual/*.asciidoc ; do \
5247 a2x -v -f manpage $$f ; \
5248 done
5249 for s in $(MAN_SECTIONS); do \
5250 mkdir -p doc/man$$s/ ; \
5251 mv doc/src/manual/*.$$s doc/man$$s/ ; \
5252 gzip doc/man$$s/*.$$s ; \
5253 done
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
52545262
52555263 install-docs:: install-asciidoc
52565264
52575265 install-asciidoc: asciidoc-manual
5258 for s in $(MAN_SECTIONS); do \
5259 mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
5260 install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
5261 done
5262 endif
5263
5264 distclean:: distclean-asciidoc
5265
5266 distclean-asciidoc:
5267 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
5268
5269 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
52705276 # This file is part of erlang.mk and subject to the terms of the ISC License.
52715277
52725278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
53235329 define bs_Makefile
53245330 PROJECT = $p
53255331 PROJECT_DESCRIPTION = New project
5326 PROJECT_VERSION = 0.0.1
5332 PROJECT_VERSION = 0.1.0
53275333
53285334 # Whitespace to be used when creating files from templates.
53295335 SP = $(SP)
53335339 define bs_Makefile
53345340 PROJECT = $p
53355341 PROJECT_DESCRIPTION = New project
5336 PROJECT_VERSION = 0.0.1
5342 PROJECT_VERSION = 0.1.0
53375343
53385344 endef
53395345 endif
53415347 define bs_apps_Makefile
53425348 PROJECT = $p
53435349 PROJECT_DESCRIPTION = New project
5344 PROJECT_VERSION = 0.0.1
5350 PROJECT_VERSION = 0.1.0
53455351
53465352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
53475353 endef
53615367 endef
53625368
53635369 define bs_relx_config
5364 {release, {$p_release, "1"}, [$p]}.
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
53655371 {extended_start_script, true}.
53665372 {sys_config, "rel/sys.config"}.
53675373 {vm_args, "rel/vm.args"}.
57125718 ifndef t
57135719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57145720 endif
5715 ifndef tpl_$(t)
5716 $(error Unknown template)
5717 endif
57185721 ifndef n
57195722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57205723 endif
57275730 list-templates:
57285731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
57295732
5730 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
57315734 # This file is part of erlang.mk and subject to the terms of the ISC License.
57325735
57335736 .PHONY: clean-c_src distclean-c_src-env
59625965 $(call render_template,bs_erl_nif,src/$n.erl)
59635966 endif
59645967
5965 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
59665969 # This file is part of erlang.mk and subject to the terms of the ISC License.
59675970
5968 .PHONY: ci ci-setup distclean-kerl
5969
5970 KERL ?= $(CURDIR)/kerl
5971 export KERL
5972
5973 KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
5974
5975 OTP_GIT ?= https://github.com/erlang/otp
5976
5977 CI_INSTALL_DIR ?= $(HOME)/erlang
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
59785973 CI_OTP ?=
5979
5980 ifeq ($(strip $(CI_OTP)),)
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
59815986 ci::
59825987 else
5983 ci:: $(addprefix ci-,$(CI_OTP))
5984
5985 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
59866007
59876008 ci-setup::
6009
6010 ci-extra::
59886011
59896012 ci_verbose_0 = @echo " CI " $(1);
59906013 ci_verbose = $(ci_verbose_$(V))
59916014
59926015 define ci_target
5993 ci-$(1): $(CI_INSTALL_DIR)/$(1)
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
59946018 $(ci_verbose) \
5995 PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
5996 CI_OTP_RELEASE="$(1)" \
5997 CT_OPTS="-label $(1)" \
5998 $(MAKE) clean ci-setup tests
5999 endef
6000
6001 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
60026030
60036031 define ci_otp_target
60046032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
60056033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6006 $(KERL) build git $(OTP_GIT) $(1) $(1)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
60076035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
60086036 endif
60096037 endef
60106038
60116039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
60126040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
60136052 $(KERL):
6014 $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
60156056 $(verbose) chmod +x $(KERL)
60166057
60176058 help::
60286069 $(gen_verbose) rm -rf $(KERL)
60296070 endif
60306071
6031 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
60326073 # This file is part of erlang.mk and subject to the terms of the ISC License.
60336074
60346075 .PHONY: ct apps-ct distclean-ct
60366077 # Configuration.
60376078
60386079 CT_OPTS ?=
6080
60396081 ifneq ($(wildcard $(TEST_DIR)),)
6040 CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6041 else
6042 CT_SUITES ?=
6043 endif
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
60446088
60456089 # Core targets.
60466090
60636107 -noinput \
60646108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
60656109 -dir $(TEST_DIR) \
6066 -logdir $(CURDIR)/logs
6110 -logdir $(CT_LOGS_DIR)
60676111
60686112 ifeq ($(CT_SUITES),)
60696113 ct: $(if $(IS_APP),,apps-ct)
60706114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
60716117 ct: test-build $(if $(IS_APP),,apps-ct)
6072 $(verbose) mkdir -p $(CURDIR)/logs/
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
60736119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
60746121 endif
60756122
60766123 ifneq ($(ALL_APPS_DIRS),)
60976144
60986145 define ct_suite_target
60996146 ct-$(1): test-build
6100 $(verbose) mkdir -p $(CURDIR)/logs/
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
61016148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
61026149 endef
61036150
61046151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
61056152
61066153 distclean-ct:
6107 $(gen_verbose) rm -rf $(CURDIR)/logs/
6108
6109 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61106157 # This file is part of erlang.mk and subject to the terms of the ISC License.
61116158
61126159 .PHONY: plt distclean-plt dialyze
61506197 endef
61516198
61526199 $(DIALYZER_PLT): deps app
6153 $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
61546204
61556205 plt: $(DIALYZER_PLT)
61566206
61646214 endif
61656215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
61666216
6167 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61686218 # This file is part of erlang.mk and subject to the terms of the ISC License.
61696219
61706220 .PHONY: distclean-edoc edoc
61726222 # Configuration.
61736223
61746224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
61756235
61766236 # Core targets.
61776237
6178 ifneq ($(wildcard doc/overview.edoc),)
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
61796239 docs:: edoc
61806240 endif
61816241
61846244 # Plugin-specific targets.
61856245
61866246 edoc: distclean-edoc doc-deps
6187 $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
61886248
61896249 distclean-edoc:
61906250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
61916251
6192 # Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61936253 # This file is part of erlang.mk and subject to the terms of the ISC License.
61946254
6195 .PHONY: distclean-escript escript
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
61966320
61976321 # Configuration.
61986322
61996323 ESCRIPT_NAME ?= $(PROJECT)
62006324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
62016325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
62026327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6203
6204 ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
6205 ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
6206 ESCRIPT_EMU_ARGS ?= -pa . \
6207 -sasl errlog_type error \
6208 -escript main $(ESCRIPT_NAME)
6209 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6210 ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
62116332
62126333 # Core targets.
62136334
62206341
62216342 # Plugin-specific targets.
62226343
6223 # Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
6224 # Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
6225 # Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
6226 # Software may only be used for the great good and the true happiness of all
6227 # sentient beings.
6228
6229 define ESCRIPT_RAW
6230 'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
6231 'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
6232 ' [F || F <- A, not filelib:is_dir(F) ] end,'\
6233 'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
6234 'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
6235 'Ez = fun(Escript) ->'\
6236 ' Static = Files([$(ESCRIPT_STATIC)]),'\
6237 ' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
6238 ' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
6239 ' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
6240 ' {archive, Archive, [memory]},'\
6241 ' {shebang, "$(ESCRIPT_SHEBANG)"},'\
6242 ' {comment, "$(ESCRIPT_COMMENT)"},'\
6243 ' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
6244 ' ]),'\
6245 ' file:change_mode(Escript, 8#755)'\
6246 'end,'\
6247 'Ez("$(ESCRIPT_FILE)"),'\
6248 'halt().'
6249 endef
6250
6251 ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
6252
6253 escript:: distclean-escript deps app
6254 $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
62556360
62566361 distclean-escript:
6257 $(gen_verbose) rm -f $(ESCRIPT_NAME)
6258
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
62596365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6260 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
62616366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
62626367
62636368 .PHONY: eunit apps-eunit
63216426
63226427 ifneq ($(ALL_APPS_DIRS),)
63236428 apps-eunit:
6324 $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done
6325 endif
6326 endif
6327
6328 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
63296436 # This file is part of erlang.mk and subject to the terms of the ISC License.
63306437
6331 .PHONY: relx-rel distclean-relx-rel distclean-relx run
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
63326439
63336440 # Configuration.
63346441
6335 RELX ?= $(CURDIR)/relx
6442 RELX ?= $(ERLANG_MK_TMP)/relx
63366443 RELX_CONFIG ?= $(CURDIR)/relx.config
63376444
63386445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
63396446 RELX_OPTS ?=
63406447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
63416454
63426455 ifeq ($(firstword $(RELX_OPTS)),-o)
63436456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
63506463 ifeq ($(IS_DEP),)
63516464 ifneq ($(wildcard $(RELX_CONFIG)),)
63526465 rel:: relx-rel
6353 endif
6354 endif
6355
6356 distclean:: distclean-relx-rel distclean-relx
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
63576472
63586473 # Plugin-specific targets.
63596474
63626477 $(verbose) chmod +x $(RELX)
63636478
63646479 relx-rel: $(RELX) rel-deps app
6365 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
63666484
63676485 distclean-relx-rel:
63686486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6369
6370 distclean-relx:
6371 $(gen_verbose) rm -rf $(RELX)
63726487
63736488 # Run target.
63746489
63776492 else
63786493
63796494 define get_relx_release.erl
6380 {ok, Config} = file:consult("$(RELX_CONFIG)"),
6381 {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
6382 io:format("~s", [Name]),
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
63836504 halt(0).
63846505 endef
63856506
6386 RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
63876514
63886515 run: all
6389 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
63906517
63916518 help::
63926519 $(verbose) printf "%s\n" "" \
63956522
63966523 endif
63976524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
63986526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6399 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
64006527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
64016528
64026529 .PHONY: shell
64216548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
64226549
64236550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6424 $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
64256552
64266553 shell: build-shell-deps
64276554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
64286555
6429 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
64306571 # This file is part of erlang.mk and subject to the terms of the ISC License.
64316572
64326573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
64376578 tests:: triq
64386579
64396580 define triq_check.erl
6440 code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
64416582 try
64426583 case $(1) of
64436584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
64696610 endif
64706611 endif
64716612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
64726614 # Copyright (c) 2015, Erlang Solutions Ltd.
64736615 # This file is part of erlang.mk and subject to the terms of the ISC License.
64746616
64856627 XREFR ?= $(CURDIR)/xrefr
64866628 export XREFR
64876629
6488 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
64896631
64906632 # Core targets.
64916633
64926634 help::
6493 $(verbose) printf "%s\n" "" \
6494 "Xref targets:" \
6495 " xref Run Xrefr using $XREF_CONFIG as config file if defined"
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
64966638
64976639 distclean:: distclean-xref
64986640
65086650 distclean-xref:
65096651 $(gen_verbose) rm -rf $(XREFR)
65106652
6511 # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
65126655 # This file is part of erlang.mk and subject to the terms of the ISC License.
65136656
65146657 COVER_REPORT_DIR = cover
65176660
65186661 ifdef COVER
65196662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
65206664 # All modules in 'ebin'
65216665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
65226666
65316675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
65326676 endif
65336677 endif
6678 endif
65346679
65356680 # Core targets
65366681
65526697 "Cover targets:" \
65536698 " cover-report Generate a HTML coverage report from previously collected" \
65546699 " cover data." \
6555 " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
65566701 "" \
65576702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
65586703 "target tests additionally generates a HTML coverage report from the combined" \
65656710
65666711 .PHONY: coverdata-clean
65676712 coverdata-clean:
6568 $(gen_verbose) rm -f *.coverdata ct.cover.spec
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
65696714
65706715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
65716721 all.coverdata: $(COVERDATA)
6572 $(gen_verbose) $(ERL) -eval ' \
6573 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
6574 cover:export("$@"), halt(0).'
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
65756723
65766724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
65776725 # empty if you want the coverdata files but not the HTML report.
65896737 # Modules which include eunit.hrl always contain one line without coverage
65906738 # because eunit defines test/0 which is never called. We compensate for this.
65916739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6592 grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
65936741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
65946742
65956743 define cover_report.erl
66246772 endef
66256773
66266774 cover-report:
6627 $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
66286776 $(gen_verbose) $(call erlang,$(cover_report.erl))
66296777
66306778 endif
66316779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
66326839
66336840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
66346841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
66976904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
66986905 endif
66996906 ifndef IS_APP
6700 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
67016908 $(MAKE) -C $$dep $@ \
67026909 IS_APP=1 \
6703 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6704 || exit $$?; \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67056911 done
67066912 endif
6707 $(verbose) for dep in $^ ; do \
6913 $(verbose) set -e; for dep in $^ ; do \
67086914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
67096915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6710 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
67116917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
67126918 $(MAKE) -C $$dep fetch-deps \
67136919 IS_DEP=1 \
6714 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6715 || exit $$?; \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67166921 fi \
67176922 fi \
67186923 done
0 -module(rabbit_trust_store_opera_com_provider).
1
2 -behaviour(rabbit_trust_store_certificate_provider).
3
4 -export([list_certs/1, list_certs/2, load_cert/3]).
5
6 %% This is an example implementation for
7 %% rabbit_trust_store_certificate_provider behaviour.
8 %% The module uses https://certs.opera.com/02/roots/ as a source of
9 %% CA certificates
10 %% The module can be used as an example when
11 %% implementing certificate provider for trust store plugin.
12
13
14 %% This function loads a list of certificates
15 list_certs(_Config) ->
16 inets:start(),
17 case httpc:request(get, {"https://certs.opera.com/02/roots/", []},
18 [], [{body_format, binary}]) of
19 {ok, {{_,Code,_}, _Headers, Body}} when Code div 100 == 2 ->
20 %% First link in directory listing is a parent dir link.
21 {match, [_ParentDirLink | CertMatches]} =
22 re:run(Body, "<td><a href=\"([^\"]*)\">",
23 [global, {capture, all_but_first, binary}]),
24
25 CertNames = lists:append(CertMatches),
26 %% certs.opera.com uses thumbprints for certificate file names
27 %% so they should be unique (there is no need to add change time)
28 {ok,
29 [{CertName,
30 [{name, CertName},
31 %% Url can be formed from CertName, so there is no
32 %% need for this attribute.
33 %% But we use it as an example for providers where CertName and
34 %% url are different.
35 {url, <<"https://certs.opera.com/02/roots/", CertName/binary>>}]}
36 || CertName <- CertNames],
37 nostate};
38 Other -> {error, {http_error, Other}}
39 end.
40
41 %% Since we have no state for the provider,
42 %% we call the stateless version of this functions
43 list_certs(Config, _) -> list_certs(Config).
44
45 %% This function loads a certificate data using certifocate ID and attributes.
46 %% We use the url parameter in attributes.
47 %% Some providers can ignore attributes and use CertId instead
48 load_cert(_CertId, Attributes, _Config) ->
49 Url = proplists:get_value(url, Attributes),
50 case httpc:request(get, {rabbit_data_coercion:to_list(Url), []},
51 [], [{body_format, binary}]) of
52 {ok, {{_,Code,_}, _Headers, Body}} when Code div 100 == 2 ->
53 %% We assume that there is only one certificate per file.
54 BodySingleLine = binary:replace(Body, <<"\n">>, <<>>, [global]),
55 {match, [CertEncoded|_]} =
56 re:run(BodySingleLine,
57 "<certificate-data>(.*)</certificate-data>",
58 [{capture, all_but_first, binary}, ungreedy]),
59 [{'Certificate', Cert, not_encrypted}] =
60 public_key:pem_decode(<<"-----BEGIN CERTIFICATE-----\n",
61 CertEncoded/binary,
62 "\n-----END CERTIFICATE-----\n">>),
63 {ok, Cert};
64 Other -> {error, {http_error, Other}}
65 end.
66
67
68
0 # RabbitMQ trust store HTTP server example
1
2 `rabbitmq_trust_store_django` is a very minimalistic [Django](https://www.djangoproject.com/) 1.10+ application
3 that rabbitmq-trust-store's `rabbit_trust_store_http_provider` can use as a source of certificates.
4 The project serves PEM encoded CA certificate files from the `certs` directory.
5 It's really not designed to be anything other than an example.
6
7
8 ## Running the Example
9
10 1. Put certificates that should be trusted in PEM format into the `certs` directory.
11
12 2. Run `python manage.py runserver` to launch it after [installing Django](https://docs.djangoproject.com/en/1.10/topics/install/).
13
14 3. Configure RabbitMQ trust store to use `rabbit_trust_store_http_provider`.
15
16 Below is a very minimalistic example that assumes the example is available
17 on the local machine via HTTP:
18
19 ```
20 {rabbitmq_trust_store,
21 [{providers, [rabbit_trust_store_http_provider]},
22 {url, "http://127.0.0.1:8000/"},
23 {refresh_interval, {seconds, 30}}
24 ]}.
25 ```
26
27 You can specify TLS options if you use HTTPS:
28
29 ```
30 {rabbitmq_trust_store,
31 [{providers, [rabbit_trust_store_http_provider]},
32 {url, "https://secure.store.example.local:8000/"},
33 {refresh_interval, {seconds, 30}},
34 {ssl_options, [{certfile, "/client/cert.pem"},
35 {keyfile, "/client/key.pem"},
36 {cacertfile, "/ca/cert.pem"}
37 ]}
38 ]}.
39 ```
40
41
42 ## HTTP API Endpoints
43
44 This project will serve static files from `certs` directory and
45 will list them in JSON format described in [rabbitmq-trust-store](https://github.com/rabbitmq/rabbitmq-trust-store/)
46
47 If you're not familiar with Django, urls.py and auth/views.py may be
48 most illuminating.
0 #!/usr/bin/env python
1 import os
2 import sys
3
4 if __name__ == "__main__":
5 os.environ.setdefault("DJANGO_SETTINGS_MODULE", "rabbitmq_trust_store_django.settings")
6
7 from django.core.management import execute_from_command_line
8
9 execute_from_command_line(sys.argv)
0 """
1 Django settings for rabbitmq_trust_store_django project.
2
3 Generated by 'django-admin startproject' using Django 1.9.4.
4
5 For more information on this file, see
6 https://docs.djangoproject.com/en/1.9/topics/settings/
7
8 For the full list of settings and their values, see
9 https://docs.djangoproject.com/en/1.9/ref/settings/
10 """
11
12 import os
13
14 # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
15 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
16
17
18 # Quick-start development settings - unsuitable for production
19 # See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/
20
21 # SECURITY WARNING: keep the secret key used in production secret!
22 SECRET_KEY = 'y3ws-)+baz%a4^p(2@+ell%&bj-q2q^f2&do)c2-feo#a4*x0t'
23
24 # SECURITY WARNING: don't run with debug turned on in production!
25 DEBUG = True
26
27 ALLOWED_HOSTS = []
28
29
30 # Application definition
31
32 INSTALLED_APPS = [
33 'django.contrib.admin',
34 'django.contrib.auth',
35 'django.contrib.contenttypes',
36 'django.contrib.sessions',
37 'django.contrib.messages',
38 'django.contrib.staticfiles',
39 ]
40
41 MIDDLEWARE_CLASSES = [
42 'django.middleware.security.SecurityMiddleware',
43 'django.contrib.sessions.middleware.SessionMiddleware',
44 'django.middleware.common.CommonMiddleware',
45 'django.middleware.csrf.CsrfViewMiddleware',
46 'django.contrib.auth.middleware.AuthenticationMiddleware',
47 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
48 'django.contrib.messages.middleware.MessageMiddleware',
49 'django.middleware.clickjacking.XFrameOptionsMiddleware',
50 ]
51
52 ROOT_URLCONF = 'rabbitmq_trust_store_django.urls'
53
54 TEMPLATES = [
55 {
56 'BACKEND': 'django.template.backends.django.DjangoTemplates',
57 'DIRS': [],
58 'APP_DIRS': True,
59 'OPTIONS': {
60 'context_processors': [
61 'django.template.context_processors.debug',
62 'django.template.context_processors.request',
63 'django.contrib.auth.context_processors.auth',
64 'django.contrib.messages.context_processors.messages',
65 ],
66 },
67 },
68 ]
69
70 WSGI_APPLICATION = 'rabbitmq_trust_store_django.wsgi.application'
71
72
73 # Database
74 # https://docs.djangoproject.com/en/1.9/ref/settings/#databases
75
76 DATABASES = {
77 'default': {
78 'ENGINE': 'django.db.backends.sqlite3',
79 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
80 }
81 }
82
83
84 # Password validation
85 # https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
86
87 AUTH_PASSWORD_VALIDATORS = [
88 {
89 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
90 },
91 {
92 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
93 },
94 {
95 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
96 },
97 {
98 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
99 },
100 ]
101
102
103 # Internationalization
104 # https://docs.djangoproject.com/en/1.9/topics/i18n/
105
106 LANGUAGE_CODE = 'en-us'
107
108 TIME_ZONE = 'UTC'
109
110 USE_I18N = True
111
112 USE_L10N = True
113
114 USE_TZ = True
115
116
117 # Static files (CSS, JavaScript, Images)
118 # https://docs.djangoproject.com/en/1.9/howto/static-files/
119
120 STATIC_URL = "/certs/"
121
122 STATICFILES_DIRS = [
123 os.path.join(BASE_DIR, "certs")
124 ]
0 from __future__ import unicode_literals
1
2 from django.apps import AppConfig
3
4
5 class TrustStoreConfig(AppConfig):
6 name = 'trust_store'
0 from django.conf.urls import url
1
2 from . import views
3
4 urlpatterns = [
5 url(r'^$', views.index, name='index'),
6 ]
0 from django.shortcuts import render
1 from django.http import HttpResponse, JsonResponse
2 import os
3 from django.conf import settings
4 from django.views.decorators.http import last_modified
5 from datetime import datetime
6
7
8 def latest_dir_change(request):
9 timestamp = os.stat(cert_directory()).st_mtime
10 return datetime.fromtimestamp(timestamp)
11
12 @last_modified(latest_dir_change)
13 def index(request):
14 request.META
15 directory = cert_directory()
16 certs = {'certificates': [file_object(file) for file in pem_files(directory)]}
17 return JsonResponse(certs)
18
19 def cert_directory():
20 return os.path.join(settings.BASE_DIR, "certs")
21
22 def pem_files(directory):
23 files = os.listdir(directory)
24 return [os.path.join(directory, file) for file in files if is_pem(file)]
25
26 def is_pem(file):
27 return 'pem' == os.path.splitext(file)[1][1:]
28
29 def file_object(file):
30 return {'id': file_id(file), 'path': path_for_file(file)}
31
32
33 def file_id(file):
34 mtime = str(int(os.stat(file).st_mtime))
35 basename = os.path.basename(file)
36 return basename + ':' + mtime
37
38 def path_for_file(file):
39 basename = os.path.basename(file)
40 return "/certs/" + basename
0 """rabbitmq_trust_store_django URL Configuration
1
2 The `urlpatterns` list routes URLs to views. For more information please see:
3 https://docs.djangoproject.com/en/1.9/topics/http/urls/
4 Examples:
5 Function views
6 1. Add an import: from my_app import views
7 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
8 Class-based views
9 1. Add an import: from other_app.views import Home
10 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
11 Including another URLconf
12 1. Import the include() function: from django.conf.urls import url, include
13 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
14 """
15 from django.conf.urls import include, url
16 from django.contrib import admin
17
18 urlpatterns = [
19 url(r'', include('rabbitmq_trust_store_django.trust_store.urls')),
20 ]
0 """
1 WSGI config for rabbitmq_trust_store_django project.
2
3 It exposes the WSGI callable as a module-level variable named ``application``.
4
5 For more information on this file, see
6 https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/
7 """
8
9 import os
10
11 from django.core.wsgi import get_wsgi_application
12
13 os.environ.setdefault("DJANGO_SETTINGS_MODULE", "rabbitmq_trust_store_django.settings")
14
15 application = get_wsgi_application()
22 # before the inclusion of erlang.mk leading to the wrong target becoming
33 # the default.
44 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
526 endif
627
728 # --------------------------------------------------------------------
2041 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
2142 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
2243 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
2345 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
2446 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
2547 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
2648 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
2749 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
2851 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
2952 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
3054 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
3155 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
3256 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
3559 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
3660 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
3761 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
3863 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
3964 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
4065 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
7095
7196 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
7297
73 # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
74 # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
75 # needs to add "ranch" as a BUILD_DEPS. The list of projects needing
76 # this workaround are:
77 # o rabbitmq-web-stomp
78 dep_ranch = git https://github.com/ninenines/ranch 1.2.1
98 # Third-party dependencies version pinning.
99 #
100 # We do that in this file, which is copied in all projects, to ensure
101 # all projects use the same versions. It avoids conflicts and makes it
102 # possible to work with rabbitmq-public-umbrella.
103
104 dep_cowboy_commit = 1.0.4
105 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
106 dep_ranch_commit = 1.3.1
107 dep_webmachine_commit = 1.10.8p2
79108
80109 RABBITMQ_COMPONENTS = amqp_client \
81110 rabbit \
82111 rabbit_common \
83112 rabbitmq_amqp1_0 \
84113 rabbitmq_auth_backend_amqp \
114 rabbitmq_auth_backend_cache \
85115 rabbitmq_auth_backend_http \
86116 rabbitmq_auth_backend_ldap \
87117 rabbitmq_auth_mechanism_ssl \
88118 rabbitmq_boot_steps_visualiser \
89119 rabbitmq_clusterer \
120 rabbitmq_cli \
90121 rabbitmq_codegen \
91122 rabbitmq_consistent_hash_exchange \
123 rabbitmq_ct_client_helpers \
92124 rabbitmq_ct_helpers \
93125 rabbitmq_delayed_message_exchange \
94126 rabbitmq_dotnet_client \
97129 rabbitmq_federation_management \
98130 rabbitmq_java_client \
99131 rabbitmq_jms_client \
132 rabbitmq_jms_cts \
100133 rabbitmq_jms_topic_exchange \
101134 rabbitmq_lvc \
102135 rabbitmq_management \
99 %%
1010 %% The Original Code is RabbitMQ.
1111 %%
12 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
12 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1313 %%
1414
1515 -module(rabbit_trust_store).
1717
1818 -export([mode/0, refresh/0, list/0]). %% Console Interface.
1919 -export([whitelisted/3, is_whitelisted/1]). %% Client-side Interface.
20 -export([start/1, start_link/1]).
20 -export([start_link/0]).
2121 -export([init/1, terminate/2,
2222 handle_call/3, handle_cast/2,
2323 handle_info/2,
2424 code_change/3]).
2525
26 -include_lib("stdlib/include/ms_transform.hrl").
2627 -include_lib("kernel/include/file.hrl").
27 -include_lib("stdlib/include/ms_transform.hrl").
2828 -include_lib("public_key/include/public_key.hrl").
2929
3030 -type certificate() :: #'OTPCertificate'{}.
3939 | {fail, Reason :: term()}
4040 | {unknown, state()}.
4141
42 -record(entry, {filename :: string(), identifier :: tuple(), change_time :: integer()}).
43 -record(state, {directory_change_time :: integer(), whitelist_directory :: string(), refresh_interval :: integer()}).
42 -record(state, {
43 providers_state :: [{module(), term()}],
44 refresh_interval :: integer()
45 }).
46 -record(entry, {
47 name :: string(),
48 cert_id :: term(),
49 provider :: module(),
50 issuer_id :: tuple(),
51 certificate :: public_key:der_encoded()
52 }).
4453
4554
4655 %% OTP Supervision
4756
48 start(Settings) ->
49 gen_server:start(?MODULE, Settings, []).
50
51 start_link(Settings) ->
52 gen_server:start_link({local, trust_store}, ?MODULE, Settings, []).
57 start_link() ->
58 gen_server:start_link({local, trust_store}, ?MODULE, [], []).
5359
5460
5561 %% Console Interface
6470
6571 -spec list() -> string().
6672 list() ->
67 gen_server:call(trust_store, list).
73 Formatted = lists:map(
74 fun(#entry{
75 name = N,
76 cert_id = CertId,
77 certificate = Cert,
78 issuer_id = {_, Serial}}) ->
79 %% Use the certificate unique identifier as a default for the name.
80 Name = case N of
81 undefined -> io_lib:format("~p", [CertId]);
82 _ -> N
83 end,
84 Validity = rabbit_ssl:peer_cert_validity(Cert),
85 Subject = rabbit_ssl:peer_cert_subject(Cert),
86 Issuer = rabbit_ssl:peer_cert_issuer(Cert),
87 Text = io_lib:format("Name: ~s~nSerial: ~p | 0x~.16.0B~n"
88 "Subject: ~s~nIssuer: ~s~nValidity: ~p~n",
89 [Name, Serial, Serial,
90 Subject, Issuer, Validity]),
91 lists:flatten(Text)
92 end,
93 ets:tab2list(table_name())),
94 string:join(Formatted, "~n~n").
6895
6996 %% Client (SSL Socket) Interface
7097
96123
97124 -spec is_whitelisted(certificate()) -> boolean().
98125 is_whitelisted(#'OTPCertificate'{}=C) ->
99 #entry{identifier = Id} = extract_unique_attributes(C),
126 Id = extract_issuer_id(C),
100127 ets:member(table_name(), Id).
101128
102
103129 %% Generic Server Callbacks
104130
105 init(Settings) ->
131 init([]) ->
106132 erlang:process_flag(trap_exit, true),
107133 ets:new(table_name(), table_options()),
108 Path = path(Settings),
109 Interval = refresh_interval(Settings),
110 Initial = modification_time(Path),
111 tabulate(Path),
134 Config = application:get_all_env(rabbitmq_trust_store),
135 ProvidersState = refresh_certs(Config, []),
136 Interval = refresh_interval(Config),
112137 if
113138 Interval =:= 0 ->
114139 ok;
116141 erlang:send_after(Interval, erlang:self(), refresh)
117142 end,
118143 {ok,
119 #state{directory_change_time = Initial,
120 whitelist_directory = Path,
144 #state{
145 providers_state = ProvidersState,
121146 refresh_interval = Interval}}.
122147
123148 handle_call(mode, _, St) ->
124149 {reply, mode(St), St};
125 handle_call(refresh, _, St) ->
126 {reply, refresh(St), St};
127 handle_call(list, _, St) ->
128 {reply, list(St), St};
150 handle_call(refresh, _, #state{providers_state = ProvidersState} = St) ->
151 Config = application:get_all_env(rabbitmq_trust_store),
152 NewProvidersState = refresh_certs(Config, ProvidersState),
153 {reply, ok, St#state{providers_state = NewProvidersState}};
129154 handle_call(_, _, St) ->
130155 {noreply, St}.
131156
132157 handle_cast(_, St) ->
133158 {noreply, St}.
134159
135 handle_info(refresh, #state{refresh_interval = Interval} = St) ->
136 New = refresh(St),
160 handle_info(refresh, #state{refresh_interval = Interval,
161 providers_state = ProvidersState} = St) ->
162 Config = application:get_all_env(rabbitmq_trust_store),
163 NewProvidersState = refresh_certs(Config, ProvidersState),
137164 erlang:send_after(Interval, erlang:self(), refresh),
138 {noreply, St#state{directory_change_time = New}};
165 {noreply, St#state{providers_state = NewProvidersState}};
139166 handle_info(_, St) ->
140167 {noreply, St}.
141168
147174
148175
149176 %% Ancillary & Constants
150
151 list(#state{whitelist_directory = Path}) ->
152 Formatted =
153 [format_cert(Path, F, S) ||
154 #entry{filename = F, identifier = {_, S}} <- ets:tab2list(table_name())],
155 to_big_string(Formatted).
156177
157178 mode(#state{refresh_interval = I}) ->
158179 if
160181 I > 0 -> 'automatic'
161182 end.
162183
163 refresh(#state{whitelist_directory = Path, directory_change_time = Old}) ->
164 New = modification_time(Path),
165 case New > Old of
166 false ->
167 ok;
168 true ->
169 tabulate(Path)
184 refresh_interval(Config) ->
185 Seconds = case proplists:get_value(refresh_interval, Config) of
186 undefined ->
187 default_refresh_interval();
188 S when is_integer(S), S >= 0 ->
189 S;
190 {seconds, S} when is_integer(S), S >= 0 ->
191 S
170192 end,
171 New.
172
173 refresh_interval(Pairs) ->
174 {refresh_interval, S} = lists:keyfind(refresh_interval, 1, Pairs),
175 timer:seconds(S).
176
177 path(Pairs) ->
178 {directory, Path} = lists:keyfind(directory, 1, Pairs),
179 Path.
193 timer:seconds(Seconds).
194
195 default_refresh_interval() ->
196 {ok, I} = application:get_env(rabbitmq_trust_store, default_refresh_interval),
197 I.
198
199
200 %% =================================
201
202 -spec refresh_certs(Config, State) -> State
203 when State :: [{module(), term()}],
204 Config :: list().
205 refresh_certs(Config, State) ->
206 Providers = providers(Config),
207 clean_deleted_providers(Providers),
208 lists:foldl(
209 fun(Provider, NewStates) ->
210 ProviderState = proplists:get_value(Provider, State, nostate),
211 RefreshedState = refresh_provider_certs(Provider, Config, ProviderState),
212 [{Provider, RefreshedState} | NewStates]
213 end,
214 [],
215 Providers).
216
217 -spec refresh_provider_certs(Provider, Config, ProviderState) -> ProviderState
218 when Provider :: module(),
219 Config :: list(),
220 ProviderState :: term().
221 refresh_provider_certs(Provider, Config, ProviderState) ->
222 case list_certs(Provider, Config, ProviderState) of
223 no_change ->
224 ProviderState;
225 {ok, CertsList, NewProviderState} ->
226 update_certs(CertsList, Provider, Config),
227 NewProviderState;
228 {error, Reason} ->
229 rabbit_log:error("Unable to load certificate list for provider ~p,"
230 " reason: ~p~n",
231 [Provider, Reason]),
232 ProviderState
233 end.
234
235 list_certs(Provider, Config, nostate) ->
236 Provider:list_certs(Config);
237 list_certs(Provider, Config, ProviderState) ->
238 Provider:list_certs(Config, ProviderState).
239
240 update_certs(CertsList, Provider, Config) ->
241 OldCertIds = get_old_cert_ids(Provider),
242 {NewCertIds, _} = lists:unzip(CertsList),
243
244 lists:foreach(
245 fun(CertId) ->
246 Attributes = proplists:get_value(CertId, CertsList),
247 Name = proplists:get_value(name, Attributes, undefined),
248 case load_and_decode_cert(Provider, CertId, Attributes, Config) of
249 {ok, Cert, IssuerId} ->
250 save_cert(CertId, Provider, IssuerId, Cert, Name);
251 {error, Reason} ->
252 rabbit_log:error("Unable to load CA certificate ~p"
253 " with provider ~p,"
254 " reason: ~p",
255 [CertId, Provider, Reason])
256 end
257 end,
258 NewCertIds -- OldCertIds),
259 lists:foreach(
260 fun(CertId) ->
261 delete_cert(CertId, Provider)
262 end,
263 OldCertIds -- NewCertIds),
264 ok.
265
266 load_and_decode_cert(Provider, CertId, Attributes, Config) ->
267 try
268 case Provider:load_cert(CertId, Attributes, Config) of
269 {ok, Cert} ->
270 DecodedCert = public_key:pkix_decode_cert(Cert, otp),
271 Id = extract_issuer_id(DecodedCert),
272 {ok, Cert, Id};
273 {error, Reason} -> {error, Reason}
274 end
275 catch _:Error ->
276 {error, {Error, erlang:get_stacktrace()}}
277 end.
278
279 delete_cert(CertId, Provider) ->
280 MS = ets:fun2ms(fun(#entry{cert_id = CId, provider = P})
281 when P == Provider, CId == CertId ->
282 true
283 end),
284 ets:select_delete(table_name(), MS).
285
286 save_cert(CertId, Provider, Id, Cert, Name) ->
287 ets:insert(table_name(), #entry{cert_id = CertId,
288 provider = Provider,
289 issuer_id = Id,
290 certificate = Cert,
291 name = Name}).
292
293 get_old_cert_ids(Provider) ->
294 MS = ets:fun2ms(fun(#entry{provider = P, cert_id = CId})
295 when P == Provider ->
296 CId
297 end),
298 ets:select(table_name(), MS).
299
300 providers(Config) ->
301 Providers = proplists:get_value(providers, Config, []),
302 lists:filter(
303 fun(Provider) ->
304 case code:ensure_loaded(Provider) of
305 {module, Provider} -> true;
306 {error, Error} ->
307 rabbit_log:warning("Unable to load trust store certificates"
308 " with provider module ~p. Reason: ~p~n",
309 [Provider, Error]),
310 false
311 end
312 end,
313 Providers).
180314
181315 table_name() ->
182316 trust_store_whitelist.
185319 [protected,
186320 named_table,
187321 set,
188 {keypos, #entry.identifier},
322 {keypos, #entry.issuer_id},
189323 {heir, none}].
190324
191 modification_time(Path) ->
192 {ok, Info} = file:read_file_info(Path, [{time, posix}]),
193 Info#file_info.mtime.
194
195 already_whitelisted_filenames() ->
196 ets:select(table_name(),
197 ets:fun2ms(fun (#entry{filename = N, change_time = T}) -> {N, T} end)).
198
199 one_whitelisted_filename({Name, Time}) ->
200 ets:fun2ms(fun (#entry{filename = N, change_time = T}) when N =:= Name, T =:= Time -> true end).
201
202 build_entry(Path, {Name, Time}) ->
203 Absolute = filename:join(Path, Name),
204 Certificate = scan_then_parse(Absolute),
205 Unique = extract_unique_attributes(Certificate),
206 Unique#entry{filename = Name, change_time = Time}.
207
208 try_build_entry(Path, {Name, Time}) ->
209 try build_entry(Path, {Name, Time}) of
210 Entry ->
211 rabbit_log:info(
212 "trust store: loading certificate '~s'", [Name]),
213 {ok, Entry}
214 catch
215 _:Err ->
216 rabbit_log:error(
217 "trust store: failed to load certificate '~s', error: ~p",
218 [Name, Err]),
219 {error, Err}
220 end.
221
222 do_insertions(Before, After, Path) ->
223 Entries = [try_build_entry(Path, NameTime) ||
224 NameTime <- (After -- Before)],
225 [insert(Entry) || {ok, Entry} <- Entries].
226
227 do_removals(Before, After) ->
228 [delete(NameTime) || NameTime <- (Before -- After)].
229
230 get_new(Path) ->
231 {ok, New} = file:list_dir(Path),
232 [{X, modification_time(filename:absname(X, Path))} || X <- New].
233
234 tabulate(Path) ->
235 Old = already_whitelisted_filenames(),
236 New = get_new(Path),
237 do_insertions(Old, New, Path),
238 do_removals(Old, New),
239 ok.
240
241 delete({Name, Time}) ->
242 rabbit_log:info("removing certificate '~s'", [Name]),
243 ets:select_delete(table_name(), one_whitelisted_filename({Name, Time})).
244
245 insert(Entry) ->
246 true = ets:insert(table_name(), Entry).
247
248 scan_then_parse(Filename) when is_list(Filename) ->
249 {ok, Bin} = file:read_file(Filename),
250 [{'Certificate', Data, not_encrypted}] = public_key:pem_decode(Bin),
251 public_key:pkix_decode_cert(Data, otp).
252
253 extract_unique_attributes(#'OTPCertificate'{}=C) ->
325 extract_issuer_id(#'OTPCertificate'{} = C) ->
254326 {Serial, Issuer} = case public_key:pkix_issuer_id(C, other) of
255327 {error, _Reason} ->
256328 {ok, Identifier} = public_key:pkix_issuer_id(C, self),
258330 {ok, Identifier} ->
259331 Identifier
260332 end,
261 %% Why change the order of attributes? For the same reason we put
262 %% the *most significant figure* first (on the left hand side).
263 #entry{identifier = {Issuer, Serial}}.
264
265 to_big_string(Formatted) ->
266 string:join([cert_to_string(X) || X <- Formatted], "~n~n").
267
268 cert_to_string({Name, Serial, Subject, Issuer, Validity}) ->
269 Text =
270 io_lib:format("Name: ~s~nSerial: ~p | 0x~.16.0B~nSubject: ~s~nIssuer: ~s~nValidity: ~p~n",
271 [ Name, Serial, Serial, Subject, Issuer, Validity]),
272 lists:flatten(Text).
273
274 format_cert(Path, Name, Serial) ->
275 {ok, Bin} = file:read_file(filename:join(Path, Name)),
276 [{'Certificate', Data, not_encrypted}] = public_key:pem_decode(Bin),
277 Validity = rabbit_ssl:peer_cert_validity(Data),
278 Subject = rabbit_ssl:peer_cert_subject(Data),
279 Issuer = rabbit_ssl:peer_cert_issuer(Data),
280 {Name, Serial, Subject, Issuer, Validity}.
281
333 {Issuer, Serial}.
334
335 clean_deleted_providers(Providers) ->
336 [{EntryMatch, _, [true]}] =
337 ets:fun2ms(fun(#entry{provider = P})-> true end),
338 Condition = [ {'=/=', '$1', Provider} || Provider <- Providers ],
339 ets:select_delete(table_name(), [{EntryMatch, Condition, [true]}]).
340
99 %%
1010 %% The Original Code is RabbitMQ.
1111 %%
12 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
12 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1313 %%
1414
1515 -module(rabbit_trust_store_app).
1717 -export([change_SSL_options/0]).
1818 -export([revert_SSL_options/0]).
1919 -export([start/2, stop/1]).
20 -define(DIRECTORY_OR_FILE_NAME_EXISTS, eexist).
21
2220
2321 -rabbit_boot_step({rabbit_trust_store, [
2422 {description, "Change necessary SSL options."},
4442 ok = application:set_env(rabbit, ssl_options, Cfg).
4543
4644 start(normal, _) ->
47
48 %% The below two are properties, that is, tuple of name/value.
49 Path = whitelist_path(),
50 Interval = refresh_interval_time(),
51
52 rabbit_trust_store_sup:start_link([Path, Interval]).
45 rabbit_trust_store_sup:start_link().
5346
5447 stop(_) ->
5548 ok.
6861 ok
6962 end,
7063 %% Only enter those options neccessary for this application.
71 lists:keymerge(1, required_options(),
72 [{verify_fun, {delegate(), continue}},
73 {partial_chain, fun partial_chain/1} | Options]).
64 %%
65 %% The `partial_chain` option was introduced in Erlng 17.3 (ssl
66 %% 5.3.6). So avoid its use with old versions.
67 {ok, SSLAppVsn} = application:get_key(ssl, vsn),
68 NewOptions = case rabbit_misc:version_compare(SSLAppVsn, "5.3.6", gte) of
69 true -> [{verify_fun, {delegate(), continue}},
70 {partial_chain, fun partial_chain/1}];
71 false -> [{verify_fun, {delegate(), continue}}]
72 end,
73 lists:keymerge(1, required_options(), NewOptions ++ Options).
7474
7575 delegate() -> fun rabbit_trust_store:whitelisted/3.
7676
9090
9191 required_options() ->
9292 [{verify, verify_peer}, {fail_if_no_peer_cert, true}].
93
94 whitelist_path() ->
95 Path = case application:get_env(rabbitmq_trust_store, directory) of
96 undefined ->
97 default_directory();
98 {ok, V} when is_binary(V) ->
99 binary_to_list(V);
100 {ok, V} when is_list(V) ->
101 V
102 end,
103 ok = ensure_directory(Path),
104 {directory, Path}.
105
106 refresh_interval_time() ->
107 case application:get_env(rabbitmq_trust_store, refresh_interval) of
108 undefined ->
109 {refresh_interval, default_refresh_interval()};
110 {ok, S} when is_integer(S), S >= 0 ->
111 {refresh_interval, S};
112 {ok, {seconds, S}} when is_integer(S), S >= 0 ->
113 {refresh_interval, S}
114 end.
115
116 default_directory() ->
117
118 %% Dismantle the directory tree: first the table & meta-data
119 %% directory, then the Mesia database directory, finally the node
120 %% directory where we will place the default whitelist in `Full`.
121
122 Table = filename:split(rabbit_mnesia:dir()),
123 Mnesia = lists:droplast(Table),
124 Node = lists:droplast(Mnesia),
125 Full = Node ++ ["trust_store", "whitelist"],
126 filename:join(Full).
127
128 default_refresh_interval() ->
129 {ok, I} = application:get_env(rabbitmq_trust_store, default_refresh_interval),
130 I.
131
132 ensure_directory(Path) ->
133 ok = ensure_parent_directories(Path),
134 case file:make_dir(Path) of
135 {error, ?DIRECTORY_OR_FILE_NAME_EXISTS} ->
136 true = filelib:is_dir(Path),
137 ok;
138 ok ->
139 ok
140 end.
141
142 ensure_parent_directories(Path) ->
143 filelib:ensure_dir(Path).
0 -module(rabbit_trust_store_certificate_provider).
1
2 -include_lib("public_key/include/public_key.hrl").
3
4 -callback list_certs(Config)
5 -> no_change | {ok, [{CertId, Attributes}], ProviderState}
6 when Config :: list(),
7 ProviderState :: term(),
8 CertId :: term(),
9 Attributes :: list().
10
11 -callback list_certs(Config, ProviderState)
12 -> no_change | {ok, [{CertId, Attributes}], ProviderState}
13 when Config :: list(),
14 ProviderState :: term(),
15 CertId :: term(),
16 Attributes :: list().
17
18 -callback load_cert(CertId, Attributes, Config)
19 -> {ok, Cert} | {error, term()}
20 when CertId :: term(),
21 Attributes :: list(),
22 Config :: list(),
23 Cert :: public_key:der_encoded().
0 -module(rabbit_trust_store_file_provider).
1
2 -include_lib("kernel/include/file.hrl").
3
4 -behaviour(rabbit_trust_store_certificate_provider).
5
6 -export([list_certs/1, list_certs/2, load_cert/3]).
7
8 -define(DIRECTORY_OR_FILE_NAME_EXISTS, eexist).
9
10 -type cert_id() :: {FileName :: string(),
11 ChangeTime :: integer(),
12 Hash :: integer()}.
13
14 -spec list_certs(Config :: list())
15 -> no_change | {ok, [{cert_id(), list()}], State}
16 when State :: nostate.
17 list_certs(Config) ->
18 Path = directory_path(Config),
19 Certs = list_certs_0(Path),
20 {ok, Certs, nostate}.
21
22 -spec list_certs(Config :: list(), State)
23 -> no_change | {ok, [{cert_id(), list()}], State}
24 when State :: nostate.
25 list_certs(Config, _) ->
26 list_certs(Config).
27
28 -spec load_cert(cert_id(), list(), Config :: list())
29 -> {ok, Cert :: public_key:der_encoded()}.
30 load_cert({FileName, _, _}, _, Config) ->
31 Path = directory_path(Config),
32 Cert = extract_cert(Path, FileName),
33 rabbit_log:info(
34 "trust store: loading certificate '~s'", [FileName]),
35 {ok, Cert}.
36
37 extract_cert(Path, FileName) ->
38 Absolute = filename:join(Path, FileName),
39 scan_then_parse(Absolute).
40
41 scan_then_parse(Filename) when is_list(Filename) ->
42 {ok, Bin} = file:read_file(Filename),
43 [{'Certificate', Data, not_encrypted}] = public_key:pem_decode(Bin),
44 Data.
45
46 list_certs_0(Path) ->
47 {ok, FileNames} = file:list_dir(Path),
48 lists:map(
49 fun(FileName) ->
50 AbsName = filename:absname(FileName, Path),
51 CertId = {FileName,
52 modification_time(AbsName),
53 file_content_hash(AbsName)},
54 {CertId, [{name, FileName}]}
55 end,
56 FileNames).
57
58 modification_time(Path) ->
59 {ok, Info} = file:read_file_info(Path, [{time, posix}]),
60 Info#file_info.mtime.
61
62 file_content_hash(Path) ->
63 {ok, Data} = file:read_file(Path),
64 erlang:phash2(Data).
65
66 directory_path(Config) ->
67 directory_path(Config, default_directory()).
68
69 directory_path(Config, Default) ->
70 Path = case proplists:get_value(directory, Config) of
71 undefined ->
72 Default;
73 V when is_binary(V) ->
74 binary_to_list(V);
75 V when is_list(V) ->
76 V
77 end,
78 ok = ensure_directory(Path),
79 Path.
80
81 default_directory() ->
82 %% Dismantle the directory tree: first the table & meta-data
83 %% directory, then the Mesia database directory, finally the node
84 %% directory where we will place the default whitelist in `Full`.
85 Table = filename:split(rabbit_mnesia:dir()),
86 Node = lists:sublist(Table, length(Table) - 2),
87 Full = Node ++ ["trust_store", "whitelist"],
88 filename:join(Full).
89
90 ensure_directory(Path) ->
91 ok = ensure_parent_directories(Path),
92 case file:make_dir(Path) of
93 {error, ?DIRECTORY_OR_FILE_NAME_EXISTS} ->
94 true = filelib:is_dir(Path),
95 ok;
96 ok ->
97 ok
98 end.
99
100 ensure_parent_directories(Path) ->
101 filelib:ensure_dir(Path).
102
0 -module(rabbit_trust_store_http_provider).
1
2 -include_lib("public_key/include/public_key.hrl").
3
4 -behaviour(rabbit_trust_store_certificate_provider).
5
6 -export([list_certs/1, list_certs/2, load_cert/3]).
7
8 -record(http_state,{
9 url :: string(),
10 http_options :: list(),
11 headers :: httpc:headers()
12 }).
13
14 list_certs(Config) ->
15 init(),
16 State = init_state(Config),
17 list_certs(Config, State).
18
19 list_certs(_, #http_state{url = Url,
20 http_options = HttpOptions,
21 headers = Headers} = State) ->
22 Res = httpc:request(get, {Url, Headers}, HttpOptions, [{body_format, binary}]),
23 case Res of
24 {ok, {{_, 200, _}, RespHeaders, Body}} ->
25 Certs = decode_cert_list(Body),
26 NewState = new_state(RespHeaders, State),
27 {ok, Certs, NewState};
28 {ok, {{_,304, _}, _, _}} -> no_change;
29 {ok, {{_,Code,_}, _, Body}} -> {error, {http_error, Code, Body}};
30 {error, Reason} -> {error, Reason}
31 end.
32
33 load_cert(_, Attributes, Config) ->
34 CertPath = proplists:get_value(path, Attributes),
35 #http_state{url = BaseUrl,
36 http_options = HttpOptions,
37 headers = Headers} = init_state(Config),
38 Url = join_url(BaseUrl, CertPath),
39 Res = httpc:request(get,
40 {Url, Headers},
41 HttpOptions,
42 [{body_format, binary}, {full_result, false}]),
43 case Res of
44 {ok, {200, Body}} ->
45 [{'Certificate', Cert, not_encrypted}] = public_key:pem_decode(Body),
46 {ok, Cert};
47 {ok, {Code, Body}} -> {error, {http_error, Code, Body}};
48 {error, Reason} -> {error, Reason}
49 end.
50
51 join_url(BaseUrl, CertPath) ->
52 string:strip(rabbit_data_coercion:to_list(BaseUrl), right, $/)
53 ++ "/" ++
54 string:strip(rabbit_data_coercion:to_list(CertPath), left, $/).
55
56 init() ->
57 inets:start(),
58 ssl:start().
59
60 init_state(Config) ->
61 Url = proplists:get_value(url, Config),
62 Headers = proplists:get_value(http_headers, Config, []),
63 HttpOptions = case proplists:get_value(ssl_options, Config) of
64 undefined -> [];
65 SslOpts -> [{ssl, SslOpts}]
66 end,
67 #http_state{url = Url, http_options = HttpOptions, headers = Headers}.
68
69 decode_cert_list(Body) ->
70 {ok, Struct} = rabbit_misc:json_decode(Body),
71 [{<<"certificates">>, Certs}] = rabbit_misc:json_to_term(Struct),
72 lists:map(
73 fun(Cert) ->
74 Path = proplists:get_value(<<"path">>, Cert),
75 CertId = proplists:get_value(<<"id">>, Cert),
76 {CertId, [{path, Path}]}
77 end,
78 Certs).
79
80 new_state(RespHeaders, #http_state{headers = Headers} = State) ->
81 LastModified = proplists:get_value("Last-Modified",
82 RespHeaders,
83 proplists:get_value("last-modified",
84 RespHeaders,
85 undefined)),
86 case LastModified of
87 undefined -> State;
88 Value ->
89 NewHeaders = lists:ukeymerge(1, Headers,
90 [{"If-Modified-Since", Value}]),
91 State#http_state{headers = NewHeaders}
92 end.
93
94
95
96
97
98
99
99 %%
1010 %% The Original Code is RabbitMQ.
1111 %%
12 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
12 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1313 %%
1414
1515 -module(rabbit_trust_store_sup).
1616 -behaviour(supervisor).
17 -export([start_link/1]).
17 -export([start_link/0]).
1818 -export([init/1]).
1919
2020 -include_lib("rabbit_common/include/rabbit.hrl").
2222
2323 %% ...
2424
25 start_link(Settings) ->
26
27 supervisor:start_link({local, ?MODULE}, ?MODULE, Settings).
25 start_link() ->
26 supervisor:start_link({local, ?MODULE}, ?MODULE, []).
2827
2928
3029 %% ...
3130
32 init(Settings) ->
31 init([]) ->
3332 {ok,
3433 {{one_for_one, 1, 5},
35 [{trust_store, {rabbit_trust_store, start_link, [Settings]},
34 [{trust_store, {rabbit_trust_store, start_link, []},
3635 permanent, timer:seconds(5), worker, [rabbit_trust_store]}]}}.
+0
-16
deps/rabbitmq_trust_store/src/rabbitmq_trust_store.app.src less more
0 {application, rabbitmq_trust_store, [
1 {description, "Client certificate trust store. Provides a way to whitelist client x509 certificates."},
2 {vsn, "3.6.6"},
3 {modules, []},
4 {registered, []},
5 {mod, {rabbit_trust_store_app, []}},
6 {env, [
7 {default_refresh_interval, 30}
8 ]},
9 {applications, [
10 kernel,
11 stdlib,
12 rabbit_common,
13 rabbit
14 ]}
15 ]}.
00 PROJECT = rabbitmq_web_dispatch
1 PROJECT_DESCRIPTION = RabbitMQ Web Dispatcher
2 PROJECT_MOD = rabbit_web_dispatch_app
13
2 DEPS = rabbit_common rabbit mochiweb webmachine
3 TEST_DEPS = rabbitmq_ct_helpers
4 LOCAL_DEPS = inets
5 DEPS = rabbit_common rabbit cowboy
6 TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers
7 dep_cowboy_commit = 1.0.3
48
9 DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
510 DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk
611
712 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
00 rabbitmq-web-dispatch
11 ---------------------
22
3 rabbitmq-web-dispatch is a thin veneer around mochiweb that provides the
4 ability for multiple applications to co-exist on mochiweb
3 rabbitmq-web-dispatch is a thin veneer around Cowboy that provides the
4 ability for multiple applications to co-exist on Cowboy
55 listeners. Applications can register static docroots or dynamic
66 handlers to be executed, dispatched by URL path prefix.
77
8 See http://www.rabbitmq.com/mochiweb.html for information on
8 See http://www.rabbitmq.com/web-dispatch.html for information on
99 configuring web plugins.
1010
1111 The most general registration procedure is
12 `rabbit_web_dispatch:register_context_handler/5`. This takes a callback
13 procedure of the form
12 `rabbit_web_dispatch:register_context_handler/5`.
1413
15 loop(Request) ->
16 ...
17
18 The module `rabbit_webmachine` provides a means of running more than
19 one webmachine in a VM, and understands rabbitmq-web-dispatch contexts. To
20 use it, supply a dispatch table term of the kind usually given to
21 webmachine in the file `priv/dispatch.conf`.
22
23 `setup/{1,2}` in the same module allows some global configuration of
24 webmachine logging and error handling.
14 This takes a dispatch list of the kind usually given to Cowboy, in compiled
15 form.
0 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
11 #
22 # Permission to use, copy, modify, and/or distribute this software for any
33 # purpose with or without fee is hereby granted, provided that the above
1111 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1212 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1313
14 .PHONY: all app apps deps search rel docs install-docs check tests clean distclean help erlang-mk
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
1515
1616 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17
18 ERLANG_MK_VERSION = 2.0.0-pre.2-144-g647ffd1
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
1931
2032 # Core configuration.
2133
2436
2537 PROJECT_VERSION ?= rolling
2638 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
2740
2841 # Verbosity.
2942
8497 rel::
8598 $(verbose) :
8699
100 relup:: deps app
101
87102 check:: tests
88103
89104 clean:: clean-crashdump
101116 help::
102117 $(verbose) printf "%s\n" \
103118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
104 "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
105120 "" \
106121 "Usage: [V=1] $(MAKE) [target]..." \
107122 "" \
149164 core_native_path = $1
150165 endif
151166
152 ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
153 define core_http_get
154 wget --no-check-certificate -O $(1) $(2)|| rm $(1)
155 endef
156 else
157 define core_http_get.erl
158 ssl:start(),
159 inets:start(),
160 case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
161 {ok, {{_, 200, _}, _, Body}} ->
162 case file:write_file("$(1)", Body) of
163 ok -> ok;
164 {error, R1} -> halt(R1)
165 end;
166 {error, R2} ->
167 halt(R2)
168 end,
169 halt(0).
170 endef
171
172 define core_http_get
173 $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
174 endef
175 endif
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
176168
177169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
178170
192184 ERLANG_MK_BUILD_CONFIG ?= build.config
193185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
194186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
195188 erlang-mk:
196189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
197190 ifdef ERLANG_MK_COMMIT
198191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
199192 endif
200193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
201 $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
202195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
203196 rm -rf $(ERLANG_MK_BUILD_DIR)
204197
285278 pkg_apns_repo = https://github.com/inaka/apns4erl
286279 pkg_apns_commit = master
287280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
288289 PACKAGES += azdht
289290 pkg_azdht_name = azdht
290291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
405406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
406407 pkg_bootstrap_commit = master
407408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
408417 PACKAGES += boss_db
409418 pkg_boss_db_name = boss_db
410419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
413422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
414423 pkg_boss_db_commit = master
415424
416 PACKAGES += boss
417 pkg_boss_name = boss
418 pkg_boss_description = Erlang web MVC, now featuring Comet
419 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
420 pkg_boss_fetch = git
421 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
422 pkg_boss_commit = master
423
424425 PACKAGES += brod
425426 pkg_brod_name = brod
426427 pkg_brod_description = Kafka client in Erlang
565566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
566567 pkg_cloudi_service_api_requests_commit = master
567568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
568585 PACKAGES += cloudi_service_db_cassandra_cql
569586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
570587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
573590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
574591 pkg_cloudi_service_db_cassandra_cql_commit = master
575592
576 PACKAGES += cloudi_service_db_cassandra
577 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
579 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
580 pkg_cloudi_service_db_cassandra_fetch = git
581 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
582 pkg_cloudi_service_db_cassandra_commit = master
583
584593 PACKAGES += cloudi_service_db_couchdb
585594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
586595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
637646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
638647 pkg_cloudi_service_db_tokyotyrant_commit = master
639648
640 PACKAGES += cloudi_service_db
641 pkg_cloudi_service_db_name = cloudi_service_db
642 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
643 pkg_cloudi_service_db_homepage = http://cloudi.org/
644 pkg_cloudi_service_db_fetch = git
645 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
646 pkg_cloudi_service_db_commit = master
647
648649 PACKAGES += cloudi_service_filesystem
649650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
650651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
10371038 pkg_edown_repo = https://github.com/uwiger/edown
10381039 pkg_edown_commit = master
10391040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
10401049 PACKAGES += eep_app
10411050 pkg_eep_app_name = eep_app
10421051 pkg_eep_app_description = Embedded Event Processing
10451054 pkg_eep_app_repo = https://github.com/darach/eep-erl
10461055 pkg_eep_app_commit = master
10471056
1048 PACKAGES += eep
1049 pkg_eep_name = eep
1050 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1051 pkg_eep_homepage = https://github.com/virtan/eep
1052 pkg_eep_fetch = git
1053 pkg_eep_repo = https://github.com/virtan/eep
1054 pkg_eep_commit = master
1055
10561057 PACKAGES += efene
10571058 pkg_efene_name = efene
10581059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
12371238 pkg_eqm_repo = https://github.com/loucash/eqm
12381239 pkg_eqm_commit = master
12391240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
12401249 PACKAGES += eredis_pool
12411250 pkg_eredis_pool_name = eredis_pool
12421251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
12451254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
12461255 pkg_eredis_pool_commit = master
12471256
1248 PACKAGES += eredis
1249 pkg_eredis_name = eredis
1250 pkg_eredis_description = Erlang Redis client
1251 pkg_eredis_homepage = https://github.com/wooga/eredis
1252 pkg_eredis_fetch = git
1253 pkg_eredis_repo = https://github.com/wooga/eredis
1254 pkg_eredis_commit = master
1255
12561257 PACKAGES += erl_streams
12571258 pkg_erl_streams_name = erl_streams
12581259 pkg_erl_streams_description = Streams in Erlang
15411542 pkg_etap_repo = https://github.com/ngerakines/etap
15421543 pkg_etap_commit = master
15431544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
15441553 PACKAGES += etest_http
15451554 pkg_etest_http_name = etest_http
15461555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
15491558 pkg_etest_http_repo = https://github.com/wooga/etest_http
15501559 pkg_etest_http_commit = master
15511560
1552 PACKAGES += etest
1553 pkg_etest_name = etest
1554 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1555 pkg_etest_homepage = https://github.com/wooga/etest
1556 pkg_etest_fetch = git
1557 pkg_etest_repo = https://github.com/wooga/etest
1558 pkg_etest_commit = master
1559
15601561 PACKAGES += etoml
15611562 pkg_etoml_name = etoml
15621563 pkg_etoml_description = TOML language erlang parser
15651566 pkg_etoml_repo = https://github.com/kalta/etoml
15661567 pkg_etoml_commit = master
15671568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
15681577 PACKAGES += eunit_formatters
15691578 pkg_eunit_formatters_name = eunit_formatters
15701579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
15731582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
15741583 pkg_eunit_formatters_commit = master
15751584
1576 PACKAGES += eunit
1577 pkg_eunit_name = eunit
1578 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1579 pkg_eunit_homepage = https://github.com/richcarl/eunit
1580 pkg_eunit_fetch = git
1581 pkg_eunit_repo = https://github.com/richcarl/eunit
1582 pkg_eunit_commit = master
1583
15841585 PACKAGES += euthanasia
15851586 pkg_euthanasia_name = euthanasia
15861587 pkg_euthanasia_description = Merciful killer for your Erlang processes
15981599 pkg_evum_commit = master
15991600
16001601 PACKAGES += exec
1601 pkg_exec_name = exec
1602 pkg_exec_name = erlexec
16021603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
16031604 pkg_exec_homepage = http://saleyn.github.com/erlexec
16041605 pkg_exec_fetch = git
17171718 pkg_fn_repo = https://github.com/reiddraper/fn
17181719 pkg_fn_commit = master
17191720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
17201729 PACKAGES += folsom_cowboy
17211730 pkg_folsom_cowboy_name = folsom_cowboy
17221731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
17251734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
17261735 pkg_folsom_cowboy_commit = master
17271736
1728 PACKAGES += folsom
1729 pkg_folsom_name = folsom
1730 pkg_folsom_description = Expose Erlang Events and Metrics
1731 pkg_folsom_homepage = https://github.com/boundary/folsom
1732 pkg_folsom_fetch = git
1733 pkg_folsom_repo = https://github.com/boundary/folsom
1734 pkg_folsom_commit = master
1735
17361737 PACKAGES += folsomite
17371738 pkg_folsomite_name = folsomite
17381739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
20932094 pkg_jesse_repo = https://github.com/for-GET/jesse
20942095 pkg_jesse_commit = master
20952096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
20962105 PACKAGES += jiffy_v
20972106 pkg_jiffy_v_name = jiffy_v
20982107 pkg_jiffy_v_description = JSON validation utility
21012110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
21022111 pkg_jiffy_v_commit = master
21032112
2104 PACKAGES += jiffy
2105 pkg_jiffy_name = jiffy
2106 pkg_jiffy_description = JSON NIFs for Erlang.
2107 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2108 pkg_jiffy_fetch = git
2109 pkg_jiffy_repo = https://github.com/davisp/jiffy
2110 pkg_jiffy_commit = master
2111
21122113 PACKAGES += jobs
21132114 pkg_jobs_name = jobs
21142115 pkg_jobs_description = a Job scheduler for load regulation
21252126 pkg_joxa_repo = https://github.com/joxa/joxa
21262127 pkg_joxa_commit = master
21272128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
21282137 PACKAGES += json_rec
21292138 pkg_json_rec_name = json_rec
21302139 pkg_json_rec_description = JSON to erlang record
21332142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
21342143 pkg_json_rec_commit = master
21352144
2136 PACKAGES += json
2137 pkg_json_name = json
2138 pkg_json_description = a high level json library for erlang (17.0+)
2139 pkg_json_homepage = https://github.com/talentdeficit/json
2140 pkg_json_fetch = git
2141 pkg_json_repo = https://github.com/talentdeficit/json
2142 pkg_json_commit = master
2143
21442145 PACKAGES += jsone
21452146 pkg_jsone_name = jsone
21462147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
21812182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
21822183 pkg_jsx_commit = master
21832184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
21842193 PACKAGES += kafka_protocol
21852194 pkg_kafka_protocol_name = kafka_protocol
21862195 pkg_kafka_protocol_description = Kafka protocol Erlang library
21892198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
21902199 pkg_kafka_protocol_commit = master
21912200
2192 PACKAGES += kafka
2193 pkg_kafka_name = kafka
2194 pkg_kafka_description = Kafka consumer and producer in Erlang
2195 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2196 pkg_kafka_fetch = git
2197 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2198 pkg_kafka_commit = master
2199
22002201 PACKAGES += kai
22012202 pkg_kai_name = kai
22022203 pkg_kai_description = DHT storage by Takeshi Inoue
22932294 pkg_kvs_repo = https://github.com/synrc/kvs
22942295 pkg_kvs_commit = master
22952296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
22962305 PACKAGES += lager_amqp_backend
22972306 pkg_lager_amqp_backend_name = lager_amqp_backend
22982307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
23092318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
23102319 pkg_lager_syslog_commit = master
23112320
2312 PACKAGES += lager
2313 pkg_lager_name = lager
2314 pkg_lager_description = A logging framework for Erlang/OTP.
2315 pkg_lager_homepage = https://github.com/basho/lager
2316 pkg_lager_fetch = git
2317 pkg_lager_repo = https://github.com/basho/lager
2318 pkg_lager_commit = master
2319
23202321 PACKAGES += lambdapad
23212322 pkg_lambdapad_name = lambdapad
23222323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
25732574 pkg_mixer_repo = https://github.com/chef/mixer
25742575 pkg_mixer_commit = master
25752576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
25762585 PACKAGES += mochiweb_xpath
25772586 pkg_mochiweb_xpath_name = mochiweb_xpath
25782587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
25812590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
25822591 pkg_mochiweb_xpath_commit = master
25832592
2584 PACKAGES += mochiweb
2585 pkg_mochiweb_name = mochiweb
2586 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2587 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2588 pkg_mochiweb_fetch = git
2589 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2590 pkg_mochiweb_commit = master
2591
25922593 PACKAGES += mockgyver
25932594 pkg_mockgyver_name = mockgyver
25942595 pkg_mockgyver_description = A mocking library for Erlang
30613062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
30623063 pkg_quickrand_commit = master
30633064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
30643073 PACKAGES += rabbit_exchange_type_riak
30653074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
30663075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
30693078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
30703079 pkg_rabbit_exchange_type_riak_commit = master
30713080
3072 PACKAGES += rabbit
3073 pkg_rabbit_name = rabbit
3074 pkg_rabbit_description = RabbitMQ Server
3075 pkg_rabbit_homepage = https://www.rabbitmq.com/
3076 pkg_rabbit_fetch = git
3077 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3078 pkg_rabbit_commit = master
3079
30803081 PACKAGES += rack
30813082 pkg_rack_name = rack
30823083 pkg_rack_description = Rack handler for erlang
34933494 pkg_smother_repo = https://github.com/ramsay-t/Smother
34943495 pkg_smother_commit = master
34953496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
34963505 PACKAGES += social
34973506 pkg_social_name = social
34983507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
35413550 pkg_stable_repo = https://github.com/dvv/stable
35423551 pkg_stable_commit = master
35433552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
35443561 PACKAGES += statebox_riak
35453562 pkg_statebox_riak_name = statebox_riak
35463563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
35493566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
35503567 pkg_statebox_riak_commit = master
35513568
3552 PACKAGES += statebox
3553 pkg_statebox_name = statebox
3554 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3555 pkg_statebox_homepage = https://github.com/mochi/statebox
3556 pkg_statebox_fetch = git
3557 pkg_statebox_repo = https://github.com/mochi/statebox
3558 pkg_statebox_commit = master
3559
35603569 PACKAGES += statman
35613570 pkg_statman_name = statman
35623571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
37253734 pkg_tirerl_repo = https://github.com/inaka/tirerl
37263735 pkg_tirerl_commit = master
37273736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
37283745 PACKAGES += traffic_tools
37293746 pkg_traffic_tools_name = traffic_tools
37303747 pkg_traffic_tools_description = Simple traffic limiting library
40614078 pkg_zucchini_repo = https://github.com/devinus/zucchini
40624079 pkg_zucchini_commit = master
40634080
4064 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
40654082 # This file is part of erlang.mk and subject to the terms of the ISC License.
40664083
40674084 .PHONY: search
40884105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
40894106 endif
40904107
4091 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
40924109 # This file is part of erlang.mk and subject to the terms of the ISC License.
40934110
4094 .PHONY: distclean-deps
4111 .PHONY: distclean-deps clean-tmp-deps.log
40954112
40964113 # Configuration.
40974114
41104127
41114128 REBAR_DEPS_DIR = $(DEPS_DIR)
41124129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
41134150
41144151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
41154152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
41164153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
41174154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
41184155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
41194157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
41204158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
41214159
41384176
41394177 # Core targets.
41404178
4141 ifdef IS_APP
4142 apps::
4143 else
4144 apps:: $(ALL_APPS_DIRS)
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
41454180 ifeq ($(IS_APP)$(IS_DEP),)
41464181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
41474182 endif
41494184 # Create ebin directory for all apps to make sure Erlang recognizes them
41504185 # as proper OTP applications when using -include_lib. This is a temporary
41514186 # fix, a proper fix would be to compile apps/* in the right order.
4152 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4153 mkdir -p $$dep/ebin || exit $$?; \
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
41544190 done
4155 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
41564196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
41574197 :; \
41584198 else \
41594199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4160 $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
4200 $(MAKE) -C $$dep IS_APP=1; \
41614201 fi \
41624202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
41634207 endif
41644208
41654209 ifneq ($(SKIP_DEPS),)
41664210 deps::
41674211 else
4168 deps:: $(ALL_DEPS_DIRS) apps
4169 ifeq ($(IS_APP)$(IS_DEP),)
4170 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4171 endif
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
41724213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4173 $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
41744215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
41754216 :; \
41764217 else \
41774218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
41784219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4179 $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
4220 $(MAKE) -C $$dep IS_DEP=1; \
41804221 else \
4181 echo "Error: No Makefile to build dependency $$dep."; \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
41824223 exit 2; \
41834224 fi \
41844225 fi \
41924233 # in practice only Makefile is needed so far.
41934234 define dep_autopatch
41944235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
41954237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
41964238 $(call dep_autopatch_erlang_mk,$(1)); \
41974239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4198 if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
41994243 $(call dep_autopatch2,$(1)); \
4200 elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
42014245 $(call dep_autopatch2,$(1)); \
4202 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
42034247 $(call dep_autopatch2,$(1)); \
4204 else \
4205 $(call erlang,$(call dep_autopatch_app.erl,$(1))); \
42064248 fi \
42074249 else \
42084250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
42144256 endef
42154257
42164258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
42174262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
42184263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
42194264 fi; \
42204265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4221 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
42224267 $(call dep_autopatch_fetch_rebar); \
42234268 $(call dep_autopatch_rebar,$(1)); \
42244269 else \
42304275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
42314276 endef
42324277
4233 # Overwrite erlang.mk with the current file by default.
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
42344280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
42354281 define dep_autopatch_erlang_mk
4236 echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \
4237 > $(DEPS_DIR)/$1/erlang.mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
42384287 endef
42394288 else
42404289 define dep_autopatch_erlang_mk
42534302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
42544303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
42554304 cd $(ERLANG_MK_TMP)/rebar; \
4256 git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
42574306 $(MAKE); \
42584307 cd -; \
42594308 fi
42704319 define dep_autopatch_rebar.erl
42714320 application:load(rebar),
42724321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
42734323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
42744324 {ok, Conf0} -> Conf0;
42754325 _ -> []
44234473 [] -> ok;
44244474 _ ->
44254475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4426 PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
44274477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4428 PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L \\"~s\\" -lerl_interface -lei\n",
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
44294479 [code:lib_dir(erl_interface, lib)])),
44304480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
44314481 FilterEnv = fun(Env) ->
44644514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44654515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44664516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4467 [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
44684518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
44694519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
44704520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
44764526 end,
44774527 [PortSpec(S) || S <- PortSpecs]
44784528 end,
4479 Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"),
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
44804530 RunPlugin = fun(Plugin, Step) ->
44814531 case erlang:function_exported(Plugin, Step, 2) of
44824532 false -> ok;
45244574 halt()
45254575 endef
45264576
4527 define dep_autopatch_app.erl
4528 UpdateModules = fun(App) ->
4529 case filelib:is_regular(App) of
4530 false -> ok;
4531 true ->
4532 {ok, [{application, '$(1)', L0}]} = file:consult(App),
4533 Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true,
4534 fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
4535 L = lists:keystore(modules, 1, L0, {modules, Mods}),
4536 ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}]))
4537 end
4538 end,
4539 UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"),
4540 halt()
4541 endef
4542
45434577 define dep_autopatch_appsrc_script.erl
45444578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
45454579 AppSrcScript = AppSrc ++ ".script",
45874621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
45884622 endef
45894623
4590 define dep_fetch_hex.erl
4591 ssl:start(),
4592 inets:start(),
4593 {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
4594 {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
4595 [], [{body_format, binary}]),
4596 {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
4597 {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
4598 ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
4599 halt()
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
46004626 endef
46014627
46024628 # Hex only has a package version. No need to look in the Erlang.mk packages.
46034629 define dep_fetch_hex
4604 $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
46054634 endef
46064635
46074636 define dep_fetch_fail
46314660 $(eval DEP_NAME := $(call dep_name,$1))
46324661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
46334662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4634 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
46354664 exit 17; \
46364665 fi
46374666 $(verbose) mkdir -p $(DEPS_DIR)
46734702 clean:: clean-apps
46744703
46754704 clean-apps:
4676 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4677 $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
46784707 done
46794708
46804709 distclean:: distclean-apps
46814710
46824711 distclean-apps:
4683 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4684 $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
46854714 done
46864715 endif
46874716
47014730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
47024731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
47034732
4704 # External plugins.
4705
4706 DEP_PLUGINS ?=
4707
4708 define core_dep_plugin
4709 -include $(DEPS_DIR)/$(1)
4710
4711 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4712 endef
4713
4714 $(foreach p,$(DEP_PLUGINS),\
4715 $(eval $(if $(findstring /,$p),\
4716 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4717 $(call core_dep_plugin,$p/plugins.mk,$p))))
4718
4719 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4720 # This file is part of erlang.mk and subject to the terms of the ISC License.
4721
4722 # Configuration.
4723
4724 DTL_FULL_PATH ?=
4725 DTL_PATH ?= templates/
4726 DTL_SUFFIX ?= _dtl
4727 DTL_OPTS ?=
4728
4729 # Verbosity.
4730
4731 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
4732 dtl_verbose = $(dtl_verbose_$(V))
4733
4734 # Core targets.
4735
4736 DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl))
4737
4738 ifneq ($(DTL_FILES),)
4739
4740 ifdef DTL_FULL_PATH
4741 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
4742 else
4743 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
4744 endif
4745
4746 ifneq ($(words $(DTL_FILES)),0)
4747 # Rebuild templates when the Makefile changes.
4748 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
4749 @mkdir -p $(ERLANG_MK_TMP)
4750 @if test -f $@; then \
4751 touch $(DTL_FILES); \
4752 fi
4753 @touch $@
4754
4755 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
4756 endif
4757
4758 define erlydtl_compile.erl
4759 [begin
4760 Module0 = case "$(strip $(DTL_FULL_PATH))" of
4761 "" ->
4762 filename:basename(F, ".dtl");
4763 _ ->
4764 "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
4765 re:replace(F2, "/", "_", [{return, list}, global])
4766 end,
4767 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
4768 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
4769 ok -> ok;
4770 {ok, _} -> ok
4771 end
4772 end || F <- string:tokens("$(1)", " ")],
4773 halt().
4774 endef
4775
4776 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
4777 $(if $(strip $?),\
4778 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
4779
4780 endif
4781
4782 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
47834734 # This file is part of erlang.mk and subject to the terms of the ISC License.
47844735
47854736 # Verbosity.
47984749
47994750 define compile_proto.erl
48004751 [begin
4801 Dir = filename:dirname(filename:dirname(F)),
48024752 protobuffs_compile:generate_source(F,
4803 [{output_include_dir, Dir ++ "/include"},
4804 {output_src_dir, Dir ++ "/ebin"}])
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
48054755 end || F <- string:tokens("$(1)", " ")],
48064756 halt().
48074757 endef
48114761 $(if $(strip $?),$(call compile_proto,$?))
48124762 endif
48134763
4814 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
48154765 # This file is part of erlang.mk and subject to the terms of the ISC License.
48164766
48174767 .PHONY: clean-app
48254775 ERLC_EXCLUDE ?=
48264776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
48274777
4778 ERLC_ASN1_OPTS ?=
4779
48284780 ERLC_MIB_OPTS ?=
48294781 COMPILE_MIB_FIRST ?=
48304782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
48744826
48754827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
48764828 define app_file
4877 {application, $(PROJECT), [
4829 {application, '$(PROJECT)', [
48784830 {description, "$(PROJECT_DESCRIPTION)"},
48794831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48804832 {id$(comma)$(space)"$(1)"}$(comma))
48814833 {modules, [$(call comma_list,$(2))]},
48824834 {registered, []},
4883 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48844837 ]}.
48854838 endef
48864839 else
48874840 define app_file
4888 {application, $(PROJECT), [
4841 {application, '$(PROJECT)', [
48894842 {description, "$(PROJECT_DESCRIPTION)"},
48904843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48914844 {id$(comma)$(space)"$(1)"}$(comma))
48924845 {modules, [$(call comma_list,$(2))]},
48934846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
48944847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4895 {mod, {$(PROJECT_MOD), []}}
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48964850 ]}.
48974851 endef
48984852 endif
49024856
49034857 # Source files.
49044858
4905 ERL_FILES = $(sort $(call core_find,src/,*.erl))
4906 CORE_FILES = $(sort $(call core_find,src/,*.core))
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
49074863
49084864 # ASN.1 files.
49094865
49134869
49144870 define compile_asn1
49154871 $(verbose) mkdir -p include/
4916 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
49174873 $(verbose) mv asn1/*.erl src/
49184874 $(verbose) mv asn1/*.hrl include/
49194875 $(verbose) mv asn1/*.asn1db include/
49364892
49374893 # Leex and Yecc files.
49384894
4939 XRL_FILES = $(sort $(call core_find,src/,*.xrl))
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
49404896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
49414897 ERL_FILES += $(XRL_ERL_FILES)
49424898
4943 YRL_FILES = $(sort $(call core_find,src/,*.yrl))
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
49444900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
49454901 ERL_FILES += $(YRL_ERL_FILES)
49464902
49474903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4948 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
49494905
49504906 # Erlang and Core Erlang files.
49514907
49954951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49964952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49974953 (F, Mod, import, {Imp, _}) ->
4998 case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
49994960 false -> ok;
50004961 true -> Add(Mod, Imp)
50014962 end;
50194980 end || F <- ErlFiles],
50204981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
50214982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
50224991 ok = file:write_file("$(1)", [
50234992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
5024 "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n"
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
50254994 ]),
50264995 halt()
50274996 endef
50345003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
50355004 # Rebuild everything when the Makefile changes.
50365005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5037 @mkdir -p $(ERLANG_MK_TMP)
5038 @if test -f $@; then \
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
50395008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
50405009 touch -c $(PROJECT).d; \
50415010 fi
5042 @touch $@
5011 $(verbose) touch $@
50435012
50445013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
50455014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
50465015 endif
50475016
5048 -include $(PROJECT).d
5017 include $(wildcard $(PROJECT).d)
50495018
50505019 ebin/$(PROJECT).app:: ebin/
50515020
50645033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
50655034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
50665035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5067 $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
50685037 > ebin/$(PROJECT).app
50695038 else
50705039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
50885057
50895058 endif
50905059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
50915061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
50925062 # This file is part of erlang.mk and subject to the terms of the ISC License.
50935063
51055075 doc-deps:
51065076 else
51075077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5108 $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5109 endif
5110
5111 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51125082 # This file is part of erlang.mk and subject to the terms of the ISC License.
51135083
51145084 .PHONY: rel-deps
51255095 rel-deps:
51265096 else
51275097 rel-deps: $(ALL_REL_DEPS_DIRS)
5128 $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5129 endif
5130
5131 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51325102 # This file is part of erlang.mk and subject to the terms of the ISC License.
51335103
51345104 .PHONY: test-deps test-dir test-build clean-test-dir
51505120 test-deps:
51515121 else
51525122 test-deps: $(ALL_TEST_DEPS_DIRS)
5153 $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
51545124 endif
51555125
51565126 ifneq ($(wildcard $(TEST_DIR)),)
51835153 endif
51845154 endif
51855155
5186 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51875157 # This file is part of erlang.mk and subject to the terms of the ISC License.
51885158
51895159 .PHONY: rebar.config
52195189 rebar.config:
52205190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
52215191
5222 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
52235193 # This file is part of erlang.mk and subject to the terms of the ISC License.
52245194
5225 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
5226
5227 MAN_INSTALL_PATH ?= /usr/local/share/man
5228 MAN_SECTIONS ?= 3 7
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
52295200
52305201 docs:: asciidoc
52315202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
52325207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
52335210
52345211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
52355212 asciidoc-guide:
52365213 else
5237 asciidoc-guide: distclean-asciidoc doc-deps
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
52385215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
52395216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5240 endif
5241
5242 ifeq ($(wildcard doc/src/manual/*.asciidoc),)
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
52435227 asciidoc-manual:
52445228 else
5245 asciidoc-manual: distclean-asciidoc doc-deps
5246 for f in doc/src/manual/*.asciidoc ; do \
5247 a2x -v -f manpage $$f ; \
5248 done
5249 for s in $(MAN_SECTIONS); do \
5250 mkdir -p doc/man$$s/ ; \
5251 mv doc/src/manual/*.$$s doc/man$$s/ ; \
5252 gzip doc/man$$s/*.$$s ; \
5253 done
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
52545262
52555263 install-docs:: install-asciidoc
52565264
52575265 install-asciidoc: asciidoc-manual
5258 for s in $(MAN_SECTIONS); do \
5259 mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
5260 install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
5261 done
5262 endif
5263
5264 distclean:: distclean-asciidoc
5265
5266 distclean-asciidoc:
5267 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
5268
5269 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
52705276 # This file is part of erlang.mk and subject to the terms of the ISC License.
52715277
52725278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
53235329 define bs_Makefile
53245330 PROJECT = $p
53255331 PROJECT_DESCRIPTION = New project
5326 PROJECT_VERSION = 0.0.1
5332 PROJECT_VERSION = 0.1.0
53275333
53285334 # Whitespace to be used when creating files from templates.
53295335 SP = $(SP)
53335339 define bs_Makefile
53345340 PROJECT = $p
53355341 PROJECT_DESCRIPTION = New project
5336 PROJECT_VERSION = 0.0.1
5342 PROJECT_VERSION = 0.1.0
53375343
53385344 endef
53395345 endif
53415347 define bs_apps_Makefile
53425348 PROJECT = $p
53435349 PROJECT_DESCRIPTION = New project
5344 PROJECT_VERSION = 0.0.1
5350 PROJECT_VERSION = 0.1.0
53455351
53465352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
53475353 endef
53615367 endef
53625368
53635369 define bs_relx_config
5364 {release, {$p_release, "1"}, [$p]}.
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
53655371 {extended_start_script, true}.
53665372 {sys_config, "rel/sys.config"}.
53675373 {vm_args, "rel/vm.args"}.
57125718 ifndef t
57135719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57145720 endif
5715 ifndef tpl_$(t)
5716 $(error Unknown template)
5717 endif
57185721 ifndef n
57195722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57205723 endif
57275730 list-templates:
57285731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
57295732
5730 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
57315734 # This file is part of erlang.mk and subject to the terms of the ISC License.
57325735
57335736 .PHONY: clean-c_src distclean-c_src-env
59625965 $(call render_template,bs_erl_nif,src/$n.erl)
59635966 endif
59645967
5965 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
59665969 # This file is part of erlang.mk and subject to the terms of the ISC License.
59675970
5968 .PHONY: ci ci-setup distclean-kerl
5969
5970 KERL ?= $(CURDIR)/kerl
5971 export KERL
5972
5973 KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
5974
5975 OTP_GIT ?= https://github.com/erlang/otp
5976
5977 CI_INSTALL_DIR ?= $(HOME)/erlang
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
59785973 CI_OTP ?=
5979
5980 ifeq ($(strip $(CI_OTP)),)
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
59815986 ci::
59825987 else
5983 ci:: $(addprefix ci-,$(CI_OTP))
5984
5985 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
59866007
59876008 ci-setup::
6009
6010 ci-extra::
59886011
59896012 ci_verbose_0 = @echo " CI " $(1);
59906013 ci_verbose = $(ci_verbose_$(V))
59916014
59926015 define ci_target
5993 ci-$(1): $(CI_INSTALL_DIR)/$(1)
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
59946018 $(ci_verbose) \
5995 PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
5996 CI_OTP_RELEASE="$(1)" \
5997 CT_OPTS="-label $(1)" \
5998 $(MAKE) clean ci-setup tests
5999 endef
6000
6001 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
60026030
60036031 define ci_otp_target
60046032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
60056033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6006 $(KERL) build git $(OTP_GIT) $(1) $(1)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
60076035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
60086036 endif
60096037 endef
60106038
60116039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
60126040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
60136052 $(KERL):
6014 $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
60156056 $(verbose) chmod +x $(KERL)
60166057
60176058 help::
60286069 $(gen_verbose) rm -rf $(KERL)
60296070 endif
60306071
6031 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
60326073 # This file is part of erlang.mk and subject to the terms of the ISC License.
60336074
60346075 .PHONY: ct apps-ct distclean-ct
60366077 # Configuration.
60376078
60386079 CT_OPTS ?=
6080
60396081 ifneq ($(wildcard $(TEST_DIR)),)
6040 CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6041 else
6042 CT_SUITES ?=
6043 endif
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
60446088
60456089 # Core targets.
60466090
60636107 -noinput \
60646108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
60656109 -dir $(TEST_DIR) \
6066 -logdir $(CURDIR)/logs
6110 -logdir $(CT_LOGS_DIR)
60676111
60686112 ifeq ($(CT_SUITES),)
60696113 ct: $(if $(IS_APP),,apps-ct)
60706114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
60716117 ct: test-build $(if $(IS_APP),,apps-ct)
6072 $(verbose) mkdir -p $(CURDIR)/logs/
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
60736119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
60746121 endif
60756122
60766123 ifneq ($(ALL_APPS_DIRS),)
60976144
60986145 define ct_suite_target
60996146 ct-$(1): test-build
6100 $(verbose) mkdir -p $(CURDIR)/logs/
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
61016148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
61026149 endef
61036150
61046151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
61056152
61066153 distclean-ct:
6107 $(gen_verbose) rm -rf $(CURDIR)/logs/
6108
6109 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61106157 # This file is part of erlang.mk and subject to the terms of the ISC License.
61116158
61126159 .PHONY: plt distclean-plt dialyze
61506197 endef
61516198
61526199 $(DIALYZER_PLT): deps app
6153 $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
61546204
61556205 plt: $(DIALYZER_PLT)
61566206
61646214 endif
61656215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
61666216
6167 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61686218 # This file is part of erlang.mk and subject to the terms of the ISC License.
61696219
61706220 .PHONY: distclean-edoc edoc
61726222 # Configuration.
61736223
61746224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
61756235
61766236 # Core targets.
61776237
6178 ifneq ($(wildcard doc/overview.edoc),)
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
61796239 docs:: edoc
61806240 endif
61816241
61846244 # Plugin-specific targets.
61856245
61866246 edoc: distclean-edoc doc-deps
6187 $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
61886248
61896249 distclean-edoc:
61906250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
61916251
6192 # Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61936253 # This file is part of erlang.mk and subject to the terms of the ISC License.
61946254
6195 .PHONY: distclean-escript escript
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
61966320
61976321 # Configuration.
61986322
61996323 ESCRIPT_NAME ?= $(PROJECT)
62006324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
62016325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
62026327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6203
6204 ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
6205 ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
6206 ESCRIPT_EMU_ARGS ?= -pa . \
6207 -sasl errlog_type error \
6208 -escript main $(ESCRIPT_NAME)
6209 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6210 ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
62116332
62126333 # Core targets.
62136334
62206341
62216342 # Plugin-specific targets.
62226343
6223 # Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
6224 # Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
6225 # Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
6226 # Software may only be used for the great good and the true happiness of all
6227 # sentient beings.
6228
6229 define ESCRIPT_RAW
6230 'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
6231 'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
6232 ' [F || F <- A, not filelib:is_dir(F) ] end,'\
6233 'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
6234 'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
6235 'Ez = fun(Escript) ->'\
6236 ' Static = Files([$(ESCRIPT_STATIC)]),'\
6237 ' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
6238 ' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
6239 ' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
6240 ' {archive, Archive, [memory]},'\
6241 ' {shebang, "$(ESCRIPT_SHEBANG)"},'\
6242 ' {comment, "$(ESCRIPT_COMMENT)"},'\
6243 ' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
6244 ' ]),'\
6245 ' file:change_mode(Escript, 8#755)'\
6246 'end,'\
6247 'Ez("$(ESCRIPT_FILE)"),'\
6248 'halt().'
6249 endef
6250
6251 ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
6252
6253 escript:: distclean-escript deps app
6254 $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
62556360
62566361 distclean-escript:
6257 $(gen_verbose) rm -f $(ESCRIPT_NAME)
6258
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
62596365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6260 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
62616366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
62626367
62636368 .PHONY: eunit apps-eunit
63216426
63226427 ifneq ($(ALL_APPS_DIRS),)
63236428 apps-eunit:
6324 $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done
6325 endif
6326 endif
6327
6328 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
63296436 # This file is part of erlang.mk and subject to the terms of the ISC License.
63306437
6331 .PHONY: relx-rel distclean-relx-rel distclean-relx run
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
63326439
63336440 # Configuration.
63346441
6335 RELX ?= $(CURDIR)/relx
6442 RELX ?= $(ERLANG_MK_TMP)/relx
63366443 RELX_CONFIG ?= $(CURDIR)/relx.config
63376444
63386445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
63396446 RELX_OPTS ?=
63406447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
63416454
63426455 ifeq ($(firstword $(RELX_OPTS)),-o)
63436456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
63506463 ifeq ($(IS_DEP),)
63516464 ifneq ($(wildcard $(RELX_CONFIG)),)
63526465 rel:: relx-rel
6353 endif
6354 endif
6355
6356 distclean:: distclean-relx-rel distclean-relx
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
63576472
63586473 # Plugin-specific targets.
63596474
63626477 $(verbose) chmod +x $(RELX)
63636478
63646479 relx-rel: $(RELX) rel-deps app
6365 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
63666484
63676485 distclean-relx-rel:
63686486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6369
6370 distclean-relx:
6371 $(gen_verbose) rm -rf $(RELX)
63726487
63736488 # Run target.
63746489
63776492 else
63786493
63796494 define get_relx_release.erl
6380 {ok, Config} = file:consult("$(RELX_CONFIG)"),
6381 {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
6382 io:format("~s", [Name]),
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
63836504 halt(0).
63846505 endef
63856506
6386 RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
63876514
63886515 run: all
6389 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
63906517
63916518 help::
63926519 $(verbose) printf "%s\n" "" \
63956522
63966523 endif
63976524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
63986526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6399 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
64006527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
64016528
64026529 .PHONY: shell
64216548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
64226549
64236550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6424 $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
64256552
64266553 shell: build-shell-deps
64276554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
64286555
6429 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
64306571 # This file is part of erlang.mk and subject to the terms of the ISC License.
64316572
64326573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
64376578 tests:: triq
64386579
64396580 define triq_check.erl
6440 code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
64416582 try
64426583 case $(1) of
64436584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
64696610 endif
64706611 endif
64716612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
64726614 # Copyright (c) 2015, Erlang Solutions Ltd.
64736615 # This file is part of erlang.mk and subject to the terms of the ISC License.
64746616
64856627 XREFR ?= $(CURDIR)/xrefr
64866628 export XREFR
64876629
6488 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
64896631
64906632 # Core targets.
64916633
64926634 help::
6493 $(verbose) printf "%s\n" "" \
6494 "Xref targets:" \
6495 " xref Run Xrefr using $XREF_CONFIG as config file if defined"
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
64966638
64976639 distclean:: distclean-xref
64986640
65086650 distclean-xref:
65096651 $(gen_verbose) rm -rf $(XREFR)
65106652
6511 # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
65126655 # This file is part of erlang.mk and subject to the terms of the ISC License.
65136656
65146657 COVER_REPORT_DIR = cover
65176660
65186661 ifdef COVER
65196662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
65206664 # All modules in 'ebin'
65216665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
65226666
65316675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
65326676 endif
65336677 endif
6678 endif
65346679
65356680 # Core targets
65366681
65526697 "Cover targets:" \
65536698 " cover-report Generate a HTML coverage report from previously collected" \
65546699 " cover data." \
6555 " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
65566701 "" \
65576702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
65586703 "target tests additionally generates a HTML coverage report from the combined" \
65656710
65666711 .PHONY: coverdata-clean
65676712 coverdata-clean:
6568 $(gen_verbose) rm -f *.coverdata ct.cover.spec
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
65696714
65706715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
65716721 all.coverdata: $(COVERDATA)
6572 $(gen_verbose) $(ERL) -eval ' \
6573 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
6574 cover:export("$@"), halt(0).'
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
65756723
65766724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
65776725 # empty if you want the coverdata files but not the HTML report.
65896737 # Modules which include eunit.hrl always contain one line without coverage
65906738 # because eunit defines test/0 which is never called. We compensate for this.
65916739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6592 grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
65936741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
65946742
65956743 define cover_report.erl
66246772 endef
66256773
66266774 cover-report:
6627 $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
66286776 $(gen_verbose) $(call erlang,$(cover_report.erl))
66296777
66306778 endif
66316779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
66326839
66336840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
66346841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
66976904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
66986905 endif
66996906 ifndef IS_APP
6700 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
67016908 $(MAKE) -C $$dep $@ \
67026909 IS_APP=1 \
6703 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6704 || exit $$?; \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67056911 done
67066912 endif
6707 $(verbose) for dep in $^ ; do \
6913 $(verbose) set -e; for dep in $^ ; do \
67086914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
67096915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6710 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
67116917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
67126918 $(MAKE) -C $$dep fetch-deps \
67136919 IS_DEP=1 \
6714 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6715 || exit $$?; \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67166921 fi \
67176922 fi \
67186923 done
22 # before the inclusion of erlang.mk leading to the wrong target becoming
33 # the default.
44 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
526 endif
627
728 # --------------------------------------------------------------------
2041 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
2142 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
2243 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
2345 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
2446 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
2547 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
2648 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
2749 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
2851 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
2952 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
3054 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
3155 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
3256 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
3559 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
3660 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
3761 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
3863 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
3964 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
4065 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
7095
7196 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
7297
73 # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
74 # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
75 # needs to add "ranch" as a BUILD_DEPS. The list of projects needing
76 # this workaround are:
77 # o rabbitmq-web-stomp
78 dep_ranch = git https://github.com/ninenines/ranch 1.2.1
98 # Third-party dependencies version pinning.
99 #
100 # We do that in this file, which is copied in all projects, to ensure
101 # all projects use the same versions. It avoids conflicts and makes it
102 # possible to work with rabbitmq-public-umbrella.
103
104 dep_cowboy_commit = 1.0.4
105 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
106 dep_ranch_commit = 1.3.1
107 dep_webmachine_commit = 1.10.8p2
79108
80109 RABBITMQ_COMPONENTS = amqp_client \
81110 rabbit \
82111 rabbit_common \
83112 rabbitmq_amqp1_0 \
84113 rabbitmq_auth_backend_amqp \
114 rabbitmq_auth_backend_cache \
85115 rabbitmq_auth_backend_http \
86116 rabbitmq_auth_backend_ldap \
87117 rabbitmq_auth_mechanism_ssl \
88118 rabbitmq_boot_steps_visualiser \
89119 rabbitmq_clusterer \
120 rabbitmq_cli \
90121 rabbitmq_codegen \
91122 rabbitmq_consistent_hash_exchange \
123 rabbitmq_ct_client_helpers \
92124 rabbitmq_ct_helpers \
93125 rabbitmq_delayed_message_exchange \
94126 rabbitmq_dotnet_client \
97129 rabbitmq_federation_management \
98130 rabbitmq_java_client \
99131 rabbitmq_jms_client \
132 rabbitmq_jms_cts \
100133 rabbitmq_jms_topic_exchange \
101134 rabbitmq_lvc \
102135 rabbitmq_management \
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2010-2015 GoPivotal, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_cowboy_middleware).
17 -behavior(cowboy_middleware).
18
19 -export([execute/2]).
20 -export([onresponse/4]).
21
22 execute(Req, Env) ->
23 %% Pre-parse the query string.
24 {_, Req1} = cowboy_req:qs_vals(Req),
25
26 %% Find the correct dispatch list for this path.
27 {_, Listener} = lists:keyfind(rabbit_listener, 1, Env),
28 case rabbit_web_dispatch_registry:lookup(Listener, Req1) of
29 {ok, Dispatch} ->
30 {ok, Req1, [{dispatch, Dispatch}|Env]};
31 {error, Reason} ->
32 {ok, Req2} = cowboy_req:reply(500,
33 [{<<"content-type">>, <<"text/plain">>}],
34 "Registry Error: " ++ io_lib:format("~p", [Reason]), Req1),
35 {halt, Req2}
36 end.
37
38 onresponse(Status = 404, Headers0, Body = <<>>, Req0) ->
39 log_access(Status, Body, Req0),
40 Json0 = {struct,
41 [{error, list_to_binary(httpd_util:reason_phrase(Status))},
42 {reason, <<"Not Found">>}]},
43 Json = mochijson2:encode(Json0),
44 Headers1 = lists:keystore(<<"content-type">>, 1, Headers0,
45 {<<"content-type">>, <<"application/json">>}),
46 Headers = lists:keystore(<<"content-length">>, 1, Headers1,
47 {<<"content-length">>, integer_to_list(iolist_size(Json))}),
48 {ok, Req} = cowboy_req:reply(Status, Headers, Json , Req0),
49 Req;
50
51 onresponse(Status, _, Body, Req) ->
52 log_access(Status, Body, Req),
53 Req.
54
55 log_access(Status, Body, Req) ->
56 webmachine_log:log_access({Status, Body, Req}).
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2010-2015 GoPivotal, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_cowboy_redirect).
17
18 -export([init/3]).
19 -export([handle/2]).
20 -export([terminate/3]).
21
22 init(_, Req, RedirectPort) ->
23 {ok, Req, RedirectPort}.
24
25 handle(Req0, RedirectPort) ->
26 %% Use a small trick to get a URL with the updated port.
27 RedReq = cowboy_req:set([{port, RedirectPort}], Req0),
28 {URL, _} = cowboy_req:url(RedReq),
29 {ok, Req} = cowboy_req:reply(301, [{<<"location">>, URL}], Req0),
30 {ok, Req, RedirectPort}.
31
32 terminate(_, _, _) ->
33 ok.
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_web_dispatch).
4949 register_port_redirect(Name, Listener, Prefix, RedirectPort) ->
5050 register_context_handler(
5151 Name, Listener, Prefix,
52 fun (Req) ->
53 Host = case Req:get_header_value("host") of
54 undefined -> {ok, {IP, _Port}} = rabbit_net:sockname(
55 Req:get(socket)),
56 rabbit_misc:ntoa(IP);
57 Header -> hd(string:tokens(Header, ":"))
58 end,
59 URL = rabbit_misc:format(
60 "~s://~s:~B~s",
61 [Req:get(scheme), Host, RedirectPort, Req:get(raw_path)]),
62 Req:respond({301, [{"Location", URL}], ""})
63 end,
52 cowboy_router:compile([{'_', [{'_', rabbit_cowboy_redirect, RedirectPort}]}]),
6453 rabbit_misc:format("Redirect to port ~B", [RedirectPort])).
6554
6655 context_selector("") ->
6756 fun(_Req) -> true end;
6857 context_selector(Prefix) ->
69 Prefix1 = "/" ++ Prefix,
58 Prefix1 = list_to_binary("/" ++ Prefix),
7059 fun(Req) ->
71 Path = Req:get(raw_path),
72 (Path == Prefix1) orelse (string:str(Path, Prefix1 ++ "/") == 1)
60 {Path, _} = cowboy_req:path(Req),
61 (Path == Prefix1) orelse (binary:match(Path, << Prefix1/binary, $/ >>) =/= nomatch)
7362 end.
7463
7564 %% Produces a handler for use with register_handler that serves up
8069 {file, Here} = code:is_loaded(Module),
8170 ModuleRoot = filename:dirname(filename:dirname(Here)),
8271 LocalPath = filename:join(ModuleRoot, FSPath),
83 static_context_handler(Prefix, LocalPath).
84
85 %% Produces a handler for use with register_handler that serves up
86 %% static content from a specified directory.
87 static_context_handler("", LocalPath) ->
88 fun(Req) ->
89 "/" ++ Path = Req:get(path),
90 serve_file(Req, Path, LocalPath)
91 end;
92 static_context_handler(Prefix, LocalPath) ->
93 fun(Req) ->
94 "/" ++ Path = Req:get(path),
95 case string:substr(Path, length(Prefix) + 1) of
96 "" -> Req:respond({301, [{"Location", "/" ++ Prefix ++ "/"}], ""});
97 "/" ++ P -> serve_file(Req, P, LocalPath)
98 end
99 end.
100
101 serve_file(Req, Path, LocalPath) ->
102 case Req:get(method) of
103 Method when Method =:= 'GET'; Method =:= 'HEAD' ->
104 Req:serve_file(Path, LocalPath);
105 _ ->
106 Req:respond({405, [{"Allow", "GET, HEAD"}],
107 "Only GET or HEAD supported for static content"})
108 end.
72 cowboy_router:compile([{'_', [
73 {"/" ++ Prefix, cowboy_static, {file, LocalPath ++ "/index.html"}},
74 {"/" ++ Prefix ++ "/[...]", cowboy_static, {dir, LocalPath}}
75 ]}]).
10976
11077 %% The opposite of all those register_* functions.
11178 unregister_context(Name) ->
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_web_dispatch_app).
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_web_dispatch_listing_handler).
17
18 -export([init/3]).
19 -export([handle/2]).
20 -export([terminate/3]).
21
22 init(_, Req, Listener) ->
23 {ok, Req, Listener}.
24
25 handle(Req0, Listener) ->
26 HTMLPrefix =
27 "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">"
28 "<head><title>RabbitMQ Web Server</title></head>"
29 "<body><h1>RabbitMQ Web Server</h1><p>Contexts available:</p><ul>",
30 HTMLSuffix = "</ul></body></html>",
31 List =
32 case rabbit_web_dispatch_registry:list(Listener) of
33 [] ->
34 "<li>No contexts installed</li>";
35 Contexts ->
36 [["<li><a href=\"/", Path, "/\">", Desc, "</a></li>"]
37 || {Path, Desc} <- Contexts]
38 end,
39 {ok, Req} = cowboy_req:reply(200, [], [HTMLPrefix, List, HTMLSuffix], Req0),
40 {ok, Req, Listener}.
41
42 terminate(_, _, _) ->
43 ok.
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_web_dispatch_registry).
2121 -export([add/5, remove/1, set_fallback/2, lookup/2, list_all/0]).
2222 -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
2323 code_change/3]).
24 -export([list/1]).
25
26 -import(rabbit_misc, [pget/2]).
2427
2528 -define(ETS, rabbitmq_web_dispatch).
2629
3740 remove(Name) ->
3841 gen_server:call(?MODULE, {remove, Name}, infinity).
3942
43 %% @todo This needs to be dispatch instead of a fun too.
44 %% But I'm not sure what code is using this.
4045 set_fallback(Listener, FallbackHandler) ->
4146 gen_server:call(?MODULE, {set_fallback, Listener, FallbackHandler},
4247 infinity).
4550 case lookup_dispatch(Listener) of
4651 {ok, {Selectors, Fallback}} ->
4752 case catch match_request(Selectors, Req) of
48 {'EXIT', Reason} -> {lookup_failure, Reason};
49 no_handler -> {handler, Fallback};
50 Handler -> {handler, Handler}
53 {'EXIT', Reason} -> {error, {lookup_failure, Reason}};
54 not_found -> {ok, Fallback};
55 Dispatch -> {ok, Dispatch}
5156 end;
5257 Err ->
5358 Err
7075 new -> set_dispatch(
7176 Listener, [],
7277 listing_fallback_handler(Listener)),
78 listener_started(Listener),
7379 true;
7480 existing -> true;
7581 ignore -> false
96102 Selectors1 = lists:keydelete(Name, 1, Selectors),
97103 set_dispatch(Listener, Selectors1, Fallback),
98104 case Selectors1 of
99 [] -> rabbit_web_dispatch_sup:stop_listener(Listener);
105 [] -> rabbit_web_dispatch_sup:stop_listener(Listener),
106 listener_stopped(Listener);
100107 _ -> ok
101108 end,
102109 {reply, ok, undefined};
115122 {stop, unknown_request, State}.
116123
117124 handle_cast(_, State) ->
118 {noreply, State}.
125 {noreply, State}.
119126
120127 handle_info(_, State) ->
121 {noreply, State}.
128 {noreply, State}.
122129
123130 terminate(_, _) ->
124131 true = ets:delete(?ETS),
125132 ok.
126133
127134 code_change(_, State, _) ->
128 {ok, State}.
135 {ok, State}.
129136
130137 %%---------------------------------------------------------------------------
131138
132139 %% Internal Methods
133140
134 port(Listener) -> proplists:get_value(port, Listener).
141 listener_started(Listener) ->
142 [rabbit_networking:tcp_listener_started(Protocol, Listener, IPAddress, Port)
143 || {Protocol, IPAddress, Port} <- listener_info(Listener)],
144 ok.
145
146 listener_stopped(Listener) ->
147 [rabbit_networking:tcp_listener_stopped(Protocol, Listener, IPAddress, Port)
148 || {Protocol, IPAddress, Port} <- listener_info(Listener)],
149 ok.
150
151 listener_info(Listener) ->
152 Protocol = case pget(ssl, Listener) of
153 true -> https;
154 _ -> http
155 end,
156 Port = pget(port, Listener),
157 [{Protocol, IPAddress, Port}
158 || {IPAddress, _Port, _Family}
159 <- rabbit_networking:tcp_listener_addresses(Port)].
135160
136161 lookup_dispatch(Lsnr) ->
137 case ets:lookup(?ETS, port(Lsnr)) of
162 case ets:lookup(?ETS, pget(port, Lsnr)) of
138163 [{_, Lsnr, S, F}] -> {ok, {S, F}};
139164 [{_, Lsnr2, S, _F}] -> {error, {different, first_desc(S), Lsnr2}};
140165 [] -> {error, {no_record_for_listener, Lsnr}}
143168 first_desc([{_N, _S, _H, {_, Desc}} | _]) -> Desc.
144169
145170 set_dispatch(Listener, Selectors, Fallback) ->
146 ets:insert(?ETS, {port(Listener), Listener, Selectors, Fallback}).
171 ets:insert(?ETS, {pget(port, Listener), Listener, Selectors, Fallback}).
147172
148173 match_request([], _) ->
149 no_handler;
150 match_request([{_Name, Selector, Handler, _Link}|Rest], Req) ->
174 not_found;
175 match_request([{_Name, Selector, Dispatch, _Link}|Rest], Req) ->
151176 case Selector(Req) of
152 true -> Handler;
177 true -> Dispatch;
153178 false -> match_request(Rest, Req)
154179 end.
155180
174199 %%---------------------------------------------------------------------------
175200
176201 listing_fallback_handler(Listener) ->
177 fun(Req) ->
178 HTMLPrefix =
179 "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">"
180 "<head><title>RabbitMQ Web Server</title></head>"
181 "<body><h1>RabbitMQ Web Server</h1><p>Contexts available:</p><ul>",
182 HTMLSuffix = "</ul></body></html>",
183 {ReqPath, _, _} = mochiweb_util:urlsplit_path(Req:get(raw_path)),
184 List =
185 case list(Listener) of
186 [] ->
187 "<li>No contexts installed</li>";
188 Contexts ->
189 [handler_listing(Path, ReqPath, Desc)
190 || {Path, Desc} <- Contexts]
191 end,
192 Req:respond({200, [], HTMLPrefix ++ List ++ HTMLSuffix})
193 end.
194
195 handler_listing(Path, ReqPath, Desc) ->
196 io_lib:format(
197 "<li><a href=\"~s\">~s</a></li>",
198 [rabbit_web_dispatch_util:relativise(ReqPath, "/" ++ Path), Desc]).
202 cowboy_router:compile([{'_', [
203 {"/", rabbit_web_dispatch_listing_handler, Listener}
204 ]}]).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_web_dispatch_sup).
3535 undefined ->
3636 {error, {no_port_given, Listener}};
3737 _ ->
38 Child = {{rabbit_web_dispatch_web, name(Listener)},
39 {mochiweb_http, start, [mochi_options(Listener)]},
40 transient, 5000, worker, dynamic},
38 {Transport, TransportOpts, ProtoOpts} = preprocess_config(Listener),
39 Child = ranch:child_spec(name(Listener), 100,
40 Transport, TransportOpts,
41 cowboy_protocol, [
42 {env, [{rabbit_listener, Listener}]},
43 {middlewares, [rabbit_cowboy_middleware, cowboy_router, cowboy_handler]},
44 {onresponse, fun rabbit_cowboy_middleware:onresponse/4}
45 | ProtoOpts]),
4146 case supervisor:start_child(?SUP, Child) of
4247 {ok, _} -> new;
4348 {error, {already_started, _}} -> existing;
4752
4853 stop_listener(Listener) ->
4954 Name = name(Listener),
50 ok = supervisor:terminate_child(?SUP, {rabbit_web_dispatch_web, Name}),
51 ok = supervisor:delete_child(?SUP, {rabbit_web_dispatch_web, Name}).
55 ok = supervisor:terminate_child(?SUP, {ranch_listener_sup, Name}),
56 ok = supervisor:delete_child(?SUP, {ranch_listener_sup, Name}).
5257
5358 %% @spec init([[instance()]]) -> SupervisorTree
5459 %% @doc supervisor callback.
5661 Registry = {rabbit_web_dispatch_registry,
5762 {rabbit_web_dispatch_registry, start_link, []},
5863 transient, 5000, worker, dynamic},
59 {ok, {{one_for_one, 10, 10}, [Registry]}}.
64 Log = {rabbit_mgmt_access_logger, {gen_event, start_link,
65 [{local, webmachine_log_event}]},
66 permanent, 5000, worker, [dynamic]},
67 {ok, {{one_for_one, 10, 10}, [Registry, Log]}}.
6068
6169 %% ----------------------------------------------------------------------
62
63 mochi_options(Listener) ->
64 [{name, name(Listener)},
65 {loop, loopfun(Listener)} |
66 ssl_config(proplists:delete(
67 name, proplists:delete(ignore_in_use, Listener)))].
68
69 loopfun(Listener) ->
70 fun (Req) ->
71 case rabbit_web_dispatch_registry:lookup(Listener, Req) of
72 no_handler ->
73 Req:not_found();
74 {error, Reason} ->
75 Req:respond({500, [], "Registry Error: " ++ Reason});
76 {handler, Handler} ->
77 Handler(Req)
78 end
79 end.
8070
8171 name(Listener) ->
8272 Port = proplists:get_value(port, Listener),
8373 list_to_atom(atom_to_list(?MODULE) ++ "_" ++ integer_to_list(Port)).
8474
85 ssl_config(Options) ->
75 preprocess_config(Options) ->
8676 case proplists:get_value(ssl, Options) of
87 true -> rabbit_networking:ensure_ssl(),
77 true -> _ = rabbit_networking:ensure_ssl(),
8878 case rabbit_networking:poodle_check('HTTP') of
8979 ok -> case proplists:get_value(ssl_opts, Options) of
9080 undefined -> auto_ssl(Options);
9181 _ -> fix_ssl(Options)
9282 end;
93 danger -> proplists:delete(ssl, Options)
83 danger -> {ranch_tcp, transport_config(Options), protocol_config(Options)}
9484 end;
95 _ -> Options
85 _ -> {ranch_tcp, transport_config(Options), protocol_config(Options)}
9686 end.
9787
9888 auto_ssl(Options) ->
10494
10595 fix_ssl(Options) ->
10696 SSLOpts = proplists:get_value(ssl_opts, Options),
107 rabbit_misc:pset(ssl_opts,
108 rabbit_networking:fix_ssl_options(SSLOpts), Options).
97 {ranch_ssl,
98 transport_config(Options ++ rabbit_networking:fix_ssl_options(SSLOpts)),
99 protocol_config(Options)}.
100
101 transport_config(Options0) ->
102 Options = proplists:delete(ssl,
103 proplists:delete(ssl_opts,
104 proplists:delete(cowboy_opts,
105 Options0))),
106 case proplists:get_value(ip, Options) of
107 undefined ->
108 Options;
109 IP when is_tuple(IP) ->
110 Options;
111 IP when is_list(IP) ->
112 {ok, ParsedIP} = inet_parse:address(IP),
113 [{ip, ParsedIP}|proplists:delete(ip, Options)]
114 end.
115
116 protocol_config(Options) ->
117 ProtoOpts = proplists:get_value(cowboy_opts, Options, []),
118 %% Compress responses by default.
119 case lists:keyfind(compress, 1, ProtoOpts) of
120 false -> [{compress, true}|ProtoOpts];
121 _ -> ProtoOpts
122 end.
109123
110124 check_error(Listener, Error) ->
111125 Ignore = proplists:get_value(ignore_in_use, Listener, false),
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_web_dispatch_util).
1818 -export([parse_auth_header/1]).
1919 -export([relativise/2, unrelativise/2]).
2020
21 %% @todo remove
2122 parse_auth_header(Header) ->
2223 case Header of
2324 "Basic " ++ Base64 ->
+0
-68
deps/rabbitmq_web_dispatch/src/rabbit_webmachine.erl less more
0 %% This file contains an adapted version of webmachine_mochiweb:loop/1
1 %% from webmachine (revision 0c4b60ac68b4).
2
3 %% All modifications are (C) 2011-2013 GoPivotal, Inc.
4
5 -module(rabbit_webmachine).
6
7 %% An alternative to webmachine_mochiweb, which places the dispatch
8 %% table (among other things) into the application env, and thereby
9 %% makes it impossible to run more than one instance of
10 %% webmachine. Since rabbit_web_dispatch is all about multi-tenanting
11 %% webapps, clearly this won't do for us.
12
13 %% Instead of using webmachine_mochiweb:start/1 or
14 %% webmachine_mochiweb:loop/1, construct a loop procedure using
15 %% makeloop/1 and supply it as the argument to
16 %% rabbit_web_dispatch:register_context_handler or to mochiweb_http:start.
17
18 %% We hardwire the "error handler" and use a "logging module" if
19 %% supplied.
20
21 -export([makeloop/1, setup/0]).
22
23 setup() ->
24 application:set_env(
25 webmachine, error_handler, rabbit_webmachine_error_handler).
26
27 makeloop(Dispatch) ->
28 fun (MochiReq) ->
29 Req = webmachine:new_request(mochiweb, MochiReq),
30 {Path, _} = Req:path(),
31 {ReqData, _} = Req:get_reqdata(),
32 %% webmachine_mochiweb:loop/1 uses dispatch/4 here;
33 %% however, we don't need to dispatch by the host name.
34 case webmachine_dispatcher:dispatch(Path, Dispatch, ReqData) of
35 {no_dispatch_match, _Host, _PathElements} ->
36 {ErrorBody, ReqState1} =
37 rabbit_webmachine_error_handler:render_error(
38 404, Req, {none, none, []}),
39 Req1 = {webmachine_request, ReqState1},
40 {ok, ReqState2} = Req1:append_to_response_body(ErrorBody),
41 Req2 = {webmachine_request, ReqState2},
42 {ok, ReqState3} = Req2:send_response(404),
43 maybe_log_access(ReqState3);
44 {Mod, ModOpts, HostTokens, Port, PathTokens, Bindings,
45 AppRoot, StringPath} ->
46 BootstrapResource = webmachine_resource:new(x,x,x,x),
47 {ok, Resource} = BootstrapResource:wrap(Mod, ModOpts),
48 {ok, RS1} = Req:load_dispatch_data(Bindings, HostTokens, Port,
49 PathTokens,
50 AppRoot, StringPath),
51 XReq1 = {webmachine_request, RS1},
52 {ok, RS2} = XReq1:set_metadata('resource_module', Mod),
53 try
54 webmachine_decision_core:handle_request(Resource, RS2)
55 catch
56 error:_ ->
57 FailReq = {webmachine_request, RS2},
58 {ok, RS3} = FailReq:send_response(500),
59 maybe_log_access(RS3)
60 end
61 end
62 end.
63
64 maybe_log_access(ReqState) ->
65 Req = {webmachine_request, ReqState},
66 {LogData, _ReqState1} = Req:log_data(),
67 webmachine_log:log_access(LogData).
+0
-63
deps/rabbitmq_web_dispatch/src/rabbit_webmachine_error_handler.erl less more
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 %% We need to ensure all responses are application/json; anything
17 %% coming back as text/html could constitute an XSS vector. Also I'm
18 %% sure it's easier on our clients if they can always expect JSON
19 %% responses.
20 %%
21 %% Based on webmachine_error_handler, but I'm not sure enough remains
22 %% to be copyrightable.
23
24 -module(rabbit_webmachine_error_handler).
25
26 -export([render_error/3]).
27
28 render_error(Code, Req, Reason) ->
29 case Req:has_response_body() of
30 {true, _} ->
31 maybe_log(Req, Reason),
32 {Body, ReqState0} = Req:response_body(),
33 {ok, ReqState} =
34 webmachine_request:remove_response_header("Content-Encoding",
35 ReqState0),
36 {Body, ReqState};
37 {false, _} -> render_error_body(Code, Req:trim_state(), Reason)
38 end.
39
40 render_error_body(404, Req, _) -> error_body(404, Req, "Not Found");
41 render_error_body(Code, Req, Reason) -> error_body(Code, Req, Reason).
42
43 error_body(Code, Req, Reason) ->
44 {ok, _ReqState0} = Req:add_response_header("Content-Type","application/json"),
45 {ok, ReqState} = Req:remove_response_header("Content-Encoding"),
46 case Code of
47 500 -> maybe_log(Req, Reason);
48 _ -> ok
49 end,
50 Json = {struct,
51 [{error, list_to_binary(httpd_util:reason_phrase(Code))},
52 {reason, list_to_binary(rabbit_misc:format("~p~n", [Reason]))}]},
53 {mochijson2:encode(Json), ReqState}.
54
55 maybe_log(_Req, {error, {exit, normal, _Stack}}) ->
56 %% webmachine_request did an exit(normal), so suppress this
57 %% message. This usually happens when a chunked upload is
58 %% interrupted by network failure.
59 ok;
60 maybe_log(Req, Reason) ->
61 {Path, _} = Req:path(),
62 error_logger:error_msg("webmachine error: path=~p~n~p~n", [Path, Reason]).
+0
-8
deps/rabbitmq_web_dispatch/src/rabbitmq_web_dispatch.app.src less more
0 {application, rabbitmq_web_dispatch,
1 [{description, "RabbitMQ Web Dispatcher"},
2 {vsn, "3.6.6"},
3 {modules, []},
4 {registered, []},
5 {mod, {rabbit_web_dispatch_app, []}},
6 {env, []},
7 {applications, [kernel, stdlib, rabbit_common, rabbit, mochiweb, webmachine]}]}.
0 %% Copyright (c) 2011-2012 Basho Technologies, Inc. All Rights Reserved.
1 %%
2 %% This file is provided to you under the Apache License,
3 %% Version 2.0 (the "License"); you may not use this file
4 %% except in compliance with the License. You may obtain
5 %% a copy of the License at
6 %%
7 %% http://www.apache.org/licenses/LICENSE-2.0
8 %%
9 %% Unless required by applicable law or agreed to in writing,
10 %% software distributed under the License is distributed on an
11 %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
12 %% KIND, either express or implied. See the License for the
13 %% specific language governing permissions and limitations
14 %% under the License.
15
16 %% @doc Helper functions for webmachine's default log handlers
17
18 -module(webmachine_log).
19
20 -include("webmachine_logger.hrl").
21
22 -export([add_handler/2,
23 call/2,
24 call/3,
25 datehour/0,
26 datehour/1,
27 defer_refresh/1,
28 delete_handler/1,
29 fix_log/2,
30 fmt_ip/1,
31 fmtnow/0,
32 log_access/1,
33 log_close/3,
34 log_open/1,
35 log_open/2,
36 log_write/2,
37 maybe_rotate/3,
38 month/1,
39 refresh/2,
40 suffix/1,
41 zeropad/2,
42 zone/0]).
43
44 -record(state, {hourstamp :: non_neg_integer(),
45 filename :: string(),
46 handle :: file:io_device()}).
47
48 %% @doc Add a handler to receive log events
49 -type add_handler_result() :: ok | {'EXIT', term()} | term().
50 -spec add_handler(atom() | {atom(), term()}, term()) -> add_handler_result().
51 add_handler(Mod, Args) ->
52 gen_event:add_handler(?EVENT_LOGGER, Mod, Args).
53
54 %% @doc Make a synchronous call directly to a specific event handler
55 %% module
56 -type error() :: {error, bad_module} | {'EXIT', term()} | term().
57 -spec call(atom(), term()) -> term() | error().
58 call(Mod, Msg) ->
59 gen_event:call(?EVENT_LOGGER, Mod, Msg).
60
61 %% @doc Make a synchronous call directly to a specific event handler
62 %% module
63 -spec call(atom(), term(), timeout()) -> term() | error().
64 call(Mod, Msg, Timeout) ->
65 gen_event:call(?EVENT_LOGGER, Mod, Msg, Timeout).
66
67 %% @doc Return a four-tuple containing year, month, day, and hour
68 %% of the current time.
69 -type datehour() :: {calendar:year(), calendar:month(), calendar:day(), calendar:hour()}.
70 -spec datehour() -> datehour().
71 datehour() ->
72 datehour(os:timestamp()).
73
74 %% @doc Return a four-tuple containing year, month, day, and hour
75 %% of the specified time.
76 -spec datehour(erlang:timestamp()) -> datehour().
77 datehour(TS) ->
78 {{Y, M, D}, {H, _, _}} = calendar:now_to_universal_time(TS),
79 {Y, M, D, H}.
80
81 %% @doc Defer the refresh of a log file.
82 -spec defer_refresh(atom()) -> {ok, timer:tref()} | {error, term()}.
83 defer_refresh(Mod) ->
84 {_, {_, M, S}} = calendar:universal_time(),
85 Time = 1000 * (3600 - ((M * 60) + S)),
86 timer:apply_after(Time, ?MODULE, refresh, [Mod, os:timestamp()]).
87
88 %% @doc Remove a log handler
89 -type delete_handler_result() :: term() | {error, module_not_found} | {'EXIT', term()}.
90 -spec delete_handler(atom() | {atom(), term()}) -> delete_handler_result().
91 delete_handler(Mod) ->
92 gen_event:delete_handler(?EVENT_LOGGER, Mod, []).
93
94 %% Seek backwards to the last valid log entry
95 -spec fix_log(file:io_device(), non_neg_integer()) -> ok.
96 fix_log(_FD, 0) ->
97 ok;
98 fix_log(FD, 1) ->
99 {ok, 0} = file:position(FD, 0),
100 ok;
101 fix_log(FD, Location) ->
102 case file:pread(FD, Location - 1, 1) of
103 {ok, [$\n | _]} ->
104 ok;
105 {ok, _} ->
106 fix_log(FD, Location - 1)
107 end.
108
109 %% @doc Format an IP address or host name
110 -spec fmt_ip(undefined | string() | inet:ip4_address() | inet:ip6_address()) -> string().
111 fmt_ip(IP) when is_tuple(IP) ->
112 inet_parse:ntoa(IP);
113 fmt_ip(undefined) ->
114 "0.0.0.0";
115 fmt_ip(HostName) ->
116 HostName.
117
118 %% @doc Format the current time into a string
119 -spec fmtnow() -> string().
120 fmtnow() ->
121 {{Year, Month, Date}, {Hour, Min, Sec}} = calendar:local_time(),
122 io_lib:format("[~2..0w/~s/~4..0w:~2..0w:~2..0w:~2..0w ~s]",
123 [Date,month(Month),Year, Hour, Min, Sec, zone()]).
124
125 %% @doc Notify registered log event handler of an access event.
126 -spec log_access(tuple()) -> ok.
127 log_access({_, _, _}=LogData) ->
128 gen_event:sync_notify(?EVENT_LOGGER, {log_access, LogData}).
129
130 %% @doc Close a log file.
131 -spec log_close(atom(), string(), file:io_device()) -> ok | {error, term()}.
132 log_close(Mod, Name, FD) ->
133 error_logger:info_msg("~p: closing log file: ~p~n", [Mod, Name]),
134 file:close(FD).
135
136 %% @doc Open a new log file for writing
137 -spec log_open(string()) -> {file:io_device(), non_neg_integer()}.
138 log_open(FileName) ->
139 DateHour = datehour(),
140 {log_open(FileName, DateHour), DateHour}.
141
142 %% @doc Open a new log file for writing
143 -spec log_open(string(), non_neg_integer()) -> file:io_device().
144 log_open(FileName, DateHour) ->
145 LogName = FileName ++ suffix(DateHour),
146 error_logger:info_msg("opening log file: ~p~n", [LogName]),
147 filelib:ensure_dir(LogName),
148 {ok, FD} = file:open(LogName, [read, write, raw]),
149 {ok, Location} = file:position(FD, eof),
150 fix_log(FD, Location),
151 file:truncate(FD),
152 FD.
153
154 -spec log_write(file:io_device(), iolist()) -> ok | {error, term()}.
155 log_write(FD, IoData) ->
156 file:write(FD, lists:flatten(IoData)).
157
158 %% @doc Rotate a log file if the hour it represents
159 %% has passed.
160 -spec maybe_rotate(atom(), erlang:timestamp(), #state{}) -> #state{}.
161 maybe_rotate(Mod, Time, State) ->
162 ThisHour = datehour(Time),
163 if ThisHour == State#state.hourstamp ->
164 State;
165 true ->
166 defer_refresh(Mod),
167 log_close(Mod, State#state.filename, State#state.handle),
168 Handle = log_open(State#state.filename, ThisHour),
169 State#state{hourstamp=ThisHour, handle=Handle}
170 end.
171
172 %% @doc Convert numeric month value to the abbreviation
173 -spec month(1..12) -> string().
174 month(1) ->
175 "Jan";
176 month(2) ->
177 "Feb";
178 month(3) ->
179 "Mar";
180 month(4) ->
181 "Apr";
182 month(5) ->
183 "May";
184 month(6) ->
185 "Jun";
186 month(7) ->
187 "Jul";
188 month(8) ->
189 "Aug";
190 month(9) ->
191 "Sep";
192 month(10) ->
193 "Oct";
194 month(11) ->
195 "Nov";
196 month(12) ->
197 "Dec".
198
199 %% @doc Make a synchronous call to instruct a log handler to refresh
200 %% itself.
201 -spec refresh(atom(), erlang:timestamp()) -> ok | {error, term()}.
202 refresh(Mod, Time) ->
203 call(Mod, {refresh, Time}, infinity).
204
205 -spec suffix(datehour()) -> string().
206 suffix({Y, M, D, H}) ->
207 YS = zeropad(Y, 4),
208 MS = zeropad(M, 2),
209 DS = zeropad(D, 2),
210 HS = zeropad(H, 2),
211 lists:flatten([$., YS, $_, MS, $_, DS, $_, HS]).
212
213 -spec zeropad(integer(), integer()) -> string().
214 zeropad(Num, MinLength) ->
215 NumStr = integer_to_list(Num),
216 zeropad_str(NumStr, MinLength - length(NumStr)).
217
218 -spec zeropad_str(string(), integer()) -> string().
219 zeropad_str(NumStr, Zeros) when Zeros > 0 ->
220 zeropad_str([$0 | NumStr], Zeros - 1);
221 zeropad_str(NumStr, _) ->
222 NumStr.
223
224 -spec zone() -> string().
225 zone() ->
226 Time = erlang:universaltime(),
227 LocalTime = calendar:universal_time_to_local_time(Time),
228 DiffSecs = calendar:datetime_to_gregorian_seconds(LocalTime) -
229 calendar:datetime_to_gregorian_seconds(Time),
230 zone((DiffSecs/3600)*100).
231
232 %% Ugly reformatting code to get times like +0000 and -1300
233
234 -spec zone(integer()) -> string().
235 zone(Val) when Val < 0 ->
236 io_lib:format("-~4..0w", [trunc(abs(Val))]);
237 zone(Val) when Val >= 0 ->
238 io_lib:format("+~4..0w", [trunc(abs(Val))]).
0 %% Copyright (c) 2011-2013 Basho Technologies, Inc. All Rights Reserved.
1 %%
2 %% This file is provided to you under the Apache License,
3 %% Version 2.0 (the "License"); you may not use this file
4 %% except in compliance with the License. You may obtain
5 %% a copy of the License at
6 %%
7 %% http://www.apache.org/licenses/LICENSE-2.0
8 %%
9 %% Unless required by applicable law or agreed to in writing,
10 %% software distributed under the License is distributed on an
11 %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
12 %% KIND, either express or implied. See the License for the
13 %% specific language governing permissions and limitations
14 %% under the License.
15
16 %% @doc Default log handler for webmachine
17
18 -module(webmachine_log_handler).
19
20 -behaviour(gen_event).
21
22 %% gen_event callbacks
23 -export([init/1,
24 handle_call/2,
25 handle_event/2,
26 handle_info/2,
27 terminate/2,
28 code_change/3]).
29
30 -include("webmachine_logger.hrl").
31
32 -ifdef(TEST).
33 -include_lib("eunit/include/eunit.hrl").
34 -endif.
35
36 -record(state, {hourstamp, filename, handle}).
37
38 -define(FILENAME, "access.log").
39
40 %% ===================================================================
41 %% gen_event callbacks
42 %% ===================================================================
43
44 %% @private
45 init([BaseDir]) ->
46 webmachine_log:defer_refresh(?MODULE),
47 FileName = filename:join(BaseDir, ?FILENAME),
48 {Handle, DateHour} = webmachine_log:log_open(FileName),
49 {ok, #state{filename=FileName, handle=Handle, hourstamp=DateHour}}.
50
51 %% @private
52 handle_call({_Label, MRef, get_modules}, State) ->
53 {ok, {MRef, [?MODULE]}, State};
54 handle_call({refresh, Time}, State) ->
55 {ok, ok, webmachine_log:maybe_rotate(?MODULE, Time, State)};
56 handle_call(_Request, State) ->
57 {ok, ok, State}.
58
59 %% @private
60 handle_event({log_access, LogData}, State) ->
61 NewState = webmachine_log:maybe_rotate(?MODULE, os:timestamp(), State),
62 Msg = format_req(LogData),
63 webmachine_log:log_write(NewState#state.handle, Msg),
64 {ok, NewState};
65 handle_event(_Event, State) ->
66 {ok, State}.
67
68 %% @private
69 handle_info(_Info, State) ->
70 {ok, State}.
71
72 %% @private
73 terminate(_Reason, _State) ->
74 ok.
75
76 %% @private
77 code_change(_OldVsn, State, _Extra) ->
78 {ok, State}.
79
80 %% ===================================================================
81 %% Internal functions
82 %% ===================================================================
83
84 %% We currently keep most of the Webmachine logging facility. But
85 %% since we are now using Cowboy, a few small parts had to change.
86 %% This is one such part. The code is however equivalent to Webmachine's.
87
88 format_req({Status0, Body, Req}) ->
89 User = "-",
90 Time = webmachine_log:fmtnow(),
91 Status = integer_to_list(Status0),
92 Length = integer_to_list(iolist_size(Body)),
93 {Method, _} = cowboy_req:method(Req),
94 {Path, _} = cowboy_req:path(Req),
95 {{Peer, _}, _} = cowboy_req:peer(Req),
96 Version = case cowboy_req:version(Req) of
97 {'HTTP/1.1', _} -> {1, 1};
98 {'HTTP/1.0', _} -> {1, 0}
99 end,
100 {Referer, _} = cowboy_req:header(<<"referer">>, Req, <<>>),
101 {UserAgent, _} = cowboy_req:header(<<"user-agent">>, Req, <<>>),
102 fmt_alog(Time, Peer, User, Method, Path, Version,
103 Status, Length, Referer, UserAgent).
104
105 fmt_alog(Time, Ip, User, Method, Path, {VM,Vm},
106 Status, Length, Referrer, UserAgent) ->
107 [webmachine_log:fmt_ip(Ip), " - ", User, [$\s], Time, [$\s, $"], Method, " ", Path,
108 " HTTP/", integer_to_list(VM), ".", integer_to_list(Vm), [$",$\s],
109 Status, [$\s], Length, [$\s,$"], Referrer,
110 [$",$\s,$"], UserAgent, [$",$\n]].
0 -record(wm_log_data,
1 {resource_module :: atom(),
2 start_time :: tuple(),
3 method :: atom(),
4 headers,
5 peer,
6 path :: string(),
7 version,
8 response_code,
9 response_length,
10 end_time :: tuple(),
11 finish_time :: tuple(),
12 notes}).
13 -type wm_log_data() :: #wm_log_data{}.
14
15 -define(EVENT_LOGGER, webmachine_log_event).
0 # Contributor Code of Conduct
1
2 As contributors and maintainers of this project, and in the interest of fostering an open
3 and welcoming community, we pledge to respect all people who contribute through reporting
4 issues, posting feature requests, updating documentation, submitting pull requests or
5 patches, and other activities.
6
7 We are committed to making participation in this project a harassment-free experience for
8 everyone, regardless of level of experience, gender, gender identity and expression,
9 sexual orientation, disability, personal appearance, body size, race, ethnicity, age,
10 religion, or nationality.
11
12 Examples of unacceptable behavior by participants include:
13
14 * The use of sexualized language or imagery
15 * Personal attacks
16 * Trolling or insulting/derogatory comments
17 * Public or private harassment
18 * Publishing other's private information, such as physical or electronic addresses,
19 without explicit permission
20 * Other unethical or unprofessional conduct
21
22 Project maintainers have the right and responsibility to remove, edit, or reject comments,
23 commits, code, wiki edits, issues, and other contributions that are not aligned to this
24 Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors
25 that they deem inappropriate, threatening, offensive, or harmful.
26
27 By adopting this Code of Conduct, project maintainers commit themselves to fairly and
28 consistently applying these principles to every aspect of managing this project. Project
29 maintainers who do not follow or enforce the Code of Conduct may be permanently removed
30 from the project team.
31
32 This Code of Conduct applies both within project spaces and in public spaces when an
33 individual is representing the project or its community.
34
35 Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
36 contacting a project maintainer at [info@rabbitmq.com](mailto:info@rabbitmq.com). All complaints will
37 be reviewed and investigated and will result in a response that is deemed necessary and
38 appropriate to the circumstances. Maintainers are obligated to maintain confidentiality
39 with regard to the reporter of an incident.
40
41 This Code of Conduct is adapted from the
42 [Contributor Covenant](http://contributor-covenant.org), version 1.3.0, available at
43 [contributor-covenant.org/version/1/3/0/](http://contributor-covenant.org/version/1/3/0/)
0 ## Overview
1
2 RabbitMQ projects use pull requests to discuss, collaborate on and accept code contributions.
3 Pull requests is the primary place of discussing code changes.
4
5 ## How to Contribute
6
7 The process is fairly standard:
8
9 * Fork the repository or repositories you plan on contributing to
10 * Clone [RabbitMQ umbrella repository](https://github.com/rabbitmq/rabbitmq-public-umbrella)
11 * `cd umbrella`, `make co`
12 * Create a branch with a descriptive name in the relevant repositories
13 * Make your changes, run tests, commit with a [descriptive message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), push to your fork
14 * Submit pull requests with an explanation what has been changed and **why**
15 * Submit a filled out and signed [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) if needed (see below)
16 * Be patient. We will get to your pull request eventually
17
18 If what you are going to work on is a substantial change, please first ask the core team
19 of their opinion on [RabbitMQ mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users).
20
21
22 ## Code of Conduct
23
24 See [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md).
25
26
27 ## Contributor Agreement
28
29 If you want to contribute a non-trivial change, please submit a signed copy of our
30 [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) around the time
31 you submit your pull request. This will make it much easier (in some cases, possible)
32 for the RabbitMQ team at Pivotal to merge your contribution.
33
34
35 ## Where to Ask Questions
36
37 If something isn't clear, feel free to ask on our [mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users).
0 This package, the rabbitmq-web-stomp, is licensed under the MPL. For
1 the MPL, please see LICENSE-MPL-RabbitMQ.
2
3 If you have any questions regarding licensing, please contact us at
4 info@rabbitmq.com.
0 MOZILLA PUBLIC LICENSE
1 Version 1.1
2
3 ---------------
4
5 1. Definitions.
6
7 1.0.1. "Commercial Use" means distribution or otherwise making the
8 Covered Code available to a third party.
9
10 1.1. "Contributor" means each entity that creates or contributes to
11 the creation of Modifications.
12
13 1.2. "Contributor Version" means the combination of the Original
14 Code, prior Modifications used by a Contributor, and the Modifications
15 made by that particular Contributor.
16
17 1.3. "Covered Code" means the Original Code or Modifications or the
18 combination of the Original Code and Modifications, in each case
19 including portions thereof.
20
21 1.4. "Electronic Distribution Mechanism" means a mechanism generally
22 accepted in the software development community for the electronic
23 transfer of data.
24
25 1.5. "Executable" means Covered Code in any form other than Source
26 Code.
27
28 1.6. "Initial Developer" means the individual or entity identified
29 as the Initial Developer in the Source Code notice required by Exhibit
30 A.
31
32 1.7. "Larger Work" means a work which combines Covered Code or
33 portions thereof with code not governed by the terms of this License.
34
35 1.8. "License" means this document.
36
37 1.8.1. "Licensable" means having the right to grant, to the maximum
38 extent possible, whether at the time of the initial grant or
39 subsequently acquired, any and all of the rights conveyed herein.
40
41 1.9. "Modifications" means any addition to or deletion from the
42 substance or structure of either the Original Code or any previous
43 Modifications. When Covered Code is released as a series of files, a
44 Modification is:
45 A. Any addition to or deletion from the contents of a file
46 containing Original Code or previous Modifications.
47
48 B. Any new file that contains any part of the Original Code or
49 previous Modifications.
50
51 1.10. "Original Code" means Source Code of computer software code
52 which is described in the Source Code notice required by Exhibit A as
53 Original Code, and which, at the time of its release under this
54 License is not already Covered Code governed by this License.
55
56 1.10.1. "Patent Claims" means any patent claim(s), now owned or
57 hereafter acquired, including without limitation, method, process,
58 and apparatus claims, in any patent Licensable by grantor.
59
60 1.11. "Source Code" means the preferred form of the Covered Code for
61 making modifications to it, including all modules it contains, plus
62 any associated interface definition files, scripts used to control
63 compilation and installation of an Executable, or source code
64 differential comparisons against either the Original Code or another
65 well known, available Covered Code of the Contributor's choice. The
66 Source Code can be in a compressed or archival form, provided the
67 appropriate decompression or de-archiving software is widely available
68 for no charge.
69
70 1.12. "You" (or "Your") means an individual or a legal entity
71 exercising rights under, and complying with all of the terms of, this
72 License or a future version of this License issued under Section 6.1.
73 For legal entities, "You" includes any entity which controls, is
74 controlled by, or is under common control with You. For purposes of
75 this definition, "control" means (a) the power, direct or indirect,
76 to cause the direction or management of such entity, whether by
77 contract or otherwise, or (b) ownership of more than fifty percent
78 (50%) of the outstanding shares or beneficial ownership of such
79 entity.
80
81 2. Source Code License.
82
83 2.1. The Initial Developer Grant.
84 The Initial Developer hereby grants You a world-wide, royalty-free,
85 non-exclusive license, subject to third party intellectual property
86 claims:
87 (a) under intellectual property rights (other than patent or
88 trademark) Licensable by Initial Developer to use, reproduce,
89 modify, display, perform, sublicense and distribute the Original
90 Code (or portions thereof) with or without Modifications, and/or
91 as part of a Larger Work; and
92
93 (b) under Patents Claims infringed by the making, using or
94 selling of Original Code, to make, have made, use, practice,
95 sell, and offer for sale, and/or otherwise dispose of the
96 Original Code (or portions thereof).
97
98 (c) the licenses granted in this Section 2.1(a) and (b) are
99 effective on the date Initial Developer first distributes
100 Original Code under the terms of this License.
101
102 (d) Notwithstanding Section 2.1(b) above, no patent license is
103 granted: 1) for code that You delete from the Original Code; 2)
104 separate from the Original Code; or 3) for infringements caused
105 by: i) the modification of the Original Code or ii) the
106 combination of the Original Code with other software or devices.
107
108 2.2. Contributor Grant.
109 Subject to third party intellectual property claims, each Contributor
110 hereby grants You a world-wide, royalty-free, non-exclusive license
111
112 (a) under intellectual property rights (other than patent or
113 trademark) Licensable by Contributor, to use, reproduce, modify,
114 display, perform, sublicense and distribute the Modifications
115 created by such Contributor (or portions thereof) either on an
116 unmodified basis, with other Modifications, as Covered Code
117 and/or as part of a Larger Work; and
118
119 (b) under Patent Claims infringed by the making, using, or
120 selling of Modifications made by that Contributor either alone
121 and/or in combination with its Contributor Version (or portions
122 of such combination), to make, use, sell, offer for sale, have
123 made, and/or otherwise dispose of: 1) Modifications made by that
124 Contributor (or portions thereof); and 2) the combination of
125 Modifications made by that Contributor with its Contributor
126 Version (or portions of such combination).
127
128 (c) the licenses granted in Sections 2.2(a) and 2.2(b) are
129 effective on the date Contributor first makes Commercial Use of
130 the Covered Code.
131
132 (d) Notwithstanding Section 2.2(b) above, no patent license is
133 granted: 1) for any code that Contributor has deleted from the
134 Contributor Version; 2) separate from the Contributor Version;
135 3) for infringements caused by: i) third party modifications of
136 Contributor Version or ii) the combination of Modifications made
137 by that Contributor with other software (except as part of the
138 Contributor Version) or other devices; or 4) under Patent Claims
139 infringed by Covered Code in the absence of Modifications made by
140 that Contributor.
141
142 3. Distribution Obligations.
143
144 3.1. Application of License.
145 The Modifications which You create or to which You contribute are
146 governed by the terms of this License, including without limitation
147 Section 2.2. The Source Code version of Covered Code may be
148 distributed only under the terms of this License or a future version
149 of this License released under Section 6.1, and You must include a
150 copy of this License with every copy of the Source Code You
151 distribute. You may not offer or impose any terms on any Source Code
152 version that alters or restricts the applicable version of this
153 License or the recipients' rights hereunder. However, You may include
154 an additional document offering the additional rights described in
155 Section 3.5.
156
157 3.2. Availability of Source Code.
158 Any Modification which You create or to which You contribute must be
159 made available in Source Code form under the terms of this License
160 either on the same media as an Executable version or via an accepted
161 Electronic Distribution Mechanism to anyone to whom you made an
162 Executable version available; and if made available via Electronic
163 Distribution Mechanism, must remain available for at least twelve (12)
164 months after the date it initially became available, or at least six
165 (6) months after a subsequent version of that particular Modification
166 has been made available to such recipients. You are responsible for
167 ensuring that the Source Code version remains available even if the
168 Electronic Distribution Mechanism is maintained by a third party.
169
170 3.3. Description of Modifications.
171 You must cause all Covered Code to which You contribute to contain a
172 file documenting the changes You made to create that Covered Code and
173 the date of any change. You must include a prominent statement that
174 the Modification is derived, directly or indirectly, from Original
175 Code provided by the Initial Developer and including the name of the
176 Initial Developer in (a) the Source Code, and (b) in any notice in an
177 Executable version or related documentation in which You describe the
178 origin or ownership of the Covered Code.
179
180 3.4. Intellectual Property Matters
181 (a) Third Party Claims.
182 If Contributor has knowledge that a license under a third party's
183 intellectual property rights is required to exercise the rights
184 granted by such Contributor under Sections 2.1 or 2.2,
185 Contributor must include a text file with the Source Code
186 distribution titled "LEGAL" which describes the claim and the
187 party making the claim in sufficient detail that a recipient will
188 know whom to contact. If Contributor obtains such knowledge after
189 the Modification is made available as described in Section 3.2,
190 Contributor shall promptly modify the LEGAL file in all copies
191 Contributor makes available thereafter and shall take other steps
192 (such as notifying appropriate mailing lists or newsgroups)
193 reasonably calculated to inform those who received the Covered
194 Code that new knowledge has been obtained.
195
196 (b) Contributor APIs.
197 If Contributor's Modifications include an application programming
198 interface and Contributor has knowledge of patent licenses which
199 are reasonably necessary to implement that API, Contributor must
200 also include this information in the LEGAL file.
201
202 (c) Representations.
203 Contributor represents that, except as disclosed pursuant to
204 Section 3.4(a) above, Contributor believes that Contributor's
205 Modifications are Contributor's original creation(s) and/or
206 Contributor has sufficient rights to grant the rights conveyed by
207 this License.
208
209 3.5. Required Notices.
210 You must duplicate the notice in Exhibit A in each file of the Source
211 Code. If it is not possible to put such notice in a particular Source
212 Code file due to its structure, then You must include such notice in a
213 location (such as a relevant directory) where a user would be likely
214 to look for such a notice. If You created one or more Modification(s)
215 You may add your name as a Contributor to the notice described in
216 Exhibit A. You must also duplicate this License in any documentation
217 for the Source Code where You describe recipients' rights or ownership
218 rights relating to Covered Code. You may choose to offer, and to
219 charge a fee for, warranty, support, indemnity or liability
220 obligations to one or more recipients of Covered Code. However, You
221 may do so only on Your own behalf, and not on behalf of the Initial
222 Developer or any Contributor. You must make it absolutely clear than
223 any such warranty, support, indemnity or liability obligation is
224 offered by You alone, and You hereby agree to indemnify the Initial
225 Developer and every Contributor for any liability incurred by the
226 Initial Developer or such Contributor as a result of warranty,
227 support, indemnity or liability terms You offer.
228
229 3.6. Distribution of Executable Versions.
230 You may distribute Covered Code in Executable form only if the
231 requirements of Section 3.1-3.5 have been met for that Covered Code,
232 and if You include a notice stating that the Source Code version of
233 the Covered Code is available under the terms of this License,
234 including a description of how and where You have fulfilled the
235 obligations of Section 3.2. The notice must be conspicuously included
236 in any notice in an Executable version, related documentation or
237 collateral in which You describe recipients' rights relating to the
238 Covered Code. You may distribute the Executable version of Covered
239 Code or ownership rights under a license of Your choice, which may
240 contain terms different from this License, provided that You are in
241 compliance with the terms of this License and that the license for the
242 Executable version does not attempt to limit or alter the recipient's
243 rights in the Source Code version from the rights set forth in this
244 License. If You distribute the Executable version under a different
245 license You must make it absolutely clear that any terms which differ
246 from this License are offered by You alone, not by the Initial
247 Developer or any Contributor. You hereby agree to indemnify the
248 Initial Developer and every Contributor for any liability incurred by
249 the Initial Developer or such Contributor as a result of any such
250 terms You offer.
251
252 3.7. Larger Works.
253 You may create a Larger Work by combining Covered Code with other code
254 not governed by the terms of this License and distribute the Larger
255 Work as a single product. In such a case, You must make sure the
256 requirements of this License are fulfilled for the Covered Code.
257
258 4. Inability to Comply Due to Statute or Regulation.
259
260 If it is impossible for You to comply with any of the terms of this
261 License with respect to some or all of the Covered Code due to
262 statute, judicial order, or regulation then You must: (a) comply with
263 the terms of this License to the maximum extent possible; and (b)
264 describe the limitations and the code they affect. Such description
265 must be included in the LEGAL file described in Section 3.4 and must
266 be included with all distributions of the Source Code. Except to the
267 extent prohibited by statute or regulation, such description must be
268 sufficiently detailed for a recipient of ordinary skill to be able to
269 understand it.
270
271 5. Application of this License.
272
273 This License applies to code to which the Initial Developer has
274 attached the notice in Exhibit A and to related Covered Code.
275
276 6. Versions of the License.
277
278 6.1. New Versions.
279 Netscape Communications Corporation ("Netscape") may publish revised
280 and/or new versions of the License from time to time. Each version
281 will be given a distinguishing version number.
282
283 6.2. Effect of New Versions.
284 Once Covered Code has been published under a particular version of the
285 License, You may always continue to use it under the terms of that
286 version. You may also choose to use such Covered Code under the terms
287 of any subsequent version of the License published by Netscape. No one
288 other than Netscape has the right to modify the terms applicable to
289 Covered Code created under this License.
290
291 6.3. Derivative Works.
292 If You create or use a modified version of this License (which you may
293 only do in order to apply it to code which is not already Covered Code
294 governed by this License), You must (a) rename Your license so that
295 the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
296 "MPL", "NPL" or any confusingly similar phrase do not appear in your
297 license (except to note that your license differs from this License)
298 and (b) otherwise make it clear that Your version of the license
299 contains terms which differ from the Mozilla Public License and
300 Netscape Public License. (Filling in the name of the Initial
301 Developer, Original Code or Contributor in the notice described in
302 Exhibit A shall not of themselves be deemed to be modifications of
303 this License.)
304
305 7. DISCLAIMER OF WARRANTY.
306
307 COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
308 WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
309 WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
310 DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
311 THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE
312 IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,
313 YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE
314 COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
315 OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
316 ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
317
318 8. TERMINATION.
319
320 8.1. This License and the rights granted hereunder will terminate
321 automatically if You fail to comply with terms herein and fail to cure
322 such breach within 30 days of becoming aware of the breach. All
323 sublicenses to the Covered Code which are properly granted shall
324 survive any termination of this License. Provisions which, by their
325 nature, must remain in effect beyond the termination of this License
326 shall survive.
327
328 8.2. If You initiate litigation by asserting a patent infringement
329 claim (excluding declatory judgment actions) against Initial Developer
330 or a Contributor (the Initial Developer or Contributor against whom
331 You file such action is referred to as "Participant") alleging that:
332
333 (a) such Participant's Contributor Version directly or indirectly
334 infringes any patent, then any and all rights granted by such
335 Participant to You under Sections 2.1 and/or 2.2 of this License
336 shall, upon 60 days notice from Participant terminate prospectively,
337 unless if within 60 days after receipt of notice You either: (i)
338 agree in writing to pay Participant a mutually agreeable reasonable
339 royalty for Your past and future use of Modifications made by such
340 Participant, or (ii) withdraw Your litigation claim with respect to
341 the Contributor Version against such Participant. If within 60 days
342 of notice, a reasonable royalty and payment arrangement are not
343 mutually agreed upon in writing by the parties or the litigation claim
344 is not withdrawn, the rights granted by Participant to You under
345 Sections 2.1 and/or 2.2 automatically terminate at the expiration of
346 the 60 day notice period specified above.
347
348 (b) any software, hardware, or device, other than such Participant's
349 Contributor Version, directly or indirectly infringes any patent, then
350 any rights granted to You by such Participant under Sections 2.1(b)
351 and 2.2(b) are revoked effective as of the date You first made, used,
352 sold, distributed, or had made, Modifications made by that
353 Participant.
354
355 8.3. If You assert a patent infringement claim against Participant
356 alleging that such Participant's Contributor Version directly or
357 indirectly infringes any patent where such claim is resolved (such as
358 by license or settlement) prior to the initiation of patent
359 infringement litigation, then the reasonable value of the licenses
360 granted by such Participant under Sections 2.1 or 2.2 shall be taken
361 into account in determining the amount or value of any payment or
362 license.
363
364 8.4. In the event of termination under Sections 8.1 or 8.2 above,
365 all end user license agreements (excluding distributors and resellers)
366 which have been validly granted by You or any distributor hereunder
367 prior to termination shall survive termination.
368
369 9. LIMITATION OF LIABILITY.
370
371 UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
372 (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
373 DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
374 OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
375 ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
376 CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
377 WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
378 COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
379 INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
380 LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
381 RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
382 PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
383 EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
384 THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
385
386 10. U.S. GOVERNMENT END USERS.
387
388 The Covered Code is a "commercial item," as that term is defined in
389 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
390 software" and "commercial computer software documentation," as such
391 terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
392 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
393 all U.S. Government End Users acquire Covered Code with only those
394 rights set forth herein.
395
396 11. MISCELLANEOUS.
397
398 This License represents the complete agreement concerning subject
399 matter hereof. If any provision of this License is held to be
400 unenforceable, such provision shall be reformed only to the extent
401 necessary to make it enforceable. This License shall be governed by
402 California law provisions (except to the extent applicable law, if
403 any, provides otherwise), excluding its conflict-of-law provisions.
404 With respect to disputes in which at least one party is a citizen of,
405 or an entity chartered or registered to do business in the United
406 States of America, any litigation relating to this License shall be
407 subject to the jurisdiction of the Federal Courts of the Northern
408 District of California, with venue lying in Santa Clara County,
409 California, with the losing party responsible for costs, including
410 without limitation, court costs and reasonable attorneys' fees and
411 expenses. The application of the United Nations Convention on
412 Contracts for the International Sale of Goods is expressly excluded.
413 Any law or regulation which provides that the language of a contract
414 shall be construed against the drafter shall not apply to this
415 License.
416
417 12. RESPONSIBILITY FOR CLAIMS.
418
419 As between Initial Developer and the Contributors, each party is
420 responsible for claims and damages arising, directly or indirectly,
421 out of its utilization of rights under this License and You agree to
422 work with Initial Developer and Contributors to distribute such
423 responsibility on an equitable basis. Nothing herein is intended or
424 shall be deemed to constitute any admission of liability.
425
426 13. MULTIPLE-LICENSED CODE.
427
428 Initial Developer may designate portions of the Covered Code as
429 "Multiple-Licensed". "Multiple-Licensed" means that the Initial
430 Developer permits you to utilize portions of the Covered Code under
431 Your choice of the NPL or the alternative licenses, if any, specified
432 by the Initial Developer in the file described in Exhibit A.
433
434 EXHIBIT A -Mozilla Public License.
435
436 ``The contents of this file are subject to the Mozilla Public License
437 Version 1.1 (the "License"); you may not use this file except in
438 compliance with the License. You may obtain a copy of the License at
439 http://www.mozilla.org/MPL/
440
441 Software distributed under the License is distributed on an "AS IS"
442 basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
443 License for the specific language governing rights and limitations
444 under the License.
445
446 The Original Code is RabbitMQ.
447
448 The Initial Developer of the Original Code is GoPivotal, Inc.
449 Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.''
450
451 [NOTE: The text of this Exhibit A may differ slightly from the text of
452 the notices in the Source Code files of the Original Code. You should
453 use the text of this Exhibit A rather than the text found in the
454 Original Code Source Code for Your Modifications.]
0 PROJECT = rabbitmq_web_mqtt
1 PROJECT_DESCRIPTION = RabbitMQ MQTT-over-WebSockets adapter
2 PROJECT_MOD = rabbit_web_mqtt_app
3
4 define PROJECT_ENV
5 [
6 {tcp_config, []},
7 {num_tcp_acceptors, 10},
8 {ssl_config, []},
9 {num_ssl_acceptors, 1},
10 {cowboy_opts, []}
11 ]
12 endef
13
14 DEPS = rabbit_common rabbit cowboy rabbitmq_mqtt
15 TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers
16 dep_cowboy_commit = 1.0.3
17
18 # FIXME: Add Ranch as a BUILD_DEPS to be sure the correct version is picked.
19 # See rabbitmq-components.mk.
20 BUILD_DEPS += ranch
21
22 DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
23 DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk
24
25 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
26 # reviewed and merged.
27
28 ERLANG_MK_REPO = https://github.com/rabbitmq/erlang.mk.git
29 ERLANG_MK_COMMIT = rabbitmq-tmp
30
31 include rabbitmq-components.mk
32 include erlang.mk
0 # RabbitMQ Web MQTT plugin
1
2
3 This plugin provides a support for MQTT-over-WebSockets to
4 RabbitMQ.
5
6
7 ## Supported RabbitMQ Versions
8
9 This plugin supports RabbitMQ `3.6.x` and later releases starting with `3.6.1`.
10
11
12 ## Installation
13
14 ### Binary Builds
15
16 Binary build is available from the [RabbitMQ Community Plugins page](http://www.rabbitmq.com/community-plugins.html).
17
18 ### From Source
19
20 * [Generic plugin build instructions](http://www.rabbitmq.com/plugin-development.html)
21 * Instructions on [how to install a plugin into RabbitMQ broker](http://www.rabbitmq.com/plugins.html#installing-plugins)
22
23 Note that release branches (`stable` vs. `master`) and target RabbitMQ version need to be taken into account
24 when building plugins from source.
25
26
27 ## Documentation
28
29 Please refer to the [RabbitMQ Web MQTT guide](http://www.rabbitmq.com/web-mqtt.html).
30
31
32 ## Copyright and License
33
34 (c) Pivotal Software Inc, 2007-2017.
35
36 Released under the same license as RabbitMQ. See LICENSE for details.
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
1 #
2 # Permission to use, copy, modify, and/or distribute this software for any
3 # purpose with or without fee is hereby granted, provided that the above
4 # copyright notice and this permission notice appear in all copies.
5 #
6 # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
7 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
8 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
9 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
10 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
11 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
12 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
13
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
15
16 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
31
32 # Core configuration.
33
34 PROJECT ?= $(notdir $(CURDIR))
35 PROJECT := $(strip $(PROJECT))
36
37 PROJECT_VERSION ?= rolling
38 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
40
41 # Verbosity.
42
43 V ?= 0
44
45 verbose_0 = @
46 verbose_2 = set -x;
47 verbose = $(verbose_$(V))
48
49 gen_verbose_0 = @echo " GEN " $@;
50 gen_verbose_2 = set -x;
51 gen_verbose = $(gen_verbose_$(V))
52
53 # Temporary files directory.
54
55 ERLANG_MK_TMP ?= $(CURDIR)/.erlang.mk
56 export ERLANG_MK_TMP
57
58 # "erl" command.
59
60 ERL = erl +A0 -noinput -boot start_clean
61
62 # Platform detection.
63
64 ifeq ($(PLATFORM),)
65 UNAME_S := $(shell uname -s)
66
67 ifeq ($(UNAME_S),Linux)
68 PLATFORM = linux
69 else ifeq ($(UNAME_S),Darwin)
70 PLATFORM = darwin
71 else ifeq ($(UNAME_S),SunOS)
72 PLATFORM = solaris
73 else ifeq ($(UNAME_S),GNU)
74 PLATFORM = gnu
75 else ifeq ($(UNAME_S),FreeBSD)
76 PLATFORM = freebsd
77 else ifeq ($(UNAME_S),NetBSD)
78 PLATFORM = netbsd
79 else ifeq ($(UNAME_S),OpenBSD)
80 PLATFORM = openbsd
81 else ifeq ($(UNAME_S),DragonFly)
82 PLATFORM = dragonfly
83 else ifeq ($(shell uname -o),Msys)
84 PLATFORM = msys2
85 else
86 $(error Unable to detect platform. Please open a ticket with the output of uname -a.)
87 endif
88
89 export PLATFORM
90 endif
91
92 # Core targets.
93
94 all:: deps app rel
95
96 # Noop to avoid a Make warning when there's nothing to do.
97 rel::
98 $(verbose) :
99
100 relup:: deps app
101
102 check:: tests
103
104 clean:: clean-crashdump
105
106 clean-crashdump:
107 ifneq ($(wildcard erl_crash.dump),)
108 $(gen_verbose) rm -f erl_crash.dump
109 endif
110
111 distclean:: clean distclean-tmp
112
113 distclean-tmp:
114 $(gen_verbose) rm -rf $(ERLANG_MK_TMP)
115
116 help::
117 $(verbose) printf "%s\n" \
118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
120 "" \
121 "Usage: [V=1] $(MAKE) [target]..." \
122 "" \
123 "Core targets:" \
124 " all Run deps, app and rel targets in that order" \
125 " app Compile the project" \
126 " deps Fetch dependencies (if needed) and compile them" \
127 " fetch-deps Fetch dependencies recursively (if needed) without compiling them" \
128 " list-deps List dependencies recursively on stdout" \
129 " search q=... Search for a package in the built-in index" \
130 " rel Build a release for this project, if applicable" \
131 " docs Build the documentation for this project" \
132 " install-docs Install the man pages for this project" \
133 " check Compile and run all tests and analysis for this project" \
134 " tests Run the tests for this project" \
135 " clean Delete temporary and output files from most targets" \
136 " distclean Delete all temporary and output files" \
137 " help Display this help and exit" \
138 " erlang-mk Update erlang.mk to the latest version"
139
140 # Core functions.
141
142 empty :=
143 space := $(empty) $(empty)
144 tab := $(empty) $(empty)
145 comma := ,
146
147 define newline
148
149
150 endef
151
152 define comma_list
153 $(subst $(space),$(comma),$(strip $(1)))
154 endef
155
156 # Adding erlang.mk to make Erlang scripts who call init:get_plain_arguments() happy.
157 define erlang
158 $(ERL) $(2) -pz $(ERLANG_MK_TMP)/rebar/ebin -eval "$(subst $(newline),,$(subst ",\",$(1)))" -- erlang.mk
159 endef
160
161 ifeq ($(PLATFORM),msys2)
162 core_native_path = $(subst \,\\\\,$(shell cygpath -w $1))
163 else
164 core_native_path = $1
165 endif
166
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
168
169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
170
171 core_find = $(if $(wildcard $1),$(shell find $(1:%/=%) -type f -name $(subst *,\*,$2)))
172
173 core_lc = $(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$(1)))))))))))))))))))))))))))
174
175 core_ls = $(filter-out $(1),$(shell echo $(1)))
176
177 # @todo Use a solution that does not require using perl.
178 core_relpath = $(shell perl -e 'use File::Spec; print File::Spec->abs2rel(@ARGV) . "\n"' $1 $2)
179
180 # Automated update.
181
182 ERLANG_MK_REPO ?= https://github.com/ninenines/erlang.mk
183 ERLANG_MK_COMMIT ?=
184 ERLANG_MK_BUILD_CONFIG ?= build.config
185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
188 erlang-mk:
189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
190 ifdef ERLANG_MK_COMMIT
191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
192 endif
193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
196 rm -rf $(ERLANG_MK_BUILD_DIR)
197
198 # The erlang.mk package index is bundled in the default erlang.mk build.
199 # Search for the string "copyright" to skip to the rest of the code.
200
201 PACKAGES += aberth
202 pkg_aberth_name = aberth
203 pkg_aberth_description = Generic BERT-RPC server in Erlang
204 pkg_aberth_homepage = https://github.com/a13x/aberth
205 pkg_aberth_fetch = git
206 pkg_aberth_repo = https://github.com/a13x/aberth
207 pkg_aberth_commit = master
208
209 PACKAGES += active
210 pkg_active_name = active
211 pkg_active_description = Active development for Erlang: rebuild and reload source/binary files while the VM is running
212 pkg_active_homepage = https://github.com/proger/active
213 pkg_active_fetch = git
214 pkg_active_repo = https://github.com/proger/active
215 pkg_active_commit = master
216
217 PACKAGES += actordb_core
218 pkg_actordb_core_name = actordb_core
219 pkg_actordb_core_description = ActorDB main source
220 pkg_actordb_core_homepage = http://www.actordb.com/
221 pkg_actordb_core_fetch = git
222 pkg_actordb_core_repo = https://github.com/biokoda/actordb_core
223 pkg_actordb_core_commit = master
224
225 PACKAGES += actordb_thrift
226 pkg_actordb_thrift_name = actordb_thrift
227 pkg_actordb_thrift_description = Thrift API for ActorDB
228 pkg_actordb_thrift_homepage = http://www.actordb.com/
229 pkg_actordb_thrift_fetch = git
230 pkg_actordb_thrift_repo = https://github.com/biokoda/actordb_thrift
231 pkg_actordb_thrift_commit = master
232
233 PACKAGES += aleppo
234 pkg_aleppo_name = aleppo
235 pkg_aleppo_description = Alternative Erlang Pre-Processor
236 pkg_aleppo_homepage = https://github.com/ErlyORM/aleppo
237 pkg_aleppo_fetch = git
238 pkg_aleppo_repo = https://github.com/ErlyORM/aleppo
239 pkg_aleppo_commit = master
240
241 PACKAGES += alog
242 pkg_alog_name = alog
243 pkg_alog_description = Simply the best logging framework for Erlang
244 pkg_alog_homepage = https://github.com/siberian-fast-food/alogger
245 pkg_alog_fetch = git
246 pkg_alog_repo = https://github.com/siberian-fast-food/alogger
247 pkg_alog_commit = master
248
249 PACKAGES += amqp_client
250 pkg_amqp_client_name = amqp_client
251 pkg_amqp_client_description = RabbitMQ Erlang AMQP client
252 pkg_amqp_client_homepage = https://www.rabbitmq.com/erlang-client-user-guide.html
253 pkg_amqp_client_fetch = git
254 pkg_amqp_client_repo = https://github.com/rabbitmq/rabbitmq-erlang-client.git
255 pkg_amqp_client_commit = master
256
257 PACKAGES += annotations
258 pkg_annotations_name = annotations
259 pkg_annotations_description = Simple code instrumentation utilities
260 pkg_annotations_homepage = https://github.com/hyperthunk/annotations
261 pkg_annotations_fetch = git
262 pkg_annotations_repo = https://github.com/hyperthunk/annotations
263 pkg_annotations_commit = master
264
265 PACKAGES += antidote
266 pkg_antidote_name = antidote
267 pkg_antidote_description = Large-scale computation without synchronisation
268 pkg_antidote_homepage = https://syncfree.lip6.fr/
269 pkg_antidote_fetch = git
270 pkg_antidote_repo = https://github.com/SyncFree/antidote
271 pkg_antidote_commit = master
272
273 PACKAGES += apns
274 pkg_apns_name = apns
275 pkg_apns_description = Apple Push Notification Server for Erlang
276 pkg_apns_homepage = http://inaka.github.com/apns4erl
277 pkg_apns_fetch = git
278 pkg_apns_repo = https://github.com/inaka/apns4erl
279 pkg_apns_commit = master
280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
289 PACKAGES += azdht
290 pkg_azdht_name = azdht
291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
292 pkg_azdht_homepage = https://github.com/arcusfelis/azdht
293 pkg_azdht_fetch = git
294 pkg_azdht_repo = https://github.com/arcusfelis/azdht
295 pkg_azdht_commit = master
296
297 PACKAGES += backoff
298 pkg_backoff_name = backoff
299 pkg_backoff_description = Simple exponential backoffs in Erlang
300 pkg_backoff_homepage = https://github.com/ferd/backoff
301 pkg_backoff_fetch = git
302 pkg_backoff_repo = https://github.com/ferd/backoff
303 pkg_backoff_commit = master
304
305 PACKAGES += barrel_tcp
306 pkg_barrel_tcp_name = barrel_tcp
307 pkg_barrel_tcp_description = barrel is a generic TCP acceptor pool with low latency in Erlang.
308 pkg_barrel_tcp_homepage = https://github.com/benoitc-attic/barrel_tcp
309 pkg_barrel_tcp_fetch = git
310 pkg_barrel_tcp_repo = https://github.com/benoitc-attic/barrel_tcp
311 pkg_barrel_tcp_commit = master
312
313 PACKAGES += basho_bench
314 pkg_basho_bench_name = basho_bench
315 pkg_basho_bench_description = A load-generation and testing tool for basically whatever you can write a returning Erlang function for.
316 pkg_basho_bench_homepage = https://github.com/basho/basho_bench
317 pkg_basho_bench_fetch = git
318 pkg_basho_bench_repo = https://github.com/basho/basho_bench
319 pkg_basho_bench_commit = master
320
321 PACKAGES += bcrypt
322 pkg_bcrypt_name = bcrypt
323 pkg_bcrypt_description = Bcrypt Erlang / C library
324 pkg_bcrypt_homepage = https://github.com/riverrun/branglecrypt
325 pkg_bcrypt_fetch = git
326 pkg_bcrypt_repo = https://github.com/riverrun/branglecrypt
327 pkg_bcrypt_commit = master
328
329 PACKAGES += beam
330 pkg_beam_name = beam
331 pkg_beam_description = BEAM emulator written in Erlang
332 pkg_beam_homepage = https://github.com/tonyrog/beam
333 pkg_beam_fetch = git
334 pkg_beam_repo = https://github.com/tonyrog/beam
335 pkg_beam_commit = master
336
337 PACKAGES += beanstalk
338 pkg_beanstalk_name = beanstalk
339 pkg_beanstalk_description = An Erlang client for beanstalkd
340 pkg_beanstalk_homepage = https://github.com/tim/erlang-beanstalk
341 pkg_beanstalk_fetch = git
342 pkg_beanstalk_repo = https://github.com/tim/erlang-beanstalk
343 pkg_beanstalk_commit = master
344
345 PACKAGES += bear
346 pkg_bear_name = bear
347 pkg_bear_description = a set of statistics functions for erlang
348 pkg_bear_homepage = https://github.com/boundary/bear
349 pkg_bear_fetch = git
350 pkg_bear_repo = https://github.com/boundary/bear
351 pkg_bear_commit = master
352
353 PACKAGES += bertconf
354 pkg_bertconf_name = bertconf
355 pkg_bertconf_description = Make ETS tables out of statc BERT files that are auto-reloaded
356 pkg_bertconf_homepage = https://github.com/ferd/bertconf
357 pkg_bertconf_fetch = git
358 pkg_bertconf_repo = https://github.com/ferd/bertconf
359 pkg_bertconf_commit = master
360
361 PACKAGES += bifrost
362 pkg_bifrost_name = bifrost
363 pkg_bifrost_description = Erlang FTP Server Framework
364 pkg_bifrost_homepage = https://github.com/thorstadt/bifrost
365 pkg_bifrost_fetch = git
366 pkg_bifrost_repo = https://github.com/thorstadt/bifrost
367 pkg_bifrost_commit = master
368
369 PACKAGES += binpp
370 pkg_binpp_name = binpp
371 pkg_binpp_description = Erlang Binary Pretty Printer
372 pkg_binpp_homepage = https://github.com/jtendo/binpp
373 pkg_binpp_fetch = git
374 pkg_binpp_repo = https://github.com/jtendo/binpp
375 pkg_binpp_commit = master
376
377 PACKAGES += bisect
378 pkg_bisect_name = bisect
379 pkg_bisect_description = Ordered fixed-size binary dictionary in Erlang
380 pkg_bisect_homepage = https://github.com/knutin/bisect
381 pkg_bisect_fetch = git
382 pkg_bisect_repo = https://github.com/knutin/bisect
383 pkg_bisect_commit = master
384
385 PACKAGES += bitcask
386 pkg_bitcask_name = bitcask
387 pkg_bitcask_description = because you need another a key/value storage engine
388 pkg_bitcask_homepage = https://github.com/basho/bitcask
389 pkg_bitcask_fetch = git
390 pkg_bitcask_repo = https://github.com/basho/bitcask
391 pkg_bitcask_commit = develop
392
393 PACKAGES += bitstore
394 pkg_bitstore_name = bitstore
395 pkg_bitstore_description = A document based ontology development environment
396 pkg_bitstore_homepage = https://github.com/bdionne/bitstore
397 pkg_bitstore_fetch = git
398 pkg_bitstore_repo = https://github.com/bdionne/bitstore
399 pkg_bitstore_commit = master
400
401 PACKAGES += bootstrap
402 pkg_bootstrap_name = bootstrap
403 pkg_bootstrap_description = A simple, yet powerful Erlang cluster bootstrapping application.
404 pkg_bootstrap_homepage = https://github.com/schlagert/bootstrap
405 pkg_bootstrap_fetch = git
406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
407 pkg_bootstrap_commit = master
408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
417 PACKAGES += boss_db
418 pkg_boss_db_name = boss_db
419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
420 pkg_boss_db_homepage = https://github.com/ErlyORM/boss_db
421 pkg_boss_db_fetch = git
422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
423 pkg_boss_db_commit = master
424
425 PACKAGES += brod
426 pkg_brod_name = brod
427 pkg_brod_description = Kafka client in Erlang
428 pkg_brod_homepage = https://github.com/klarna/brod
429 pkg_brod_fetch = git
430 pkg_brod_repo = https://github.com/klarna/brod.git
431 pkg_brod_commit = master
432
433 PACKAGES += bson
434 pkg_bson_name = bson
435 pkg_bson_description = BSON documents in Erlang, see bsonspec.org
436 pkg_bson_homepage = https://github.com/comtihon/bson-erlang
437 pkg_bson_fetch = git
438 pkg_bson_repo = https://github.com/comtihon/bson-erlang
439 pkg_bson_commit = master
440
441 PACKAGES += bullet
442 pkg_bullet_name = bullet
443 pkg_bullet_description = Simple, reliable, efficient streaming for Cowboy.
444 pkg_bullet_homepage = http://ninenines.eu
445 pkg_bullet_fetch = git
446 pkg_bullet_repo = https://github.com/ninenines/bullet
447 pkg_bullet_commit = master
448
449 PACKAGES += cache
450 pkg_cache_name = cache
451 pkg_cache_description = Erlang in-memory cache
452 pkg_cache_homepage = https://github.com/fogfish/cache
453 pkg_cache_fetch = git
454 pkg_cache_repo = https://github.com/fogfish/cache
455 pkg_cache_commit = master
456
457 PACKAGES += cake
458 pkg_cake_name = cake
459 pkg_cake_description = Really simple terminal colorization
460 pkg_cake_homepage = https://github.com/darach/cake-erl
461 pkg_cake_fetch = git
462 pkg_cake_repo = https://github.com/darach/cake-erl
463 pkg_cake_commit = master
464
465 PACKAGES += carotene
466 pkg_carotene_name = carotene
467 pkg_carotene_description = Real-time server
468 pkg_carotene_homepage = https://github.com/carotene/carotene
469 pkg_carotene_fetch = git
470 pkg_carotene_repo = https://github.com/carotene/carotene
471 pkg_carotene_commit = master
472
473 PACKAGES += cberl
474 pkg_cberl_name = cberl
475 pkg_cberl_description = NIF based Erlang bindings for Couchbase
476 pkg_cberl_homepage = https://github.com/chitika/cberl
477 pkg_cberl_fetch = git
478 pkg_cberl_repo = https://github.com/chitika/cberl
479 pkg_cberl_commit = master
480
481 PACKAGES += cecho
482 pkg_cecho_name = cecho
483 pkg_cecho_description = An ncurses library for Erlang
484 pkg_cecho_homepage = https://github.com/mazenharake/cecho
485 pkg_cecho_fetch = git
486 pkg_cecho_repo = https://github.com/mazenharake/cecho
487 pkg_cecho_commit = master
488
489 PACKAGES += cferl
490 pkg_cferl_name = cferl
491 pkg_cferl_description = Rackspace / Open Stack Cloud Files Erlang Client
492 pkg_cferl_homepage = https://github.com/ddossot/cferl
493 pkg_cferl_fetch = git
494 pkg_cferl_repo = https://github.com/ddossot/cferl
495 pkg_cferl_commit = master
496
497 PACKAGES += chaos_monkey
498 pkg_chaos_monkey_name = chaos_monkey
499 pkg_chaos_monkey_description = This is The CHAOS MONKEY. It will kill your processes.
500 pkg_chaos_monkey_homepage = https://github.com/dLuna/chaos_monkey
501 pkg_chaos_monkey_fetch = git
502 pkg_chaos_monkey_repo = https://github.com/dLuna/chaos_monkey
503 pkg_chaos_monkey_commit = master
504
505 PACKAGES += check_node
506 pkg_check_node_name = check_node
507 pkg_check_node_description = Nagios Scripts for monitoring Riak
508 pkg_check_node_homepage = https://github.com/basho-labs/riak_nagios
509 pkg_check_node_fetch = git
510 pkg_check_node_repo = https://github.com/basho-labs/riak_nagios
511 pkg_check_node_commit = master
512
513 PACKAGES += chronos
514 pkg_chronos_name = chronos
515 pkg_chronos_description = Timer module for Erlang that makes it easy to abstact time out of the tests.
516 pkg_chronos_homepage = https://github.com/lehoff/chronos
517 pkg_chronos_fetch = git
518 pkg_chronos_repo = https://github.com/lehoff/chronos
519 pkg_chronos_commit = master
520
521 PACKAGES += chumak
522 pkg_chumak_name = chumak
523 pkg_chumak_description = Pure Erlang implementation of ZeroMQ Message Transport Protocol.
524 pkg_chumak_homepage = http://choven.ca
525 pkg_chumak_fetch = git
526 pkg_chumak_repo = https://github.com/chovencorp/chumak
527 pkg_chumak_commit = master
528
529 PACKAGES += cl
530 pkg_cl_name = cl
531 pkg_cl_description = OpenCL binding for Erlang
532 pkg_cl_homepage = https://github.com/tonyrog/cl
533 pkg_cl_fetch = git
534 pkg_cl_repo = https://github.com/tonyrog/cl
535 pkg_cl_commit = master
536
537 PACKAGES += classifier
538 pkg_classifier_name = classifier
539 pkg_classifier_description = An Erlang Bayesian Filter and Text Classifier
540 pkg_classifier_homepage = https://github.com/inaka/classifier
541 pkg_classifier_fetch = git
542 pkg_classifier_repo = https://github.com/inaka/classifier
543 pkg_classifier_commit = master
544
545 PACKAGES += clique
546 pkg_clique_name = clique
547 pkg_clique_description = CLI Framework for Erlang
548 pkg_clique_homepage = https://github.com/basho/clique
549 pkg_clique_fetch = git
550 pkg_clique_repo = https://github.com/basho/clique
551 pkg_clique_commit = develop
552
553 PACKAGES += cloudi_core
554 pkg_cloudi_core_name = cloudi_core
555 pkg_cloudi_core_description = CloudI internal service runtime
556 pkg_cloudi_core_homepage = http://cloudi.org/
557 pkg_cloudi_core_fetch = git
558 pkg_cloudi_core_repo = https://github.com/CloudI/cloudi_core
559 pkg_cloudi_core_commit = master
560
561 PACKAGES += cloudi_service_api_requests
562 pkg_cloudi_service_api_requests_name = cloudi_service_api_requests
563 pkg_cloudi_service_api_requests_description = CloudI Service API requests (JSON-RPC/Erlang-term support)
564 pkg_cloudi_service_api_requests_homepage = http://cloudi.org/
565 pkg_cloudi_service_api_requests_fetch = git
566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
567 pkg_cloudi_service_api_requests_commit = master
568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
585 PACKAGES += cloudi_service_db_cassandra_cql
586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
588 pkg_cloudi_service_db_cassandra_cql_homepage = http://cloudi.org/
589 pkg_cloudi_service_db_cassandra_cql_fetch = git
590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
591 pkg_cloudi_service_db_cassandra_cql_commit = master
592
593 PACKAGES += cloudi_service_db_couchdb
594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
596 pkg_cloudi_service_db_couchdb_homepage = http://cloudi.org/
597 pkg_cloudi_service_db_couchdb_fetch = git
598 pkg_cloudi_service_db_couchdb_repo = https://github.com/CloudI/cloudi_service_db_couchdb
599 pkg_cloudi_service_db_couchdb_commit = master
600
601 PACKAGES += cloudi_service_db_elasticsearch
602 pkg_cloudi_service_db_elasticsearch_name = cloudi_service_db_elasticsearch
603 pkg_cloudi_service_db_elasticsearch_description = elasticsearch CloudI Service
604 pkg_cloudi_service_db_elasticsearch_homepage = http://cloudi.org/
605 pkg_cloudi_service_db_elasticsearch_fetch = git
606 pkg_cloudi_service_db_elasticsearch_repo = https://github.com/CloudI/cloudi_service_db_elasticsearch
607 pkg_cloudi_service_db_elasticsearch_commit = master
608
609 PACKAGES += cloudi_service_db_memcached
610 pkg_cloudi_service_db_memcached_name = cloudi_service_db_memcached
611 pkg_cloudi_service_db_memcached_description = memcached CloudI Service
612 pkg_cloudi_service_db_memcached_homepage = http://cloudi.org/
613 pkg_cloudi_service_db_memcached_fetch = git
614 pkg_cloudi_service_db_memcached_repo = https://github.com/CloudI/cloudi_service_db_memcached
615 pkg_cloudi_service_db_memcached_commit = master
616
617 PACKAGES += cloudi_service_db_mysql
618 pkg_cloudi_service_db_mysql_name = cloudi_service_db_mysql
619 pkg_cloudi_service_db_mysql_description = MySQL CloudI Service
620 pkg_cloudi_service_db_mysql_homepage = http://cloudi.org/
621 pkg_cloudi_service_db_mysql_fetch = git
622 pkg_cloudi_service_db_mysql_repo = https://github.com/CloudI/cloudi_service_db_mysql
623 pkg_cloudi_service_db_mysql_commit = master
624
625 PACKAGES += cloudi_service_db_pgsql
626 pkg_cloudi_service_db_pgsql_name = cloudi_service_db_pgsql
627 pkg_cloudi_service_db_pgsql_description = PostgreSQL CloudI Service
628 pkg_cloudi_service_db_pgsql_homepage = http://cloudi.org/
629 pkg_cloudi_service_db_pgsql_fetch = git
630 pkg_cloudi_service_db_pgsql_repo = https://github.com/CloudI/cloudi_service_db_pgsql
631 pkg_cloudi_service_db_pgsql_commit = master
632
633 PACKAGES += cloudi_service_db_riak
634 pkg_cloudi_service_db_riak_name = cloudi_service_db_riak
635 pkg_cloudi_service_db_riak_description = Riak CloudI Service
636 pkg_cloudi_service_db_riak_homepage = http://cloudi.org/
637 pkg_cloudi_service_db_riak_fetch = git
638 pkg_cloudi_service_db_riak_repo = https://github.com/CloudI/cloudi_service_db_riak
639 pkg_cloudi_service_db_riak_commit = master
640
641 PACKAGES += cloudi_service_db_tokyotyrant
642 pkg_cloudi_service_db_tokyotyrant_name = cloudi_service_db_tokyotyrant
643 pkg_cloudi_service_db_tokyotyrant_description = Tokyo Tyrant CloudI Service
644 pkg_cloudi_service_db_tokyotyrant_homepage = http://cloudi.org/
645 pkg_cloudi_service_db_tokyotyrant_fetch = git
646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
647 pkg_cloudi_service_db_tokyotyrant_commit = master
648
649 PACKAGES += cloudi_service_filesystem
650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
652 pkg_cloudi_service_filesystem_homepage = http://cloudi.org/
653 pkg_cloudi_service_filesystem_fetch = git
654 pkg_cloudi_service_filesystem_repo = https://github.com/CloudI/cloudi_service_filesystem
655 pkg_cloudi_service_filesystem_commit = master
656
657 PACKAGES += cloudi_service_http_client
658 pkg_cloudi_service_http_client_name = cloudi_service_http_client
659 pkg_cloudi_service_http_client_description = HTTP client CloudI Service
660 pkg_cloudi_service_http_client_homepage = http://cloudi.org/
661 pkg_cloudi_service_http_client_fetch = git
662 pkg_cloudi_service_http_client_repo = https://github.com/CloudI/cloudi_service_http_client
663 pkg_cloudi_service_http_client_commit = master
664
665 PACKAGES += cloudi_service_http_cowboy
666 pkg_cloudi_service_http_cowboy_name = cloudi_service_http_cowboy
667 pkg_cloudi_service_http_cowboy_description = cowboy HTTP/HTTPS CloudI Service
668 pkg_cloudi_service_http_cowboy_homepage = http://cloudi.org/
669 pkg_cloudi_service_http_cowboy_fetch = git
670 pkg_cloudi_service_http_cowboy_repo = https://github.com/CloudI/cloudi_service_http_cowboy
671 pkg_cloudi_service_http_cowboy_commit = master
672
673 PACKAGES += cloudi_service_http_elli
674 pkg_cloudi_service_http_elli_name = cloudi_service_http_elli
675 pkg_cloudi_service_http_elli_description = elli HTTP CloudI Service
676 pkg_cloudi_service_http_elli_homepage = http://cloudi.org/
677 pkg_cloudi_service_http_elli_fetch = git
678 pkg_cloudi_service_http_elli_repo = https://github.com/CloudI/cloudi_service_http_elli
679 pkg_cloudi_service_http_elli_commit = master
680
681 PACKAGES += cloudi_service_map_reduce
682 pkg_cloudi_service_map_reduce_name = cloudi_service_map_reduce
683 pkg_cloudi_service_map_reduce_description = Map/Reduce CloudI Service
684 pkg_cloudi_service_map_reduce_homepage = http://cloudi.org/
685 pkg_cloudi_service_map_reduce_fetch = git
686 pkg_cloudi_service_map_reduce_repo = https://github.com/CloudI/cloudi_service_map_reduce
687 pkg_cloudi_service_map_reduce_commit = master
688
689 PACKAGES += cloudi_service_oauth1
690 pkg_cloudi_service_oauth1_name = cloudi_service_oauth1
691 pkg_cloudi_service_oauth1_description = OAuth v1.0 CloudI Service
692 pkg_cloudi_service_oauth1_homepage = http://cloudi.org/
693 pkg_cloudi_service_oauth1_fetch = git
694 pkg_cloudi_service_oauth1_repo = https://github.com/CloudI/cloudi_service_oauth1
695 pkg_cloudi_service_oauth1_commit = master
696
697 PACKAGES += cloudi_service_queue
698 pkg_cloudi_service_queue_name = cloudi_service_queue
699 pkg_cloudi_service_queue_description = Persistent Queue Service
700 pkg_cloudi_service_queue_homepage = http://cloudi.org/
701 pkg_cloudi_service_queue_fetch = git
702 pkg_cloudi_service_queue_repo = https://github.com/CloudI/cloudi_service_queue
703 pkg_cloudi_service_queue_commit = master
704
705 PACKAGES += cloudi_service_quorum
706 pkg_cloudi_service_quorum_name = cloudi_service_quorum
707 pkg_cloudi_service_quorum_description = CloudI Quorum Service
708 pkg_cloudi_service_quorum_homepage = http://cloudi.org/
709 pkg_cloudi_service_quorum_fetch = git
710 pkg_cloudi_service_quorum_repo = https://github.com/CloudI/cloudi_service_quorum
711 pkg_cloudi_service_quorum_commit = master
712
713 PACKAGES += cloudi_service_router
714 pkg_cloudi_service_router_name = cloudi_service_router
715 pkg_cloudi_service_router_description = CloudI Router Service
716 pkg_cloudi_service_router_homepage = http://cloudi.org/
717 pkg_cloudi_service_router_fetch = git
718 pkg_cloudi_service_router_repo = https://github.com/CloudI/cloudi_service_router
719 pkg_cloudi_service_router_commit = master
720
721 PACKAGES += cloudi_service_tcp
722 pkg_cloudi_service_tcp_name = cloudi_service_tcp
723 pkg_cloudi_service_tcp_description = TCP CloudI Service
724 pkg_cloudi_service_tcp_homepage = http://cloudi.org/
725 pkg_cloudi_service_tcp_fetch = git
726 pkg_cloudi_service_tcp_repo = https://github.com/CloudI/cloudi_service_tcp
727 pkg_cloudi_service_tcp_commit = master
728
729 PACKAGES += cloudi_service_timers
730 pkg_cloudi_service_timers_name = cloudi_service_timers
731 pkg_cloudi_service_timers_description = Timers CloudI Service
732 pkg_cloudi_service_timers_homepage = http://cloudi.org/
733 pkg_cloudi_service_timers_fetch = git
734 pkg_cloudi_service_timers_repo = https://github.com/CloudI/cloudi_service_timers
735 pkg_cloudi_service_timers_commit = master
736
737 PACKAGES += cloudi_service_udp
738 pkg_cloudi_service_udp_name = cloudi_service_udp
739 pkg_cloudi_service_udp_description = UDP CloudI Service
740 pkg_cloudi_service_udp_homepage = http://cloudi.org/
741 pkg_cloudi_service_udp_fetch = git
742 pkg_cloudi_service_udp_repo = https://github.com/CloudI/cloudi_service_udp
743 pkg_cloudi_service_udp_commit = master
744
745 PACKAGES += cloudi_service_validate
746 pkg_cloudi_service_validate_name = cloudi_service_validate
747 pkg_cloudi_service_validate_description = CloudI Validate Service
748 pkg_cloudi_service_validate_homepage = http://cloudi.org/
749 pkg_cloudi_service_validate_fetch = git
750 pkg_cloudi_service_validate_repo = https://github.com/CloudI/cloudi_service_validate
751 pkg_cloudi_service_validate_commit = master
752
753 PACKAGES += cloudi_service_zeromq
754 pkg_cloudi_service_zeromq_name = cloudi_service_zeromq
755 pkg_cloudi_service_zeromq_description = ZeroMQ CloudI Service
756 pkg_cloudi_service_zeromq_homepage = http://cloudi.org/
757 pkg_cloudi_service_zeromq_fetch = git
758 pkg_cloudi_service_zeromq_repo = https://github.com/CloudI/cloudi_service_zeromq
759 pkg_cloudi_service_zeromq_commit = master
760
761 PACKAGES += cluster_info
762 pkg_cluster_info_name = cluster_info
763 pkg_cluster_info_description = Fork of Hibari's nifty cluster_info OTP app
764 pkg_cluster_info_homepage = https://github.com/basho/cluster_info
765 pkg_cluster_info_fetch = git
766 pkg_cluster_info_repo = https://github.com/basho/cluster_info
767 pkg_cluster_info_commit = master
768
769 PACKAGES += color
770 pkg_color_name = color
771 pkg_color_description = ANSI colors for your Erlang
772 pkg_color_homepage = https://github.com/julianduque/erlang-color
773 pkg_color_fetch = git
774 pkg_color_repo = https://github.com/julianduque/erlang-color
775 pkg_color_commit = master
776
777 PACKAGES += confetti
778 pkg_confetti_name = confetti
779 pkg_confetti_description = Erlang configuration provider / application:get_env/2 on steroids
780 pkg_confetti_homepage = https://github.com/jtendo/confetti
781 pkg_confetti_fetch = git
782 pkg_confetti_repo = https://github.com/jtendo/confetti
783 pkg_confetti_commit = master
784
785 PACKAGES += couchbeam
786 pkg_couchbeam_name = couchbeam
787 pkg_couchbeam_description = Apache CouchDB client in Erlang
788 pkg_couchbeam_homepage = https://github.com/benoitc/couchbeam
789 pkg_couchbeam_fetch = git
790 pkg_couchbeam_repo = https://github.com/benoitc/couchbeam
791 pkg_couchbeam_commit = master
792
793 PACKAGES += covertool
794 pkg_covertool_name = covertool
795 pkg_covertool_description = Tool to convert Erlang cover data files into Cobertura XML reports
796 pkg_covertool_homepage = https://github.com/idubrov/covertool
797 pkg_covertool_fetch = git
798 pkg_covertool_repo = https://github.com/idubrov/covertool
799 pkg_covertool_commit = master
800
801 PACKAGES += cowboy
802 pkg_cowboy_name = cowboy
803 pkg_cowboy_description = Small, fast and modular HTTP server.
804 pkg_cowboy_homepage = http://ninenines.eu
805 pkg_cowboy_fetch = git
806 pkg_cowboy_repo = https://github.com/ninenines/cowboy
807 pkg_cowboy_commit = 1.0.4
808
809 PACKAGES += cowdb
810 pkg_cowdb_name = cowdb
811 pkg_cowdb_description = Pure Key/Value database library for Erlang Applications
812 pkg_cowdb_homepage = https://github.com/refuge/cowdb
813 pkg_cowdb_fetch = git
814 pkg_cowdb_repo = https://github.com/refuge/cowdb
815 pkg_cowdb_commit = master
816
817 PACKAGES += cowlib
818 pkg_cowlib_name = cowlib
819 pkg_cowlib_description = Support library for manipulating Web protocols.
820 pkg_cowlib_homepage = http://ninenines.eu
821 pkg_cowlib_fetch = git
822 pkg_cowlib_repo = https://github.com/ninenines/cowlib
823 pkg_cowlib_commit = 1.0.2
824
825 PACKAGES += cpg
826 pkg_cpg_name = cpg
827 pkg_cpg_description = CloudI Process Groups
828 pkg_cpg_homepage = https://github.com/okeuday/cpg
829 pkg_cpg_fetch = git
830 pkg_cpg_repo = https://github.com/okeuday/cpg
831 pkg_cpg_commit = master
832
833 PACKAGES += cqerl
834 pkg_cqerl_name = cqerl
835 pkg_cqerl_description = Native Erlang CQL client for Cassandra
836 pkg_cqerl_homepage = https://matehat.github.io/cqerl/
837 pkg_cqerl_fetch = git
838 pkg_cqerl_repo = https://github.com/matehat/cqerl
839 pkg_cqerl_commit = master
840
841 PACKAGES += cr
842 pkg_cr_name = cr
843 pkg_cr_description = Chain Replication
844 pkg_cr_homepage = https://synrc.com/apps/cr/doc/cr.htm
845 pkg_cr_fetch = git
846 pkg_cr_repo = https://github.com/spawnproc/cr
847 pkg_cr_commit = master
848
849 PACKAGES += cuttlefish
850 pkg_cuttlefish_name = cuttlefish
851 pkg_cuttlefish_description = never lose your childlike sense of wonder baby cuttlefish, promise me?
852 pkg_cuttlefish_homepage = https://github.com/basho/cuttlefish
853 pkg_cuttlefish_fetch = git
854 pkg_cuttlefish_repo = https://github.com/basho/cuttlefish
855 pkg_cuttlefish_commit = master
856
857 PACKAGES += damocles
858 pkg_damocles_name = damocles
859 pkg_damocles_description = Erlang library for generating adversarial network conditions for QAing distributed applications/systems on a single Linux box.
860 pkg_damocles_homepage = https://github.com/lostcolony/damocles
861 pkg_damocles_fetch = git
862 pkg_damocles_repo = https://github.com/lostcolony/damocles
863 pkg_damocles_commit = master
864
865 PACKAGES += debbie
866 pkg_debbie_name = debbie
867 pkg_debbie_description = .DEB Built In Erlang
868 pkg_debbie_homepage = https://github.com/crownedgrouse/debbie
869 pkg_debbie_fetch = git
870 pkg_debbie_repo = https://github.com/crownedgrouse/debbie
871 pkg_debbie_commit = master
872
873 PACKAGES += decimal
874 pkg_decimal_name = decimal
875 pkg_decimal_description = An Erlang decimal arithmetic library
876 pkg_decimal_homepage = https://github.com/tim/erlang-decimal
877 pkg_decimal_fetch = git
878 pkg_decimal_repo = https://github.com/tim/erlang-decimal
879 pkg_decimal_commit = master
880
881 PACKAGES += detergent
882 pkg_detergent_name = detergent
883 pkg_detergent_description = An emulsifying Erlang SOAP library
884 pkg_detergent_homepage = https://github.com/devinus/detergent
885 pkg_detergent_fetch = git
886 pkg_detergent_repo = https://github.com/devinus/detergent
887 pkg_detergent_commit = master
888
889 PACKAGES += detest
890 pkg_detest_name = detest
891 pkg_detest_description = Tool for running tests on a cluster of erlang nodes
892 pkg_detest_homepage = https://github.com/biokoda/detest
893 pkg_detest_fetch = git
894 pkg_detest_repo = https://github.com/biokoda/detest
895 pkg_detest_commit = master
896
897 PACKAGES += dh_date
898 pkg_dh_date_name = dh_date
899 pkg_dh_date_description = Date formatting / parsing library for erlang
900 pkg_dh_date_homepage = https://github.com/daleharvey/dh_date
901 pkg_dh_date_fetch = git
902 pkg_dh_date_repo = https://github.com/daleharvey/dh_date
903 pkg_dh_date_commit = master
904
905 PACKAGES += dirbusterl
906 pkg_dirbusterl_name = dirbusterl
907 pkg_dirbusterl_description = DirBuster successor in Erlang
908 pkg_dirbusterl_homepage = https://github.com/silentsignal/DirBustErl
909 pkg_dirbusterl_fetch = git
910 pkg_dirbusterl_repo = https://github.com/silentsignal/DirBustErl
911 pkg_dirbusterl_commit = master
912
913 PACKAGES += dispcount
914 pkg_dispcount_name = dispcount
915 pkg_dispcount_description = Erlang task dispatcher based on ETS counters.
916 pkg_dispcount_homepage = https://github.com/ferd/dispcount
917 pkg_dispcount_fetch = git
918 pkg_dispcount_repo = https://github.com/ferd/dispcount
919 pkg_dispcount_commit = master
920
921 PACKAGES += dlhttpc
922 pkg_dlhttpc_name = dlhttpc
923 pkg_dlhttpc_description = dispcount-based lhttpc fork for massive amounts of requests to limited endpoints
924 pkg_dlhttpc_homepage = https://github.com/ferd/dlhttpc
925 pkg_dlhttpc_fetch = git
926 pkg_dlhttpc_repo = https://github.com/ferd/dlhttpc
927 pkg_dlhttpc_commit = master
928
929 PACKAGES += dns
930 pkg_dns_name = dns
931 pkg_dns_description = Erlang DNS library
932 pkg_dns_homepage = https://github.com/aetrion/dns_erlang
933 pkg_dns_fetch = git
934 pkg_dns_repo = https://github.com/aetrion/dns_erlang
935 pkg_dns_commit = master
936
937 PACKAGES += dnssd
938 pkg_dnssd_name = dnssd
939 pkg_dnssd_description = Erlang interface to Apple's Bonjour D NS Service Discovery implementation
940 pkg_dnssd_homepage = https://github.com/benoitc/dnssd_erlang
941 pkg_dnssd_fetch = git
942 pkg_dnssd_repo = https://github.com/benoitc/dnssd_erlang
943 pkg_dnssd_commit = master
944
945 PACKAGES += dtl
946 pkg_dtl_name = dtl
947 pkg_dtl_description = Django Template Language: A full-featured port of the Django template engine to Erlang.
948 pkg_dtl_homepage = https://github.com/oinksoft/dtl
949 pkg_dtl_fetch = git
950 pkg_dtl_repo = https://github.com/oinksoft/dtl
951 pkg_dtl_commit = master
952
953 PACKAGES += dynamic_compile
954 pkg_dynamic_compile_name = dynamic_compile
955 pkg_dynamic_compile_description = compile and load erlang modules from string input
956 pkg_dynamic_compile_homepage = https://github.com/jkvor/dynamic_compile
957 pkg_dynamic_compile_fetch = git
958 pkg_dynamic_compile_repo = https://github.com/jkvor/dynamic_compile
959 pkg_dynamic_compile_commit = master
960
961 PACKAGES += e2
962 pkg_e2_name = e2
963 pkg_e2_description = Library to simply writing correct OTP applications.
964 pkg_e2_homepage = http://e2project.org
965 pkg_e2_fetch = git
966 pkg_e2_repo = https://github.com/gar1t/e2
967 pkg_e2_commit = master
968
969 PACKAGES += eamf
970 pkg_eamf_name = eamf
971 pkg_eamf_description = eAMF provides Action Message Format (AMF) support for Erlang
972 pkg_eamf_homepage = https://github.com/mrinalwadhwa/eamf
973 pkg_eamf_fetch = git
974 pkg_eamf_repo = https://github.com/mrinalwadhwa/eamf
975 pkg_eamf_commit = master
976
977 PACKAGES += eavro
978 pkg_eavro_name = eavro
979 pkg_eavro_description = Apache Avro encoder/decoder
980 pkg_eavro_homepage = https://github.com/SIfoxDevTeam/eavro
981 pkg_eavro_fetch = git
982 pkg_eavro_repo = https://github.com/SIfoxDevTeam/eavro
983 pkg_eavro_commit = master
984
985 PACKAGES += ecapnp
986 pkg_ecapnp_name = ecapnp
987 pkg_ecapnp_description = Cap'n Proto library for Erlang
988 pkg_ecapnp_homepage = https://github.com/kaos/ecapnp
989 pkg_ecapnp_fetch = git
990 pkg_ecapnp_repo = https://github.com/kaos/ecapnp
991 pkg_ecapnp_commit = master
992
993 PACKAGES += econfig
994 pkg_econfig_name = econfig
995 pkg_econfig_description = simple Erlang config handler using INI files
996 pkg_econfig_homepage = https://github.com/benoitc/econfig
997 pkg_econfig_fetch = git
998 pkg_econfig_repo = https://github.com/benoitc/econfig
999 pkg_econfig_commit = master
1000
1001 PACKAGES += edate
1002 pkg_edate_name = edate
1003 pkg_edate_description = date manipulation library for erlang
1004 pkg_edate_homepage = https://github.com/dweldon/edate
1005 pkg_edate_fetch = git
1006 pkg_edate_repo = https://github.com/dweldon/edate
1007 pkg_edate_commit = master
1008
1009 PACKAGES += edgar
1010 pkg_edgar_name = edgar
1011 pkg_edgar_description = Erlang Does GNU AR
1012 pkg_edgar_homepage = https://github.com/crownedgrouse/edgar
1013 pkg_edgar_fetch = git
1014 pkg_edgar_repo = https://github.com/crownedgrouse/edgar
1015 pkg_edgar_commit = master
1016
1017 PACKAGES += edis
1018 pkg_edis_name = edis
1019 pkg_edis_description = An Erlang implementation of Redis KV Store
1020 pkg_edis_homepage = http://inaka.github.com/edis/
1021 pkg_edis_fetch = git
1022 pkg_edis_repo = https://github.com/inaka/edis
1023 pkg_edis_commit = master
1024
1025 PACKAGES += edns
1026 pkg_edns_name = edns
1027 pkg_edns_description = Erlang/OTP DNS server
1028 pkg_edns_homepage = https://github.com/hcvst/erlang-dns
1029 pkg_edns_fetch = git
1030 pkg_edns_repo = https://github.com/hcvst/erlang-dns
1031 pkg_edns_commit = master
1032
1033 PACKAGES += edown
1034 pkg_edown_name = edown
1035 pkg_edown_description = EDoc extension for generating Github-flavored Markdown
1036 pkg_edown_homepage = https://github.com/uwiger/edown
1037 pkg_edown_fetch = git
1038 pkg_edown_repo = https://github.com/uwiger/edown
1039 pkg_edown_commit = master
1040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
1049 PACKAGES += eep_app
1050 pkg_eep_app_name = eep_app
1051 pkg_eep_app_description = Embedded Event Processing
1052 pkg_eep_app_homepage = https://github.com/darach/eep-erl
1053 pkg_eep_app_fetch = git
1054 pkg_eep_app_repo = https://github.com/darach/eep-erl
1055 pkg_eep_app_commit = master
1056
1057 PACKAGES += efene
1058 pkg_efene_name = efene
1059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
1060 pkg_efene_homepage = https://github.com/efene/efene
1061 pkg_efene_fetch = git
1062 pkg_efene_repo = https://github.com/efene/efene
1063 pkg_efene_commit = master
1064
1065 PACKAGES += egeoip
1066 pkg_egeoip_name = egeoip
1067 pkg_egeoip_description = Erlang IP Geolocation module, currently supporting the MaxMind GeoLite City Database.
1068 pkg_egeoip_homepage = https://github.com/mochi/egeoip
1069 pkg_egeoip_fetch = git
1070 pkg_egeoip_repo = https://github.com/mochi/egeoip
1071 pkg_egeoip_commit = master
1072
1073 PACKAGES += ehsa
1074 pkg_ehsa_name = ehsa
1075 pkg_ehsa_description = Erlang HTTP server basic and digest authentication modules
1076 pkg_ehsa_homepage = https://bitbucket.org/a12n/ehsa
1077 pkg_ehsa_fetch = hg
1078 pkg_ehsa_repo = https://bitbucket.org/a12n/ehsa
1079 pkg_ehsa_commit = default
1080
1081 PACKAGES += ej
1082 pkg_ej_name = ej
1083 pkg_ej_description = Helper module for working with Erlang terms representing JSON
1084 pkg_ej_homepage = https://github.com/seth/ej
1085 pkg_ej_fetch = git
1086 pkg_ej_repo = https://github.com/seth/ej
1087 pkg_ej_commit = master
1088
1089 PACKAGES += ejabberd
1090 pkg_ejabberd_name = ejabberd
1091 pkg_ejabberd_description = Robust, ubiquitous and massively scalable Jabber / XMPP Instant Messaging platform
1092 pkg_ejabberd_homepage = https://github.com/processone/ejabberd
1093 pkg_ejabberd_fetch = git
1094 pkg_ejabberd_repo = https://github.com/processone/ejabberd
1095 pkg_ejabberd_commit = master
1096
1097 PACKAGES += ejwt
1098 pkg_ejwt_name = ejwt
1099 pkg_ejwt_description = erlang library for JSON Web Token
1100 pkg_ejwt_homepage = https://github.com/artefactop/ejwt
1101 pkg_ejwt_fetch = git
1102 pkg_ejwt_repo = https://github.com/artefactop/ejwt
1103 pkg_ejwt_commit = master
1104
1105 PACKAGES += ekaf
1106 pkg_ekaf_name = ekaf
1107 pkg_ekaf_description = A minimal, high-performance Kafka client in Erlang.
1108 pkg_ekaf_homepage = https://github.com/helpshift/ekaf
1109 pkg_ekaf_fetch = git
1110 pkg_ekaf_repo = https://github.com/helpshift/ekaf
1111 pkg_ekaf_commit = master
1112
1113 PACKAGES += elarm
1114 pkg_elarm_name = elarm
1115 pkg_elarm_description = Alarm Manager for Erlang.
1116 pkg_elarm_homepage = https://github.com/esl/elarm
1117 pkg_elarm_fetch = git
1118 pkg_elarm_repo = https://github.com/esl/elarm
1119 pkg_elarm_commit = master
1120
1121 PACKAGES += eleveldb
1122 pkg_eleveldb_name = eleveldb
1123 pkg_eleveldb_description = Erlang LevelDB API
1124 pkg_eleveldb_homepage = https://github.com/basho/eleveldb
1125 pkg_eleveldb_fetch = git
1126 pkg_eleveldb_repo = https://github.com/basho/eleveldb
1127 pkg_eleveldb_commit = master
1128
1129 PACKAGES += elli
1130 pkg_elli_name = elli
1131 pkg_elli_description = Simple, robust and performant Erlang web server
1132 pkg_elli_homepage = https://github.com/knutin/elli
1133 pkg_elli_fetch = git
1134 pkg_elli_repo = https://github.com/knutin/elli
1135 pkg_elli_commit = master
1136
1137 PACKAGES += elvis
1138 pkg_elvis_name = elvis
1139 pkg_elvis_description = Erlang Style Reviewer
1140 pkg_elvis_homepage = https://github.com/inaka/elvis
1141 pkg_elvis_fetch = git
1142 pkg_elvis_repo = https://github.com/inaka/elvis
1143 pkg_elvis_commit = master
1144
1145 PACKAGES += emagick
1146 pkg_emagick_name = emagick
1147 pkg_emagick_description = Wrapper for Graphics/ImageMagick command line tool.
1148 pkg_emagick_homepage = https://github.com/kivra/emagick
1149 pkg_emagick_fetch = git
1150 pkg_emagick_repo = https://github.com/kivra/emagick
1151 pkg_emagick_commit = master
1152
1153 PACKAGES += emysql
1154 pkg_emysql_name = emysql
1155 pkg_emysql_description = Stable, pure Erlang MySQL driver.
1156 pkg_emysql_homepage = https://github.com/Eonblast/Emysql
1157 pkg_emysql_fetch = git
1158 pkg_emysql_repo = https://github.com/Eonblast/Emysql
1159 pkg_emysql_commit = master
1160
1161 PACKAGES += enm
1162 pkg_enm_name = enm
1163 pkg_enm_description = Erlang driver for nanomsg
1164 pkg_enm_homepage = https://github.com/basho/enm
1165 pkg_enm_fetch = git
1166 pkg_enm_repo = https://github.com/basho/enm
1167 pkg_enm_commit = master
1168
1169 PACKAGES += entop
1170 pkg_entop_name = entop
1171 pkg_entop_description = A top-like tool for monitoring an Erlang node
1172 pkg_entop_homepage = https://github.com/mazenharake/entop
1173 pkg_entop_fetch = git
1174 pkg_entop_repo = https://github.com/mazenharake/entop
1175 pkg_entop_commit = master
1176
1177 PACKAGES += epcap
1178 pkg_epcap_name = epcap
1179 pkg_epcap_description = Erlang packet capture interface using pcap
1180 pkg_epcap_homepage = https://github.com/msantos/epcap
1181 pkg_epcap_fetch = git
1182 pkg_epcap_repo = https://github.com/msantos/epcap
1183 pkg_epcap_commit = master
1184
1185 PACKAGES += eper
1186 pkg_eper_name = eper
1187 pkg_eper_description = Erlang performance and debugging tools.
1188 pkg_eper_homepage = https://github.com/massemanet/eper
1189 pkg_eper_fetch = git
1190 pkg_eper_repo = https://github.com/massemanet/eper
1191 pkg_eper_commit = master
1192
1193 PACKAGES += epgsql
1194 pkg_epgsql_name = epgsql
1195 pkg_epgsql_description = Erlang PostgreSQL client library.
1196 pkg_epgsql_homepage = https://github.com/epgsql/epgsql
1197 pkg_epgsql_fetch = git
1198 pkg_epgsql_repo = https://github.com/epgsql/epgsql
1199 pkg_epgsql_commit = master
1200
1201 PACKAGES += episcina
1202 pkg_episcina_name = episcina
1203 pkg_episcina_description = A simple non intrusive resource pool for connections
1204 pkg_episcina_homepage = https://github.com/erlware/episcina
1205 pkg_episcina_fetch = git
1206 pkg_episcina_repo = https://github.com/erlware/episcina
1207 pkg_episcina_commit = master
1208
1209 PACKAGES += eplot
1210 pkg_eplot_name = eplot
1211 pkg_eplot_description = A plot engine written in erlang.
1212 pkg_eplot_homepage = https://github.com/psyeugenic/eplot
1213 pkg_eplot_fetch = git
1214 pkg_eplot_repo = https://github.com/psyeugenic/eplot
1215 pkg_eplot_commit = master
1216
1217 PACKAGES += epocxy
1218 pkg_epocxy_name = epocxy
1219 pkg_epocxy_description = Erlang Patterns of Concurrency
1220 pkg_epocxy_homepage = https://github.com/duomark/epocxy
1221 pkg_epocxy_fetch = git
1222 pkg_epocxy_repo = https://github.com/duomark/epocxy
1223 pkg_epocxy_commit = master
1224
1225 PACKAGES += epubnub
1226 pkg_epubnub_name = epubnub
1227 pkg_epubnub_description = Erlang PubNub API
1228 pkg_epubnub_homepage = https://github.com/tsloughter/epubnub
1229 pkg_epubnub_fetch = git
1230 pkg_epubnub_repo = https://github.com/tsloughter/epubnub
1231 pkg_epubnub_commit = master
1232
1233 PACKAGES += eqm
1234 pkg_eqm_name = eqm
1235 pkg_eqm_description = Erlang pub sub with supply-demand channels
1236 pkg_eqm_homepage = https://github.com/loucash/eqm
1237 pkg_eqm_fetch = git
1238 pkg_eqm_repo = https://github.com/loucash/eqm
1239 pkg_eqm_commit = master
1240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
1249 PACKAGES += eredis_pool
1250 pkg_eredis_pool_name = eredis_pool
1251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
1252 pkg_eredis_pool_homepage = https://github.com/hiroeorz/eredis_pool
1253 pkg_eredis_pool_fetch = git
1254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
1255 pkg_eredis_pool_commit = master
1256
1257 PACKAGES += erl_streams
1258 pkg_erl_streams_name = erl_streams
1259 pkg_erl_streams_description = Streams in Erlang
1260 pkg_erl_streams_homepage = https://github.com/epappas/erl_streams
1261 pkg_erl_streams_fetch = git
1262 pkg_erl_streams_repo = https://github.com/epappas/erl_streams
1263 pkg_erl_streams_commit = master
1264
1265 PACKAGES += erlang_cep
1266 pkg_erlang_cep_name = erlang_cep
1267 pkg_erlang_cep_description = A basic CEP package written in erlang
1268 pkg_erlang_cep_homepage = https://github.com/danmacklin/erlang_cep
1269 pkg_erlang_cep_fetch = git
1270 pkg_erlang_cep_repo = https://github.com/danmacklin/erlang_cep
1271 pkg_erlang_cep_commit = master
1272
1273 PACKAGES += erlang_js
1274 pkg_erlang_js_name = erlang_js
1275 pkg_erlang_js_description = A linked-in driver for Erlang to Mozilla's Spidermonkey Javascript runtime.
1276 pkg_erlang_js_homepage = https://github.com/basho/erlang_js
1277 pkg_erlang_js_fetch = git
1278 pkg_erlang_js_repo = https://github.com/basho/erlang_js
1279 pkg_erlang_js_commit = master
1280
1281 PACKAGES += erlang_localtime
1282 pkg_erlang_localtime_name = erlang_localtime
1283 pkg_erlang_localtime_description = Erlang library for conversion from one local time to another
1284 pkg_erlang_localtime_homepage = https://github.com/dmitryme/erlang_localtime
1285 pkg_erlang_localtime_fetch = git
1286 pkg_erlang_localtime_repo = https://github.com/dmitryme/erlang_localtime
1287 pkg_erlang_localtime_commit = master
1288
1289 PACKAGES += erlang_smtp
1290 pkg_erlang_smtp_name = erlang_smtp
1291 pkg_erlang_smtp_description = Erlang SMTP and POP3 server code.
1292 pkg_erlang_smtp_homepage = https://github.com/tonyg/erlang-smtp
1293 pkg_erlang_smtp_fetch = git
1294 pkg_erlang_smtp_repo = https://github.com/tonyg/erlang-smtp
1295 pkg_erlang_smtp_commit = master
1296
1297 PACKAGES += erlang_term
1298 pkg_erlang_term_name = erlang_term
1299 pkg_erlang_term_description = Erlang Term Info
1300 pkg_erlang_term_homepage = https://github.com/okeuday/erlang_term
1301 pkg_erlang_term_fetch = git
1302 pkg_erlang_term_repo = https://github.com/okeuday/erlang_term
1303 pkg_erlang_term_commit = master
1304
1305 PACKAGES += erlastic_search
1306 pkg_erlastic_search_name = erlastic_search
1307 pkg_erlastic_search_description = An Erlang app for communicating with Elastic Search's rest interface.
1308 pkg_erlastic_search_homepage = https://github.com/tsloughter/erlastic_search
1309 pkg_erlastic_search_fetch = git
1310 pkg_erlastic_search_repo = https://github.com/tsloughter/erlastic_search
1311 pkg_erlastic_search_commit = master
1312
1313 PACKAGES += erlasticsearch
1314 pkg_erlasticsearch_name = erlasticsearch
1315 pkg_erlasticsearch_description = Erlang thrift interface to elastic_search
1316 pkg_erlasticsearch_homepage = https://github.com/dieswaytoofast/erlasticsearch
1317 pkg_erlasticsearch_fetch = git
1318 pkg_erlasticsearch_repo = https://github.com/dieswaytoofast/erlasticsearch
1319 pkg_erlasticsearch_commit = master
1320
1321 PACKAGES += erlbrake
1322 pkg_erlbrake_name = erlbrake
1323 pkg_erlbrake_description = Erlang Airbrake notification client
1324 pkg_erlbrake_homepage = https://github.com/kenpratt/erlbrake
1325 pkg_erlbrake_fetch = git
1326 pkg_erlbrake_repo = https://github.com/kenpratt/erlbrake
1327 pkg_erlbrake_commit = master
1328
1329 PACKAGES += erlcloud
1330 pkg_erlcloud_name = erlcloud
1331 pkg_erlcloud_description = Cloud Computing library for erlang (Amazon EC2, S3, SQS, SimpleDB, Mechanical Turk, ELB)
1332 pkg_erlcloud_homepage = https://github.com/gleber/erlcloud
1333 pkg_erlcloud_fetch = git
1334 pkg_erlcloud_repo = https://github.com/gleber/erlcloud
1335 pkg_erlcloud_commit = master
1336
1337 PACKAGES += erlcron
1338 pkg_erlcron_name = erlcron
1339 pkg_erlcron_description = Erlang cronish system
1340 pkg_erlcron_homepage = https://github.com/erlware/erlcron
1341 pkg_erlcron_fetch = git
1342 pkg_erlcron_repo = https://github.com/erlware/erlcron
1343 pkg_erlcron_commit = master
1344
1345 PACKAGES += erldb
1346 pkg_erldb_name = erldb
1347 pkg_erldb_description = ORM (Object-relational mapping) application implemented in Erlang
1348 pkg_erldb_homepage = http://erldb.org
1349 pkg_erldb_fetch = git
1350 pkg_erldb_repo = https://github.com/erldb/erldb
1351 pkg_erldb_commit = master
1352
1353 PACKAGES += erldis
1354 pkg_erldis_name = erldis
1355 pkg_erldis_description = redis erlang client library
1356 pkg_erldis_homepage = https://github.com/cstar/erldis
1357 pkg_erldis_fetch = git
1358 pkg_erldis_repo = https://github.com/cstar/erldis
1359 pkg_erldis_commit = master
1360
1361 PACKAGES += erldns
1362 pkg_erldns_name = erldns
1363 pkg_erldns_description = DNS server, in erlang.
1364 pkg_erldns_homepage = https://github.com/aetrion/erl-dns
1365 pkg_erldns_fetch = git
1366 pkg_erldns_repo = https://github.com/aetrion/erl-dns
1367 pkg_erldns_commit = master
1368
1369 PACKAGES += erldocker
1370 pkg_erldocker_name = erldocker
1371 pkg_erldocker_description = Docker Remote API client for Erlang
1372 pkg_erldocker_homepage = https://github.com/proger/erldocker
1373 pkg_erldocker_fetch = git
1374 pkg_erldocker_repo = https://github.com/proger/erldocker
1375 pkg_erldocker_commit = master
1376
1377 PACKAGES += erlfsmon
1378 pkg_erlfsmon_name = erlfsmon
1379 pkg_erlfsmon_description = Erlang filesystem event watcher for Linux and OSX
1380 pkg_erlfsmon_homepage = https://github.com/proger/erlfsmon
1381 pkg_erlfsmon_fetch = git
1382 pkg_erlfsmon_repo = https://github.com/proger/erlfsmon
1383 pkg_erlfsmon_commit = master
1384
1385 PACKAGES += erlgit
1386 pkg_erlgit_name = erlgit
1387 pkg_erlgit_description = Erlang convenience wrapper around git executable
1388 pkg_erlgit_homepage = https://github.com/gleber/erlgit
1389 pkg_erlgit_fetch = git
1390 pkg_erlgit_repo = https://github.com/gleber/erlgit
1391 pkg_erlgit_commit = master
1392
1393 PACKAGES += erlguten
1394 pkg_erlguten_name = erlguten
1395 pkg_erlguten_description = ErlGuten is a system for high-quality typesetting, written purely in Erlang.
1396 pkg_erlguten_homepage = https://github.com/richcarl/erlguten
1397 pkg_erlguten_fetch = git
1398 pkg_erlguten_repo = https://github.com/richcarl/erlguten
1399 pkg_erlguten_commit = master
1400
1401 PACKAGES += erlmc
1402 pkg_erlmc_name = erlmc
1403 pkg_erlmc_description = Erlang memcached binary protocol client
1404 pkg_erlmc_homepage = https://github.com/jkvor/erlmc
1405 pkg_erlmc_fetch = git
1406 pkg_erlmc_repo = https://github.com/jkvor/erlmc
1407 pkg_erlmc_commit = master
1408
1409 PACKAGES += erlmongo
1410 pkg_erlmongo_name = erlmongo
1411 pkg_erlmongo_description = Record based Erlang driver for MongoDB with gridfs support
1412 pkg_erlmongo_homepage = https://github.com/SergejJurecko/erlmongo
1413 pkg_erlmongo_fetch = git
1414 pkg_erlmongo_repo = https://github.com/SergejJurecko/erlmongo
1415 pkg_erlmongo_commit = master
1416
1417 PACKAGES += erlog
1418 pkg_erlog_name = erlog
1419 pkg_erlog_description = Prolog interpreter in and for Erlang
1420 pkg_erlog_homepage = https://github.com/rvirding/erlog
1421 pkg_erlog_fetch = git
1422 pkg_erlog_repo = https://github.com/rvirding/erlog
1423 pkg_erlog_commit = master
1424
1425 PACKAGES += erlpass
1426 pkg_erlpass_name = erlpass
1427 pkg_erlpass_description = A library to handle password hashing and changing in a safe manner, independent from any kind of storage whatsoever.
1428 pkg_erlpass_homepage = https://github.com/ferd/erlpass
1429 pkg_erlpass_fetch = git
1430 pkg_erlpass_repo = https://github.com/ferd/erlpass
1431 pkg_erlpass_commit = master
1432
1433 PACKAGES += erlport
1434 pkg_erlport_name = erlport
1435 pkg_erlport_description = ErlPort - connect Erlang to other languages
1436 pkg_erlport_homepage = https://github.com/hdima/erlport
1437 pkg_erlport_fetch = git
1438 pkg_erlport_repo = https://github.com/hdima/erlport
1439 pkg_erlport_commit = master
1440
1441 PACKAGES += erlsh
1442 pkg_erlsh_name = erlsh
1443 pkg_erlsh_description = Erlang shell tools
1444 pkg_erlsh_homepage = https://github.com/proger/erlsh
1445 pkg_erlsh_fetch = git
1446 pkg_erlsh_repo = https://github.com/proger/erlsh
1447 pkg_erlsh_commit = master
1448
1449 PACKAGES += erlsha2
1450 pkg_erlsha2_name = erlsha2
1451 pkg_erlsha2_description = SHA-224, SHA-256, SHA-384, SHA-512 implemented in Erlang NIFs.
1452 pkg_erlsha2_homepage = https://github.com/vinoski/erlsha2
1453 pkg_erlsha2_fetch = git
1454 pkg_erlsha2_repo = https://github.com/vinoski/erlsha2
1455 pkg_erlsha2_commit = master
1456
1457 PACKAGES += erlsom
1458 pkg_erlsom_name = erlsom
1459 pkg_erlsom_description = XML parser for Erlang
1460 pkg_erlsom_homepage = https://github.com/willemdj/erlsom
1461 pkg_erlsom_fetch = git
1462 pkg_erlsom_repo = https://github.com/willemdj/erlsom
1463 pkg_erlsom_commit = master
1464
1465 PACKAGES += erlubi
1466 pkg_erlubi_name = erlubi
1467 pkg_erlubi_description = Ubigraph Erlang Client (and Process Visualizer)
1468 pkg_erlubi_homepage = https://github.com/krestenkrab/erlubi
1469 pkg_erlubi_fetch = git
1470 pkg_erlubi_repo = https://github.com/krestenkrab/erlubi
1471 pkg_erlubi_commit = master
1472
1473 PACKAGES += erlvolt
1474 pkg_erlvolt_name = erlvolt
1475 pkg_erlvolt_description = VoltDB Erlang Client Driver
1476 pkg_erlvolt_homepage = https://github.com/VoltDB/voltdb-client-erlang
1477 pkg_erlvolt_fetch = git
1478 pkg_erlvolt_repo = https://github.com/VoltDB/voltdb-client-erlang
1479 pkg_erlvolt_commit = master
1480
1481 PACKAGES += erlware_commons
1482 pkg_erlware_commons_name = erlware_commons
1483 pkg_erlware_commons_description = Erlware Commons is an Erlware project focused on all aspects of reusable Erlang components.
1484 pkg_erlware_commons_homepage = https://github.com/erlware/erlware_commons
1485 pkg_erlware_commons_fetch = git
1486 pkg_erlware_commons_repo = https://github.com/erlware/erlware_commons
1487 pkg_erlware_commons_commit = master
1488
1489 PACKAGES += erlydtl
1490 pkg_erlydtl_name = erlydtl
1491 pkg_erlydtl_description = Django Template Language for Erlang.
1492 pkg_erlydtl_homepage = https://github.com/erlydtl/erlydtl
1493 pkg_erlydtl_fetch = git
1494 pkg_erlydtl_repo = https://github.com/erlydtl/erlydtl
1495 pkg_erlydtl_commit = master
1496
1497 PACKAGES += errd
1498 pkg_errd_name = errd
1499 pkg_errd_description = Erlang RRDTool library
1500 pkg_errd_homepage = https://github.com/archaelus/errd
1501 pkg_errd_fetch = git
1502 pkg_errd_repo = https://github.com/archaelus/errd
1503 pkg_errd_commit = master
1504
1505 PACKAGES += erserve
1506 pkg_erserve_name = erserve
1507 pkg_erserve_description = Erlang/Rserve communication interface
1508 pkg_erserve_homepage = https://github.com/del/erserve
1509 pkg_erserve_fetch = git
1510 pkg_erserve_repo = https://github.com/del/erserve
1511 pkg_erserve_commit = master
1512
1513 PACKAGES += erwa
1514 pkg_erwa_name = erwa
1515 pkg_erwa_description = A WAMP router and client written in Erlang.
1516 pkg_erwa_homepage = https://github.com/bwegh/erwa
1517 pkg_erwa_fetch = git
1518 pkg_erwa_repo = https://github.com/bwegh/erwa
1519 pkg_erwa_commit = master
1520
1521 PACKAGES += espec
1522 pkg_espec_name = espec
1523 pkg_espec_description = ESpec: Behaviour driven development framework for Erlang
1524 pkg_espec_homepage = https://github.com/lucaspiller/espec
1525 pkg_espec_fetch = git
1526 pkg_espec_repo = https://github.com/lucaspiller/espec
1527 pkg_espec_commit = master
1528
1529 PACKAGES += estatsd
1530 pkg_estatsd_name = estatsd
1531 pkg_estatsd_description = Erlang stats aggregation app that periodically flushes data to graphite
1532 pkg_estatsd_homepage = https://github.com/RJ/estatsd
1533 pkg_estatsd_fetch = git
1534 pkg_estatsd_repo = https://github.com/RJ/estatsd
1535 pkg_estatsd_commit = master
1536
1537 PACKAGES += etap
1538 pkg_etap_name = etap
1539 pkg_etap_description = etap is a simple erlang testing library that provides TAP compliant output.
1540 pkg_etap_homepage = https://github.com/ngerakines/etap
1541 pkg_etap_fetch = git
1542 pkg_etap_repo = https://github.com/ngerakines/etap
1543 pkg_etap_commit = master
1544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
1553 PACKAGES += etest_http
1554 pkg_etest_http_name = etest_http
1555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
1556 pkg_etest_http_homepage = https://github.com/wooga/etest_http
1557 pkg_etest_http_fetch = git
1558 pkg_etest_http_repo = https://github.com/wooga/etest_http
1559 pkg_etest_http_commit = master
1560
1561 PACKAGES += etoml
1562 pkg_etoml_name = etoml
1563 pkg_etoml_description = TOML language erlang parser
1564 pkg_etoml_homepage = https://github.com/kalta/etoml
1565 pkg_etoml_fetch = git
1566 pkg_etoml_repo = https://github.com/kalta/etoml
1567 pkg_etoml_commit = master
1568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
1577 PACKAGES += eunit_formatters
1578 pkg_eunit_formatters_name = eunit_formatters
1579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
1580 pkg_eunit_formatters_homepage = https://github.com/seancribbs/eunit_formatters
1581 pkg_eunit_formatters_fetch = git
1582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
1583 pkg_eunit_formatters_commit = master
1584
1585 PACKAGES += euthanasia
1586 pkg_euthanasia_name = euthanasia
1587 pkg_euthanasia_description = Merciful killer for your Erlang processes
1588 pkg_euthanasia_homepage = https://github.com/doubleyou/euthanasia
1589 pkg_euthanasia_fetch = git
1590 pkg_euthanasia_repo = https://github.com/doubleyou/euthanasia
1591 pkg_euthanasia_commit = master
1592
1593 PACKAGES += evum
1594 pkg_evum_name = evum
1595 pkg_evum_description = Spawn Linux VMs as Erlang processes in the Erlang VM
1596 pkg_evum_homepage = https://github.com/msantos/evum
1597 pkg_evum_fetch = git
1598 pkg_evum_repo = https://github.com/msantos/evum
1599 pkg_evum_commit = master
1600
1601 PACKAGES += exec
1602 pkg_exec_name = erlexec
1603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
1604 pkg_exec_homepage = http://saleyn.github.com/erlexec
1605 pkg_exec_fetch = git
1606 pkg_exec_repo = https://github.com/saleyn/erlexec
1607 pkg_exec_commit = master
1608
1609 PACKAGES += exml
1610 pkg_exml_name = exml
1611 pkg_exml_description = XML parsing library in Erlang
1612 pkg_exml_homepage = https://github.com/paulgray/exml
1613 pkg_exml_fetch = git
1614 pkg_exml_repo = https://github.com/paulgray/exml
1615 pkg_exml_commit = master
1616
1617 PACKAGES += exometer
1618 pkg_exometer_name = exometer
1619 pkg_exometer_description = Basic measurement objects and probe behavior
1620 pkg_exometer_homepage = https://github.com/Feuerlabs/exometer
1621 pkg_exometer_fetch = git
1622 pkg_exometer_repo = https://github.com/Feuerlabs/exometer
1623 pkg_exometer_commit = master
1624
1625 PACKAGES += exs1024
1626 pkg_exs1024_name = exs1024
1627 pkg_exs1024_description = Xorshift1024star pseudo random number generator for Erlang.
1628 pkg_exs1024_homepage = https://github.com/jj1bdx/exs1024
1629 pkg_exs1024_fetch = git
1630 pkg_exs1024_repo = https://github.com/jj1bdx/exs1024
1631 pkg_exs1024_commit = master
1632
1633 PACKAGES += exs64
1634 pkg_exs64_name = exs64
1635 pkg_exs64_description = Xorshift64star pseudo random number generator for Erlang.
1636 pkg_exs64_homepage = https://github.com/jj1bdx/exs64
1637 pkg_exs64_fetch = git
1638 pkg_exs64_repo = https://github.com/jj1bdx/exs64
1639 pkg_exs64_commit = master
1640
1641 PACKAGES += exsplus116
1642 pkg_exsplus116_name = exsplus116
1643 pkg_exsplus116_description = Xorshift116plus for Erlang
1644 pkg_exsplus116_homepage = https://github.com/jj1bdx/exsplus116
1645 pkg_exsplus116_fetch = git
1646 pkg_exsplus116_repo = https://github.com/jj1bdx/exsplus116
1647 pkg_exsplus116_commit = master
1648
1649 PACKAGES += exsplus128
1650 pkg_exsplus128_name = exsplus128
1651 pkg_exsplus128_description = Xorshift128plus pseudo random number generator for Erlang.
1652 pkg_exsplus128_homepage = https://github.com/jj1bdx/exsplus128
1653 pkg_exsplus128_fetch = git
1654 pkg_exsplus128_repo = https://github.com/jj1bdx/exsplus128
1655 pkg_exsplus128_commit = master
1656
1657 PACKAGES += ezmq
1658 pkg_ezmq_name = ezmq
1659 pkg_ezmq_description = zMQ implemented in Erlang
1660 pkg_ezmq_homepage = https://github.com/RoadRunnr/ezmq
1661 pkg_ezmq_fetch = git
1662 pkg_ezmq_repo = https://github.com/RoadRunnr/ezmq
1663 pkg_ezmq_commit = master
1664
1665 PACKAGES += ezmtp
1666 pkg_ezmtp_name = ezmtp
1667 pkg_ezmtp_description = ZMTP protocol in pure Erlang.
1668 pkg_ezmtp_homepage = https://github.com/a13x/ezmtp
1669 pkg_ezmtp_fetch = git
1670 pkg_ezmtp_repo = https://github.com/a13x/ezmtp
1671 pkg_ezmtp_commit = master
1672
1673 PACKAGES += fast_disk_log
1674 pkg_fast_disk_log_name = fast_disk_log
1675 pkg_fast_disk_log_description = Pool-based asynchronous Erlang disk logger
1676 pkg_fast_disk_log_homepage = https://github.com/lpgauth/fast_disk_log
1677 pkg_fast_disk_log_fetch = git
1678 pkg_fast_disk_log_repo = https://github.com/lpgauth/fast_disk_log
1679 pkg_fast_disk_log_commit = master
1680
1681 PACKAGES += feeder
1682 pkg_feeder_name = feeder
1683 pkg_feeder_description = Stream parse RSS and Atom formatted XML feeds.
1684 pkg_feeder_homepage = https://github.com/michaelnisi/feeder
1685 pkg_feeder_fetch = git
1686 pkg_feeder_repo = https://github.com/michaelnisi/feeder
1687 pkg_feeder_commit = master
1688
1689 PACKAGES += find_crate
1690 pkg_find_crate_name = find_crate
1691 pkg_find_crate_description = Find Rust libs and exes in Erlang application priv directory
1692 pkg_find_crate_homepage = https://github.com/goertzenator/find_crate
1693 pkg_find_crate_fetch = git
1694 pkg_find_crate_repo = https://github.com/goertzenator/find_crate
1695 pkg_find_crate_commit = master
1696
1697 PACKAGES += fix
1698 pkg_fix_name = fix
1699 pkg_fix_description = http://fixprotocol.org/ implementation.
1700 pkg_fix_homepage = https://github.com/maxlapshin/fix
1701 pkg_fix_fetch = git
1702 pkg_fix_repo = https://github.com/maxlapshin/fix
1703 pkg_fix_commit = master
1704
1705 PACKAGES += flower
1706 pkg_flower_name = flower
1707 pkg_flower_description = FlowER - a Erlang OpenFlow development platform
1708 pkg_flower_homepage = https://github.com/travelping/flower
1709 pkg_flower_fetch = git
1710 pkg_flower_repo = https://github.com/travelping/flower
1711 pkg_flower_commit = master
1712
1713 PACKAGES += fn
1714 pkg_fn_name = fn
1715 pkg_fn_description = Function utilities for Erlang
1716 pkg_fn_homepage = https://github.com/reiddraper/fn
1717 pkg_fn_fetch = git
1718 pkg_fn_repo = https://github.com/reiddraper/fn
1719 pkg_fn_commit = master
1720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
1729 PACKAGES += folsom_cowboy
1730 pkg_folsom_cowboy_name = folsom_cowboy
1731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
1732 pkg_folsom_cowboy_homepage = https://github.com/boundary/folsom_cowboy
1733 pkg_folsom_cowboy_fetch = git
1734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
1735 pkg_folsom_cowboy_commit = master
1736
1737 PACKAGES += folsomite
1738 pkg_folsomite_name = folsomite
1739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
1740 pkg_folsomite_homepage = https://github.com/campanja/folsomite
1741 pkg_folsomite_fetch = git
1742 pkg_folsomite_repo = https://github.com/campanja/folsomite
1743 pkg_folsomite_commit = master
1744
1745 PACKAGES += fs
1746 pkg_fs_name = fs
1747 pkg_fs_description = Erlang FileSystem Listener
1748 pkg_fs_homepage = https://github.com/synrc/fs
1749 pkg_fs_fetch = git
1750 pkg_fs_repo = https://github.com/synrc/fs
1751 pkg_fs_commit = master
1752
1753 PACKAGES += fuse
1754 pkg_fuse_name = fuse
1755 pkg_fuse_description = A Circuit Breaker for Erlang
1756 pkg_fuse_homepage = https://github.com/jlouis/fuse
1757 pkg_fuse_fetch = git
1758 pkg_fuse_repo = https://github.com/jlouis/fuse
1759 pkg_fuse_commit = master
1760
1761 PACKAGES += gcm
1762 pkg_gcm_name = gcm
1763 pkg_gcm_description = An Erlang application for Google Cloud Messaging
1764 pkg_gcm_homepage = https://github.com/pdincau/gcm-erlang
1765 pkg_gcm_fetch = git
1766 pkg_gcm_repo = https://github.com/pdincau/gcm-erlang
1767 pkg_gcm_commit = master
1768
1769 PACKAGES += gcprof
1770 pkg_gcprof_name = gcprof
1771 pkg_gcprof_description = Garbage Collection profiler for Erlang
1772 pkg_gcprof_homepage = https://github.com/knutin/gcprof
1773 pkg_gcprof_fetch = git
1774 pkg_gcprof_repo = https://github.com/knutin/gcprof
1775 pkg_gcprof_commit = master
1776
1777 PACKAGES += geas
1778 pkg_geas_name = geas
1779 pkg_geas_description = Guess Erlang Application Scattering
1780 pkg_geas_homepage = https://github.com/crownedgrouse/geas
1781 pkg_geas_fetch = git
1782 pkg_geas_repo = https://github.com/crownedgrouse/geas
1783 pkg_geas_commit = master
1784
1785 PACKAGES += geef
1786 pkg_geef_name = geef
1787 pkg_geef_description = Git NEEEEF (Erlang NIF)
1788 pkg_geef_homepage = https://github.com/carlosmn/geef
1789 pkg_geef_fetch = git
1790 pkg_geef_repo = https://github.com/carlosmn/geef
1791 pkg_geef_commit = master
1792
1793 PACKAGES += gen_coap
1794 pkg_gen_coap_name = gen_coap
1795 pkg_gen_coap_description = Generic Erlang CoAP Client/Server
1796 pkg_gen_coap_homepage = https://github.com/gotthardp/gen_coap
1797 pkg_gen_coap_fetch = git
1798 pkg_gen_coap_repo = https://github.com/gotthardp/gen_coap
1799 pkg_gen_coap_commit = master
1800
1801 PACKAGES += gen_cycle
1802 pkg_gen_cycle_name = gen_cycle
1803 pkg_gen_cycle_description = Simple, generic OTP behaviour for recurring tasks
1804 pkg_gen_cycle_homepage = https://github.com/aerosol/gen_cycle
1805 pkg_gen_cycle_fetch = git
1806 pkg_gen_cycle_repo = https://github.com/aerosol/gen_cycle
1807 pkg_gen_cycle_commit = develop
1808
1809 PACKAGES += gen_icmp
1810 pkg_gen_icmp_name = gen_icmp
1811 pkg_gen_icmp_description = Erlang interface to ICMP sockets
1812 pkg_gen_icmp_homepage = https://github.com/msantos/gen_icmp
1813 pkg_gen_icmp_fetch = git
1814 pkg_gen_icmp_repo = https://github.com/msantos/gen_icmp
1815 pkg_gen_icmp_commit = master
1816
1817 PACKAGES += gen_nb_server
1818 pkg_gen_nb_server_name = gen_nb_server
1819 pkg_gen_nb_server_description = OTP behavior for writing non-blocking servers
1820 pkg_gen_nb_server_homepage = https://github.com/kevsmith/gen_nb_server
1821 pkg_gen_nb_server_fetch = git
1822 pkg_gen_nb_server_repo = https://github.com/kevsmith/gen_nb_server
1823 pkg_gen_nb_server_commit = master
1824
1825 PACKAGES += gen_paxos
1826 pkg_gen_paxos_name = gen_paxos
1827 pkg_gen_paxos_description = An Erlang/OTP-style implementation of the PAXOS distributed consensus protocol
1828 pkg_gen_paxos_homepage = https://github.com/gburd/gen_paxos
1829 pkg_gen_paxos_fetch = git
1830 pkg_gen_paxos_repo = https://github.com/gburd/gen_paxos
1831 pkg_gen_paxos_commit = master
1832
1833 PACKAGES += gen_smtp
1834 pkg_gen_smtp_name = gen_smtp
1835 pkg_gen_smtp_description = A generic Erlang SMTP server and client that can be extended via callback modules
1836 pkg_gen_smtp_homepage = https://github.com/Vagabond/gen_smtp
1837 pkg_gen_smtp_fetch = git
1838 pkg_gen_smtp_repo = https://github.com/Vagabond/gen_smtp
1839 pkg_gen_smtp_commit = master
1840
1841 PACKAGES += gen_tracker
1842 pkg_gen_tracker_name = gen_tracker
1843 pkg_gen_tracker_description = supervisor with ets handling of children and their metadata
1844 pkg_gen_tracker_homepage = https://github.com/erlyvideo/gen_tracker
1845 pkg_gen_tracker_fetch = git
1846 pkg_gen_tracker_repo = https://github.com/erlyvideo/gen_tracker
1847 pkg_gen_tracker_commit = master
1848
1849 PACKAGES += gen_unix
1850 pkg_gen_unix_name = gen_unix
1851 pkg_gen_unix_description = Erlang Unix socket interface
1852 pkg_gen_unix_homepage = https://github.com/msantos/gen_unix
1853 pkg_gen_unix_fetch = git
1854 pkg_gen_unix_repo = https://github.com/msantos/gen_unix
1855 pkg_gen_unix_commit = master
1856
1857 PACKAGES += geode
1858 pkg_geode_name = geode
1859 pkg_geode_description = geohash/proximity lookup in pure, uncut erlang.
1860 pkg_geode_homepage = https://github.com/bradfordw/geode
1861 pkg_geode_fetch = git
1862 pkg_geode_repo = https://github.com/bradfordw/geode
1863 pkg_geode_commit = master
1864
1865 PACKAGES += getopt
1866 pkg_getopt_name = getopt
1867 pkg_getopt_description = Module to parse command line arguments using the GNU getopt syntax
1868 pkg_getopt_homepage = https://github.com/jcomellas/getopt
1869 pkg_getopt_fetch = git
1870 pkg_getopt_repo = https://github.com/jcomellas/getopt
1871 pkg_getopt_commit = master
1872
1873 PACKAGES += gettext
1874 pkg_gettext_name = gettext
1875 pkg_gettext_description = Erlang internationalization library.
1876 pkg_gettext_homepage = https://github.com/etnt/gettext
1877 pkg_gettext_fetch = git
1878 pkg_gettext_repo = https://github.com/etnt/gettext
1879 pkg_gettext_commit = master
1880
1881 PACKAGES += giallo
1882 pkg_giallo_name = giallo
1883 pkg_giallo_description = Small and flexible web framework on top of Cowboy
1884 pkg_giallo_homepage = https://github.com/kivra/giallo
1885 pkg_giallo_fetch = git
1886 pkg_giallo_repo = https://github.com/kivra/giallo
1887 pkg_giallo_commit = master
1888
1889 PACKAGES += gin
1890 pkg_gin_name = gin
1891 pkg_gin_description = The guards and for Erlang parse_transform
1892 pkg_gin_homepage = https://github.com/mad-cocktail/gin
1893 pkg_gin_fetch = git
1894 pkg_gin_repo = https://github.com/mad-cocktail/gin
1895 pkg_gin_commit = master
1896
1897 PACKAGES += gitty
1898 pkg_gitty_name = gitty
1899 pkg_gitty_description = Git access in erlang
1900 pkg_gitty_homepage = https://github.com/maxlapshin/gitty
1901 pkg_gitty_fetch = git
1902 pkg_gitty_repo = https://github.com/maxlapshin/gitty
1903 pkg_gitty_commit = master
1904
1905 PACKAGES += gold_fever
1906 pkg_gold_fever_name = gold_fever
1907 pkg_gold_fever_description = A Treasure Hunt for Erlangers
1908 pkg_gold_fever_homepage = https://github.com/inaka/gold_fever
1909 pkg_gold_fever_fetch = git
1910 pkg_gold_fever_repo = https://github.com/inaka/gold_fever
1911 pkg_gold_fever_commit = master
1912
1913 PACKAGES += gossiperl
1914 pkg_gossiperl_name = gossiperl
1915 pkg_gossiperl_description = Gossip middleware in Erlang
1916 pkg_gossiperl_homepage = http://gossiperl.com/
1917 pkg_gossiperl_fetch = git
1918 pkg_gossiperl_repo = https://github.com/gossiperl/gossiperl
1919 pkg_gossiperl_commit = master
1920
1921 PACKAGES += gpb
1922 pkg_gpb_name = gpb
1923 pkg_gpb_description = A Google Protobuf implementation for Erlang
1924 pkg_gpb_homepage = https://github.com/tomas-abrahamsson/gpb
1925 pkg_gpb_fetch = git
1926 pkg_gpb_repo = https://github.com/tomas-abrahamsson/gpb
1927 pkg_gpb_commit = master
1928
1929 PACKAGES += gproc
1930 pkg_gproc_name = gproc
1931 pkg_gproc_description = Extended process registry for Erlang
1932 pkg_gproc_homepage = https://github.com/uwiger/gproc
1933 pkg_gproc_fetch = git
1934 pkg_gproc_repo = https://github.com/uwiger/gproc
1935 pkg_gproc_commit = master
1936
1937 PACKAGES += grapherl
1938 pkg_grapherl_name = grapherl
1939 pkg_grapherl_description = Create graphs of Erlang systems and programs
1940 pkg_grapherl_homepage = https://github.com/eproxus/grapherl
1941 pkg_grapherl_fetch = git
1942 pkg_grapherl_repo = https://github.com/eproxus/grapherl
1943 pkg_grapherl_commit = master
1944
1945 PACKAGES += gun
1946 pkg_gun_name = gun
1947 pkg_gun_description = Asynchronous SPDY, HTTP and Websocket client written in Erlang.
1948 pkg_gun_homepage = http//ninenines.eu
1949 pkg_gun_fetch = git
1950 pkg_gun_repo = https://github.com/ninenines/gun
1951 pkg_gun_commit = master
1952
1953 PACKAGES += gut
1954 pkg_gut_name = gut
1955 pkg_gut_description = gut is a template printing, aka scaffolding, tool for Erlang. Like rails generate or yeoman
1956 pkg_gut_homepage = https://github.com/unbalancedparentheses/gut
1957 pkg_gut_fetch = git
1958 pkg_gut_repo = https://github.com/unbalancedparentheses/gut
1959 pkg_gut_commit = master
1960
1961 PACKAGES += hackney
1962 pkg_hackney_name = hackney
1963 pkg_hackney_description = simple HTTP client in Erlang
1964 pkg_hackney_homepage = https://github.com/benoitc/hackney
1965 pkg_hackney_fetch = git
1966 pkg_hackney_repo = https://github.com/benoitc/hackney
1967 pkg_hackney_commit = master
1968
1969 PACKAGES += hamcrest
1970 pkg_hamcrest_name = hamcrest
1971 pkg_hamcrest_description = Erlang port of Hamcrest
1972 pkg_hamcrest_homepage = https://github.com/hyperthunk/hamcrest-erlang
1973 pkg_hamcrest_fetch = git
1974 pkg_hamcrest_repo = https://github.com/hyperthunk/hamcrest-erlang
1975 pkg_hamcrest_commit = master
1976
1977 PACKAGES += hanoidb
1978 pkg_hanoidb_name = hanoidb
1979 pkg_hanoidb_description = Erlang LSM BTree Storage
1980 pkg_hanoidb_homepage = https://github.com/krestenkrab/hanoidb
1981 pkg_hanoidb_fetch = git
1982 pkg_hanoidb_repo = https://github.com/krestenkrab/hanoidb
1983 pkg_hanoidb_commit = master
1984
1985 PACKAGES += hottub
1986 pkg_hottub_name = hottub
1987 pkg_hottub_description = Permanent Erlang Worker Pool
1988 pkg_hottub_homepage = https://github.com/bfrog/hottub
1989 pkg_hottub_fetch = git
1990 pkg_hottub_repo = https://github.com/bfrog/hottub
1991 pkg_hottub_commit = master
1992
1993 PACKAGES += hpack
1994 pkg_hpack_name = hpack
1995 pkg_hpack_description = HPACK Implementation for Erlang
1996 pkg_hpack_homepage = https://github.com/joedevivo/hpack
1997 pkg_hpack_fetch = git
1998 pkg_hpack_repo = https://github.com/joedevivo/hpack
1999 pkg_hpack_commit = master
2000
2001 PACKAGES += hyper
2002 pkg_hyper_name = hyper
2003 pkg_hyper_description = Erlang implementation of HyperLogLog
2004 pkg_hyper_homepage = https://github.com/GameAnalytics/hyper
2005 pkg_hyper_fetch = git
2006 pkg_hyper_repo = https://github.com/GameAnalytics/hyper
2007 pkg_hyper_commit = master
2008
2009 PACKAGES += i18n
2010 pkg_i18n_name = i18n
2011 pkg_i18n_description = International components for unicode from Erlang (unicode, date, string, number, format, locale, localization, transliteration, icu4e)
2012 pkg_i18n_homepage = https://github.com/erlang-unicode/i18n
2013 pkg_i18n_fetch = git
2014 pkg_i18n_repo = https://github.com/erlang-unicode/i18n
2015 pkg_i18n_commit = master
2016
2017 PACKAGES += ibrowse
2018 pkg_ibrowse_name = ibrowse
2019 pkg_ibrowse_description = Erlang HTTP client
2020 pkg_ibrowse_homepage = https://github.com/cmullaparthi/ibrowse
2021 pkg_ibrowse_fetch = git
2022 pkg_ibrowse_repo = https://github.com/cmullaparthi/ibrowse
2023 pkg_ibrowse_commit = master
2024
2025 PACKAGES += ierlang
2026 pkg_ierlang_name = ierlang
2027 pkg_ierlang_description = An Erlang language kernel for IPython.
2028 pkg_ierlang_homepage = https://github.com/robbielynch/ierlang
2029 pkg_ierlang_fetch = git
2030 pkg_ierlang_repo = https://github.com/robbielynch/ierlang
2031 pkg_ierlang_commit = master
2032
2033 PACKAGES += iota
2034 pkg_iota_name = iota
2035 pkg_iota_description = iota (Inter-dependency Objective Testing Apparatus) - a tool to enforce clean separation of responsibilities in Erlang code
2036 pkg_iota_homepage = https://github.com/jpgneves/iota
2037 pkg_iota_fetch = git
2038 pkg_iota_repo = https://github.com/jpgneves/iota
2039 pkg_iota_commit = master
2040
2041 PACKAGES += irc_lib
2042 pkg_irc_lib_name = irc_lib
2043 pkg_irc_lib_description = Erlang irc client library
2044 pkg_irc_lib_homepage = https://github.com/OtpChatBot/irc_lib
2045 pkg_irc_lib_fetch = git
2046 pkg_irc_lib_repo = https://github.com/OtpChatBot/irc_lib
2047 pkg_irc_lib_commit = master
2048
2049 PACKAGES += ircd
2050 pkg_ircd_name = ircd
2051 pkg_ircd_description = A pluggable IRC daemon application/library for Erlang.
2052 pkg_ircd_homepage = https://github.com/tonyg/erlang-ircd
2053 pkg_ircd_fetch = git
2054 pkg_ircd_repo = https://github.com/tonyg/erlang-ircd
2055 pkg_ircd_commit = master
2056
2057 PACKAGES += iris
2058 pkg_iris_name = iris
2059 pkg_iris_description = Iris Erlang binding
2060 pkg_iris_homepage = https://github.com/project-iris/iris-erl
2061 pkg_iris_fetch = git
2062 pkg_iris_repo = https://github.com/project-iris/iris-erl
2063 pkg_iris_commit = master
2064
2065 PACKAGES += iso8601
2066 pkg_iso8601_name = iso8601
2067 pkg_iso8601_description = Erlang ISO 8601 date formatter/parser
2068 pkg_iso8601_homepage = https://github.com/seansawyer/erlang_iso8601
2069 pkg_iso8601_fetch = git
2070 pkg_iso8601_repo = https://github.com/seansawyer/erlang_iso8601
2071 pkg_iso8601_commit = master
2072
2073 PACKAGES += jamdb_sybase
2074 pkg_jamdb_sybase_name = jamdb_sybase
2075 pkg_jamdb_sybase_description = Erlang driver for SAP Sybase ASE
2076 pkg_jamdb_sybase_homepage = https://github.com/erlangbureau/jamdb_sybase
2077 pkg_jamdb_sybase_fetch = git
2078 pkg_jamdb_sybase_repo = https://github.com/erlangbureau/jamdb_sybase
2079 pkg_jamdb_sybase_commit = master
2080
2081 PACKAGES += jerg
2082 pkg_jerg_name = jerg
2083 pkg_jerg_description = JSON Schema to Erlang Records Generator
2084 pkg_jerg_homepage = https://github.com/ddossot/jerg
2085 pkg_jerg_fetch = git
2086 pkg_jerg_repo = https://github.com/ddossot/jerg
2087 pkg_jerg_commit = master
2088
2089 PACKAGES += jesse
2090 pkg_jesse_name = jesse
2091 pkg_jesse_description = jesse (JSon Schema Erlang) is an implementation of a json schema validator for Erlang.
2092 pkg_jesse_homepage = https://github.com/for-GET/jesse
2093 pkg_jesse_fetch = git
2094 pkg_jesse_repo = https://github.com/for-GET/jesse
2095 pkg_jesse_commit = master
2096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
2105 PACKAGES += jiffy_v
2106 pkg_jiffy_v_name = jiffy_v
2107 pkg_jiffy_v_description = JSON validation utility
2108 pkg_jiffy_v_homepage = https://github.com/shizzard/jiffy-v
2109 pkg_jiffy_v_fetch = git
2110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
2111 pkg_jiffy_v_commit = master
2112
2113 PACKAGES += jobs
2114 pkg_jobs_name = jobs
2115 pkg_jobs_description = a Job scheduler for load regulation
2116 pkg_jobs_homepage = https://github.com/esl/jobs
2117 pkg_jobs_fetch = git
2118 pkg_jobs_repo = https://github.com/esl/jobs
2119 pkg_jobs_commit = master
2120
2121 PACKAGES += joxa
2122 pkg_joxa_name = joxa
2123 pkg_joxa_description = A Modern Lisp for the Erlang VM
2124 pkg_joxa_homepage = https://github.com/joxa/joxa
2125 pkg_joxa_fetch = git
2126 pkg_joxa_repo = https://github.com/joxa/joxa
2127 pkg_joxa_commit = master
2128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
2137 PACKAGES += json_rec
2138 pkg_json_rec_name = json_rec
2139 pkg_json_rec_description = JSON to erlang record
2140 pkg_json_rec_homepage = https://github.com/justinkirby/json_rec
2141 pkg_json_rec_fetch = git
2142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
2143 pkg_json_rec_commit = master
2144
2145 PACKAGES += jsone
2146 pkg_jsone_name = jsone
2147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
2148 pkg_jsone_homepage = https://github.com/sile/jsone.git
2149 pkg_jsone_fetch = git
2150 pkg_jsone_repo = https://github.com/sile/jsone.git
2151 pkg_jsone_commit = master
2152
2153 PACKAGES += jsonerl
2154 pkg_jsonerl_name = jsonerl
2155 pkg_jsonerl_description = yet another but slightly different erlang <-> json encoder/decoder
2156 pkg_jsonerl_homepage = https://github.com/lambder/jsonerl
2157 pkg_jsonerl_fetch = git
2158 pkg_jsonerl_repo = https://github.com/lambder/jsonerl
2159 pkg_jsonerl_commit = master
2160
2161 PACKAGES += jsonpath
2162 pkg_jsonpath_name = jsonpath
2163 pkg_jsonpath_description = Fast Erlang JSON data retrieval and updates via javascript-like notation
2164 pkg_jsonpath_homepage = https://github.com/GeneStevens/jsonpath
2165 pkg_jsonpath_fetch = git
2166 pkg_jsonpath_repo = https://github.com/GeneStevens/jsonpath
2167 pkg_jsonpath_commit = master
2168
2169 PACKAGES += jsonx
2170 pkg_jsonx_name = jsonx
2171 pkg_jsonx_description = JSONX is an Erlang library for efficient decode and encode JSON, written in C.
2172 pkg_jsonx_homepage = https://github.com/iskra/jsonx
2173 pkg_jsonx_fetch = git
2174 pkg_jsonx_repo = https://github.com/iskra/jsonx
2175 pkg_jsonx_commit = master
2176
2177 PACKAGES += jsx
2178 pkg_jsx_name = jsx
2179 pkg_jsx_description = An Erlang application for consuming, producing and manipulating JSON.
2180 pkg_jsx_homepage = https://github.com/talentdeficit/jsx
2181 pkg_jsx_fetch = git
2182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
2183 pkg_jsx_commit = master
2184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
2193 PACKAGES += kafka_protocol
2194 pkg_kafka_protocol_name = kafka_protocol
2195 pkg_kafka_protocol_description = Kafka protocol Erlang library
2196 pkg_kafka_protocol_homepage = https://github.com/klarna/kafka_protocol
2197 pkg_kafka_protocol_fetch = git
2198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
2199 pkg_kafka_protocol_commit = master
2200
2201 PACKAGES += kai
2202 pkg_kai_name = kai
2203 pkg_kai_description = DHT storage by Takeshi Inoue
2204 pkg_kai_homepage = https://github.com/synrc/kai
2205 pkg_kai_fetch = git
2206 pkg_kai_repo = https://github.com/synrc/kai
2207 pkg_kai_commit = master
2208
2209 PACKAGES += katja
2210 pkg_katja_name = katja
2211 pkg_katja_description = A simple Riemann client written in Erlang.
2212 pkg_katja_homepage = https://github.com/nifoc/katja
2213 pkg_katja_fetch = git
2214 pkg_katja_repo = https://github.com/nifoc/katja
2215 pkg_katja_commit = master
2216
2217 PACKAGES += kdht
2218 pkg_kdht_name = kdht
2219 pkg_kdht_description = kdht is an erlang DHT implementation
2220 pkg_kdht_homepage = https://github.com/kevinlynx/kdht
2221 pkg_kdht_fetch = git
2222 pkg_kdht_repo = https://github.com/kevinlynx/kdht
2223 pkg_kdht_commit = master
2224
2225 PACKAGES += key2value
2226 pkg_key2value_name = key2value
2227 pkg_key2value_description = Erlang 2-way map
2228 pkg_key2value_homepage = https://github.com/okeuday/key2value
2229 pkg_key2value_fetch = git
2230 pkg_key2value_repo = https://github.com/okeuday/key2value
2231 pkg_key2value_commit = master
2232
2233 PACKAGES += keys1value
2234 pkg_keys1value_name = keys1value
2235 pkg_keys1value_description = Erlang set associative map for key lists
2236 pkg_keys1value_homepage = https://github.com/okeuday/keys1value
2237 pkg_keys1value_fetch = git
2238 pkg_keys1value_repo = https://github.com/okeuday/keys1value
2239 pkg_keys1value_commit = master
2240
2241 PACKAGES += kinetic
2242 pkg_kinetic_name = kinetic
2243 pkg_kinetic_description = Erlang Kinesis Client
2244 pkg_kinetic_homepage = https://github.com/AdRoll/kinetic
2245 pkg_kinetic_fetch = git
2246 pkg_kinetic_repo = https://github.com/AdRoll/kinetic
2247 pkg_kinetic_commit = master
2248
2249 PACKAGES += kjell
2250 pkg_kjell_name = kjell
2251 pkg_kjell_description = Erlang Shell
2252 pkg_kjell_homepage = https://github.com/karlll/kjell
2253 pkg_kjell_fetch = git
2254 pkg_kjell_repo = https://github.com/karlll/kjell
2255 pkg_kjell_commit = master
2256
2257 PACKAGES += kraken
2258 pkg_kraken_name = kraken
2259 pkg_kraken_description = Distributed Pubsub Server for Realtime Apps
2260 pkg_kraken_homepage = https://github.com/Asana/kraken
2261 pkg_kraken_fetch = git
2262 pkg_kraken_repo = https://github.com/Asana/kraken
2263 pkg_kraken_commit = master
2264
2265 PACKAGES += kucumberl
2266 pkg_kucumberl_name = kucumberl
2267 pkg_kucumberl_description = A pure-erlang, open-source, implementation of Cucumber
2268 pkg_kucumberl_homepage = https://github.com/openshine/kucumberl
2269 pkg_kucumberl_fetch = git
2270 pkg_kucumberl_repo = https://github.com/openshine/kucumberl
2271 pkg_kucumberl_commit = master
2272
2273 PACKAGES += kvc
2274 pkg_kvc_name = kvc
2275 pkg_kvc_description = KVC - Key Value Coding for Erlang data structures
2276 pkg_kvc_homepage = https://github.com/etrepum/kvc
2277 pkg_kvc_fetch = git
2278 pkg_kvc_repo = https://github.com/etrepum/kvc
2279 pkg_kvc_commit = master
2280
2281 PACKAGES += kvlists
2282 pkg_kvlists_name = kvlists
2283 pkg_kvlists_description = Lists of key-value pairs (decoded JSON) in Erlang
2284 pkg_kvlists_homepage = https://github.com/jcomellas/kvlists
2285 pkg_kvlists_fetch = git
2286 pkg_kvlists_repo = https://github.com/jcomellas/kvlists
2287 pkg_kvlists_commit = master
2288
2289 PACKAGES += kvs
2290 pkg_kvs_name = kvs
2291 pkg_kvs_description = Container and Iterator
2292 pkg_kvs_homepage = https://github.com/synrc/kvs
2293 pkg_kvs_fetch = git
2294 pkg_kvs_repo = https://github.com/synrc/kvs
2295 pkg_kvs_commit = master
2296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
2305 PACKAGES += lager_amqp_backend
2306 pkg_lager_amqp_backend_name = lager_amqp_backend
2307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
2308 pkg_lager_amqp_backend_homepage = https://github.com/jbrisbin/lager_amqp_backend
2309 pkg_lager_amqp_backend_fetch = git
2310 pkg_lager_amqp_backend_repo = https://github.com/jbrisbin/lager_amqp_backend
2311 pkg_lager_amqp_backend_commit = master
2312
2313 PACKAGES += lager_syslog
2314 pkg_lager_syslog_name = lager_syslog
2315 pkg_lager_syslog_description = Syslog backend for lager
2316 pkg_lager_syslog_homepage = https://github.com/basho/lager_syslog
2317 pkg_lager_syslog_fetch = git
2318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
2319 pkg_lager_syslog_commit = master
2320
2321 PACKAGES += lambdapad
2322 pkg_lambdapad_name = lambdapad
2323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
2324 pkg_lambdapad_homepage = https://github.com/gar1t/lambdapad
2325 pkg_lambdapad_fetch = git
2326 pkg_lambdapad_repo = https://github.com/gar1t/lambdapad
2327 pkg_lambdapad_commit = master
2328
2329 PACKAGES += lasp
2330 pkg_lasp_name = lasp
2331 pkg_lasp_description = A Language for Distributed, Eventually Consistent Computations
2332 pkg_lasp_homepage = http://lasp-lang.org/
2333 pkg_lasp_fetch = git
2334 pkg_lasp_repo = https://github.com/lasp-lang/lasp
2335 pkg_lasp_commit = master
2336
2337 PACKAGES += lasse
2338 pkg_lasse_name = lasse
2339 pkg_lasse_description = SSE handler for Cowboy
2340 pkg_lasse_homepage = https://github.com/inaka/lasse
2341 pkg_lasse_fetch = git
2342 pkg_lasse_repo = https://github.com/inaka/lasse
2343 pkg_lasse_commit = master
2344
2345 PACKAGES += ldap
2346 pkg_ldap_name = ldap
2347 pkg_ldap_description = LDAP server written in Erlang
2348 pkg_ldap_homepage = https://github.com/spawnproc/ldap
2349 pkg_ldap_fetch = git
2350 pkg_ldap_repo = https://github.com/spawnproc/ldap
2351 pkg_ldap_commit = master
2352
2353 PACKAGES += lethink
2354 pkg_lethink_name = lethink
2355 pkg_lethink_description = erlang driver for rethinkdb
2356 pkg_lethink_homepage = https://github.com/taybin/lethink
2357 pkg_lethink_fetch = git
2358 pkg_lethink_repo = https://github.com/taybin/lethink
2359 pkg_lethink_commit = master
2360
2361 PACKAGES += lfe
2362 pkg_lfe_name = lfe
2363 pkg_lfe_description = Lisp Flavoured Erlang (LFE)
2364 pkg_lfe_homepage = https://github.com/rvirding/lfe
2365 pkg_lfe_fetch = git
2366 pkg_lfe_repo = https://github.com/rvirding/lfe
2367 pkg_lfe_commit = master
2368
2369 PACKAGES += ling
2370 pkg_ling_name = ling
2371 pkg_ling_description = Erlang on Xen
2372 pkg_ling_homepage = https://github.com/cloudozer/ling
2373 pkg_ling_fetch = git
2374 pkg_ling_repo = https://github.com/cloudozer/ling
2375 pkg_ling_commit = master
2376
2377 PACKAGES += live
2378 pkg_live_name = live
2379 pkg_live_description = Automated module and configuration reloader.
2380 pkg_live_homepage = http://ninenines.eu
2381 pkg_live_fetch = git
2382 pkg_live_repo = https://github.com/ninenines/live
2383 pkg_live_commit = master
2384
2385 PACKAGES += lmq
2386 pkg_lmq_name = lmq
2387 pkg_lmq_description = Lightweight Message Queue
2388 pkg_lmq_homepage = https://github.com/iij/lmq
2389 pkg_lmq_fetch = git
2390 pkg_lmq_repo = https://github.com/iij/lmq
2391 pkg_lmq_commit = master
2392
2393 PACKAGES += locker
2394 pkg_locker_name = locker
2395 pkg_locker_description = Atomic distributed 'check and set' for short-lived keys
2396 pkg_locker_homepage = https://github.com/wooga/locker
2397 pkg_locker_fetch = git
2398 pkg_locker_repo = https://github.com/wooga/locker
2399 pkg_locker_commit = master
2400
2401 PACKAGES += locks
2402 pkg_locks_name = locks
2403 pkg_locks_description = A scalable, deadlock-resolving resource locker
2404 pkg_locks_homepage = https://github.com/uwiger/locks
2405 pkg_locks_fetch = git
2406 pkg_locks_repo = https://github.com/uwiger/locks
2407 pkg_locks_commit = master
2408
2409 PACKAGES += log4erl
2410 pkg_log4erl_name = log4erl
2411 pkg_log4erl_description = A logger for erlang in the spirit of Log4J.
2412 pkg_log4erl_homepage = https://github.com/ahmednawras/log4erl
2413 pkg_log4erl_fetch = git
2414 pkg_log4erl_repo = https://github.com/ahmednawras/log4erl
2415 pkg_log4erl_commit = master
2416
2417 PACKAGES += lol
2418 pkg_lol_name = lol
2419 pkg_lol_description = Lisp on erLang, and programming is fun again
2420 pkg_lol_homepage = https://github.com/b0oh/lol
2421 pkg_lol_fetch = git
2422 pkg_lol_repo = https://github.com/b0oh/lol
2423 pkg_lol_commit = master
2424
2425 PACKAGES += lucid
2426 pkg_lucid_name = lucid
2427 pkg_lucid_description = HTTP/2 server written in Erlang
2428 pkg_lucid_homepage = https://github.com/tatsuhiro-t/lucid
2429 pkg_lucid_fetch = git
2430 pkg_lucid_repo = https://github.com/tatsuhiro-t/lucid
2431 pkg_lucid_commit = master
2432
2433 PACKAGES += luerl
2434 pkg_luerl_name = luerl
2435 pkg_luerl_description = Lua in Erlang
2436 pkg_luerl_homepage = https://github.com/rvirding/luerl
2437 pkg_luerl_fetch = git
2438 pkg_luerl_repo = https://github.com/rvirding/luerl
2439 pkg_luerl_commit = develop
2440
2441 PACKAGES += luwak
2442 pkg_luwak_name = luwak
2443 pkg_luwak_description = Large-object storage interface for Riak
2444 pkg_luwak_homepage = https://github.com/basho/luwak
2445 pkg_luwak_fetch = git
2446 pkg_luwak_repo = https://github.com/basho/luwak
2447 pkg_luwak_commit = master
2448
2449 PACKAGES += lux
2450 pkg_lux_name = lux
2451 pkg_lux_description = Lux (LUcid eXpect scripting) simplifies test automation and provides an Expect-style execution of commands
2452 pkg_lux_homepage = https://github.com/hawk/lux
2453 pkg_lux_fetch = git
2454 pkg_lux_repo = https://github.com/hawk/lux
2455 pkg_lux_commit = master
2456
2457 PACKAGES += machi
2458 pkg_machi_name = machi
2459 pkg_machi_description = Machi file store
2460 pkg_machi_homepage = https://github.com/basho/machi
2461 pkg_machi_fetch = git
2462 pkg_machi_repo = https://github.com/basho/machi
2463 pkg_machi_commit = master
2464
2465 PACKAGES += mad
2466 pkg_mad_name = mad
2467 pkg_mad_description = Small and Fast Rebar Replacement
2468 pkg_mad_homepage = https://github.com/synrc/mad
2469 pkg_mad_fetch = git
2470 pkg_mad_repo = https://github.com/synrc/mad
2471 pkg_mad_commit = master
2472
2473 PACKAGES += marina
2474 pkg_marina_name = marina
2475 pkg_marina_description = Non-blocking Erlang Cassandra CQL3 client
2476 pkg_marina_homepage = https://github.com/lpgauth/marina
2477 pkg_marina_fetch = git
2478 pkg_marina_repo = https://github.com/lpgauth/marina
2479 pkg_marina_commit = master
2480
2481 PACKAGES += mavg
2482 pkg_mavg_name = mavg
2483 pkg_mavg_description = Erlang :: Exponential moving average library
2484 pkg_mavg_homepage = https://github.com/EchoTeam/mavg
2485 pkg_mavg_fetch = git
2486 pkg_mavg_repo = https://github.com/EchoTeam/mavg
2487 pkg_mavg_commit = master
2488
2489 PACKAGES += mc_erl
2490 pkg_mc_erl_name = mc_erl
2491 pkg_mc_erl_description = mc-erl is a server for Minecraft 1.4.7 written in Erlang.
2492 pkg_mc_erl_homepage = https://github.com/clonejo/mc-erl
2493 pkg_mc_erl_fetch = git
2494 pkg_mc_erl_repo = https://github.com/clonejo/mc-erl
2495 pkg_mc_erl_commit = master
2496
2497 PACKAGES += mcd
2498 pkg_mcd_name = mcd
2499 pkg_mcd_description = Fast memcached protocol client in pure Erlang
2500 pkg_mcd_homepage = https://github.com/EchoTeam/mcd
2501 pkg_mcd_fetch = git
2502 pkg_mcd_repo = https://github.com/EchoTeam/mcd
2503 pkg_mcd_commit = master
2504
2505 PACKAGES += mcerlang
2506 pkg_mcerlang_name = mcerlang
2507 pkg_mcerlang_description = The McErlang model checker for Erlang
2508 pkg_mcerlang_homepage = https://github.com/fredlund/McErlang
2509 pkg_mcerlang_fetch = git
2510 pkg_mcerlang_repo = https://github.com/fredlund/McErlang
2511 pkg_mcerlang_commit = master
2512
2513 PACKAGES += meck
2514 pkg_meck_name = meck
2515 pkg_meck_description = A mocking library for Erlang
2516 pkg_meck_homepage = https://github.com/eproxus/meck
2517 pkg_meck_fetch = git
2518 pkg_meck_repo = https://github.com/eproxus/meck
2519 pkg_meck_commit = master
2520
2521 PACKAGES += mekao
2522 pkg_mekao_name = mekao
2523 pkg_mekao_description = SQL constructor
2524 pkg_mekao_homepage = https://github.com/ddosia/mekao
2525 pkg_mekao_fetch = git
2526 pkg_mekao_repo = https://github.com/ddosia/mekao
2527 pkg_mekao_commit = master
2528
2529 PACKAGES += memo
2530 pkg_memo_name = memo
2531 pkg_memo_description = Erlang memoization server
2532 pkg_memo_homepage = https://github.com/tuncer/memo
2533 pkg_memo_fetch = git
2534 pkg_memo_repo = https://github.com/tuncer/memo
2535 pkg_memo_commit = master
2536
2537 PACKAGES += merge_index
2538 pkg_merge_index_name = merge_index
2539 pkg_merge_index_description = MergeIndex is an Erlang library for storing ordered sets on disk. It is very similar to an SSTable (in Google's Bigtable) or an HFile (in Hadoop).
2540 pkg_merge_index_homepage = https://github.com/basho/merge_index
2541 pkg_merge_index_fetch = git
2542 pkg_merge_index_repo = https://github.com/basho/merge_index
2543 pkg_merge_index_commit = master
2544
2545 PACKAGES += merl
2546 pkg_merl_name = merl
2547 pkg_merl_description = Metaprogramming in Erlang
2548 pkg_merl_homepage = https://github.com/richcarl/merl
2549 pkg_merl_fetch = git
2550 pkg_merl_repo = https://github.com/richcarl/merl
2551 pkg_merl_commit = master
2552
2553 PACKAGES += mimerl
2554 pkg_mimerl_name = mimerl
2555 pkg_mimerl_description = library to handle mimetypes
2556 pkg_mimerl_homepage = https://github.com/benoitc/mimerl
2557 pkg_mimerl_fetch = git
2558 pkg_mimerl_repo = https://github.com/benoitc/mimerl
2559 pkg_mimerl_commit = master
2560
2561 PACKAGES += mimetypes
2562 pkg_mimetypes_name = mimetypes
2563 pkg_mimetypes_description = Erlang MIME types library
2564 pkg_mimetypes_homepage = https://github.com/spawngrid/mimetypes
2565 pkg_mimetypes_fetch = git
2566 pkg_mimetypes_repo = https://github.com/spawngrid/mimetypes
2567 pkg_mimetypes_commit = master
2568
2569 PACKAGES += mixer
2570 pkg_mixer_name = mixer
2571 pkg_mixer_description = Mix in functions from other modules
2572 pkg_mixer_homepage = https://github.com/chef/mixer
2573 pkg_mixer_fetch = git
2574 pkg_mixer_repo = https://github.com/chef/mixer
2575 pkg_mixer_commit = master
2576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
2585 PACKAGES += mochiweb_xpath
2586 pkg_mochiweb_xpath_name = mochiweb_xpath
2587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
2588 pkg_mochiweb_xpath_homepage = https://github.com/retnuh/mochiweb_xpath
2589 pkg_mochiweb_xpath_fetch = git
2590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
2591 pkg_mochiweb_xpath_commit = master
2592
2593 PACKAGES += mockgyver
2594 pkg_mockgyver_name = mockgyver
2595 pkg_mockgyver_description = A mocking library for Erlang
2596 pkg_mockgyver_homepage = https://github.com/klajo/mockgyver
2597 pkg_mockgyver_fetch = git
2598 pkg_mockgyver_repo = https://github.com/klajo/mockgyver
2599 pkg_mockgyver_commit = master
2600
2601 PACKAGES += modlib
2602 pkg_modlib_name = modlib
2603 pkg_modlib_description = Web framework based on Erlang's inets httpd
2604 pkg_modlib_homepage = https://github.com/gar1t/modlib
2605 pkg_modlib_fetch = git
2606 pkg_modlib_repo = https://github.com/gar1t/modlib
2607 pkg_modlib_commit = master
2608
2609 PACKAGES += mongodb
2610 pkg_mongodb_name = mongodb
2611 pkg_mongodb_description = MongoDB driver for Erlang
2612 pkg_mongodb_homepage = https://github.com/comtihon/mongodb-erlang
2613 pkg_mongodb_fetch = git
2614 pkg_mongodb_repo = https://github.com/comtihon/mongodb-erlang
2615 pkg_mongodb_commit = master
2616
2617 PACKAGES += mongooseim
2618 pkg_mongooseim_name = mongooseim
2619 pkg_mongooseim_description = Jabber / XMPP server with focus on performance and scalability, by Erlang Solutions
2620 pkg_mongooseim_homepage = https://www.erlang-solutions.com/products/mongooseim-massively-scalable-ejabberd-platform
2621 pkg_mongooseim_fetch = git
2622 pkg_mongooseim_repo = https://github.com/esl/MongooseIM
2623 pkg_mongooseim_commit = master
2624
2625 PACKAGES += moyo
2626 pkg_moyo_name = moyo
2627 pkg_moyo_description = Erlang utility functions library
2628 pkg_moyo_homepage = https://github.com/dwango/moyo
2629 pkg_moyo_fetch = git
2630 pkg_moyo_repo = https://github.com/dwango/moyo
2631 pkg_moyo_commit = master
2632
2633 PACKAGES += msgpack
2634 pkg_msgpack_name = msgpack
2635 pkg_msgpack_description = MessagePack (de)serializer implementation for Erlang
2636 pkg_msgpack_homepage = https://github.com/msgpack/msgpack-erlang
2637 pkg_msgpack_fetch = git
2638 pkg_msgpack_repo = https://github.com/msgpack/msgpack-erlang
2639 pkg_msgpack_commit = master
2640
2641 PACKAGES += mu2
2642 pkg_mu2_name = mu2
2643 pkg_mu2_description = Erlang mutation testing tool
2644 pkg_mu2_homepage = https://github.com/ramsay-t/mu2
2645 pkg_mu2_fetch = git
2646 pkg_mu2_repo = https://github.com/ramsay-t/mu2
2647 pkg_mu2_commit = master
2648
2649 PACKAGES += mustache
2650 pkg_mustache_name = mustache
2651 pkg_mustache_description = Mustache template engine for Erlang.
2652 pkg_mustache_homepage = https://github.com/mojombo/mustache.erl
2653 pkg_mustache_fetch = git
2654 pkg_mustache_repo = https://github.com/mojombo/mustache.erl
2655 pkg_mustache_commit = master
2656
2657 PACKAGES += myproto
2658 pkg_myproto_name = myproto
2659 pkg_myproto_description = MySQL Server Protocol in Erlang
2660 pkg_myproto_homepage = https://github.com/altenwald/myproto
2661 pkg_myproto_fetch = git
2662 pkg_myproto_repo = https://github.com/altenwald/myproto
2663 pkg_myproto_commit = master
2664
2665 PACKAGES += mysql
2666 pkg_mysql_name = mysql
2667 pkg_mysql_description = Erlang MySQL Driver (from code.google.com)
2668 pkg_mysql_homepage = https://github.com/dizzyd/erlang-mysql-driver
2669 pkg_mysql_fetch = git
2670 pkg_mysql_repo = https://github.com/dizzyd/erlang-mysql-driver
2671 pkg_mysql_commit = master
2672
2673 PACKAGES += n2o
2674 pkg_n2o_name = n2o
2675 pkg_n2o_description = WebSocket Application Server
2676 pkg_n2o_homepage = https://github.com/5HT/n2o
2677 pkg_n2o_fetch = git
2678 pkg_n2o_repo = https://github.com/5HT/n2o
2679 pkg_n2o_commit = master
2680
2681 PACKAGES += nat_upnp
2682 pkg_nat_upnp_name = nat_upnp
2683 pkg_nat_upnp_description = Erlang library to map your internal port to an external using UNP IGD
2684 pkg_nat_upnp_homepage = https://github.com/benoitc/nat_upnp
2685 pkg_nat_upnp_fetch = git
2686 pkg_nat_upnp_repo = https://github.com/benoitc/nat_upnp
2687 pkg_nat_upnp_commit = master
2688
2689 PACKAGES += neo4j
2690 pkg_neo4j_name = neo4j
2691 pkg_neo4j_description = Erlang client library for Neo4J.
2692 pkg_neo4j_homepage = https://github.com/dmitriid/neo4j-erlang
2693 pkg_neo4j_fetch = git
2694 pkg_neo4j_repo = https://github.com/dmitriid/neo4j-erlang
2695 pkg_neo4j_commit = master
2696
2697 PACKAGES += neotoma
2698 pkg_neotoma_name = neotoma
2699 pkg_neotoma_description = Erlang library and packrat parser-generator for parsing expression grammars.
2700 pkg_neotoma_homepage = https://github.com/seancribbs/neotoma
2701 pkg_neotoma_fetch = git
2702 pkg_neotoma_repo = https://github.com/seancribbs/neotoma
2703 pkg_neotoma_commit = master
2704
2705 PACKAGES += newrelic
2706 pkg_newrelic_name = newrelic
2707 pkg_newrelic_description = Erlang library for sending metrics to New Relic
2708 pkg_newrelic_homepage = https://github.com/wooga/newrelic-erlang
2709 pkg_newrelic_fetch = git
2710 pkg_newrelic_repo = https://github.com/wooga/newrelic-erlang
2711 pkg_newrelic_commit = master
2712
2713 PACKAGES += nifty
2714 pkg_nifty_name = nifty
2715 pkg_nifty_description = Erlang NIF wrapper generator
2716 pkg_nifty_homepage = https://github.com/parapluu/nifty
2717 pkg_nifty_fetch = git
2718 pkg_nifty_repo = https://github.com/parapluu/nifty
2719 pkg_nifty_commit = master
2720
2721 PACKAGES += nitrogen_core
2722 pkg_nitrogen_core_name = nitrogen_core
2723 pkg_nitrogen_core_description = The core Nitrogen library.
2724 pkg_nitrogen_core_homepage = http://nitrogenproject.com/
2725 pkg_nitrogen_core_fetch = git
2726 pkg_nitrogen_core_repo = https://github.com/nitrogen/nitrogen_core
2727 pkg_nitrogen_core_commit = master
2728
2729 PACKAGES += nkbase
2730 pkg_nkbase_name = nkbase
2731 pkg_nkbase_description = NkBASE distributed database
2732 pkg_nkbase_homepage = https://github.com/Nekso/nkbase
2733 pkg_nkbase_fetch = git
2734 pkg_nkbase_repo = https://github.com/Nekso/nkbase
2735 pkg_nkbase_commit = develop
2736
2737 PACKAGES += nkdocker
2738 pkg_nkdocker_name = nkdocker
2739 pkg_nkdocker_description = Erlang Docker client
2740 pkg_nkdocker_homepage = https://github.com/Nekso/nkdocker
2741 pkg_nkdocker_fetch = git
2742 pkg_nkdocker_repo = https://github.com/Nekso/nkdocker
2743 pkg_nkdocker_commit = master
2744
2745 PACKAGES += nkpacket
2746 pkg_nkpacket_name = nkpacket
2747 pkg_nkpacket_description = Generic Erlang transport layer
2748 pkg_nkpacket_homepage = https://github.com/Nekso/nkpacket
2749 pkg_nkpacket_fetch = git
2750 pkg_nkpacket_repo = https://github.com/Nekso/nkpacket
2751 pkg_nkpacket_commit = master
2752
2753 PACKAGES += nksip
2754 pkg_nksip_name = nksip
2755 pkg_nksip_description = Erlang SIP application server
2756 pkg_nksip_homepage = https://github.com/kalta/nksip
2757 pkg_nksip_fetch = git
2758 pkg_nksip_repo = https://github.com/kalta/nksip
2759 pkg_nksip_commit = master
2760
2761 PACKAGES += nodefinder
2762 pkg_nodefinder_name = nodefinder
2763 pkg_nodefinder_description = automatic node discovery via UDP multicast
2764 pkg_nodefinder_homepage = https://github.com/erlanger/nodefinder
2765 pkg_nodefinder_fetch = git
2766 pkg_nodefinder_repo = https://github.com/okeuday/nodefinder
2767 pkg_nodefinder_commit = master
2768
2769 PACKAGES += nprocreg
2770 pkg_nprocreg_name = nprocreg
2771 pkg_nprocreg_description = Minimal Distributed Erlang Process Registry
2772 pkg_nprocreg_homepage = http://nitrogenproject.com/
2773 pkg_nprocreg_fetch = git
2774 pkg_nprocreg_repo = https://github.com/nitrogen/nprocreg
2775 pkg_nprocreg_commit = master
2776
2777 PACKAGES += oauth
2778 pkg_oauth_name = oauth
2779 pkg_oauth_description = An Erlang OAuth 1.0 implementation
2780 pkg_oauth_homepage = https://github.com/tim/erlang-oauth
2781 pkg_oauth_fetch = git
2782 pkg_oauth_repo = https://github.com/tim/erlang-oauth
2783 pkg_oauth_commit = master
2784
2785 PACKAGES += oauth2
2786 pkg_oauth2_name = oauth2
2787 pkg_oauth2_description = Erlang Oauth2 implementation
2788 pkg_oauth2_homepage = https://github.com/kivra/oauth2
2789 pkg_oauth2_fetch = git
2790 pkg_oauth2_repo = https://github.com/kivra/oauth2
2791 pkg_oauth2_commit = master
2792
2793 PACKAGES += octopus
2794 pkg_octopus_name = octopus
2795 pkg_octopus_description = Small and flexible pool manager written in Erlang
2796 pkg_octopus_homepage = https://github.com/erlangbureau/octopus
2797 pkg_octopus_fetch = git
2798 pkg_octopus_repo = https://github.com/erlangbureau/octopus
2799 pkg_octopus_commit = master
2800
2801 PACKAGES += of_protocol
2802 pkg_of_protocol_name = of_protocol
2803 pkg_of_protocol_description = OpenFlow Protocol Library for Erlang
2804 pkg_of_protocol_homepage = https://github.com/FlowForwarding/of_protocol
2805 pkg_of_protocol_fetch = git
2806 pkg_of_protocol_repo = https://github.com/FlowForwarding/of_protocol
2807 pkg_of_protocol_commit = master
2808
2809 PACKAGES += opencouch
2810 pkg_opencouch_name = couch
2811 pkg_opencouch_description = A embeddable document oriented database compatible with Apache CouchDB
2812 pkg_opencouch_homepage = https://github.com/benoitc/opencouch
2813 pkg_opencouch_fetch = git
2814 pkg_opencouch_repo = https://github.com/benoitc/opencouch
2815 pkg_opencouch_commit = master
2816
2817 PACKAGES += openflow
2818 pkg_openflow_name = openflow
2819 pkg_openflow_description = An OpenFlow controller written in pure erlang
2820 pkg_openflow_homepage = https://github.com/renatoaguiar/erlang-openflow
2821 pkg_openflow_fetch = git
2822 pkg_openflow_repo = https://github.com/renatoaguiar/erlang-openflow
2823 pkg_openflow_commit = master
2824
2825 PACKAGES += openid
2826 pkg_openid_name = openid
2827 pkg_openid_description = Erlang OpenID
2828 pkg_openid_homepage = https://github.com/brendonh/erl_openid
2829 pkg_openid_fetch = git
2830 pkg_openid_repo = https://github.com/brendonh/erl_openid
2831 pkg_openid_commit = master
2832
2833 PACKAGES += openpoker
2834 pkg_openpoker_name = openpoker
2835 pkg_openpoker_description = Genesis Texas hold'em Game Server
2836 pkg_openpoker_homepage = https://github.com/hpyhacking/openpoker
2837 pkg_openpoker_fetch = git
2838 pkg_openpoker_repo = https://github.com/hpyhacking/openpoker
2839 pkg_openpoker_commit = master
2840
2841 PACKAGES += pal
2842 pkg_pal_name = pal
2843 pkg_pal_description = Pragmatic Authentication Library
2844 pkg_pal_homepage = https://github.com/manifest/pal
2845 pkg_pal_fetch = git
2846 pkg_pal_repo = https://github.com/manifest/pal
2847 pkg_pal_commit = master
2848
2849 PACKAGES += parse_trans
2850 pkg_parse_trans_name = parse_trans
2851 pkg_parse_trans_description = Parse transform utilities for Erlang
2852 pkg_parse_trans_homepage = https://github.com/uwiger/parse_trans
2853 pkg_parse_trans_fetch = git
2854 pkg_parse_trans_repo = https://github.com/uwiger/parse_trans
2855 pkg_parse_trans_commit = master
2856
2857 PACKAGES += parsexml
2858 pkg_parsexml_name = parsexml
2859 pkg_parsexml_description = Simple DOM XML parser with convenient and very simple API
2860 pkg_parsexml_homepage = https://github.com/maxlapshin/parsexml
2861 pkg_parsexml_fetch = git
2862 pkg_parsexml_repo = https://github.com/maxlapshin/parsexml
2863 pkg_parsexml_commit = master
2864
2865 PACKAGES += pegjs
2866 pkg_pegjs_name = pegjs
2867 pkg_pegjs_description = An implementation of PEG.js grammar for Erlang.
2868 pkg_pegjs_homepage = https://github.com/dmitriid/pegjs
2869 pkg_pegjs_fetch = git
2870 pkg_pegjs_repo = https://github.com/dmitriid/pegjs
2871 pkg_pegjs_commit = master
2872
2873 PACKAGES += percept2
2874 pkg_percept2_name = percept2
2875 pkg_percept2_description = Concurrent profiling tool for Erlang
2876 pkg_percept2_homepage = https://github.com/huiqing/percept2
2877 pkg_percept2_fetch = git
2878 pkg_percept2_repo = https://github.com/huiqing/percept2
2879 pkg_percept2_commit = master
2880
2881 PACKAGES += pgsql
2882 pkg_pgsql_name = pgsql
2883 pkg_pgsql_description = Erlang PostgreSQL driver
2884 pkg_pgsql_homepage = https://github.com/semiocast/pgsql
2885 pkg_pgsql_fetch = git
2886 pkg_pgsql_repo = https://github.com/semiocast/pgsql
2887 pkg_pgsql_commit = master
2888
2889 PACKAGES += pkgx
2890 pkg_pkgx_name = pkgx
2891 pkg_pkgx_description = Build .deb packages from Erlang releases
2892 pkg_pkgx_homepage = https://github.com/arjan/pkgx
2893 pkg_pkgx_fetch = git
2894 pkg_pkgx_repo = https://github.com/arjan/pkgx
2895 pkg_pkgx_commit = master
2896
2897 PACKAGES += pkt
2898 pkg_pkt_name = pkt
2899 pkg_pkt_description = Erlang network protocol library
2900 pkg_pkt_homepage = https://github.com/msantos/pkt
2901 pkg_pkt_fetch = git
2902 pkg_pkt_repo = https://github.com/msantos/pkt
2903 pkg_pkt_commit = master
2904
2905 PACKAGES += plain_fsm
2906 pkg_plain_fsm_name = plain_fsm
2907 pkg_plain_fsm_description = A behaviour/support library for writing plain Erlang FSMs.
2908 pkg_plain_fsm_homepage = https://github.com/uwiger/plain_fsm
2909 pkg_plain_fsm_fetch = git
2910 pkg_plain_fsm_repo = https://github.com/uwiger/plain_fsm
2911 pkg_plain_fsm_commit = master
2912
2913 PACKAGES += plumtree
2914 pkg_plumtree_name = plumtree
2915 pkg_plumtree_description = Epidemic Broadcast Trees
2916 pkg_plumtree_homepage = https://github.com/helium/plumtree
2917 pkg_plumtree_fetch = git
2918 pkg_plumtree_repo = https://github.com/helium/plumtree
2919 pkg_plumtree_commit = master
2920
2921 PACKAGES += pmod_transform
2922 pkg_pmod_transform_name = pmod_transform
2923 pkg_pmod_transform_description = Parse transform for parameterized modules
2924 pkg_pmod_transform_homepage = https://github.com/erlang/pmod_transform
2925 pkg_pmod_transform_fetch = git
2926 pkg_pmod_transform_repo = https://github.com/erlang/pmod_transform
2927 pkg_pmod_transform_commit = master
2928
2929 PACKAGES += pobox
2930 pkg_pobox_name = pobox
2931 pkg_pobox_description = External buffer processes to protect against mailbox overflow in Erlang
2932 pkg_pobox_homepage = https://github.com/ferd/pobox
2933 pkg_pobox_fetch = git
2934 pkg_pobox_repo = https://github.com/ferd/pobox
2935 pkg_pobox_commit = master
2936
2937 PACKAGES += ponos
2938 pkg_ponos_name = ponos
2939 pkg_ponos_description = ponos is a simple yet powerful load generator written in erlang
2940 pkg_ponos_homepage = https://github.com/klarna/ponos
2941 pkg_ponos_fetch = git
2942 pkg_ponos_repo = https://github.com/klarna/ponos
2943 pkg_ponos_commit = master
2944
2945 PACKAGES += poolboy
2946 pkg_poolboy_name = poolboy
2947 pkg_poolboy_description = A hunky Erlang worker pool factory
2948 pkg_poolboy_homepage = https://github.com/devinus/poolboy
2949 pkg_poolboy_fetch = git
2950 pkg_poolboy_repo = https://github.com/devinus/poolboy
2951 pkg_poolboy_commit = master
2952
2953 PACKAGES += pooler
2954 pkg_pooler_name = pooler
2955 pkg_pooler_description = An OTP Process Pool Application
2956 pkg_pooler_homepage = https://github.com/seth/pooler
2957 pkg_pooler_fetch = git
2958 pkg_pooler_repo = https://github.com/seth/pooler
2959 pkg_pooler_commit = master
2960
2961 PACKAGES += pqueue
2962 pkg_pqueue_name = pqueue
2963 pkg_pqueue_description = Erlang Priority Queues
2964 pkg_pqueue_homepage = https://github.com/okeuday/pqueue
2965 pkg_pqueue_fetch = git
2966 pkg_pqueue_repo = https://github.com/okeuday/pqueue
2967 pkg_pqueue_commit = master
2968
2969 PACKAGES += procket
2970 pkg_procket_name = procket
2971 pkg_procket_description = Erlang interface to low level socket operations
2972 pkg_procket_homepage = http://blog.listincomprehension.com/search/label/procket
2973 pkg_procket_fetch = git
2974 pkg_procket_repo = https://github.com/msantos/procket
2975 pkg_procket_commit = master
2976
2977 PACKAGES += prop
2978 pkg_prop_name = prop
2979 pkg_prop_description = An Erlang code scaffolding and generator system.
2980 pkg_prop_homepage = https://github.com/nuex/prop
2981 pkg_prop_fetch = git
2982 pkg_prop_repo = https://github.com/nuex/prop
2983 pkg_prop_commit = master
2984
2985 PACKAGES += proper
2986 pkg_proper_name = proper
2987 pkg_proper_description = PropEr: a QuickCheck-inspired property-based testing tool for Erlang.
2988 pkg_proper_homepage = http://proper.softlab.ntua.gr
2989 pkg_proper_fetch = git
2990 pkg_proper_repo = https://github.com/manopapad/proper
2991 pkg_proper_commit = master
2992
2993 PACKAGES += props
2994 pkg_props_name = props
2995 pkg_props_description = Property structure library
2996 pkg_props_homepage = https://github.com/greyarea/props
2997 pkg_props_fetch = git
2998 pkg_props_repo = https://github.com/greyarea/props
2999 pkg_props_commit = master
3000
3001 PACKAGES += protobuffs
3002 pkg_protobuffs_name = protobuffs
3003 pkg_protobuffs_description = An implementation of Google's Protocol Buffers for Erlang, based on ngerakines/erlang_protobuffs.
3004 pkg_protobuffs_homepage = https://github.com/basho/erlang_protobuffs
3005 pkg_protobuffs_fetch = git
3006 pkg_protobuffs_repo = https://github.com/basho/erlang_protobuffs
3007 pkg_protobuffs_commit = master
3008
3009 PACKAGES += psycho
3010 pkg_psycho_name = psycho
3011 pkg_psycho_description = HTTP server that provides a WSGI-like interface for applications and middleware.
3012 pkg_psycho_homepage = https://github.com/gar1t/psycho
3013 pkg_psycho_fetch = git
3014 pkg_psycho_repo = https://github.com/gar1t/psycho
3015 pkg_psycho_commit = master
3016
3017 PACKAGES += purity
3018 pkg_purity_name = purity
3019 pkg_purity_description = A side-effect analyzer for Erlang
3020 pkg_purity_homepage = https://github.com/mpitid/purity
3021 pkg_purity_fetch = git
3022 pkg_purity_repo = https://github.com/mpitid/purity
3023 pkg_purity_commit = master
3024
3025 PACKAGES += push_service
3026 pkg_push_service_name = push_service
3027 pkg_push_service_description = Push service
3028 pkg_push_service_homepage = https://github.com/hairyhum/push_service
3029 pkg_push_service_fetch = git
3030 pkg_push_service_repo = https://github.com/hairyhum/push_service
3031 pkg_push_service_commit = master
3032
3033 PACKAGES += qdate
3034 pkg_qdate_name = qdate
3035 pkg_qdate_description = Date, time, and timezone parsing, formatting, and conversion for Erlang.
3036 pkg_qdate_homepage = https://github.com/choptastic/qdate
3037 pkg_qdate_fetch = git
3038 pkg_qdate_repo = https://github.com/choptastic/qdate
3039 pkg_qdate_commit = master
3040
3041 PACKAGES += qrcode
3042 pkg_qrcode_name = qrcode
3043 pkg_qrcode_description = QR Code encoder in Erlang
3044 pkg_qrcode_homepage = https://github.com/komone/qrcode
3045 pkg_qrcode_fetch = git
3046 pkg_qrcode_repo = https://github.com/komone/qrcode
3047 pkg_qrcode_commit = master
3048
3049 PACKAGES += quest
3050 pkg_quest_name = quest
3051 pkg_quest_description = Learn Erlang through this set of challenges. An interactive system for getting to know Erlang.
3052 pkg_quest_homepage = https://github.com/eriksoe/ErlangQuest
3053 pkg_quest_fetch = git
3054 pkg_quest_repo = https://github.com/eriksoe/ErlangQuest
3055 pkg_quest_commit = master
3056
3057 PACKAGES += quickrand
3058 pkg_quickrand_name = quickrand
3059 pkg_quickrand_description = Quick Erlang Random Number Generation
3060 pkg_quickrand_homepage = https://github.com/okeuday/quickrand
3061 pkg_quickrand_fetch = git
3062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
3063 pkg_quickrand_commit = master
3064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
3073 PACKAGES += rabbit_exchange_type_riak
3074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
3075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
3076 pkg_rabbit_exchange_type_riak_homepage = https://github.com/jbrisbin/riak-exchange
3077 pkg_rabbit_exchange_type_riak_fetch = git
3078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
3079 pkg_rabbit_exchange_type_riak_commit = master
3080
3081 PACKAGES += rack
3082 pkg_rack_name = rack
3083 pkg_rack_description = Rack handler for erlang
3084 pkg_rack_homepage = https://github.com/erlyvideo/rack
3085 pkg_rack_fetch = git
3086 pkg_rack_repo = https://github.com/erlyvideo/rack
3087 pkg_rack_commit = master
3088
3089 PACKAGES += radierl
3090 pkg_radierl_name = radierl
3091 pkg_radierl_description = RADIUS protocol stack implemented in Erlang.
3092 pkg_radierl_homepage = https://github.com/vances/radierl
3093 pkg_radierl_fetch = git
3094 pkg_radierl_repo = https://github.com/vances/radierl
3095 pkg_radierl_commit = master
3096
3097 PACKAGES += rafter
3098 pkg_rafter_name = rafter
3099 pkg_rafter_description = An Erlang library application which implements the Raft consensus protocol
3100 pkg_rafter_homepage = https://github.com/andrewjstone/rafter
3101 pkg_rafter_fetch = git
3102 pkg_rafter_repo = https://github.com/andrewjstone/rafter
3103 pkg_rafter_commit = master
3104
3105 PACKAGES += ranch
3106 pkg_ranch_name = ranch
3107 pkg_ranch_description = Socket acceptor pool for TCP protocols.
3108 pkg_ranch_homepage = http://ninenines.eu
3109 pkg_ranch_fetch = git
3110 pkg_ranch_repo = https://github.com/ninenines/ranch
3111 pkg_ranch_commit = 1.2.1
3112
3113 PACKAGES += rbeacon
3114 pkg_rbeacon_name = rbeacon
3115 pkg_rbeacon_description = LAN discovery and presence in Erlang.
3116 pkg_rbeacon_homepage = https://github.com/refuge/rbeacon
3117 pkg_rbeacon_fetch = git
3118 pkg_rbeacon_repo = https://github.com/refuge/rbeacon
3119 pkg_rbeacon_commit = master
3120
3121 PACKAGES += rebar
3122 pkg_rebar_name = rebar
3123 pkg_rebar_description = Erlang build tool that makes it easy to compile and test Erlang applications, port drivers and releases.
3124 pkg_rebar_homepage = http://www.rebar3.org
3125 pkg_rebar_fetch = git
3126 pkg_rebar_repo = https://github.com/rebar/rebar3
3127 pkg_rebar_commit = master
3128
3129 PACKAGES += rebus
3130 pkg_rebus_name = rebus
3131 pkg_rebus_description = A stupid simple, internal, pub/sub event bus written in- and for Erlang.
3132 pkg_rebus_homepage = https://github.com/olle/rebus
3133 pkg_rebus_fetch = git
3134 pkg_rebus_repo = https://github.com/olle/rebus
3135 pkg_rebus_commit = master
3136
3137 PACKAGES += rec2json
3138 pkg_rec2json_name = rec2json
3139 pkg_rec2json_description = Compile erlang record definitions into modules to convert them to/from json easily.
3140 pkg_rec2json_homepage = https://github.com/lordnull/rec2json
3141 pkg_rec2json_fetch = git
3142 pkg_rec2json_repo = https://github.com/lordnull/rec2json
3143 pkg_rec2json_commit = master
3144
3145 PACKAGES += recon
3146 pkg_recon_name = recon
3147 pkg_recon_description = Collection of functions and scripts to debug Erlang in production.
3148 pkg_recon_homepage = https://github.com/ferd/recon
3149 pkg_recon_fetch = git
3150 pkg_recon_repo = https://github.com/ferd/recon
3151 pkg_recon_commit = master
3152
3153 PACKAGES += record_info
3154 pkg_record_info_name = record_info
3155 pkg_record_info_description = Convert between record and proplist
3156 pkg_record_info_homepage = https://github.com/bipthelin/erlang-record_info
3157 pkg_record_info_fetch = git
3158 pkg_record_info_repo = https://github.com/bipthelin/erlang-record_info
3159 pkg_record_info_commit = master
3160
3161 PACKAGES += redgrid
3162 pkg_redgrid_name = redgrid
3163 pkg_redgrid_description = automatic Erlang node discovery via redis
3164 pkg_redgrid_homepage = https://github.com/jkvor/redgrid
3165 pkg_redgrid_fetch = git
3166 pkg_redgrid_repo = https://github.com/jkvor/redgrid
3167 pkg_redgrid_commit = master
3168
3169 PACKAGES += redo
3170 pkg_redo_name = redo
3171 pkg_redo_description = pipelined erlang redis client
3172 pkg_redo_homepage = https://github.com/jkvor/redo
3173 pkg_redo_fetch = git
3174 pkg_redo_repo = https://github.com/jkvor/redo
3175 pkg_redo_commit = master
3176
3177 PACKAGES += reload_mk
3178 pkg_reload_mk_name = reload_mk
3179 pkg_reload_mk_description = Live reload plugin for erlang.mk.
3180 pkg_reload_mk_homepage = https://github.com/bullno1/reload.mk
3181 pkg_reload_mk_fetch = git
3182 pkg_reload_mk_repo = https://github.com/bullno1/reload.mk
3183 pkg_reload_mk_commit = master
3184
3185 PACKAGES += reltool_util
3186 pkg_reltool_util_name = reltool_util
3187 pkg_reltool_util_description = Erlang reltool utility functionality application
3188 pkg_reltool_util_homepage = https://github.com/okeuday/reltool_util
3189 pkg_reltool_util_fetch = git
3190 pkg_reltool_util_repo = https://github.com/okeuday/reltool_util
3191 pkg_reltool_util_commit = master
3192
3193 PACKAGES += relx
3194 pkg_relx_name = relx
3195 pkg_relx_description = Sane, simple release creation for Erlang
3196 pkg_relx_homepage = https://github.com/erlware/relx
3197 pkg_relx_fetch = git
3198 pkg_relx_repo = https://github.com/erlware/relx
3199 pkg_relx_commit = master
3200
3201 PACKAGES += resource_discovery
3202 pkg_resource_discovery_name = resource_discovery
3203 pkg_resource_discovery_description = An application used to dynamically discover resources present in an Erlang node cluster.
3204 pkg_resource_discovery_homepage = http://erlware.org/
3205 pkg_resource_discovery_fetch = git
3206 pkg_resource_discovery_repo = https://github.com/erlware/resource_discovery
3207 pkg_resource_discovery_commit = master
3208
3209 PACKAGES += restc
3210 pkg_restc_name = restc
3211 pkg_restc_description = Erlang Rest Client
3212 pkg_restc_homepage = https://github.com/kivra/restclient
3213 pkg_restc_fetch = git
3214 pkg_restc_repo = https://github.com/kivra/restclient
3215 pkg_restc_commit = master
3216
3217 PACKAGES += rfc4627_jsonrpc
3218 pkg_rfc4627_jsonrpc_name = rfc4627_jsonrpc
3219 pkg_rfc4627_jsonrpc_description = Erlang RFC4627 (JSON) codec and JSON-RPC server implementation.
3220 pkg_rfc4627_jsonrpc_homepage = https://github.com/tonyg/erlang-rfc4627
3221 pkg_rfc4627_jsonrpc_fetch = git
3222 pkg_rfc4627_jsonrpc_repo = https://github.com/tonyg/erlang-rfc4627
3223 pkg_rfc4627_jsonrpc_commit = master
3224
3225 PACKAGES += riak_control
3226 pkg_riak_control_name = riak_control
3227 pkg_riak_control_description = Webmachine-based administration interface for Riak.
3228 pkg_riak_control_homepage = https://github.com/basho/riak_control
3229 pkg_riak_control_fetch = git
3230 pkg_riak_control_repo = https://github.com/basho/riak_control
3231 pkg_riak_control_commit = master
3232
3233 PACKAGES += riak_core
3234 pkg_riak_core_name = riak_core
3235 pkg_riak_core_description = Distributed systems infrastructure used by Riak.
3236 pkg_riak_core_homepage = https://github.com/basho/riak_core
3237 pkg_riak_core_fetch = git
3238 pkg_riak_core_repo = https://github.com/basho/riak_core
3239 pkg_riak_core_commit = master
3240
3241 PACKAGES += riak_dt
3242 pkg_riak_dt_name = riak_dt
3243 pkg_riak_dt_description = Convergent replicated datatypes in Erlang
3244 pkg_riak_dt_homepage = https://github.com/basho/riak_dt
3245 pkg_riak_dt_fetch = git
3246 pkg_riak_dt_repo = https://github.com/basho/riak_dt
3247 pkg_riak_dt_commit = master
3248
3249 PACKAGES += riak_ensemble
3250 pkg_riak_ensemble_name = riak_ensemble
3251 pkg_riak_ensemble_description = Multi-Paxos framework in Erlang
3252 pkg_riak_ensemble_homepage = https://github.com/basho/riak_ensemble
3253 pkg_riak_ensemble_fetch = git
3254 pkg_riak_ensemble_repo = https://github.com/basho/riak_ensemble
3255 pkg_riak_ensemble_commit = master
3256
3257 PACKAGES += riak_kv
3258 pkg_riak_kv_name = riak_kv
3259 pkg_riak_kv_description = Riak Key/Value Store
3260 pkg_riak_kv_homepage = https://github.com/basho/riak_kv
3261 pkg_riak_kv_fetch = git
3262 pkg_riak_kv_repo = https://github.com/basho/riak_kv
3263 pkg_riak_kv_commit = master
3264
3265 PACKAGES += riak_pg
3266 pkg_riak_pg_name = riak_pg
3267 pkg_riak_pg_description = Distributed process groups with riak_core.
3268 pkg_riak_pg_homepage = https://github.com/cmeiklejohn/riak_pg
3269 pkg_riak_pg_fetch = git
3270 pkg_riak_pg_repo = https://github.com/cmeiklejohn/riak_pg
3271 pkg_riak_pg_commit = master
3272
3273 PACKAGES += riak_pipe
3274 pkg_riak_pipe_name = riak_pipe
3275 pkg_riak_pipe_description = Riak Pipelines
3276 pkg_riak_pipe_homepage = https://github.com/basho/riak_pipe
3277 pkg_riak_pipe_fetch = git
3278 pkg_riak_pipe_repo = https://github.com/basho/riak_pipe
3279 pkg_riak_pipe_commit = master
3280
3281 PACKAGES += riak_sysmon
3282 pkg_riak_sysmon_name = riak_sysmon
3283 pkg_riak_sysmon_description = Simple OTP app for managing Erlang VM system_monitor event messages
3284 pkg_riak_sysmon_homepage = https://github.com/basho/riak_sysmon
3285 pkg_riak_sysmon_fetch = git
3286 pkg_riak_sysmon_repo = https://github.com/basho/riak_sysmon
3287 pkg_riak_sysmon_commit = master
3288
3289 PACKAGES += riak_test
3290 pkg_riak_test_name = riak_test
3291 pkg_riak_test_description = I'm in your cluster, testing your riaks
3292 pkg_riak_test_homepage = https://github.com/basho/riak_test
3293 pkg_riak_test_fetch = git
3294 pkg_riak_test_repo = https://github.com/basho/riak_test
3295 pkg_riak_test_commit = master
3296
3297 PACKAGES += riakc
3298 pkg_riakc_name = riakc
3299 pkg_riakc_description = Erlang clients for Riak.
3300 pkg_riakc_homepage = https://github.com/basho/riak-erlang-client
3301 pkg_riakc_fetch = git
3302 pkg_riakc_repo = https://github.com/basho/riak-erlang-client
3303 pkg_riakc_commit = master
3304
3305 PACKAGES += riakhttpc
3306 pkg_riakhttpc_name = riakhttpc
3307 pkg_riakhttpc_description = Riak Erlang client using the HTTP interface
3308 pkg_riakhttpc_homepage = https://github.com/basho/riak-erlang-http-client
3309 pkg_riakhttpc_fetch = git
3310 pkg_riakhttpc_repo = https://github.com/basho/riak-erlang-http-client
3311 pkg_riakhttpc_commit = master
3312
3313 PACKAGES += riaknostic
3314 pkg_riaknostic_name = riaknostic
3315 pkg_riaknostic_description = A diagnostic tool for Riak installations, to find common errors asap
3316 pkg_riaknostic_homepage = https://github.com/basho/riaknostic
3317 pkg_riaknostic_fetch = git
3318 pkg_riaknostic_repo = https://github.com/basho/riaknostic
3319 pkg_riaknostic_commit = master
3320
3321 PACKAGES += riakpool
3322 pkg_riakpool_name = riakpool
3323 pkg_riakpool_description = erlang riak client pool
3324 pkg_riakpool_homepage = https://github.com/dweldon/riakpool
3325 pkg_riakpool_fetch = git
3326 pkg_riakpool_repo = https://github.com/dweldon/riakpool
3327 pkg_riakpool_commit = master
3328
3329 PACKAGES += rivus_cep
3330 pkg_rivus_cep_name = rivus_cep
3331 pkg_rivus_cep_description = Complex event processing in Erlang
3332 pkg_rivus_cep_homepage = https://github.com/vascokk/rivus_cep
3333 pkg_rivus_cep_fetch = git
3334 pkg_rivus_cep_repo = https://github.com/vascokk/rivus_cep
3335 pkg_rivus_cep_commit = master
3336
3337 PACKAGES += rlimit
3338 pkg_rlimit_name = rlimit
3339 pkg_rlimit_description = Magnus Klaar's rate limiter code from etorrent
3340 pkg_rlimit_homepage = https://github.com/jlouis/rlimit
3341 pkg_rlimit_fetch = git
3342 pkg_rlimit_repo = https://github.com/jlouis/rlimit
3343 pkg_rlimit_commit = master
3344
3345 PACKAGES += rust_mk
3346 pkg_rust_mk_name = rust_mk
3347 pkg_rust_mk_description = Build Rust crates in an Erlang application
3348 pkg_rust_mk_homepage = https://github.com/goertzenator/rust.mk
3349 pkg_rust_mk_fetch = git
3350 pkg_rust_mk_repo = https://github.com/goertzenator/rust.mk
3351 pkg_rust_mk_commit = master
3352
3353 PACKAGES += safetyvalve
3354 pkg_safetyvalve_name = safetyvalve
3355 pkg_safetyvalve_description = A safety valve for your erlang node
3356 pkg_safetyvalve_homepage = https://github.com/jlouis/safetyvalve
3357 pkg_safetyvalve_fetch = git
3358 pkg_safetyvalve_repo = https://github.com/jlouis/safetyvalve
3359 pkg_safetyvalve_commit = master
3360
3361 PACKAGES += seestar
3362 pkg_seestar_name = seestar
3363 pkg_seestar_description = The Erlang client for Cassandra 1.2+ binary protocol
3364 pkg_seestar_homepage = https://github.com/iamaleksey/seestar
3365 pkg_seestar_fetch = git
3366 pkg_seestar_repo = https://github.com/iamaleksey/seestar
3367 pkg_seestar_commit = master
3368
3369 PACKAGES += service
3370 pkg_service_name = service
3371 pkg_service_description = A minimal Erlang behavior for creating CloudI internal services
3372 pkg_service_homepage = http://cloudi.org/
3373 pkg_service_fetch = git
3374 pkg_service_repo = https://github.com/CloudI/service
3375 pkg_service_commit = master
3376
3377 PACKAGES += setup
3378 pkg_setup_name = setup
3379 pkg_setup_description = Generic setup utility for Erlang-based systems
3380 pkg_setup_homepage = https://github.com/uwiger/setup
3381 pkg_setup_fetch = git
3382 pkg_setup_repo = https://github.com/uwiger/setup
3383 pkg_setup_commit = master
3384
3385 PACKAGES += sext
3386 pkg_sext_name = sext
3387 pkg_sext_description = Sortable Erlang Term Serialization
3388 pkg_sext_homepage = https://github.com/uwiger/sext
3389 pkg_sext_fetch = git
3390 pkg_sext_repo = https://github.com/uwiger/sext
3391 pkg_sext_commit = master
3392
3393 PACKAGES += sfmt
3394 pkg_sfmt_name = sfmt
3395 pkg_sfmt_description = SFMT pseudo random number generator for Erlang.
3396 pkg_sfmt_homepage = https://github.com/jj1bdx/sfmt-erlang
3397 pkg_sfmt_fetch = git
3398 pkg_sfmt_repo = https://github.com/jj1bdx/sfmt-erlang
3399 pkg_sfmt_commit = master
3400
3401 PACKAGES += sgte
3402 pkg_sgte_name = sgte
3403 pkg_sgte_description = A simple Erlang Template Engine
3404 pkg_sgte_homepage = https://github.com/filippo/sgte
3405 pkg_sgte_fetch = git
3406 pkg_sgte_repo = https://github.com/filippo/sgte
3407 pkg_sgte_commit = master
3408
3409 PACKAGES += sheriff
3410 pkg_sheriff_name = sheriff
3411 pkg_sheriff_description = Parse transform for type based validation.
3412 pkg_sheriff_homepage = http://ninenines.eu
3413 pkg_sheriff_fetch = git
3414 pkg_sheriff_repo = https://github.com/extend/sheriff
3415 pkg_sheriff_commit = master
3416
3417 PACKAGES += shotgun
3418 pkg_shotgun_name = shotgun
3419 pkg_shotgun_description = better than just a gun
3420 pkg_shotgun_homepage = https://github.com/inaka/shotgun
3421 pkg_shotgun_fetch = git
3422 pkg_shotgun_repo = https://github.com/inaka/shotgun
3423 pkg_shotgun_commit = master
3424
3425 PACKAGES += sidejob
3426 pkg_sidejob_name = sidejob
3427 pkg_sidejob_description = Parallel worker and capacity limiting library for Erlang
3428 pkg_sidejob_homepage = https://github.com/basho/sidejob
3429 pkg_sidejob_fetch = git
3430 pkg_sidejob_repo = https://github.com/basho/sidejob
3431 pkg_sidejob_commit = master
3432
3433 PACKAGES += sieve
3434 pkg_sieve_name = sieve
3435 pkg_sieve_description = sieve is a simple TCP routing proxy (layer 7) in erlang
3436 pkg_sieve_homepage = https://github.com/benoitc/sieve
3437 pkg_sieve_fetch = git
3438 pkg_sieve_repo = https://github.com/benoitc/sieve
3439 pkg_sieve_commit = master
3440
3441 PACKAGES += sighandler
3442 pkg_sighandler_name = sighandler
3443 pkg_sighandler_description = Handle UNIX signals in Er lang
3444 pkg_sighandler_homepage = https://github.com/jkingsbery/sighandler
3445 pkg_sighandler_fetch = git
3446 pkg_sighandler_repo = https://github.com/jkingsbery/sighandler
3447 pkg_sighandler_commit = master
3448
3449 PACKAGES += simhash
3450 pkg_simhash_name = simhash
3451 pkg_simhash_description = Simhashing for Erlang -- hashing algorithm to find near-duplicates in binary data.
3452 pkg_simhash_homepage = https://github.com/ferd/simhash
3453 pkg_simhash_fetch = git
3454 pkg_simhash_repo = https://github.com/ferd/simhash
3455 pkg_simhash_commit = master
3456
3457 PACKAGES += simple_bridge
3458 pkg_simple_bridge_name = simple_bridge
3459 pkg_simple_bridge_description = A simple, standardized interface library to Erlang HTTP Servers.
3460 pkg_simple_bridge_homepage = https://github.com/nitrogen/simple_bridge
3461 pkg_simple_bridge_fetch = git
3462 pkg_simple_bridge_repo = https://github.com/nitrogen/simple_bridge
3463 pkg_simple_bridge_commit = master
3464
3465 PACKAGES += simple_oauth2
3466 pkg_simple_oauth2_name = simple_oauth2
3467 pkg_simple_oauth2_description = Simple erlang OAuth2 client module for any http server framework (Google, Facebook, Yandex, Vkontakte are preconfigured)
3468 pkg_simple_oauth2_homepage = https://github.com/virtan/simple_oauth2
3469 pkg_simple_oauth2_fetch = git
3470 pkg_simple_oauth2_repo = https://github.com/virtan/simple_oauth2
3471 pkg_simple_oauth2_commit = master
3472
3473 PACKAGES += skel
3474 pkg_skel_name = skel
3475 pkg_skel_description = A Streaming Process-based Skeleton Library for Erlang
3476 pkg_skel_homepage = https://github.com/ParaPhrase/skel
3477 pkg_skel_fetch = git
3478 pkg_skel_repo = https://github.com/ParaPhrase/skel
3479 pkg_skel_commit = master
3480
3481 PACKAGES += slack
3482 pkg_slack_name = slack
3483 pkg_slack_description = Minimal slack notification OTP library.
3484 pkg_slack_homepage = https://github.com/DonBranson/slack
3485 pkg_slack_fetch = git
3486 pkg_slack_repo = https://github.com/DonBranson/slack.git
3487 pkg_slack_commit = master
3488
3489 PACKAGES += smother
3490 pkg_smother_name = smother
3491 pkg_smother_description = Extended code coverage metrics for Erlang.
3492 pkg_smother_homepage = https://ramsay-t.github.io/Smother/
3493 pkg_smother_fetch = git
3494 pkg_smother_repo = https://github.com/ramsay-t/Smother
3495 pkg_smother_commit = master
3496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
3505 PACKAGES += social
3506 pkg_social_name = social
3507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
3508 pkg_social_homepage = https://github.com/dvv/social
3509 pkg_social_fetch = git
3510 pkg_social_repo = https://github.com/dvv/social
3511 pkg_social_commit = master
3512
3513 PACKAGES += spapi_router
3514 pkg_spapi_router_name = spapi_router
3515 pkg_spapi_router_description = Partially-connected Erlang clustering
3516 pkg_spapi_router_homepage = https://github.com/spilgames/spapi-router
3517 pkg_spapi_router_fetch = git
3518 pkg_spapi_router_repo = https://github.com/spilgames/spapi-router
3519 pkg_spapi_router_commit = master
3520
3521 PACKAGES += sqerl
3522 pkg_sqerl_name = sqerl
3523 pkg_sqerl_description = An Erlang-flavoured SQL DSL
3524 pkg_sqerl_homepage = https://github.com/hairyhum/sqerl
3525 pkg_sqerl_fetch = git
3526 pkg_sqerl_repo = https://github.com/hairyhum/sqerl
3527 pkg_sqerl_commit = master
3528
3529 PACKAGES += srly
3530 pkg_srly_name = srly
3531 pkg_srly_description = Native Erlang Unix serial interface
3532 pkg_srly_homepage = https://github.com/msantos/srly
3533 pkg_srly_fetch = git
3534 pkg_srly_repo = https://github.com/msantos/srly
3535 pkg_srly_commit = master
3536
3537 PACKAGES += sshrpc
3538 pkg_sshrpc_name = sshrpc
3539 pkg_sshrpc_description = Erlang SSH RPC module (experimental)
3540 pkg_sshrpc_homepage = https://github.com/jj1bdx/sshrpc
3541 pkg_sshrpc_fetch = git
3542 pkg_sshrpc_repo = https://github.com/jj1bdx/sshrpc
3543 pkg_sshrpc_commit = master
3544
3545 PACKAGES += stable
3546 pkg_stable_name = stable
3547 pkg_stable_description = Library of assorted helpers for Cowboy web server.
3548 pkg_stable_homepage = https://github.com/dvv/stable
3549 pkg_stable_fetch = git
3550 pkg_stable_repo = https://github.com/dvv/stable
3551 pkg_stable_commit = master
3552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
3561 PACKAGES += statebox_riak
3562 pkg_statebox_riak_name = statebox_riak
3563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
3564 pkg_statebox_riak_homepage = https://github.com/mochi/statebox_riak
3565 pkg_statebox_riak_fetch = git
3566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
3567 pkg_statebox_riak_commit = master
3568
3569 PACKAGES += statman
3570 pkg_statman_name = statman
3571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
3572 pkg_statman_homepage = https://github.com/knutin/statman
3573 pkg_statman_fetch = git
3574 pkg_statman_repo = https://github.com/knutin/statman
3575 pkg_statman_commit = master
3576
3577 PACKAGES += statsderl
3578 pkg_statsderl_name = statsderl
3579 pkg_statsderl_description = StatsD client (erlang)
3580 pkg_statsderl_homepage = https://github.com/lpgauth/statsderl
3581 pkg_statsderl_fetch = git
3582 pkg_statsderl_repo = https://github.com/lpgauth/statsderl
3583 pkg_statsderl_commit = master
3584
3585 PACKAGES += stdinout_pool
3586 pkg_stdinout_pool_name = stdinout_pool
3587 pkg_stdinout_pool_description = stdinout_pool : stuff goes in, stuff goes out. there's never any miscommunication.
3588 pkg_stdinout_pool_homepage = https://github.com/mattsta/erlang-stdinout-pool
3589 pkg_stdinout_pool_fetch = git
3590 pkg_stdinout_pool_repo = https://github.com/mattsta/erlang-stdinout-pool
3591 pkg_stdinout_pool_commit = master
3592
3593 PACKAGES += stockdb
3594 pkg_stockdb_name = stockdb
3595 pkg_stockdb_description = Database for storing Stock Exchange quotes in erlang
3596 pkg_stockdb_homepage = https://github.com/maxlapshin/stockdb
3597 pkg_stockdb_fetch = git
3598 pkg_stockdb_repo = https://github.com/maxlapshin/stockdb
3599 pkg_stockdb_commit = master
3600
3601 PACKAGES += stripe
3602 pkg_stripe_name = stripe
3603 pkg_stripe_description = Erlang interface to the stripe.com API
3604 pkg_stripe_homepage = https://github.com/mattsta/stripe-erlang
3605 pkg_stripe_fetch = git
3606 pkg_stripe_repo = https://github.com/mattsta/stripe-erlang
3607 pkg_stripe_commit = v1
3608
3609 PACKAGES += supervisor3
3610 pkg_supervisor3_name = supervisor3
3611 pkg_supervisor3_description = OTP supervisor with additional strategies
3612 pkg_supervisor3_homepage = https://github.com/klarna/supervisor3
3613 pkg_supervisor3_fetch = git
3614 pkg_supervisor3_repo = https://github.com/klarna/supervisor3.git
3615 pkg_supervisor3_commit = master
3616
3617 PACKAGES += surrogate
3618 pkg_surrogate_name = surrogate
3619 pkg_surrogate_description = Proxy server written in erlang. Supports reverse proxy load balancing and forward proxy with http (including CONNECT), socks4, socks5, and transparent proxy modes.
3620 pkg_surrogate_homepage = https://github.com/skruger/Surrogate
3621 pkg_surrogate_fetch = git
3622 pkg_surrogate_repo = https://github.com/skruger/Surrogate
3623 pkg_surrogate_commit = master
3624
3625 PACKAGES += swab
3626 pkg_swab_name = swab
3627 pkg_swab_description = General purpose buffer handling module
3628 pkg_swab_homepage = https://github.com/crownedgrouse/swab
3629 pkg_swab_fetch = git
3630 pkg_swab_repo = https://github.com/crownedgrouse/swab
3631 pkg_swab_commit = master
3632
3633 PACKAGES += swarm
3634 pkg_swarm_name = swarm
3635 pkg_swarm_description = Fast and simple acceptor pool for Erlang
3636 pkg_swarm_homepage = https://github.com/jeremey/swarm
3637 pkg_swarm_fetch = git
3638 pkg_swarm_repo = https://github.com/jeremey/swarm
3639 pkg_swarm_commit = master
3640
3641 PACKAGES += switchboard
3642 pkg_switchboard_name = switchboard
3643 pkg_switchboard_description = A framework for processing email using worker plugins.
3644 pkg_switchboard_homepage = https://github.com/thusfresh/switchboard
3645 pkg_switchboard_fetch = git
3646 pkg_switchboard_repo = https://github.com/thusfresh/switchboard
3647 pkg_switchboard_commit = master
3648
3649 PACKAGES += syn
3650 pkg_syn_name = syn
3651 pkg_syn_description = A global Process Registry and Process Group manager for Erlang.
3652 pkg_syn_homepage = https://github.com/ostinelli/syn
3653 pkg_syn_fetch = git
3654 pkg_syn_repo = https://github.com/ostinelli/syn
3655 pkg_syn_commit = master
3656
3657 PACKAGES += sync
3658 pkg_sync_name = sync
3659 pkg_sync_description = On-the-fly recompiling and reloading in Erlang.
3660 pkg_sync_homepage = https://github.com/rustyio/sync
3661 pkg_sync_fetch = git
3662 pkg_sync_repo = https://github.com/rustyio/sync
3663 pkg_sync_commit = master
3664
3665 PACKAGES += syntaxerl
3666 pkg_syntaxerl_name = syntaxerl
3667 pkg_syntaxerl_description = Syntax checker for Erlang
3668 pkg_syntaxerl_homepage = https://github.com/ten0s/syntaxerl
3669 pkg_syntaxerl_fetch = git
3670 pkg_syntaxerl_repo = https://github.com/ten0s/syntaxerl
3671 pkg_syntaxerl_commit = master
3672
3673 PACKAGES += syslog
3674 pkg_syslog_name = syslog
3675 pkg_syslog_description = Erlang port driver for interacting with syslog via syslog(3)
3676 pkg_syslog_homepage = https://github.com/Vagabond/erlang-syslog
3677 pkg_syslog_fetch = git
3678 pkg_syslog_repo = https://github.com/Vagabond/erlang-syslog
3679 pkg_syslog_commit = master
3680
3681 PACKAGES += taskforce
3682 pkg_taskforce_name = taskforce
3683 pkg_taskforce_description = Erlang worker pools for controlled parallelisation of arbitrary tasks.
3684 pkg_taskforce_homepage = https://github.com/g-andrade/taskforce
3685 pkg_taskforce_fetch = git
3686 pkg_taskforce_repo = https://github.com/g-andrade/taskforce
3687 pkg_taskforce_commit = master
3688
3689 PACKAGES += tddreloader
3690 pkg_tddreloader_name = tddreloader
3691 pkg_tddreloader_description = Shell utility for recompiling, reloading, and testing code as it changes
3692 pkg_tddreloader_homepage = https://github.com/version2beta/tddreloader
3693 pkg_tddreloader_fetch = git
3694 pkg_tddreloader_repo = https://github.com/version2beta/tddreloader
3695 pkg_tddreloader_commit = master
3696
3697 PACKAGES += tempo
3698 pkg_tempo_name = tempo
3699 pkg_tempo_description = NIF-based date and time parsing and formatting for Erlang.
3700 pkg_tempo_homepage = https://github.com/selectel/tempo
3701 pkg_tempo_fetch = git
3702 pkg_tempo_repo = https://github.com/selectel/tempo
3703 pkg_tempo_commit = master
3704
3705 PACKAGES += ticktick
3706 pkg_ticktick_name = ticktick
3707 pkg_ticktick_description = Ticktick is an id generator for message service.
3708 pkg_ticktick_homepage = https://github.com/ericliang/ticktick
3709 pkg_ticktick_fetch = git
3710 pkg_ticktick_repo = https://github.com/ericliang/ticktick
3711 pkg_ticktick_commit = master
3712
3713 PACKAGES += tinymq
3714 pkg_tinymq_name = tinymq
3715 pkg_tinymq_description = TinyMQ - a diminutive, in-memory message queue
3716 pkg_tinymq_homepage = https://github.com/ChicagoBoss/tinymq
3717 pkg_tinymq_fetch = git
3718 pkg_tinymq_repo = https://github.com/ChicagoBoss/tinymq
3719 pkg_tinymq_commit = master
3720
3721 PACKAGES += tinymt
3722 pkg_tinymt_name = tinymt
3723 pkg_tinymt_description = TinyMT pseudo random number generator for Erlang.
3724 pkg_tinymt_homepage = https://github.com/jj1bdx/tinymt-erlang
3725 pkg_tinymt_fetch = git
3726 pkg_tinymt_repo = https://github.com/jj1bdx/tinymt-erlang
3727 pkg_tinymt_commit = master
3728
3729 PACKAGES += tirerl
3730 pkg_tirerl_name = tirerl
3731 pkg_tirerl_description = Erlang interface to Elastic Search
3732 pkg_tirerl_homepage = https://github.com/inaka/tirerl
3733 pkg_tirerl_fetch = git
3734 pkg_tirerl_repo = https://github.com/inaka/tirerl
3735 pkg_tirerl_commit = master
3736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
3745 PACKAGES += traffic_tools
3746 pkg_traffic_tools_name = traffic_tools
3747 pkg_traffic_tools_description = Simple traffic limiting library
3748 pkg_traffic_tools_homepage = https://github.com/systra/traffic_tools
3749 pkg_traffic_tools_fetch = git
3750 pkg_traffic_tools_repo = https://github.com/systra/traffic_tools
3751 pkg_traffic_tools_commit = master
3752
3753 PACKAGES += trails
3754 pkg_trails_name = trails
3755 pkg_trails_description = A couple of improvements over Cowboy Routes
3756 pkg_trails_homepage = http://inaka.github.io/cowboy-trails/
3757 pkg_trails_fetch = git
3758 pkg_trails_repo = https://github.com/inaka/cowboy-trails
3759 pkg_trails_commit = master
3760
3761 PACKAGES += trane
3762 pkg_trane_name = trane
3763 pkg_trane_description = SAX style broken HTML parser in Erlang
3764 pkg_trane_homepage = https://github.com/massemanet/trane
3765 pkg_trane_fetch = git
3766 pkg_trane_repo = https://github.com/massemanet/trane
3767 pkg_trane_commit = master
3768
3769 PACKAGES += transit
3770 pkg_transit_name = transit
3771 pkg_transit_description = transit format for erlang
3772 pkg_transit_homepage = https://github.com/isaiah/transit-erlang
3773 pkg_transit_fetch = git
3774 pkg_transit_repo = https://github.com/isaiah/transit-erlang
3775 pkg_transit_commit = master
3776
3777 PACKAGES += trie
3778 pkg_trie_name = trie
3779 pkg_trie_description = Erlang Trie Implementation
3780 pkg_trie_homepage = https://github.com/okeuday/trie
3781 pkg_trie_fetch = git
3782 pkg_trie_repo = https://github.com/okeuday/trie
3783 pkg_trie_commit = master
3784
3785 PACKAGES += triq
3786 pkg_triq_name = triq
3787 pkg_triq_description = Trifork QuickCheck
3788 pkg_triq_homepage = https://github.com/krestenkrab/triq
3789 pkg_triq_fetch = git
3790 pkg_triq_repo = https://github.com/krestenkrab/triq
3791 pkg_triq_commit = master
3792
3793 PACKAGES += tunctl
3794 pkg_tunctl_name = tunctl
3795 pkg_tunctl_description = Erlang TUN/TAP interface
3796 pkg_tunctl_homepage = https://github.com/msantos/tunctl
3797 pkg_tunctl_fetch = git
3798 pkg_tunctl_repo = https://github.com/msantos/tunctl
3799 pkg_tunctl_commit = master
3800
3801 PACKAGES += twerl
3802 pkg_twerl_name = twerl
3803 pkg_twerl_description = Erlang client for the Twitter Streaming API
3804 pkg_twerl_homepage = https://github.com/lucaspiller/twerl
3805 pkg_twerl_fetch = git
3806 pkg_twerl_repo = https://github.com/lucaspiller/twerl
3807 pkg_twerl_commit = oauth
3808
3809 PACKAGES += twitter_erlang
3810 pkg_twitter_erlang_name = twitter_erlang
3811 pkg_twitter_erlang_description = An Erlang twitter client
3812 pkg_twitter_erlang_homepage = https://github.com/ngerakines/erlang_twitter
3813 pkg_twitter_erlang_fetch = git
3814 pkg_twitter_erlang_repo = https://github.com/ngerakines/erlang_twitter
3815 pkg_twitter_erlang_commit = master
3816
3817 PACKAGES += ucol_nif
3818 pkg_ucol_nif_name = ucol_nif
3819 pkg_ucol_nif_description = ICU based collation Erlang module
3820 pkg_ucol_nif_homepage = https://github.com/refuge/ucol_nif
3821 pkg_ucol_nif_fetch = git
3822 pkg_ucol_nif_repo = https://github.com/refuge/ucol_nif
3823 pkg_ucol_nif_commit = master
3824
3825 PACKAGES += unicorn
3826 pkg_unicorn_name = unicorn
3827 pkg_unicorn_description = Generic configuration server
3828 pkg_unicorn_homepage = https://github.com/shizzard/unicorn
3829 pkg_unicorn_fetch = git
3830 pkg_unicorn_repo = https://github.com/shizzard/unicorn
3831 pkg_unicorn_commit = master
3832
3833 PACKAGES += unsplit
3834 pkg_unsplit_name = unsplit
3835 pkg_unsplit_description = Resolves conflicts in Mnesia after network splits
3836 pkg_unsplit_homepage = https://github.com/uwiger/unsplit
3837 pkg_unsplit_fetch = git
3838 pkg_unsplit_repo = https://github.com/uwiger/unsplit
3839 pkg_unsplit_commit = master
3840
3841 PACKAGES += uuid
3842 pkg_uuid_name = uuid
3843 pkg_uuid_description = Erlang UUID Implementation
3844 pkg_uuid_homepage = https://github.com/okeuday/uuid
3845 pkg_uuid_fetch = git
3846 pkg_uuid_repo = https://github.com/okeuday/uuid
3847 pkg_uuid_commit = master
3848
3849 PACKAGES += ux
3850 pkg_ux_name = ux
3851 pkg_ux_description = Unicode eXtention for Erlang (Strings, Collation)
3852 pkg_ux_homepage = https://github.com/erlang-unicode/ux
3853 pkg_ux_fetch = git
3854 pkg_ux_repo = https://github.com/erlang-unicode/ux
3855 pkg_ux_commit = master
3856
3857 PACKAGES += vert
3858 pkg_vert_name = vert
3859 pkg_vert_description = Erlang binding to libvirt virtualization API
3860 pkg_vert_homepage = https://github.com/msantos/erlang-libvirt
3861 pkg_vert_fetch = git
3862 pkg_vert_repo = https://github.com/msantos/erlang-libvirt
3863 pkg_vert_commit = master
3864
3865 PACKAGES += verx
3866 pkg_verx_name = verx
3867 pkg_verx_description = Erlang implementation of the libvirtd remote protocol
3868 pkg_verx_homepage = https://github.com/msantos/verx
3869 pkg_verx_fetch = git
3870 pkg_verx_repo = https://github.com/msantos/verx
3871 pkg_verx_commit = master
3872
3873 PACKAGES += vmq_acl
3874 pkg_vmq_acl_name = vmq_acl
3875 pkg_vmq_acl_description = Component of VerneMQ: A distributed MQTT message broker
3876 pkg_vmq_acl_homepage = https://verne.mq/
3877 pkg_vmq_acl_fetch = git
3878 pkg_vmq_acl_repo = https://github.com/erlio/vmq_acl
3879 pkg_vmq_acl_commit = master
3880
3881 PACKAGES += vmq_bridge
3882 pkg_vmq_bridge_name = vmq_bridge
3883 pkg_vmq_bridge_description = Component of VerneMQ: A distributed MQTT message broker
3884 pkg_vmq_bridge_homepage = https://verne.mq/
3885 pkg_vmq_bridge_fetch = git
3886 pkg_vmq_bridge_repo = https://github.com/erlio/vmq_bridge
3887 pkg_vmq_bridge_commit = master
3888
3889 PACKAGES += vmq_graphite
3890 pkg_vmq_graphite_name = vmq_graphite
3891 pkg_vmq_graphite_description = Component of VerneMQ: A distributed MQTT message broker
3892 pkg_vmq_graphite_homepage = https://verne.mq/
3893 pkg_vmq_graphite_fetch = git
3894 pkg_vmq_graphite_repo = https://github.com/erlio/vmq_graphite
3895 pkg_vmq_graphite_commit = master
3896
3897 PACKAGES += vmq_passwd
3898 pkg_vmq_passwd_name = vmq_passwd
3899 pkg_vmq_passwd_description = Component of VerneMQ: A distributed MQTT message broker
3900 pkg_vmq_passwd_homepage = https://verne.mq/
3901 pkg_vmq_passwd_fetch = git
3902 pkg_vmq_passwd_repo = https://github.com/erlio/vmq_passwd
3903 pkg_vmq_passwd_commit = master
3904
3905 PACKAGES += vmq_server
3906 pkg_vmq_server_name = vmq_server
3907 pkg_vmq_server_description = Component of VerneMQ: A distributed MQTT message broker
3908 pkg_vmq_server_homepage = https://verne.mq/
3909 pkg_vmq_server_fetch = git
3910 pkg_vmq_server_repo = https://github.com/erlio/vmq_server
3911 pkg_vmq_server_commit = master
3912
3913 PACKAGES += vmq_snmp
3914 pkg_vmq_snmp_name = vmq_snmp
3915 pkg_vmq_snmp_description = Component of VerneMQ: A distributed MQTT message broker
3916 pkg_vmq_snmp_homepage = https://verne.mq/
3917 pkg_vmq_snmp_fetch = git
3918 pkg_vmq_snmp_repo = https://github.com/erlio/vmq_snmp
3919 pkg_vmq_snmp_commit = master
3920
3921 PACKAGES += vmq_systree
3922 pkg_vmq_systree_name = vmq_systree
3923 pkg_vmq_systree_description = Component of VerneMQ: A distributed MQTT message broker
3924 pkg_vmq_systree_homepage = https://verne.mq/
3925 pkg_vmq_systree_fetch = git
3926 pkg_vmq_systree_repo = https://github.com/erlio/vmq_systree
3927 pkg_vmq_systree_commit = master
3928
3929 PACKAGES += vmstats
3930 pkg_vmstats_name = vmstats
3931 pkg_vmstats_description = tiny Erlang app that works in conjunction with statsderl in order to generate information on the Erlang VM for graphite logs.
3932 pkg_vmstats_homepage = https://github.com/ferd/vmstats
3933 pkg_vmstats_fetch = git
3934 pkg_vmstats_repo = https://github.com/ferd/vmstats
3935 pkg_vmstats_commit = master
3936
3937 PACKAGES += walrus
3938 pkg_walrus_name = walrus
3939 pkg_walrus_description = Walrus - Mustache-like Templating
3940 pkg_walrus_homepage = https://github.com/devinus/walrus
3941 pkg_walrus_fetch = git
3942 pkg_walrus_repo = https://github.com/devinus/walrus
3943 pkg_walrus_commit = master
3944
3945 PACKAGES += webmachine
3946 pkg_webmachine_name = webmachine
3947 pkg_webmachine_description = A REST-based system for building web applications.
3948 pkg_webmachine_homepage = https://github.com/basho/webmachine
3949 pkg_webmachine_fetch = git
3950 pkg_webmachine_repo = https://github.com/basho/webmachine
3951 pkg_webmachine_commit = master
3952
3953 PACKAGES += websocket_client
3954 pkg_websocket_client_name = websocket_client
3955 pkg_websocket_client_description = Erlang websocket client (ws and wss supported)
3956 pkg_websocket_client_homepage = https://github.com/jeremyong/websocket_client
3957 pkg_websocket_client_fetch = git
3958 pkg_websocket_client_repo = https://github.com/jeremyong/websocket_client
3959 pkg_websocket_client_commit = master
3960
3961 PACKAGES += worker_pool
3962 pkg_worker_pool_name = worker_pool
3963 pkg_worker_pool_description = a simple erlang worker pool
3964 pkg_worker_pool_homepage = https://github.com/inaka/worker_pool
3965 pkg_worker_pool_fetch = git
3966 pkg_worker_pool_repo = https://github.com/inaka/worker_pool
3967 pkg_worker_pool_commit = master
3968
3969 PACKAGES += wrangler
3970 pkg_wrangler_name = wrangler
3971 pkg_wrangler_description = Import of the Wrangler svn repository.
3972 pkg_wrangler_homepage = http://www.cs.kent.ac.uk/projects/wrangler/Home.html
3973 pkg_wrangler_fetch = git
3974 pkg_wrangler_repo = https://github.com/RefactoringTools/wrangler
3975 pkg_wrangler_commit = master
3976
3977 PACKAGES += wsock
3978 pkg_wsock_name = wsock
3979 pkg_wsock_description = Erlang library to build WebSocket clients and servers
3980 pkg_wsock_homepage = https://github.com/madtrick/wsock
3981 pkg_wsock_fetch = git
3982 pkg_wsock_repo = https://github.com/madtrick/wsock
3983 pkg_wsock_commit = master
3984
3985 PACKAGES += xhttpc
3986 pkg_xhttpc_name = xhttpc
3987 pkg_xhttpc_description = Extensible HTTP Client for Erlang
3988 pkg_xhttpc_homepage = https://github.com/seriyps/xhttpc
3989 pkg_xhttpc_fetch = git
3990 pkg_xhttpc_repo = https://github.com/seriyps/xhttpc
3991 pkg_xhttpc_commit = master
3992
3993 PACKAGES += xref_runner
3994 pkg_xref_runner_name = xref_runner
3995 pkg_xref_runner_description = Erlang Xref Runner (inspired in rebar xref)
3996 pkg_xref_runner_homepage = https://github.com/inaka/xref_runner
3997 pkg_xref_runner_fetch = git
3998 pkg_xref_runner_repo = https://github.com/inaka/xref_runner
3999 pkg_xref_runner_commit = master
4000
4001 PACKAGES += yamerl
4002 pkg_yamerl_name = yamerl
4003 pkg_yamerl_description = YAML 1.2 parser in pure Erlang
4004 pkg_yamerl_homepage = https://github.com/yakaz/yamerl
4005 pkg_yamerl_fetch = git
4006 pkg_yamerl_repo = https://github.com/yakaz/yamerl
4007 pkg_yamerl_commit = master
4008
4009 PACKAGES += yamler
4010 pkg_yamler_name = yamler
4011 pkg_yamler_description = libyaml-based yaml loader for Erlang
4012 pkg_yamler_homepage = https://github.com/goertzenator/yamler
4013 pkg_yamler_fetch = git
4014 pkg_yamler_repo = https://github.com/goertzenator/yamler
4015 pkg_yamler_commit = master
4016
4017 PACKAGES += yaws
4018 pkg_yaws_name = yaws
4019 pkg_yaws_description = Yaws webserver
4020 pkg_yaws_homepage = http://yaws.hyber.org
4021 pkg_yaws_fetch = git
4022 pkg_yaws_repo = https://github.com/klacke/yaws
4023 pkg_yaws_commit = master
4024
4025 PACKAGES += zab_engine
4026 pkg_zab_engine_name = zab_engine
4027 pkg_zab_engine_description = zab propotocol implement by erlang
4028 pkg_zab_engine_homepage = https://github.com/xinmingyao/zab_engine
4029 pkg_zab_engine_fetch = git
4030 pkg_zab_engine_repo = https://github.com/xinmingyao/zab_engine
4031 pkg_zab_engine_commit = master
4032
4033 PACKAGES += zabbix_sender
4034 pkg_zabbix_sender_name = zabbix_sender
4035 pkg_zabbix_sender_description = Zabbix trapper for sending data to Zabbix in pure Erlang
4036 pkg_zabbix_sender_homepage = https://github.com/stalkermn/zabbix_sender
4037 pkg_zabbix_sender_fetch = git
4038 pkg_zabbix_sender_repo = https://github.com/stalkermn/zabbix_sender.git
4039 pkg_zabbix_sender_commit = master
4040
4041 PACKAGES += zeta
4042 pkg_zeta_name = zeta
4043 pkg_zeta_description = HTTP access log parser in Erlang
4044 pkg_zeta_homepage = https://github.com/s1n4/zeta
4045 pkg_zeta_fetch = git
4046 pkg_zeta_repo = https://github.com/s1n4/zeta
4047 pkg_zeta_commit = master
4048
4049 PACKAGES += zippers
4050 pkg_zippers_name = zippers
4051 pkg_zippers_description = A library for functional zipper data structures in Erlang. Read more on zippers
4052 pkg_zippers_homepage = https://github.com/ferd/zippers
4053 pkg_zippers_fetch = git
4054 pkg_zippers_repo = https://github.com/ferd/zippers
4055 pkg_zippers_commit = master
4056
4057 PACKAGES += zlists
4058 pkg_zlists_name = zlists
4059 pkg_zlists_description = Erlang lazy lists library.
4060 pkg_zlists_homepage = https://github.com/vjache/erlang-zlists
4061 pkg_zlists_fetch = git
4062 pkg_zlists_repo = https://github.com/vjache/erlang-zlists
4063 pkg_zlists_commit = master
4064
4065 PACKAGES += zraft_lib
4066 pkg_zraft_lib_name = zraft_lib
4067 pkg_zraft_lib_description = Erlang raft consensus protocol implementation
4068 pkg_zraft_lib_homepage = https://github.com/dreyk/zraft_lib
4069 pkg_zraft_lib_fetch = git
4070 pkg_zraft_lib_repo = https://github.com/dreyk/zraft_lib
4071 pkg_zraft_lib_commit = master
4072
4073 PACKAGES += zucchini
4074 pkg_zucchini_name = zucchini
4075 pkg_zucchini_description = An Erlang INI parser
4076 pkg_zucchini_homepage = https://github.com/devinus/zucchini
4077 pkg_zucchini_fetch = git
4078 pkg_zucchini_repo = https://github.com/devinus/zucchini
4079 pkg_zucchini_commit = master
4080
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
4082 # This file is part of erlang.mk and subject to the terms of the ISC License.
4083
4084 .PHONY: search
4085
4086 define pkg_print
4087 $(verbose) printf "%s\n" \
4088 $(if $(call core_eq,$(1),$(pkg_$(1)_name)),,"Pkg name: $(1)") \
4089 "App name: $(pkg_$(1)_name)" \
4090 "Description: $(pkg_$(1)_description)" \
4091 "Home page: $(pkg_$(1)_homepage)" \
4092 "Fetch with: $(pkg_$(1)_fetch)" \
4093 "Repository: $(pkg_$(1)_repo)" \
4094 "Commit: $(pkg_$(1)_commit)" \
4095 ""
4096
4097 endef
4098
4099 search:
4100 ifdef q
4101 $(foreach p,$(PACKAGES), \
4102 $(if $(findstring $(call core_lc,$(q)),$(call core_lc,$(pkg_$(p)_name) $(pkg_$(p)_description))), \
4103 $(call pkg_print,$(p))))
4104 else
4105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
4106 endif
4107
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
4109 # This file is part of erlang.mk and subject to the terms of the ISC License.
4110
4111 .PHONY: distclean-deps clean-tmp-deps.log
4112
4113 # Configuration.
4114
4115 ifdef OTP_DEPS
4116 $(warning The variable OTP_DEPS is deprecated in favor of LOCAL_DEPS.)
4117 endif
4118
4119 IGNORE_DEPS ?=
4120 export IGNORE_DEPS
4121
4122 APPS_DIR ?= $(CURDIR)/apps
4123 export APPS_DIR
4124
4125 DEPS_DIR ?= $(CURDIR)/deps
4126 export DEPS_DIR
4127
4128 REBAR_DEPS_DIR = $(DEPS_DIR)
4129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
4150
4151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
4152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
4153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
4154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
4155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
4157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
4158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
4159
4160 ifeq ($(filter $(APPS_DIR) $(DEPS_DIR),$(subst :, ,$(ERL_LIBS))),)
4161 ifeq ($(ERL_LIBS),)
4162 ERL_LIBS = $(APPS_DIR):$(DEPS_DIR)
4163 else
4164 ERL_LIBS := $(ERL_LIBS):$(APPS_DIR):$(DEPS_DIR)
4165 endif
4166 endif
4167 export ERL_LIBS
4168
4169 export NO_AUTOPATCH
4170
4171 # Verbosity.
4172
4173 dep_verbose_0 = @echo " DEP " $(1);
4174 dep_verbose_2 = set -x;
4175 dep_verbose = $(dep_verbose_$(V))
4176
4177 # Core targets.
4178
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
4180 ifeq ($(IS_APP)$(IS_DEP),)
4181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
4182 endif
4183 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4184 # Create ebin directory for all apps to make sure Erlang recognizes them
4185 # as proper OTP applications when using -include_lib. This is a temporary
4186 # fix, a proper fix would be to compile apps/* in the right order.
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
4190 done
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
4196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
4197 :; \
4198 else \
4199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4200 $(MAKE) -C $$dep IS_APP=1; \
4201 fi \
4202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4207 endif
4208
4209 ifneq ($(SKIP_DEPS),)
4210 deps::
4211 else
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
4213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
4215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
4216 :; \
4217 else \
4218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
4219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4220 $(MAKE) -C $$dep IS_DEP=1; \
4221 else \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
4223 exit 2; \
4224 fi \
4225 fi \
4226 done
4227 endif
4228
4229 # Deps related targets.
4230
4231 # @todo rename GNUmakefile and makefile into Makefile first, if they exist
4232 # While Makefile file could be GNUmakefile or makefile,
4233 # in practice only Makefile is needed so far.
4234 define dep_autopatch
4235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
4237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4238 $(call dep_autopatch_erlang_mk,$(1)); \
4239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4243 $(call dep_autopatch2,$(1)); \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4245 $(call dep_autopatch2,$(1)); \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
4247 $(call dep_autopatch2,$(1)); \
4248 fi \
4249 else \
4250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
4251 $(call dep_autopatch_noop,$(1)); \
4252 else \
4253 $(call dep_autopatch2,$(1)); \
4254 fi \
4255 fi
4256 endef
4257
4258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
4262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
4263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
4264 fi; \
4265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4267 $(call dep_autopatch_fetch_rebar); \
4268 $(call dep_autopatch_rebar,$(1)); \
4269 else \
4270 $(call dep_autopatch_gen,$(1)); \
4271 fi
4272 endef
4273
4274 define dep_autopatch_noop
4275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
4276 endef
4277
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
4280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
4281 define dep_autopatch_erlang_mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
4287 endef
4288 else
4289 define dep_autopatch_erlang_mk
4290 :
4291 endef
4292 endif
4293
4294 define dep_autopatch_gen
4295 printf "%s\n" \
4296 "ERLC_OPTS = +debug_info" \
4297 "include ../../erlang.mk" > $(DEPS_DIR)/$(1)/Makefile
4298 endef
4299
4300 define dep_autopatch_fetch_rebar
4301 mkdir -p $(ERLANG_MK_TMP); \
4302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
4303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
4304 cd $(ERLANG_MK_TMP)/rebar; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
4306 $(MAKE); \
4307 cd -; \
4308 fi
4309 endef
4310
4311 define dep_autopatch_rebar
4312 if [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4313 mv $(DEPS_DIR)/$(1)/Makefile $(DEPS_DIR)/$(1)/Makefile.orig.mk; \
4314 fi; \
4315 $(call erlang,$(call dep_autopatch_rebar.erl,$(1))); \
4316 rm -f $(DEPS_DIR)/$(1)/ebin/$(1).app
4317 endef
4318
4319 define dep_autopatch_rebar.erl
4320 application:load(rebar),
4321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
4323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
4324 {ok, Conf0} -> Conf0;
4325 _ -> []
4326 end,
4327 {Conf, OsEnv} = fun() ->
4328 case filelib:is_file("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config.script)") of
4329 false -> {Conf1, []};
4330 true ->
4331 Bindings0 = erl_eval:new_bindings(),
4332 Bindings1 = erl_eval:add_binding('CONFIG', Conf1, Bindings0),
4333 Bindings = erl_eval:add_binding('SCRIPT', "$(call core_native_path,$(DEPS_DIR)/$1/rebar.config.script)", Bindings1),
4334 Before = os:getenv(),
4335 {ok, Conf2} = file:script("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config.script)", Bindings),
4336 {Conf2, lists:foldl(fun(E, Acc) -> lists:delete(E, Acc) end, os:getenv(), Before)}
4337 end
4338 end(),
4339 Write = fun (Text) ->
4340 file:write_file("$(call core_native_path,$(DEPS_DIR)/$1/Makefile)", Text, [append])
4341 end,
4342 Escape = fun (Text) ->
4343 re:replace(Text, "\\\\$$", "\$$$$", [global, {return, list}])
4344 end,
4345 Write("IGNORE_DEPS += edown eper eunit_formatters meck node_package "
4346 "rebar_lock_deps_plugin rebar_vsn_plugin reltool_util\n"),
4347 Write("C_SRC_DIR = /path/do/not/exist\n"),
4348 Write("C_SRC_TYPE = rebar\n"),
4349 Write("DRV_CFLAGS = -fPIC\nexport DRV_CFLAGS\n"),
4350 Write(["ERLANG_ARCH = ", rebar_utils:wordsize(), "\nexport ERLANG_ARCH\n"]),
4351 fun() ->
4352 Write("ERLC_OPTS = +debug_info\nexport ERLC_OPTS\n"),
4353 case lists:keyfind(erl_opts, 1, Conf) of
4354 false -> ok;
4355 {_, ErlOpts} ->
4356 lists:foreach(fun
4357 ({d, D}) ->
4358 Write("ERLC_OPTS += -D" ++ atom_to_list(D) ++ "=1\n");
4359 ({i, I}) ->
4360 Write(["ERLC_OPTS += -I ", I, "\n"]);
4361 ({platform_define, Regex, D}) ->
4362 case rebar_utils:is_arch(Regex) of
4363 true -> Write("ERLC_OPTS += -D" ++ atom_to_list(D) ++ "=1\n");
4364 false -> ok
4365 end;
4366 ({parse_transform, PT}) ->
4367 Write("ERLC_OPTS += +'{parse_transform, " ++ atom_to_list(PT) ++ "}'\n");
4368 (_) -> ok
4369 end, ErlOpts)
4370 end,
4371 Write("\n")
4372 end(),
4373 fun() ->
4374 File = case lists:keyfind(deps, 1, Conf) of
4375 false -> [];
4376 {_, Deps} ->
4377 [begin case case Dep of
4378 {N, S} when is_atom(N), is_list(S) -> {N, {hex, S}};
4379 {N, S} when is_tuple(S) -> {N, S};
4380 {N, _, S} -> {N, S};
4381 {N, _, S, _} -> {N, S};
4382 _ -> false
4383 end of
4384 false -> ok;
4385 {Name, Source} ->
4386 {Method, Repo, Commit} = case Source of
4387 {hex, V} -> {hex, V, undefined};
4388 {git, R} -> {git, R, master};
4389 {M, R, {branch, C}} -> {M, R, C};
4390 {M, R, {ref, C}} -> {M, R, C};
4391 {M, R, {tag, C}} -> {M, R, C};
4392 {M, R, C} -> {M, R, C}
4393 end,
4394 Write(io_lib:format("DEPS += ~s\ndep_~s = ~s ~s ~s~n", [Name, Name, Method, Repo, Commit]))
4395 end end || Dep <- Deps]
4396 end
4397 end(),
4398 fun() ->
4399 case lists:keyfind(erl_first_files, 1, Conf) of
4400 false -> ok;
4401 {_, Files} ->
4402 Names = [[" ", case lists:reverse(F) of
4403 "lre." ++ Elif -> lists:reverse(Elif);
4404 Elif -> lists:reverse(Elif)
4405 end] || "src/" ++ F <- Files],
4406 Write(io_lib:format("COMPILE_FIRST +=~s\n", [Names]))
4407 end
4408 end(),
4409 Write("\n\nrebar_dep: preprocess pre-deps deps pre-app app\n"),
4410 Write("\npreprocess::\n"),
4411 Write("\npre-deps::\n"),
4412 Write("\npre-app::\n"),
4413 PatchHook = fun(Cmd) ->
4414 case Cmd of
4415 "make -C" ++ Cmd1 -> "$$\(MAKE) -C" ++ Escape(Cmd1);
4416 "gmake -C" ++ Cmd1 -> "$$\(MAKE) -C" ++ Escape(Cmd1);
4417 "make " ++ Cmd1 -> "$$\(MAKE) -f Makefile.orig.mk " ++ Escape(Cmd1);
4418 "gmake " ++ Cmd1 -> "$$\(MAKE) -f Makefile.orig.mk " ++ Escape(Cmd1);
4419 _ -> Escape(Cmd)
4420 end
4421 end,
4422 fun() ->
4423 case lists:keyfind(pre_hooks, 1, Conf) of
4424 false -> ok;
4425 {_, Hooks} ->
4426 [case H of
4427 {'get-deps', Cmd} ->
4428 Write("\npre-deps::\n\t" ++ PatchHook(Cmd) ++ "\n");
4429 {compile, Cmd} ->
4430 Write("\npre-app::\n\tCC=$$\(CC) " ++ PatchHook(Cmd) ++ "\n");
4431 {Regex, compile, Cmd} ->
4432 case rebar_utils:is_arch(Regex) of
4433 true -> Write("\npre-app::\n\tCC=$$\(CC) " ++ PatchHook(Cmd) ++ "\n");
4434 false -> ok
4435 end;
4436 _ -> ok
4437 end || H <- Hooks]
4438 end
4439 end(),
4440 ShellToMk = fun(V) ->
4441 re:replace(re:replace(V, "(\\\\$$)(\\\\w*)", "\\\\1(\\\\2)", [global]),
4442 "-Werror\\\\b", "", [{return, list}, global])
4443 end,
4444 PortSpecs = fun() ->
4445 case lists:keyfind(port_specs, 1, Conf) of
4446 false ->
4447 case filelib:is_dir("$(call core_native_path,$(DEPS_DIR)/$1/c_src)") of
4448 false -> [];
4449 true ->
4450 [{"priv/" ++ proplists:get_value(so_name, Conf, "$(1)_drv.so"),
4451 proplists:get_value(port_sources, Conf, ["c_src/*.c"]), []}]
4452 end;
4453 {_, Specs} ->
4454 lists:flatten([case S of
4455 {Output, Input} -> {ShellToMk(Output), Input, []};
4456 {Regex, Output, Input} ->
4457 case rebar_utils:is_arch(Regex) of
4458 true -> {ShellToMk(Output), Input, []};
4459 false -> []
4460 end;
4461 {Regex, Output, Input, [{env, Env}]} ->
4462 case rebar_utils:is_arch(Regex) of
4463 true -> {ShellToMk(Output), Input, Env};
4464 false -> []
4465 end
4466 end || S <- Specs])
4467 end
4468 end(),
4469 PortSpecWrite = fun (Text) ->
4470 file:write_file("$(call core_native_path,$(DEPS_DIR)/$1/c_src/Makefile.erlang.mk)", Text, [append])
4471 end,
4472 case PortSpecs of
4473 [] -> ok;
4474 _ ->
4475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
4479 [code:lib_dir(erl_interface, lib)])),
4480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
4481 FilterEnv = fun(Env) ->
4482 lists:flatten([case E of
4483 {_, _} -> E;
4484 {Regex, K, V} ->
4485 case rebar_utils:is_arch(Regex) of
4486 true -> {K, V};
4487 false -> []
4488 end
4489 end || E <- Env])
4490 end,
4491 MergeEnv = fun(Env) ->
4492 lists:foldl(fun ({K, V}, Acc) ->
4493 case lists:keyfind(K, 1, Acc) of
4494 false -> [{K, rebar_utils:expand_env_variable(V, K, "")}|Acc];
4495 {_, V0} -> [{K, rebar_utils:expand_env_variable(V, K, V0)}|Acc]
4496 end
4497 end, [], Env)
4498 end,
4499 PortEnv = case lists:keyfind(port_env, 1, Conf) of
4500 false -> [];
4501 {_, PortEnv0} -> FilterEnv(PortEnv0)
4502 end,
4503 PortSpec = fun ({Output, Input0, Env}) ->
4504 filelib:ensure_dir("$(call core_native_path,$(DEPS_DIR)/$1/)" ++ Output),
4505 Input = [[" ", I] || I <- Input0],
4506 PortSpecWrite([
4507 [["\n", K, " = ", ShellToMk(V)] || {K, V} <- lists:reverse(MergeEnv(PortEnv))],
4508 case $(PLATFORM) of
4509 darwin -> "\n\nLDFLAGS += -flat_namespace -undefined suppress";
4510 _ -> ""
4511 end,
4512 "\n\nall:: ", Output, "\n\n",
4513 "%.o: %.c\n\t$$\(CC) -c -o $$\@ $$\< $$\(CFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
4519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
4520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
4521 case {filename:extension(Output), $(PLATFORM)} of
4522 {[], _} -> "\n";
4523 {_, darwin} -> "\n";
4524 _ -> " -shared\n"
4525 end])
4526 end,
4527 [PortSpec(S) || S <- PortSpecs]
4528 end,
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
4530 RunPlugin = fun(Plugin, Step) ->
4531 case erlang:function_exported(Plugin, Step, 2) of
4532 false -> ok;
4533 true ->
4534 c:cd("$(call core_native_path,$(DEPS_DIR)/$1/)"),
4535 Ret = Plugin:Step({config, "", Conf, dict:new(), dict:new(), dict:new(),
4536 dict:store(base_dir, "", dict:new())}, undefined),
4537 io:format("rebar plugin ~p step ~p ret ~p~n", [Plugin, Step, Ret])
4538 end
4539 end,
4540 fun() ->
4541 case lists:keyfind(plugins, 1, Conf) of
4542 false -> ok;
4543 {_, Plugins} ->
4544 [begin
4545 case lists:keyfind(deps, 1, Conf) of
4546 false -> ok;
4547 {_, Deps} ->
4548 case lists:keyfind(P, 1, Deps) of
4549 false -> ok;
4550 _ ->
4551 Path = "$(call core_native_path,$(DEPS_DIR)/)" ++ atom_to_list(P),
4552 io:format("~s", [os:cmd("$(MAKE) -C $(call core_native_path,$(DEPS_DIR)/$1) " ++ Path)]),
4553 io:format("~s", [os:cmd("$(MAKE) -C " ++ Path ++ " IS_DEP=1")]),
4554 code:add_patha(Path ++ "/ebin")
4555 end
4556 end
4557 end || P <- Plugins],
4558 [case code:load_file(P) of
4559 {module, P} -> ok;
4560 _ ->
4561 case lists:keyfind(plugin_dir, 1, Conf) of
4562 false -> ok;
4563 {_, PluginsDir} ->
4564 ErlFile = "$(call core_native_path,$(DEPS_DIR)/$1/)" ++ PluginsDir ++ "/" ++ atom_to_list(P) ++ ".erl",
4565 {ok, P, Bin} = compile:file(ErlFile, [binary]),
4566 {module, P} = code:load_binary(P, ErlFile, Bin)
4567 end
4568 end || P <- Plugins],
4569 [RunPlugin(P, preprocess) || P <- Plugins],
4570 [RunPlugin(P, pre_compile) || P <- Plugins],
4571 [RunPlugin(P, compile) || P <- Plugins]
4572 end
4573 end(),
4574 halt()
4575 endef
4576
4577 define dep_autopatch_appsrc_script.erl
4578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
4579 AppSrcScript = AppSrc ++ ".script",
4580 Bindings = erl_eval:new_bindings(),
4581 {ok, Conf} = file:script(AppSrcScript, Bindings),
4582 ok = file:write_file(AppSrc, io_lib:format("~p.~n", [Conf])),
4583 halt()
4584 endef
4585
4586 define dep_autopatch_appsrc.erl
4587 AppSrcOut = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
4588 AppSrcIn = case filelib:is_regular(AppSrcOut) of false -> "$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"; true -> AppSrcOut end,
4589 case filelib:is_regular(AppSrcIn) of
4590 false -> ok;
4591 true ->
4592 {ok, [{application, $(1), L0}]} = file:consult(AppSrcIn),
4593 L1 = lists:keystore(modules, 1, L0, {modules, []}),
4594 L2 = case lists:keyfind(vsn, 1, L1) of {_, git} -> lists:keyreplace(vsn, 1, L1, {vsn, "git"}); _ -> L1 end,
4595 L3 = case lists:keyfind(registered, 1, L2) of false -> [{registered, []}|L2]; _ -> L2 end,
4596 ok = file:write_file(AppSrcOut, io_lib:format("~p.~n", [{application, $(1), L3}])),
4597 case AppSrcOut of AppSrcIn -> ok; _ -> ok = file:delete(AppSrcIn) end
4598 end,
4599 halt()
4600 endef
4601
4602 define dep_fetch_git
4603 git clone -q -n -- $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1)); \
4604 cd $(DEPS_DIR)/$(call dep_name,$(1)) && git checkout -q $(call dep_commit,$(1));
4605 endef
4606
4607 define dep_fetch_git-submodule
4608 git submodule update --init -- $(DEPS_DIR)/$1;
4609 endef
4610
4611 define dep_fetch_hg
4612 hg clone -q -U $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1)); \
4613 cd $(DEPS_DIR)/$(call dep_name,$(1)) && hg update -q $(call dep_commit,$(1));
4614 endef
4615
4616 define dep_fetch_svn
4617 svn checkout -q $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
4618 endef
4619
4620 define dep_fetch_cp
4621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
4622 endef
4623
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
4626 endef
4627
4628 # Hex only has a package version. No need to look in the Erlang.mk packages.
4629 define dep_fetch_hex
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
4634 endef
4635
4636 define dep_fetch_fail
4637 echo "Error: Unknown or invalid dependency: $(1)." >&2; \
4638 exit 78;
4639 endef
4640
4641 # Kept for compatibility purposes with older Erlang.mk configuration.
4642 define dep_fetch_legacy
4643 $(warning WARNING: '$(1)' dependency configuration uses deprecated format.) \
4644 git clone -q -n -- $(word 1,$(dep_$(1))) $(DEPS_DIR)/$(1); \
4645 cd $(DEPS_DIR)/$(1) && git checkout -q $(if $(word 2,$(dep_$(1))),$(word 2,$(dep_$(1))),master);
4646 endef
4647
4648 define dep_fetch
4649 $(if $(dep_$(1)), \
4650 $(if $(dep_fetch_$(word 1,$(dep_$(1)))), \
4651 $(word 1,$(dep_$(1))), \
4652 $(if $(IS_DEP),legacy,fail)), \
4653 $(if $(filter $(1),$(PACKAGES)), \
4654 $(pkg_$(1)_fetch), \
4655 fail))
4656 endef
4657
4658 define dep_target
4659 $(DEPS_DIR)/$(call dep_name,$1):
4660 $(eval DEP_NAME := $(call dep_name,$1))
4661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
4662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
4664 exit 17; \
4665 fi
4666 $(verbose) mkdir -p $(DEPS_DIR)
4667 $(dep_verbose) $(call dep_fetch_$(strip $(call dep_fetch,$(1))),$(1))
4668 $(verbose) if [ -f $(DEPS_DIR)/$(1)/configure.ac -o -f $(DEPS_DIR)/$(1)/configure.in ] \
4669 && [ ! -f $(DEPS_DIR)/$(1)/configure ]; then \
4670 echo " AUTO " $(1); \
4671 cd $(DEPS_DIR)/$(1) && autoreconf -Wall -vif -I m4; \
4672 fi
4673 - $(verbose) if [ -f $(DEPS_DIR)/$(DEP_NAME)/configure ]; then \
4674 echo " CONF " $(DEP_STR); \
4675 cd $(DEPS_DIR)/$(DEP_NAME) && ./configure; \
4676 fi
4677 ifeq ($(filter $(1),$(NO_AUTOPATCH)),)
4678 $(verbose) if [ "$(1)" = "amqp_client" -a "$(RABBITMQ_CLIENT_PATCH)" ]; then \
4679 if [ ! -d $(DEPS_DIR)/rabbitmq-codegen ]; then \
4680 echo " PATCH Downloading rabbitmq-codegen"; \
4681 git clone https://github.com/rabbitmq/rabbitmq-codegen.git $(DEPS_DIR)/rabbitmq-codegen; \
4682 fi; \
4683 if [ ! -d $(DEPS_DIR)/rabbitmq-server ]; then \
4684 echo " PATCH Downloading rabbitmq-server"; \
4685 git clone https://github.com/rabbitmq/rabbitmq-server.git $(DEPS_DIR)/rabbitmq-server; \
4686 fi; \
4687 ln -s $(DEPS_DIR)/amqp_client/deps/rabbit_common-0.0.0 $(DEPS_DIR)/rabbit_common; \
4688 elif [ "$(1)" = "rabbit" -a "$(RABBITMQ_SERVER_PATCH)" ]; then \
4689 if [ ! -d $(DEPS_DIR)/rabbitmq-codegen ]; then \
4690 echo " PATCH Downloading rabbitmq-codegen"; \
4691 git clone https://github.com/rabbitmq/rabbitmq-codegen.git $(DEPS_DIR)/rabbitmq-codegen; \
4692 fi \
4693 else \
4694 $$(call dep_autopatch,$(DEP_NAME)) \
4695 fi
4696 endif
4697 endef
4698
4699 $(foreach dep,$(BUILD_DEPS) $(DEPS),$(eval $(call dep_target,$(dep))))
4700
4701 ifndef IS_APP
4702 clean:: clean-apps
4703
4704 clean-apps:
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
4707 done
4708
4709 distclean:: distclean-apps
4710
4711 distclean-apps:
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
4714 done
4715 endif
4716
4717 ifndef SKIP_DEPS
4718 distclean:: distclean-deps
4719
4720 distclean-deps:
4721 $(gen_verbose) rm -rf $(DEPS_DIR)
4722 endif
4723
4724 # Forward-declare variables used in core/deps-tools.mk. This is required
4725 # in case plugins use them.
4726
4727 ERLANG_MK_RECURSIVE_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-deps-list.log
4728 ERLANG_MK_RECURSIVE_DOC_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-doc-deps-list.log
4729 ERLANG_MK_RECURSIVE_REL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-rel-deps-list.log
4730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
4731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
4732
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
4734 # This file is part of erlang.mk and subject to the terms of the ISC License.
4735
4736 # Verbosity.
4737
4738 proto_verbose_0 = @echo " PROTO " $(filter %.proto,$(?F));
4739 proto_verbose = $(proto_verbose_$(V))
4740
4741 # Core targets.
4742
4743 define compile_proto
4744 $(verbose) mkdir -p ebin/ include/
4745 $(proto_verbose) $(call erlang,$(call compile_proto.erl,$(1)))
4746 $(proto_verbose) erlc +debug_info -o ebin/ ebin/*.erl
4747 $(verbose) rm ebin/*.erl
4748 endef
4749
4750 define compile_proto.erl
4751 [begin
4752 protobuffs_compile:generate_source(F,
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
4755 end || F <- string:tokens("$(1)", " ")],
4756 halt().
4757 endef
4758
4759 ifneq ($(wildcard src/),)
4760 ebin/$(PROJECT).app:: $(sort $(call core_find,src/,*.proto))
4761 $(if $(strip $?),$(call compile_proto,$?))
4762 endif
4763
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
4765 # This file is part of erlang.mk and subject to the terms of the ISC License.
4766
4767 .PHONY: clean-app
4768
4769 # Configuration.
4770
4771 ERLC_OPTS ?= -Werror +debug_info +warn_export_vars +warn_shadow_vars \
4772 +warn_obsolete_guard # +bin_opt_info +warn_export_all +warn_missing_spec
4773 COMPILE_FIRST ?=
4774 COMPILE_FIRST_PATHS = $(addprefix src/,$(addsuffix .erl,$(COMPILE_FIRST)))
4775 ERLC_EXCLUDE ?=
4776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
4777
4778 ERLC_ASN1_OPTS ?=
4779
4780 ERLC_MIB_OPTS ?=
4781 COMPILE_MIB_FIRST ?=
4782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
4783
4784 # Verbosity.
4785
4786 app_verbose_0 = @echo " APP " $(PROJECT);
4787 app_verbose_2 = set -x;
4788 app_verbose = $(app_verbose_$(V))
4789
4790 appsrc_verbose_0 = @echo " APP " $(PROJECT).app.src;
4791 appsrc_verbose_2 = set -x;
4792 appsrc_verbose = $(appsrc_verbose_$(V))
4793
4794 makedep_verbose_0 = @echo " DEPEND" $(PROJECT).d;
4795 makedep_verbose_2 = set -x;
4796 makedep_verbose = $(makedep_verbose_$(V))
4797
4798 erlc_verbose_0 = @echo " ERLC " $(filter-out $(patsubst %,%.erl,$(ERLC_EXCLUDE)),\
4799 $(filter %.erl %.core,$(?F)));
4800 erlc_verbose_2 = set -x;
4801 erlc_verbose = $(erlc_verbose_$(V))
4802
4803 xyrl_verbose_0 = @echo " XYRL " $(filter %.xrl %.yrl,$(?F));
4804 xyrl_verbose_2 = set -x;
4805 xyrl_verbose = $(xyrl_verbose_$(V))
4806
4807 asn1_verbose_0 = @echo " ASN1 " $(filter %.asn1,$(?F));
4808 asn1_verbose_2 = set -x;
4809 asn1_verbose = $(asn1_verbose_$(V))
4810
4811 mib_verbose_0 = @echo " MIB " $(filter %.bin %.mib,$(?F));
4812 mib_verbose_2 = set -x;
4813 mib_verbose = $(mib_verbose_$(V))
4814
4815 ifneq ($(wildcard src/),)
4816
4817 # Targets.
4818
4819 ifeq ($(wildcard ebin/test),)
4820 app:: deps $(PROJECT).d
4821 $(verbose) $(MAKE) --no-print-directory app-build
4822 else
4823 app:: clean deps $(PROJECT).d
4824 $(verbose) $(MAKE) --no-print-directory app-build
4825 endif
4826
4827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
4828 define app_file
4829 {application, '$(PROJECT)', [
4830 {description, "$(PROJECT_DESCRIPTION)"},
4831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
4832 {id$(comma)$(space)"$(1)"}$(comma))
4833 {modules, [$(call comma_list,$(2))]},
4834 {registered, []},
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
4837 ]}.
4838 endef
4839 else
4840 define app_file
4841 {application, '$(PROJECT)', [
4842 {description, "$(PROJECT_DESCRIPTION)"},
4843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
4844 {id$(comma)$(space)"$(1)"}$(comma))
4845 {modules, [$(call comma_list,$(2))]},
4846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
4847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
4850 ]}.
4851 endef
4852 endif
4853
4854 app-build: ebin/$(PROJECT).app
4855 $(verbose) :
4856
4857 # Source files.
4858
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
4863
4864 # ASN.1 files.
4865
4866 ifneq ($(wildcard asn1/),)
4867 ASN1_FILES = $(sort $(call core_find,asn1/,*.asn1))
4868 ERL_FILES += $(addprefix src/,$(patsubst %.asn1,%.erl,$(notdir $(ASN1_FILES))))
4869
4870 define compile_asn1
4871 $(verbose) mkdir -p include/
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
4873 $(verbose) mv asn1/*.erl src/
4874 $(verbose) mv asn1/*.hrl include/
4875 $(verbose) mv asn1/*.asn1db include/
4876 endef
4877
4878 $(PROJECT).d:: $(ASN1_FILES)
4879 $(if $(strip $?),$(call compile_asn1,$?))
4880 endif
4881
4882 # SNMP MIB files.
4883
4884 ifneq ($(wildcard mibs/),)
4885 MIB_FILES = $(sort $(call core_find,mibs/,*.mib))
4886
4887 $(PROJECT).d:: $(COMPILE_MIB_FIRST_PATHS) $(MIB_FILES)
4888 $(verbose) mkdir -p include/ priv/mibs/
4889 $(mib_verbose) erlc -v $(ERLC_MIB_OPTS) -o priv/mibs/ -I priv/mibs/ $?
4890 $(mib_verbose) erlc -o include/ -- $(addprefix priv/mibs/,$(patsubst %.mib,%.bin,$(notdir $?)))
4891 endif
4892
4893 # Leex and Yecc files.
4894
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
4896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
4897 ERL_FILES += $(XRL_ERL_FILES)
4898
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
4900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
4901 ERL_FILES += $(YRL_ERL_FILES)
4902
4903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
4905
4906 # Erlang and Core Erlang files.
4907
4908 define makedep.erl
4909 E = ets:new(makedep, [bag]),
4910 G = digraph:new([acyclic]),
4911 ErlFiles = lists:usort(string:tokens("$(ERL_FILES)", " ")),
4912 Modules = [{list_to_atom(filename:basename(F, ".erl")), F} || F <- ErlFiles],
4913 Add = fun (Mod, Dep) ->
4914 case lists:keyfind(Dep, 1, Modules) of
4915 false -> ok;
4916 {_, DepFile} ->
4917 {_, ModFile} = lists:keyfind(Mod, 1, Modules),
4918 ets:insert(E, {ModFile, DepFile}),
4919 digraph:add_vertex(G, Mod),
4920 digraph:add_vertex(G, Dep),
4921 digraph:add_edge(G, Mod, Dep)
4922 end
4923 end,
4924 AddHd = fun (F, Mod, DepFile) ->
4925 case file:open(DepFile, [read]) of
4926 {error, enoent} -> ok;
4927 {ok, Fd} ->
4928 F(F, Fd, Mod),
4929 {_, ModFile} = lists:keyfind(Mod, 1, Modules),
4930 ets:insert(E, {ModFile, DepFile})
4931 end
4932 end,
4933 Attr = fun
4934 (F, Mod, behavior, Dep) -> Add(Mod, Dep);
4935 (F, Mod, behaviour, Dep) -> Add(Mod, Dep);
4936 (F, Mod, compile, {parse_transform, Dep}) -> Add(Mod, Dep);
4937 (F, Mod, compile, Opts) when is_list(Opts) ->
4938 case proplists:get_value(parse_transform, Opts) of
4939 undefined -> ok;
4940 Dep -> Add(Mod, Dep)
4941 end;
4942 (F, Mod, include, Hrl) ->
4943 case filelib:is_file("include/" ++ Hrl) of
4944 true -> AddHd(F, Mod, "include/" ++ Hrl);
4945 false ->
4946 case filelib:is_file("src/" ++ Hrl) of
4947 true -> AddHd(F, Mod, "src/" ++ Hrl);
4948 false -> false
4949 end
4950 end;
4951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
4952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
4953 (F, Mod, import, {Imp, _}) ->
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
4960 false -> ok;
4961 true -> Add(Mod, Imp)
4962 end;
4963 (_, _, _, _) -> ok
4964 end,
4965 MakeDepend = fun(F, Fd, Mod) ->
4966 case io:parse_erl_form(Fd, undefined) of
4967 {ok, {attribute, _, Key, Value}, _} ->
4968 Attr(F, Mod, Key, Value),
4969 F(F, Fd, Mod);
4970 {eof, _} ->
4971 file:close(Fd);
4972 _ ->
4973 F(F, Fd, Mod)
4974 end
4975 end,
4976 [begin
4977 Mod = list_to_atom(filename:basename(F, ".erl")),
4978 {ok, Fd} = file:open(F, [read]),
4979 MakeDepend(MakeDepend, Fd, Mod)
4980 end || F <- ErlFiles],
4981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
4982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
4991 ok = file:write_file("$(1)", [
4992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
4994 ]),
4995 halt()
4996 endef
4997
4998 ifeq ($(if $(NO_MAKEDEP),$(wildcard $(PROJECT).d),),)
4999 $(PROJECT).d:: $(ERL_FILES) $(call core_find,include/,*.hrl) $(MAKEFILE_LIST)
5000 $(makedep_verbose) $(call erlang,$(call makedep.erl,$@))
5001 endif
5002
5003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
5004 # Rebuild everything when the Makefile changes.
5005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
5008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
5009 touch -c $(PROJECT).d; \
5010 fi
5011 $(verbose) touch $@
5012
5013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
5014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
5015 endif
5016
5017 include $(wildcard $(PROJECT).d)
5018
5019 ebin/$(PROJECT).app:: ebin/
5020
5021 ebin/:
5022 $(verbose) mkdir -p ebin/
5023
5024 define compile_erl
5025 $(erlc_verbose) erlc -v $(if $(IS_DEP),$(filter-out -Werror,$(ERLC_OPTS)),$(ERLC_OPTS)) -o ebin/ \
5026 -pa ebin/ -I include/ $(filter-out $(ERLC_EXCLUDE_PATHS),$(COMPILE_FIRST_PATHS) $(1))
5027 endef
5028
5029 ebin/$(PROJECT).app:: $(ERL_FILES) $(CORE_FILES) $(wildcard src/$(PROJECT).app.src)
5030 $(eval FILES_TO_COMPILE := $(filter-out src/$(PROJECT).app.src,$?))
5031 $(if $(strip $(FILES_TO_COMPILE)),$(call compile_erl,$(FILES_TO_COMPILE)))
5032 $(eval GITDESCRIBE := $(shell git describe --dirty --abbrev=7 --tags --always --first-parent 2>/dev/null || true))
5033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
5034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
5035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
5037 > ebin/$(PROJECT).app
5038 else
5039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
5040 echo "Empty modules entry not found in $(PROJECT).app.src. Please consult the erlang.mk README for instructions." >&2; \
5041 exit 1; \
5042 fi
5043 $(appsrc_verbose) cat src/$(PROJECT).app.src \
5044 | sed "s/{[[:space:]]*modules[[:space:]]*,[[:space:]]*\[\]}/{modules, \[$(call comma_list,$(MODULES))\]}/" \
5045 | sed "s/{id,[[:space:]]*\"git\"}/{id, \"$(subst /,\/,$(GITDESCRIBE))\"}/" \
5046 > ebin/$(PROJECT).app
5047 endif
5048
5049 clean:: clean-app
5050
5051 clean-app:
5052 $(gen_verbose) rm -rf $(PROJECT).d ebin/ priv/mibs/ $(XRL_ERL_FILES) $(YRL_ERL_FILES) \
5053 $(addprefix include/,$(patsubst %.mib,%.hrl,$(notdir $(MIB_FILES)))) \
5054 $(addprefix include/,$(patsubst %.asn1,%.hrl,$(notdir $(ASN1_FILES)))) \
5055 $(addprefix include/,$(patsubst %.asn1,%.asn1db,$(notdir $(ASN1_FILES)))) \
5056 $(addprefix src/,$(patsubst %.asn1,%.erl,$(notdir $(ASN1_FILES))))
5057
5058 endif
5059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
5061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
5062 # This file is part of erlang.mk and subject to the terms of the ISC License.
5063
5064 .PHONY: docs-deps
5065
5066 # Configuration.
5067
5068 ALL_DOC_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(DOC_DEPS))
5069
5070 # Targets.
5071
5072 $(foreach dep,$(DOC_DEPS),$(eval $(call dep_target,$(dep))))
5073
5074 ifneq ($(SKIP_DEPS),)
5075 doc-deps:
5076 else
5077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
5082 # This file is part of erlang.mk and subject to the terms of the ISC License.
5083
5084 .PHONY: rel-deps
5085
5086 # Configuration.
5087
5088 ALL_REL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(REL_DEPS))
5089
5090 # Targets.
5091
5092 $(foreach dep,$(REL_DEPS),$(eval $(call dep_target,$(dep))))
5093
5094 ifneq ($(SKIP_DEPS),)
5095 rel-deps:
5096 else
5097 rel-deps: $(ALL_REL_DEPS_DIRS)
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
5102 # This file is part of erlang.mk and subject to the terms of the ISC License.
5103
5104 .PHONY: test-deps test-dir test-build clean-test-dir
5105
5106 # Configuration.
5107
5108 TEST_DIR ?= $(CURDIR)/test
5109
5110 ALL_TEST_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(TEST_DEPS))
5111
5112 TEST_ERLC_OPTS ?= +debug_info +warn_export_vars +warn_shadow_vars +warn_obsolete_guard
5113 TEST_ERLC_OPTS += -DTEST=1
5114
5115 # Targets.
5116
5117 $(foreach dep,$(TEST_DEPS),$(eval $(call dep_target,$(dep))))
5118
5119 ifneq ($(SKIP_DEPS),)
5120 test-deps:
5121 else
5122 test-deps: $(ALL_TEST_DEPS_DIRS)
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5124 endif
5125
5126 ifneq ($(wildcard $(TEST_DIR)),)
5127 test-dir:
5128 $(gen_verbose) erlc -v $(TEST_ERLC_OPTS) -I include/ -o $(TEST_DIR) \
5129 $(call core_find,$(TEST_DIR)/,*.erl) -pa ebin/
5130 endif
5131
5132 ifeq ($(wildcard src),)
5133 test-build:: ERLC_OPTS=$(TEST_ERLC_OPTS)
5134 test-build:: clean deps test-deps
5135 $(verbose) $(MAKE) --no-print-directory test-dir ERLC_OPTS="$(TEST_ERLC_OPTS)"
5136 else
5137 ifeq ($(wildcard ebin/test),)
5138 test-build:: ERLC_OPTS=$(TEST_ERLC_OPTS)
5139 test-build:: clean deps test-deps $(PROJECT).d
5140 $(verbose) $(MAKE) --no-print-directory app-build test-dir ERLC_OPTS="$(TEST_ERLC_OPTS)"
5141 $(gen_verbose) touch ebin/test
5142 else
5143 test-build:: ERLC_OPTS=$(TEST_ERLC_OPTS)
5144 test-build:: deps test-deps $(PROJECT).d
5145 $(verbose) $(MAKE) --no-print-directory app-build test-dir ERLC_OPTS="$(TEST_ERLC_OPTS)"
5146 endif
5147
5148 clean:: clean-test-dir
5149
5150 clean-test-dir:
5151 ifneq ($(wildcard $(TEST_DIR)/*.beam),)
5152 $(gen_verbose) rm -f $(TEST_DIR)/*.beam
5153 endif
5154 endif
5155
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
5157 # This file is part of erlang.mk and subject to the terms of the ISC License.
5158
5159 .PHONY: rebar.config
5160
5161 # We strip out -Werror because we don't want to fail due to
5162 # warnings when used as a dependency.
5163
5164 compat_prepare_erlc_opts = $(shell echo "$1" | sed 's/, */,/g')
5165
5166 define compat_convert_erlc_opts
5167 $(if $(filter-out -Werror,$1),\
5168 $(if $(findstring +,$1),\
5169 $(shell echo $1 | cut -b 2-)))
5170 endef
5171
5172 define compat_erlc_opts_to_list
5173 [$(call comma_list,$(foreach o,$(call compat_prepare_erlc_opts,$1),$(call compat_convert_erlc_opts,$o)))]
5174 endef
5175
5176 define compat_rebar_config
5177 {deps, [
5178 $(call comma_list,$(foreach d,$(DEPS),\
5179 $(if $(filter hex,$(call dep_fetch,$d)),\
5180 {$(call dep_name,$d)$(comma)"$(call dep_repo,$d)"},\
5181 {$(call dep_name,$d)$(comma)".*"$(comma){git,"$(call dep_repo,$d)"$(comma)"$(call dep_commit,$d)"}})))
5182 ]}.
5183 {erl_opts, $(call compat_erlc_opts_to_list,$(ERLC_OPTS))}.
5184 endef
5185
5186 $(eval _compat_rebar_config = $$(compat_rebar_config))
5187 $(eval export _compat_rebar_config)
5188
5189 rebar.config:
5190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
5191
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
5193 # This file is part of erlang.mk and subject to the terms of the ISC License.
5194
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
5200
5201 docs:: asciidoc
5202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
5207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
5210
5211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
5212 asciidoc-guide:
5213 else
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
5215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
5216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
5227 asciidoc-manual:
5228 else
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
5262
5263 install-docs:: install-asciidoc
5264
5265 install-asciidoc: asciidoc-manual
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
5276 # This file is part of erlang.mk and subject to the terms of the ISC License.
5277
5278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
5279
5280 # Core targets.
5281
5282 help::
5283 $(verbose) printf "%s\n" "" \
5284 "Bootstrap targets:" \
5285 " bootstrap Generate a skeleton of an OTP application" \
5286 " bootstrap-lib Generate a skeleton of an OTP library" \
5287 " bootstrap-rel Generate the files needed to build a release" \
5288 " new-app in=NAME Create a new local OTP application NAME" \
5289 " new-lib in=NAME Create a new local OTP library NAME" \
5290 " new t=TPL n=NAME Generate a module NAME based on the template TPL" \
5291 " new t=T n=N in=APP Generate a module NAME based on the template TPL in APP" \
5292 " list-templates List available templates"
5293
5294 # Bootstrap templates.
5295
5296 define bs_appsrc
5297 {application, $p, [
5298 {description, ""},
5299 {vsn, "0.1.0"},
5300 {id, "git"},
5301 {modules, []},
5302 {registered, []},
5303 {applications, [
5304 kernel,
5305 stdlib
5306 ]},
5307 {mod, {$p_app, []}},
5308 {env, []}
5309 ]}.
5310 endef
5311
5312 define bs_appsrc_lib
5313 {application, $p, [
5314 {description, ""},
5315 {vsn, "0.1.0"},
5316 {id, "git"},
5317 {modules, []},
5318 {registered, []},
5319 {applications, [
5320 kernel,
5321 stdlib
5322 ]}
5323 ]}.
5324 endef
5325
5326 # To prevent autocompletion issues with ZSH, we add "include erlang.mk"
5327 # separately during the actual bootstrap.
5328 ifdef SP
5329 define bs_Makefile
5330 PROJECT = $p
5331 PROJECT_DESCRIPTION = New project
5332 PROJECT_VERSION = 0.1.0
5333
5334 # Whitespace to be used when creating files from templates.
5335 SP = $(SP)
5336
5337 endef
5338 else
5339 define bs_Makefile
5340 PROJECT = $p
5341 PROJECT_DESCRIPTION = New project
5342 PROJECT_VERSION = 0.1.0
5343
5344 endef
5345 endif
5346
5347 define bs_apps_Makefile
5348 PROJECT = $p
5349 PROJECT_DESCRIPTION = New project
5350 PROJECT_VERSION = 0.1.0
5351
5352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
5353 endef
5354
5355 define bs_app
5356 -module($p_app).
5357 -behaviour(application).
5358
5359 -export([start/2]).
5360 -export([stop/1]).
5361
5362 start(_Type, _Args) ->
5363 $p_sup:start_link().
5364
5365 stop(_State) ->
5366 ok.
5367 endef
5368
5369 define bs_relx_config
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
5371 {extended_start_script, true}.
5372 {sys_config, "rel/sys.config"}.
5373 {vm_args, "rel/vm.args"}.
5374 endef
5375
5376 define bs_sys_config
5377 [
5378 ].
5379 endef
5380
5381 define bs_vm_args
5382 -name $p@127.0.0.1
5383 -setcookie $p
5384 -heart
5385 endef
5386
5387 # Normal templates.
5388
5389 define tpl_supervisor
5390 -module($(n)).
5391 -behaviour(supervisor).
5392
5393 -export([start_link/0]).
5394 -export([init/1]).
5395
5396 start_link() ->
5397 supervisor:start_link({local, ?MODULE}, ?MODULE, []).
5398
5399 init([]) ->
5400 Procs = [],
5401 {ok, {{one_for_one, 1, 5}, Procs}}.
5402 endef
5403
5404 define tpl_gen_server
5405 -module($(n)).
5406 -behaviour(gen_server).
5407
5408 %% API.
5409 -export([start_link/0]).
5410
5411 %% gen_server.
5412 -export([init/1]).
5413 -export([handle_call/3]).
5414 -export([handle_cast/2]).
5415 -export([handle_info/2]).
5416 -export([terminate/2]).
5417 -export([code_change/3]).
5418
5419 -record(state, {
5420 }).
5421
5422 %% API.
5423
5424 -spec start_link() -> {ok, pid()}.
5425 start_link() ->
5426 gen_server:start_link(?MODULE, [], []).
5427
5428 %% gen_server.
5429
5430 init([]) ->
5431 {ok, #state{}}.
5432
5433 handle_call(_Request, _From, State) ->
5434 {reply, ignored, State}.
5435
5436 handle_cast(_Msg, State) ->
5437 {noreply, State}.
5438
5439 handle_info(_Info, State) ->
5440 {noreply, State}.
5441
5442 terminate(_Reason, _State) ->
5443 ok.
5444
5445 code_change(_OldVsn, State, _Extra) ->
5446 {ok, State}.
5447 endef
5448
5449 define tpl_module
5450 -module($(n)).
5451 -export([]).
5452 endef
5453
5454 define tpl_cowboy_http
5455 -module($(n)).
5456 -behaviour(cowboy_http_handler).
5457
5458 -export([init/3]).
5459 -export([handle/2]).
5460 -export([terminate/3]).
5461
5462 -record(state, {
5463 }).
5464
5465 init(_, Req, _Opts) ->
5466 {ok, Req, #state{}}.
5467
5468 handle(Req, State=#state{}) ->
5469 {ok, Req2} = cowboy_req:reply(200, Req),
5470 {ok, Req2, State}.
5471
5472 terminate(_Reason, _Req, _State) ->
5473 ok.
5474 endef
5475
5476 define tpl_gen_fsm
5477 -module($(n)).
5478 -behaviour(gen_fsm).
5479
5480 %% API.
5481 -export([start_link/0]).
5482
5483 %% gen_fsm.
5484 -export([init/1]).
5485 -export([state_name/2]).
5486 -export([handle_event/3]).
5487 -export([state_name/3]).
5488 -export([handle_sync_event/4]).
5489 -export([handle_info/3]).
5490 -export([terminate/3]).
5491 -export([code_change/4]).
5492
5493 -record(state, {
5494 }).
5495
5496 %% API.
5497
5498 -spec start_link() -> {ok, pid()}.
5499 start_link() ->
5500 gen_fsm:start_link(?MODULE, [], []).
5501
5502 %% gen_fsm.
5503
5504 init([]) ->
5505 {ok, state_name, #state{}}.
5506
5507 state_name(_Event, StateData) ->
5508 {next_state, state_name, StateData}.
5509
5510 handle_event(_Event, StateName, StateData) ->
5511 {next_state, StateName, StateData}.
5512
5513 state_name(_Event, _From, StateData) ->
5514 {reply, ignored, state_name, StateData}.
5515
5516 handle_sync_event(_Event, _From, StateName, StateData) ->
5517 {reply, ignored, StateName, StateData}.
5518
5519 handle_info(_Info, StateName, StateData) ->
5520 {next_state, StateName, StateData}.
5521
5522 terminate(_Reason, _StateName, _StateData) ->
5523 ok.
5524
5525 code_change(_OldVsn, StateName, StateData, _Extra) ->
5526 {ok, StateName, StateData}.
5527 endef
5528
5529 define tpl_cowboy_loop
5530 -module($(n)).
5531 -behaviour(cowboy_loop_handler).
5532
5533 -export([init/3]).
5534 -export([info/3]).
5535 -export([terminate/3]).
5536
5537 -record(state, {
5538 }).
5539
5540 init(_, Req, _Opts) ->
5541 {loop, Req, #state{}, 5000, hibernate}.
5542
5543 info(_Info, Req, State) ->
5544 {loop, Req, State, hibernate}.
5545
5546 terminate(_Reason, _Req, _State) ->
5547 ok.
5548 endef
5549
5550 define tpl_cowboy_rest
5551 -module($(n)).
5552
5553 -export([init/3]).
5554 -export([content_types_provided/2]).
5555 -export([get_html/2]).
5556
5557 init(_, _Req, _Opts) ->
5558 {upgrade, protocol, cowboy_rest}.
5559
5560 content_types_provided(Req, State) ->
5561 {[{{<<"text">>, <<"html">>, '*'}, get_html}], Req, State}.
5562
5563 get_html(Req, State) ->
5564 {<<"<html><body>This is REST!</body></html>">>, Req, State}.
5565 endef
5566
5567 define tpl_cowboy_ws
5568 -module($(n)).
5569 -behaviour(cowboy_websocket_handler).
5570
5571 -export([init/3]).
5572 -export([websocket_init/3]).
5573 -export([websocket_handle/3]).
5574 -export([websocket_info/3]).
5575 -export([websocket_terminate/3]).
5576
5577 -record(state, {
5578 }).
5579
5580 init(_, _, _) ->
5581 {upgrade, protocol, cowboy_websocket}.
5582
5583 websocket_init(_, Req, _Opts) ->
5584 Req2 = cowboy_req:compact(Req),
5585 {ok, Req2, #state{}}.
5586
5587 websocket_handle({text, Data}, Req, State) ->
5588 {reply, {text, Data}, Req, State};
5589 websocket_handle({binary, Data}, Req, State) ->
5590 {reply, {binary, Data}, Req, State};
5591 websocket_handle(_Frame, Req, State) ->
5592 {ok, Req, State}.
5593
5594 websocket_info(_Info, Req, State) ->
5595 {ok, Req, State}.
5596
5597 websocket_terminate(_Reason, _Req, _State) ->
5598 ok.
5599 endef
5600
5601 define tpl_ranch_protocol
5602 -module($(n)).
5603 -behaviour(ranch_protocol).
5604
5605 -export([start_link/4]).
5606 -export([init/4]).
5607
5608 -type opts() :: [].
5609 -export_type([opts/0]).
5610
5611 -record(state, {
5612 socket :: inet:socket(),
5613 transport :: module()
5614 }).
5615
5616 start_link(Ref, Socket, Transport, Opts) ->
5617 Pid = spawn_link(?MODULE, init, [Ref, Socket, Transport, Opts]),
5618 {ok, Pid}.
5619
5620 -spec init(ranch:ref(), inet:socket(), module(), opts()) -> ok.
5621 init(Ref, Socket, Transport, _Opts) ->
5622 ok = ranch:accept_ack(Ref),
5623 loop(#state{socket=Socket, transport=Transport}).
5624
5625 loop(State) ->
5626 loop(State).
5627 endef
5628
5629 # Plugin-specific targets.
5630
5631 define render_template
5632 $(verbose) printf -- '$(subst $(newline),\n,$(subst %,%%,$(subst ','\'',$(subst $(tab),$(WS),$(call $(1))))))\n' > $(2)
5633 endef
5634
5635 ifndef WS
5636 ifdef SP
5637 WS = $(subst a,,a $(wordlist 1,$(SP),a a a a a a a a a a a a a a a a a a a a))
5638 else
5639 WS = $(tab)
5640 endif
5641 endif
5642
5643 bootstrap:
5644 ifneq ($(wildcard src/),)
5645 $(error Error: src/ directory already exists)
5646 endif
5647 $(eval p := $(PROJECT))
5648 $(eval n := $(PROJECT)_sup)
5649 $(call render_template,bs_Makefile,Makefile)
5650 $(verbose) echo "include erlang.mk" >> Makefile
5651 $(verbose) mkdir src/
5652 ifdef LEGACY
5653 $(call render_template,bs_appsrc,src/$(PROJECT).app.src)
5654 endif
5655 $(call render_template,bs_app,src/$(PROJECT)_app.erl)
5656 $(call render_template,tpl_supervisor,src/$(PROJECT)_sup.erl)
5657
5658 bootstrap-lib:
5659 ifneq ($(wildcard src/),)
5660 $(error Error: src/ directory already exists)
5661 endif
5662 $(eval p := $(PROJECT))
5663 $(call render_template,bs_Makefile,Makefile)
5664 $(verbose) echo "include erlang.mk" >> Makefile
5665 $(verbose) mkdir src/
5666 ifdef LEGACY
5667 $(call render_template,bs_appsrc_lib,src/$(PROJECT).app.src)
5668 endif
5669
5670 bootstrap-rel:
5671 ifneq ($(wildcard relx.config),)
5672 $(error Error: relx.config already exists)
5673 endif
5674 ifneq ($(wildcard rel/),)
5675 $(error Error: rel/ directory already exists)
5676 endif
5677 $(eval p := $(PROJECT))
5678 $(call render_template,bs_relx_config,relx.config)
5679 $(verbose) mkdir rel/
5680 $(call render_template,bs_sys_config,rel/sys.config)
5681 $(call render_template,bs_vm_args,rel/vm.args)
5682
5683 new-app:
5684 ifndef in
5685 $(error Usage: $(MAKE) new-app in=APP)
5686 endif
5687 ifneq ($(wildcard $(APPS_DIR)/$in),)
5688 $(error Error: Application $in already exists)
5689 endif
5690 $(eval p := $(in))
5691 $(eval n := $(in)_sup)
5692 $(verbose) mkdir -p $(APPS_DIR)/$p/src/
5693 $(call render_template,bs_apps_Makefile,$(APPS_DIR)/$p/Makefile)
5694 ifdef LEGACY
5695 $(call render_template,bs_appsrc,$(APPS_DIR)/$p/src/$p.app.src)
5696 endif
5697 $(call render_template,bs_app,$(APPS_DIR)/$p/src/$p_app.erl)
5698 $(call render_template,tpl_supervisor,$(APPS_DIR)/$p/src/$p_sup.erl)
5699
5700 new-lib:
5701 ifndef in
5702 $(error Usage: $(MAKE) new-lib in=APP)
5703 endif
5704 ifneq ($(wildcard $(APPS_DIR)/$in),)
5705 $(error Error: Application $in already exists)
5706 endif
5707 $(eval p := $(in))
5708 $(verbose) mkdir -p $(APPS_DIR)/$p/src/
5709 $(call render_template,bs_apps_Makefile,$(APPS_DIR)/$p/Makefile)
5710 ifdef LEGACY
5711 $(call render_template,bs_appsrc_lib,$(APPS_DIR)/$p/src/$p.app.src)
5712 endif
5713
5714 new:
5715 ifeq ($(wildcard src/)$(in),)
5716 $(error Error: src/ directory does not exist)
5717 endif
5718 ifndef t
5719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
5720 endif
5721 ifndef n
5722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
5723 endif
5724 ifdef in
5725 $(verbose) $(MAKE) -C $(APPS_DIR)/$(in)/ new t=$t n=$n in=
5726 else
5727 $(call render_template,tpl_$(t),src/$(n).erl)
5728 endif
5729
5730 list-templates:
5731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
5732
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
5734 # This file is part of erlang.mk and subject to the terms of the ISC License.
5735
5736 .PHONY: clean-c_src distclean-c_src-env
5737
5738 # Configuration.
5739
5740 C_SRC_DIR ?= $(CURDIR)/c_src
5741 C_SRC_ENV ?= $(C_SRC_DIR)/env.mk
5742 C_SRC_OUTPUT ?= $(CURDIR)/priv/$(PROJECT)
5743 C_SRC_TYPE ?= shared
5744
5745 # System type and C compiler/flags.
5746
5747 ifeq ($(PLATFORM),msys2)
5748 C_SRC_OUTPUT_EXECUTABLE_EXTENSION ?= .exe
5749 C_SRC_OUTPUT_SHARED_EXTENSION ?= .dll
5750 else
5751 C_SRC_OUTPUT_EXECUTABLE_EXTENSION ?=
5752 C_SRC_OUTPUT_SHARED_EXTENSION ?= .so
5753 endif
5754
5755 ifeq ($(C_SRC_TYPE),shared)
5756 C_SRC_OUTPUT_FILE = $(C_SRC_OUTPUT)$(C_SRC_OUTPUT_SHARED_EXTENSION)
5757 else
5758 C_SRC_OUTPUT_FILE = $(C_SRC_OUTPUT)$(C_SRC_OUTPUT_EXECUTABLE_EXTENSION)
5759 endif
5760
5761 ifeq ($(PLATFORM),msys2)
5762 # We hardcode the compiler used on MSYS2. The default CC=cc does
5763 # not produce working code. The "gcc" MSYS2 package also doesn't.
5764 CC = /mingw64/bin/gcc
5765 export CC
5766 CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes
5767 CXXFLAGS ?= -O3 -finline-functions -Wall
5768 else ifeq ($(PLATFORM),darwin)
5769 CC ?= cc
5770 CFLAGS ?= -O3 -std=c99 -arch x86_64 -finline-functions -Wall -Wmissing-prototypes
5771 CXXFLAGS ?= -O3 -arch x86_64 -finline-functions -Wall
5772 LDFLAGS ?= -arch x86_64 -flat_namespace -undefined suppress
5773 else ifeq ($(PLATFORM),freebsd)
5774 CC ?= cc
5775 CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes
5776 CXXFLAGS ?= -O3 -finline-functions -Wall
5777 else ifeq ($(PLATFORM),linux)
5778 CC ?= gcc
5779 CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes
5780 CXXFLAGS ?= -O3 -finline-functions -Wall
5781 endif
5782
5783 ifneq ($(PLATFORM),msys2)
5784 CFLAGS += -fPIC
5785 CXXFLAGS += -fPIC
5786 endif
5787
5788 CFLAGS += -I"$(ERTS_INCLUDE_DIR)" -I"$(ERL_INTERFACE_INCLUDE_DIR)"
5789 CXXFLAGS += -I"$(ERTS_INCLUDE_DIR)" -I"$(ERL_INTERFACE_INCLUDE_DIR)"
5790
5791 LDLIBS += -L"$(ERL_INTERFACE_LIB_DIR)" -lerl_interface -lei
5792
5793 # Verbosity.
5794
5795 c_verbose_0 = @echo " C " $(?F);
5796 c_verbose = $(c_verbose_$(V))
5797
5798 cpp_verbose_0 = @echo " CPP " $(?F);
5799 cpp_verbose = $(cpp_verbose_$(V))
5800
5801 link_verbose_0 = @echo " LD " $(@F);
5802 link_verbose = $(link_verbose_$(V))
5803
5804 # Targets.
5805
5806 ifeq ($(wildcard $(C_SRC_DIR)),)
5807 else ifneq ($(wildcard $(C_SRC_DIR)/Makefile),)
5808 app:: app-c_src
5809
5810 test-build:: app-c_src
5811
5812 app-c_src:
5813 $(MAKE) -C $(C_SRC_DIR)
5814
5815 clean::
5816 $(MAKE) -C $(C_SRC_DIR) clean
5817
5818 else
5819
5820 ifeq ($(SOURCES),)
5821 SOURCES := $(sort $(foreach pat,*.c *.C *.cc *.cpp,$(call core_find,$(C_SRC_DIR)/,$(pat))))
5822 endif
5823 OBJECTS = $(addsuffix .o, $(basename $(SOURCES)))
5824
5825 COMPILE_C = $(c_verbose) $(CC) $(CFLAGS) $(CPPFLAGS) -c
5826 COMPILE_CPP = $(cpp_verbose) $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c
5827
5828 app:: $(C_SRC_ENV) $(C_SRC_OUTPUT_FILE)
5829
5830 test-build:: $(C_SRC_ENV) $(C_SRC_OUTPUT_FILE)
5831
5832 $(C_SRC_OUTPUT_FILE): $(OBJECTS)
5833 $(verbose) mkdir -p priv/
5834 $(link_verbose) $(CC) $(OBJECTS) \
5835 $(LDFLAGS) $(if $(filter $(C_SRC_TYPE),shared),-shared) $(LDLIBS) \
5836 -o $(C_SRC_OUTPUT_FILE)
5837
5838 %.o: %.c
5839 $(COMPILE_C) $(OUTPUT_OPTION) $<
5840
5841 %.o: %.cc
5842 $(COMPILE_CPP) $(OUTPUT_OPTION) $<
5843
5844 %.o: %.C
5845 $(COMPILE_CPP) $(OUTPUT_OPTION) $<
5846
5847 %.o: %.cpp
5848 $(COMPILE_CPP) $(OUTPUT_OPTION) $<
5849
5850 clean:: clean-c_src
5851
5852 clean-c_src:
5853 $(gen_verbose) rm -f $(C_SRC_OUTPUT_FILE) $(OBJECTS)
5854
5855 endif
5856
5857 ifneq ($(wildcard $(C_SRC_DIR)),)
5858 $(C_SRC_ENV):
5859 $(verbose) $(ERL) -eval "file:write_file(\"$(call core_native_path,$(C_SRC_ENV))\", \
5860 io_lib:format( \
5861 \"ERTS_INCLUDE_DIR ?= ~s/erts-~s/include/~n\" \
5862 \"ERL_INTERFACE_INCLUDE_DIR ?= ~s~n\" \
5863 \"ERL_INTERFACE_LIB_DIR ?= ~s~n\", \
5864 [code:root_dir(), erlang:system_info(version), \
5865 code:lib_dir(erl_interface, include), \
5866 code:lib_dir(erl_interface, lib)])), \
5867 halt()."
5868
5869 distclean:: distclean-c_src-env
5870
5871 distclean-c_src-env:
5872 $(gen_verbose) rm -f $(C_SRC_ENV)
5873
5874 -include $(C_SRC_ENV)
5875 endif
5876
5877 # Templates.
5878
5879 define bs_c_nif
5880 #include "erl_nif.h"
5881
5882 static int loads = 0;
5883
5884 static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
5885 {
5886 /* Initialize private data. */
5887 *priv_data = NULL;
5888
5889 loads++;
5890
5891 return 0;
5892 }
5893
5894 static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info)
5895 {
5896 /* Convert the private data to the new version. */
5897 *priv_data = *old_priv_data;
5898
5899 loads++;
5900
5901 return 0;
5902 }
5903
5904 static void unload(ErlNifEnv* env, void* priv_data)
5905 {
5906 if (loads == 1) {
5907 /* Destroy the private data. */
5908 }
5909
5910 loads--;
5911 }
5912
5913 static ERL_NIF_TERM hello(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
5914 {
5915 if (enif_is_atom(env, argv[0])) {
5916 return enif_make_tuple2(env,
5917 enif_make_atom(env, "hello"),
5918 argv[0]);
5919 }
5920
5921 return enif_make_tuple2(env,
5922 enif_make_atom(env, "error"),
5923 enif_make_atom(env, "badarg"));
5924 }
5925
5926 static ErlNifFunc nif_funcs[] = {
5927 {"hello", 1, hello}
5928 };
5929
5930 ERL_NIF_INIT($n, nif_funcs, load, NULL, upgrade, unload)
5931 endef
5932
5933 define bs_erl_nif
5934 -module($n).
5935
5936 -export([hello/1]).
5937
5938 -on_load(on_load/0).
5939 on_load() ->
5940 PrivDir = case code:priv_dir(?MODULE) of
5941 {error, _} ->
5942 AppPath = filename:dirname(filename:dirname(code:which(?MODULE))),
5943 filename:join(AppPath, "priv");
5944 Path ->
5945 Path
5946 end,
5947 erlang:load_nif(filename:join(PrivDir, atom_to_list(?MODULE)), 0).
5948
5949 hello(_) ->
5950 erlang:nif_error({not_loaded, ?MODULE}).
5951 endef
5952
5953 new-nif:
5954 ifneq ($(wildcard $(C_SRC_DIR)/$n.c),)
5955 $(error Error: $(C_SRC_DIR)/$n.c already exists)
5956 endif
5957 ifneq ($(wildcard src/$n.erl),)
5958 $(error Error: src/$n.erl already exists)
5959 endif
5960 ifdef in
5961 $(verbose) $(MAKE) -C $(APPS_DIR)/$(in)/ new-nif n=$n in=
5962 else
5963 $(verbose) mkdir -p $(C_SRC_DIR) src/
5964 $(call render_template,bs_c_nif,$(C_SRC_DIR)/$n.c)
5965 $(call render_template,bs_erl_nif,src/$n.erl)
5966 endif
5967
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
5969 # This file is part of erlang.mk and subject to the terms of the ISC License.
5970
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
5973 CI_OTP ?=
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
5986 ci::
5987 else
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
6007
6008 ci-setup::
6009
6010 ci-extra::
6011
6012 ci_verbose_0 = @echo " CI " $(1);
6013 ci_verbose = $(ci_verbose_$(V))
6014
6015 define ci_target
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
6018 $(ci_verbose) \
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
6030
6031 define ci_otp_target
6032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
6033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
6035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
6036 endif
6037 endef
6038
6039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
6040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
6052 $(KERL):
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
6056 $(verbose) chmod +x $(KERL)
6057
6058 help::
6059 $(verbose) printf "%s\n" "" \
6060 "Continuous Integration targets:" \
6061 " ci Run '$(MAKE) tests' on all configured Erlang versions." \
6062 "" \
6063 "The CI_OTP variable must be defined with the Erlang versions" \
6064 "that must be tested. For example: CI_OTP = OTP-17.3.4 OTP-17.5.3"
6065
6066 distclean:: distclean-kerl
6067
6068 distclean-kerl:
6069 $(gen_verbose) rm -rf $(KERL)
6070 endif
6071
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
6073 # This file is part of erlang.mk and subject to the terms of the ISC License.
6074
6075 .PHONY: ct apps-ct distclean-ct
6076
6077 # Configuration.
6078
6079 CT_OPTS ?=
6080
6081 ifneq ($(wildcard $(TEST_DIR)),)
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
6088
6089 # Core targets.
6090
6091 tests:: ct
6092
6093 distclean:: distclean-ct
6094
6095 help::
6096 $(verbose) printf "%s\n" "" \
6097 "Common_test targets:" \
6098 " ct Run all the common_test suites for this project" \
6099 "" \
6100 "All your common_test suites have their associated targets." \
6101 "A suite named http_SUITE can be ran using the ct-http target."
6102
6103 # Plugin-specific targets.
6104
6105 CT_RUN = ct_run \
6106 -no_auto_compile \
6107 -noinput \
6108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
6109 -dir $(TEST_DIR) \
6110 -logdir $(CT_LOGS_DIR)
6111
6112 ifeq ($(CT_SUITES),)
6113 ct: $(if $(IS_APP),,apps-ct)
6114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
6117 ct: test-build $(if $(IS_APP),,apps-ct)
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
6119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
6121 endif
6122
6123 ifneq ($(ALL_APPS_DIRS),)
6124 define ct_app_target
6125 apps-ct-$1:
6126 $(MAKE) -C $1 ct IS_APP=1
6127 endef
6128
6129 $(foreach app,$(ALL_APPS_DIRS),$(eval $(call ct_app_target,$(app))))
6130
6131 apps-ct: test-build $(addprefix apps-ct-,$(ALL_APPS_DIRS))
6132 endif
6133
6134 ifndef t
6135 CT_EXTRA =
6136 else
6137 ifeq (,$(findstring :,$t))
6138 CT_EXTRA = -group $t
6139 else
6140 t_words = $(subst :, ,$t)
6141 CT_EXTRA = -group $(firstword $(t_words)) -case $(lastword $(t_words))
6142 endif
6143 endif
6144
6145 define ct_suite_target
6146 ct-$(1): test-build
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
6148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
6149 endef
6150
6151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
6152
6153 distclean-ct:
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
6157 # This file is part of erlang.mk and subject to the terms of the ISC License.
6158
6159 .PHONY: plt distclean-plt dialyze
6160
6161 # Configuration.
6162
6163 DIALYZER_PLT ?= $(CURDIR)/.$(PROJECT).plt
6164 export DIALYZER_PLT
6165
6166 PLT_APPS ?=
6167 DIALYZER_DIRS ?= --src -r $(wildcard src) $(ALL_APPS_DIRS)
6168 DIALYZER_OPTS ?= -Werror_handling -Wrace_conditions -Wunmatched_returns # -Wunderspecs
6169
6170 # Core targets.
6171
6172 check:: dialyze
6173
6174 distclean:: distclean-plt
6175
6176 help::
6177 $(verbose) printf "%s\n" "" \
6178 "Dialyzer targets:" \
6179 " plt Build a PLT file for this project" \
6180 " dialyze Analyze the project using Dialyzer"
6181
6182 # Plugin-specific targets.
6183
6184 define filter_opts.erl
6185 Opts = init:get_plain_arguments(),
6186 {Filtered, _} = lists:foldl(fun
6187 (O, {Os, true}) -> {[O|Os], false};
6188 (O = "-D", {Os, _}) -> {[O|Os], true};
6189 (O = [\\$$-, \\$$D, _ | _], {Os, _}) -> {[O|Os], false};
6190 (O = "-I", {Os, _}) -> {[O|Os], true};
6191 (O = [\\$$-, \\$$I, _ | _], {Os, _}) -> {[O|Os], false};
6192 (O = "-pa", {Os, _}) -> {[O|Os], true};
6193 (_, Acc) -> Acc
6194 end, {[], false}, Opts),
6195 io:format("~s~n", [string:join(lists:reverse(Filtered), " ")]),
6196 halt().
6197 endef
6198
6199 $(DIALYZER_PLT): deps app
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
6204
6205 plt: $(DIALYZER_PLT)
6206
6207 distclean-plt:
6208 $(gen_verbose) rm -f $(DIALYZER_PLT)
6209
6210 ifneq ($(wildcard $(DIALYZER_PLT)),)
6211 dialyze:
6212 else
6213 dialyze: $(DIALYZER_PLT)
6214 endif
6215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
6216
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
6218 # This file is part of erlang.mk and subject to the terms of the ISC License.
6219
6220 .PHONY: distclean-edoc edoc
6221
6222 # Configuration.
6223
6224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
6235
6236 # Core targets.
6237
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
6239 docs:: edoc
6240 endif
6241
6242 distclean:: distclean-edoc
6243
6244 # Plugin-specific targets.
6245
6246 edoc: distclean-edoc doc-deps
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
6248
6249 distclean-edoc:
6250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
6251
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
6253 # This file is part of erlang.mk and subject to the terms of the ISC License.
6254
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
6320
6321 # Configuration.
6322
6323 ESCRIPT_NAME ?= $(PROJECT)
6324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
6325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
6332
6333 # Core targets.
6334
6335 distclean:: distclean-escript
6336
6337 help::
6338 $(verbose) printf "%s\n" "" \
6339 "Escript targets:" \
6340 " escript Build an executable escript archive" \
6341
6342 # Plugin-specific targets.
6343
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
6360
6361 distclean-escript:
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
6365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6367
6368 .PHONY: eunit apps-eunit
6369
6370 # Configuration
6371
6372 EUNIT_OPTS ?=
6373 EUNIT_ERL_OPTS ?=
6374
6375 # Core targets.
6376
6377 tests:: eunit
6378
6379 help::
6380 $(verbose) printf "%s\n" "" \
6381 "EUnit targets:" \
6382 " eunit Run all the EUnit tests for this project"
6383
6384 # Plugin-specific targets.
6385
6386 define eunit.erl
6387 case "$(COVER)" of
6388 "" -> ok;
6389 _ ->
6390 case cover:compile_beam_directory("ebin") of
6391 {error, _} -> halt(1);
6392 _ -> ok
6393 end
6394 end,
6395 case eunit:test($1, [$(EUNIT_OPTS)]) of
6396 ok -> ok;
6397 error -> halt(2)
6398 end,
6399 case "$(COVER)" of
6400 "" -> ok;
6401 _ ->
6402 cover:export("eunit.coverdata")
6403 end,
6404 halt()
6405 endef
6406
6407 EUNIT_ERL_OPTS += -pa $(TEST_DIR) $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(CURDIR)/ebin
6408
6409 ifdef t
6410 ifeq (,$(findstring :,$(t)))
6411 eunit: test-build
6412 $(gen_verbose) $(call erlang,$(call eunit.erl,['$(t)']),$(EUNIT_ERL_OPTS))
6413 else
6414 eunit: test-build
6415 $(gen_verbose) $(call erlang,$(call eunit.erl,fun $(t)/0),$(EUNIT_ERL_OPTS))
6416 endif
6417 else
6418 EUNIT_EBIN_MODS = $(notdir $(basename $(ERL_FILES) $(BEAM_FILES)))
6419 EUNIT_TEST_MODS = $(notdir $(basename $(call core_find,$(TEST_DIR)/,*.erl)))
6420
6421 EUNIT_MODS = $(foreach mod,$(EUNIT_EBIN_MODS) $(filter-out \
6422 $(patsubst %,%_tests,$(EUNIT_EBIN_MODS)),$(EUNIT_TEST_MODS)),'$(mod)')
6423
6424 eunit: test-build $(if $(IS_APP),,apps-eunit)
6425 $(gen_verbose) $(call erlang,$(call eunit.erl,[$(call comma_list,$(EUNIT_MODS))]),$(EUNIT_ERL_OPTS))
6426
6427 ifneq ($(ALL_APPS_DIRS),)
6428 apps-eunit:
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
6436 # This file is part of erlang.mk and subject to the terms of the ISC License.
6437
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
6439
6440 # Configuration.
6441
6442 RELX ?= $(ERLANG_MK_TMP)/relx
6443 RELX_CONFIG ?= $(CURDIR)/relx.config
6444
6445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
6446 RELX_OPTS ?=
6447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
6454
6455 ifeq ($(firstword $(RELX_OPTS)),-o)
6456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
6457 else
6458 RELX_OPTS += -o $(RELX_OUTPUT_DIR)
6459 endif
6460
6461 # Core targets.
6462
6463 ifeq ($(IS_DEP),)
6464 ifneq ($(wildcard $(RELX_CONFIG)),)
6465 rel:: relx-rel
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
6472
6473 # Plugin-specific targets.
6474
6475 $(RELX):
6476 $(gen_verbose) $(call core_http_get,$(RELX),$(RELX_URL))
6477 $(verbose) chmod +x $(RELX)
6478
6479 relx-rel: $(RELX) rel-deps app
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
6484
6485 distclean-relx-rel:
6486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6487
6488 # Run target.
6489
6490 ifeq ($(wildcard $(RELX_CONFIG)),)
6491 run:
6492 else
6493
6494 define get_relx_release.erl
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
6504 halt(0).
6505 endef
6506
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
6514
6515 run: all
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
6517
6518 help::
6519 $(verbose) printf "%s\n" "" \
6520 "Relx targets:" \
6521 " run Compile the project, build the release and run it"
6522
6523 endif
6524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
6526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6528
6529 .PHONY: shell
6530
6531 # Configuration.
6532
6533 SHELL_ERL ?= erl
6534 SHELL_PATHS ?= $(CURDIR)/ebin $(APPS_DIR)/*/ebin $(DEPS_DIR)/*/ebin
6535 SHELL_OPTS ?=
6536
6537 ALL_SHELL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(SHELL_DEPS))
6538
6539 # Core targets
6540
6541 help::
6542 $(verbose) printf "%s\n" "" \
6543 "Shell targets:" \
6544 " shell Run an erlang shell with SHELL_OPTS or reasonable default"
6545
6546 # Plugin-specific targets.
6547
6548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
6549
6550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6552
6553 shell: build-shell-deps
6554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
6555
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
6571 # This file is part of erlang.mk and subject to the terms of the ISC License.
6572
6573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
6574 .PHONY: triq
6575
6576 # Targets.
6577
6578 tests:: triq
6579
6580 define triq_check.erl
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
6582 try
6583 case $(1) of
6584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
6585 module -> triq:check($(2));
6586 function -> triq:check($(2))
6587 end
6588 of
6589 true -> halt(0);
6590 _ -> halt(1)
6591 catch error:undef ->
6592 io:format("Undefined property or module~n"),
6593 halt(0)
6594 end.
6595 endef
6596
6597 ifdef t
6598 ifeq (,$(findstring :,$(t)))
6599 triq: test-build
6600 $(verbose) $(call erlang,$(call triq_check.erl,module,$(t)))
6601 else
6602 triq: test-build
6603 $(verbose) echo Testing $(t)/0
6604 $(verbose) $(call erlang,$(call triq_check.erl,function,$(t)()))
6605 endif
6606 else
6607 triq: test-build
6608 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename $(wildcard ebin/*.beam))))))
6609 $(gen_verbose) $(call erlang,$(call triq_check.erl,all,undefined,$(MODULES)))
6610 endif
6611 endif
6612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6614 # Copyright (c) 2015, Erlang Solutions Ltd.
6615 # This file is part of erlang.mk and subject to the terms of the ISC License.
6616
6617 .PHONY: xref distclean-xref
6618
6619 # Configuration.
6620
6621 ifeq ($(XREF_CONFIG),)
6622 XREFR_ARGS :=
6623 else
6624 XREFR_ARGS := -c $(XREF_CONFIG)
6625 endif
6626
6627 XREFR ?= $(CURDIR)/xrefr
6628 export XREFR
6629
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
6631
6632 # Core targets.
6633
6634 help::
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
6638
6639 distclean:: distclean-xref
6640
6641 # Plugin-specific targets.
6642
6643 $(XREFR):
6644 $(gen_verbose) $(call core_http_get,$(XREFR),$(XREFR_URL))
6645 $(verbose) chmod +x $(XREFR)
6646
6647 xref: deps app $(XREFR)
6648 $(gen_verbose) $(XREFR) $(XREFR_ARGS)
6649
6650 distclean-xref:
6651 $(gen_verbose) rm -rf $(XREFR)
6652
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6655 # This file is part of erlang.mk and subject to the terms of the ISC License.
6656
6657 COVER_REPORT_DIR = cover
6658
6659 # Hook in coverage to ct
6660
6661 ifdef COVER
6662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
6664 # All modules in 'ebin'
6665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
6666
6667 test-build:: $(TEST_DIR)/ct.cover.spec
6668
6669 $(TEST_DIR)/ct.cover.spec:
6670 $(verbose) echo Cover mods: $(COVER_MODS)
6671 $(gen_verbose) printf "%s\n" \
6672 '{incl_mods,[$(subst $(space),$(comma),$(COVER_MODS))]}.' \
6673 '{export,"$(CURDIR)/ct.coverdata"}.' > $@
6674
6675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
6676 endif
6677 endif
6678 endif
6679
6680 # Core targets
6681
6682 ifdef COVER
6683 ifneq ($(COVER_REPORT_DIR),)
6684 tests::
6685 $(verbose) $(MAKE) --no-print-directory cover-report
6686 endif
6687 endif
6688
6689 clean:: coverdata-clean
6690
6691 ifneq ($(COVER_REPORT_DIR),)
6692 distclean:: cover-report-clean
6693 endif
6694
6695 help::
6696 $(verbose) printf "%s\n" "" \
6697 "Cover targets:" \
6698 " cover-report Generate a HTML coverage report from previously collected" \
6699 " cover data." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
6701 "" \
6702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
6703 "target tests additionally generates a HTML coverage report from the combined" \
6704 "coverdata files from each of these testing tools. HTML reports can be disabled" \
6705 "by setting COVER_REPORT_DIR to empty."
6706
6707 # Plugin specific targets
6708
6709 COVERDATA = $(filter-out all.coverdata,$(wildcard *.coverdata))
6710
6711 .PHONY: coverdata-clean
6712 coverdata-clean:
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
6714
6715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
6721 all.coverdata: $(COVERDATA)
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
6723
6724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
6725 # empty if you want the coverdata files but not the HTML report.
6726 ifneq ($(COVER_REPORT_DIR),)
6727
6728 .PHONY: cover-report-clean cover-report
6729
6730 cover-report-clean:
6731 $(gen_verbose) rm -rf $(COVER_REPORT_DIR)
6732
6733 ifeq ($(COVERDATA),)
6734 cover-report:
6735 else
6736
6737 # Modules which include eunit.hrl always contain one line without coverage
6738 # because eunit defines test/0 which is never called. We compensate for this.
6739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
6742
6743 define cover_report.erl
6744 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6745 Ms = cover:imported_modules(),
6746 [cover:analyse_to_file(M, "$(COVER_REPORT_DIR)/" ++ atom_to_list(M)
6747 ++ ".COVER.html", [html]) || M <- Ms],
6748 Report = [begin {ok, R} = cover:analyse(M, module), R end || M <- Ms],
6749 EunitHrlMods = [$(EUNIT_HRL_MODS)],
6750 Report1 = [{M, {Y, case lists:member(M, EunitHrlMods) of
6751 true -> N - 1; false -> N end}} || {M, {Y, N}} <- Report],
6752 TotalY = lists:sum([Y || {_, {Y, _}} <- Report1]),
6753 TotalN = lists:sum([N || {_, {_, N}} <- Report1]),
6754 Perc = fun(Y, N) -> case Y + N of 0 -> 100; S -> round(100 * Y / S) end end,
6755 TotalPerc = Perc(TotalY, TotalN),
6756 {ok, F} = file:open("$(COVER_REPORT_DIR)/index.html", [write]),
6757 io:format(F, "<!DOCTYPE html><html>~n"
6758 "<head><meta charset=\"UTF-8\">~n"
6759 "<title>Coverage report</title></head>~n"
6760 "<body>~n", []),
6761 io:format(F, "<h1>Coverage</h1>~n<p>Total: ~p%</p>~n", [TotalPerc]),
6762 io:format(F, "<table><tr><th>Module</th><th>Coverage</th></tr>~n", []),
6763 [io:format(F, "<tr><td><a href=\"~p.COVER.html\">~p</a></td>"
6764 "<td>~p%</td></tr>~n",
6765 [M, M, Perc(Y, N)]) || {M, {Y, N}} <- Report1],
6766 How = "$(subst $(space),$(comma)$(space),$(basename $(COVERDATA)))",
6767 Date = "$(shell date -u "+%Y-%m-%dT%H:%M:%SZ")",
6768 io:format(F, "</table>~n"
6769 "<p>Generated using ~s and erlang.mk on ~s.</p>~n"
6770 "</body></html>", [How, Date]),
6771 halt().
6772 endef
6773
6774 cover-report:
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
6776 $(gen_verbose) $(call erlang,$(cover_report.erl))
6777
6778 endif
6779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
6839
6840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6842 # This file is part of erlang.mk and subject to the terms of the ISC License.
6843
6844 # Fetch dependencies recursively (without building them).
6845
6846 .PHONY: fetch-deps fetch-doc-deps fetch-rel-deps fetch-test-deps \
6847 fetch-shell-deps
6848
6849 .PHONY: $(ERLANG_MK_RECURSIVE_DEPS_LIST) \
6850 $(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST) \
6851 $(ERLANG_MK_RECURSIVE_REL_DEPS_LIST) \
6852 $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST) \
6853 $(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST)
6854
6855 fetch-deps: $(ERLANG_MK_RECURSIVE_DEPS_LIST)
6856 fetch-doc-deps: $(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST)
6857 fetch-rel-deps: $(ERLANG_MK_RECURSIVE_REL_DEPS_LIST)
6858 fetch-test-deps: $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST)
6859 fetch-shell-deps: $(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST)
6860
6861 ifneq ($(SKIP_DEPS),)
6862 $(ERLANG_MK_RECURSIVE_DEPS_LIST) \
6863 $(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST) \
6864 $(ERLANG_MK_RECURSIVE_REL_DEPS_LIST) \
6865 $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST) \
6866 $(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST):
6867 $(verbose) :> $@
6868 else
6869 # By default, we fetch "normal" dependencies. They are also included no
6870 # matter the type of requested dependencies.
6871 #
6872 # $(ALL_DEPS_DIRS) includes $(BUILD_DEPS).
6873
6874 $(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_DEPS_DIRS)
6875 $(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST): $(ALL_DEPS_DIRS) $(ALL_DOC_DEPS_DIRS)
6876 $(ERLANG_MK_RECURSIVE_REL_DEPS_LIST): $(ALL_DEPS_DIRS) $(ALL_REL_DEPS_DIRS)
6877 $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST): $(ALL_DEPS_DIRS) $(ALL_TEST_DEPS_DIRS)
6878 $(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST): $(ALL_DEPS_DIRS) $(ALL_SHELL_DEPS_DIRS)
6879
6880 # Allow to use fetch-deps and $(DEP_TYPES) to fetch multiple types of
6881 # dependencies with a single target.
6882 ifneq ($(filter doc,$(DEP_TYPES)),)
6883 $(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_DOC_DEPS_DIRS)
6884 endif
6885 ifneq ($(filter rel,$(DEP_TYPES)),)
6886 $(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_REL_DEPS_DIRS)
6887 endif
6888 ifneq ($(filter test,$(DEP_TYPES)),)
6889 $(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_TEST_DEPS_DIRS)
6890 endif
6891 ifneq ($(filter shell,$(DEP_TYPES)),)
6892 $(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_SHELL_DEPS_DIRS)
6893 endif
6894
6895 ERLANG_MK_RECURSIVE_TMP_LIST := $(abspath $(ERLANG_MK_TMP)/recursive-tmp-deps.log)
6896
6897 $(ERLANG_MK_RECURSIVE_DEPS_LIST) \
6898 $(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST) \
6899 $(ERLANG_MK_RECURSIVE_REL_DEPS_LIST) \
6900 $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST) \
6901 $(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST):
6902 ifeq ($(IS_APP)$(IS_DEP),)
6903 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
6905 endif
6906 ifndef IS_APP
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
6908 $(MAKE) -C $$dep $@ \
6909 IS_APP=1 \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
6911 done
6912 endif
6913 $(verbose) set -e; for dep in $^ ; do \
6914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
6915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
6917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
6918 $(MAKE) -C $$dep fetch-deps \
6919 IS_DEP=1 \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
6921 fi \
6922 fi \
6923 done
6924 ifeq ($(IS_APP)$(IS_DEP),)
6925 $(verbose) sort < $(ERLANG_MK_RECURSIVE_TMP_LIST) | uniq > $@
6926 $(verbose) rm $(ERLANG_MK_RECURSIVE_TMP_LIST)
6927 endif
6928 endif # ifneq ($(SKIP_DEPS),)
6929
6930 # List dependencies recursively.
6931
6932 .PHONY: list-deps list-doc-deps list-rel-deps list-test-deps \
6933 list-shell-deps
6934
6935 list-deps: $(ERLANG_MK_RECURSIVE_DEPS_LIST)
6936 list-doc-deps: $(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST)
6937 list-rel-deps: $(ERLANG_MK_RECURSIVE_REL_DEPS_LIST)
6938 list-test-deps: $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST)
6939 list-shell-deps: $(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST)
6940
6941 list-deps list-doc-deps list-rel-deps list-test-deps list-shell-deps:
6942 $(verbose) cat $^
0 ifeq ($(.DEFAULT_GOAL),)
1 # Define default goal to `all` because this file defines some targets
2 # before the inclusion of erlang.mk leading to the wrong target becoming
3 # the default.
4 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
26 endif
27
28 # --------------------------------------------------------------------
29 # RabbitMQ components.
30 # --------------------------------------------------------------------
31
32 # For RabbitMQ repositories, we want to checkout branches which match
33 # the parent project. For instance, if the parent project is on a
34 # release tag, dependencies must be on the same release tag. If the
35 # parent project is on a topic branch, dependencies must be on the same
36 # topic branch or fallback to `stable` or `master` whichever was the
37 # base of the topic branch.
38
39 dep_amqp_client = git_rmq rabbitmq-erlang-client $(current_rmq_ref) $(base_rmq_ref) master
40 dep_rabbit = git_rmq rabbitmq-server $(current_rmq_ref) $(base_rmq_ref) master
41 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
42 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
43 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
45 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
46 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
47 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
48 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
49 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
51 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
52 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
54 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
55 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
56 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
57 dep_rabbitmq_event_exchange = git_rmq rabbitmq-event-exchange $(current_rmq_ref) $(base_rmq_ref) master
58 dep_rabbitmq_federation = git_rmq rabbitmq-federation $(current_rmq_ref) $(base_rmq_ref) master
59 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
60 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
61 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
63 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
64 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
65 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
66 dep_rabbitmq_management_agent = git_rmq rabbitmq-management-agent $(current_rmq_ref) $(base_rmq_ref) master
67 dep_rabbitmq_management_exchange = git_rmq rabbitmq-management-exchange $(current_rmq_ref) $(base_rmq_ref) master
68 dep_rabbitmq_management_themes = git_rmq rabbitmq-management-themes $(current_rmq_ref) $(base_rmq_ref) master
69 dep_rabbitmq_management_visualiser = git_rmq rabbitmq-management-visualiser $(current_rmq_ref) $(base_rmq_ref) master
70 dep_rabbitmq_message_timestamp = git_rmq rabbitmq-message-timestamp $(current_rmq_ref) $(base_rmq_ref) master
71 dep_rabbitmq_metronome = git_rmq rabbitmq-metronome $(current_rmq_ref) $(base_rmq_ref) master
72 dep_rabbitmq_mqtt = git_rmq rabbitmq-mqtt $(current_rmq_ref) $(base_rmq_ref) master
73 dep_rabbitmq_objc_client = git_rmq rabbitmq-objc-client $(current_rmq_ref) $(base_rmq_ref) master
74 dep_rabbitmq_recent_history_exchange = git_rmq rabbitmq-recent-history-exchange $(current_rmq_ref) $(base_rmq_ref) master
75 dep_rabbitmq_routing_node_stamp = git_rmq rabbitmq-routing-node-stamp $(current_rmq_ref) $(base_rmq_ref) master
76 dep_rabbitmq_rtopic_exchange = git_rmq rabbitmq-rtopic-exchange $(current_rmq_ref) $(base_rmq_ref) master
77 dep_rabbitmq_server_release = git_rmq rabbitmq-server-release $(current_rmq_ref) $(base_rmq_ref) master
78 dep_rabbitmq_sharding = git_rmq rabbitmq-sharding $(current_rmq_ref) $(base_rmq_ref) master
79 dep_rabbitmq_shovel = git_rmq rabbitmq-shovel $(current_rmq_ref) $(base_rmq_ref) master
80 dep_rabbitmq_shovel_management = git_rmq rabbitmq-shovel-management $(current_rmq_ref) $(base_rmq_ref) master
81 dep_rabbitmq_stomp = git_rmq rabbitmq-stomp $(current_rmq_ref) $(base_rmq_ref) master
82 dep_rabbitmq_toke = git_rmq rabbitmq-toke $(current_rmq_ref) $(base_rmq_ref) master
83 dep_rabbitmq_top = git_rmq rabbitmq-top $(current_rmq_ref) $(base_rmq_ref) master
84 dep_rabbitmq_tracing = git_rmq rabbitmq-tracing $(current_rmq_ref) $(base_rmq_ref) master
85 dep_rabbitmq_trust_store = git_rmq rabbitmq-trust-store $(current_rmq_ref) $(base_rmq_ref) master
86 dep_rabbitmq_test = git_rmq rabbitmq-test $(current_rmq_ref) $(base_rmq_ref) master
87 dep_rabbitmq_web_dispatch = git_rmq rabbitmq-web-dispatch $(current_rmq_ref) $(base_rmq_ref) master
88 dep_rabbitmq_web_stomp = git_rmq rabbitmq-web-stomp $(current_rmq_ref) $(base_rmq_ref) master
89 dep_rabbitmq_web_stomp_examples = git_rmq rabbitmq-web-stomp-examples $(current_rmq_ref) $(base_rmq_ref) master
90 dep_rabbitmq_web_mqtt = git_rmq rabbitmq-web-mqtt $(current_rmq_ref) $(base_rmq_ref) master
91 dep_rabbitmq_web_mqtt_examples = git_rmq rabbitmq-web-mqtt-examples $(current_rmq_ref) $(base_rmq_ref) master
92 dep_rabbitmq_website = git_rmq rabbitmq-website $(current_rmq_ref) $(base_rmq_ref) live master
93 dep_sockjs = git_rmq sockjs-erlang $(current_rmq_ref) $(base_rmq_ref) master
94 dep_toke = git_rmq toke $(current_rmq_ref) $(base_rmq_ref) master
95
96 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
97
98 # Third-party dependencies version pinning.
99 #
100 # We do that in this file, which is copied in all projects, to ensure
101 # all projects use the same versions. It avoids conflicts and makes it
102 # possible to work with rabbitmq-public-umbrella.
103
104 dep_cowboy_commit = 1.0.4
105 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
106 dep_ranch_commit = 1.3.1
107 dep_webmachine_commit = 1.10.8p2
108
109 RABBITMQ_COMPONENTS = amqp_client \
110 rabbit \
111 rabbit_common \
112 rabbitmq_amqp1_0 \
113 rabbitmq_auth_backend_amqp \
114 rabbitmq_auth_backend_cache \
115 rabbitmq_auth_backend_http \
116 rabbitmq_auth_backend_ldap \
117 rabbitmq_auth_mechanism_ssl \
118 rabbitmq_boot_steps_visualiser \
119 rabbitmq_clusterer \
120 rabbitmq_cli \
121 rabbitmq_codegen \
122 rabbitmq_consistent_hash_exchange \
123 rabbitmq_ct_client_helpers \
124 rabbitmq_ct_helpers \
125 rabbitmq_delayed_message_exchange \
126 rabbitmq_dotnet_client \
127 rabbitmq_event_exchange \
128 rabbitmq_federation \
129 rabbitmq_federation_management \
130 rabbitmq_java_client \
131 rabbitmq_jms_client \
132 rabbitmq_jms_cts \
133 rabbitmq_jms_topic_exchange \
134 rabbitmq_lvc \
135 rabbitmq_management \
136 rabbitmq_management_agent \
137 rabbitmq_management_exchange \
138 rabbitmq_management_themes \
139 rabbitmq_management_visualiser \
140 rabbitmq_message_timestamp \
141 rabbitmq_metronome \
142 rabbitmq_mqtt \
143 rabbitmq_objc_client \
144 rabbitmq_recent_history_exchange \
145 rabbitmq_routing_node_stamp \
146 rabbitmq_rtopic_exchange \
147 rabbitmq_server_release \
148 rabbitmq_sharding \
149 rabbitmq_shovel \
150 rabbitmq_shovel_management \
151 rabbitmq_stomp \
152 rabbitmq_toke \
153 rabbitmq_top \
154 rabbitmq_tracing \
155 rabbitmq_trust_store \
156 rabbitmq_web_dispatch \
157 rabbitmq_web_mqtt \
158 rabbitmq_web_mqtt_examples \
159 rabbitmq_web_stomp \
160 rabbitmq_web_stomp_examples \
161 rabbitmq_website
162
163 # Several components have a custom erlang.mk/build.config, mainly
164 # to disable eunit. Therefore, we can't use the top-level project's
165 # erlang.mk copy.
166 NO_AUTOPATCH += $(RABBITMQ_COMPONENTS)
167
168 ifeq ($(origin current_rmq_ref),undefined)
169 ifneq ($(wildcard .git),)
170 current_rmq_ref := $(shell (\
171 ref=$$(git branch --list | awk '/^\* \(.*detached / {ref=$$0; sub(/.*detached [^ ]+ /, "", ref); sub(/\)$$/, "", ref); print ref; exit;} /^\* / {ref=$$0; sub(/^\* /, "", ref); print ref; exit}');\
172 if test "$$(git rev-parse --short HEAD)" != "$$ref"; then echo "$$ref"; fi))
173 else
174 current_rmq_ref := master
175 endif
176 endif
177 export current_rmq_ref
178
179 ifeq ($(origin base_rmq_ref),undefined)
180 ifneq ($(wildcard .git),)
181 base_rmq_ref := $(shell \
182 (git rev-parse --verify -q stable >/dev/null && \
183 git merge-base --is-ancestor $$(git merge-base master HEAD) stable && \
184 echo stable) || \
185 echo master)
186 else
187 base_rmq_ref := master
188 endif
189 endif
190 export base_rmq_ref
191
192 # Repository URL selection.
193 #
194 # First, we infer other components' location from the current project
195 # repository URL, if it's a Git repository:
196 # - We take the "origin" remote URL as the base
197 # - The current project name and repository name is replaced by the
198 # target's properties:
199 # eg. rabbitmq-common is replaced by rabbitmq-codegen
200 # eg. rabbit_common is replaced by rabbitmq_codegen
201 #
202 # If cloning from this computed location fails, we fallback to RabbitMQ
203 # upstream which is GitHub.
204
205 # Maccro to transform eg. "rabbit_common" to "rabbitmq-common".
206 rmq_cmp_repo_name = $(word 2,$(dep_$(1)))
207
208 # Upstream URL for the current project.
209 RABBITMQ_COMPONENT_REPO_NAME := $(call rmq_cmp_repo_name,$(PROJECT))
210 RABBITMQ_UPSTREAM_FETCH_URL ?= https://github.com/rabbitmq/$(RABBITMQ_COMPONENT_REPO_NAME).git
211 RABBITMQ_UPSTREAM_PUSH_URL ?= git@github.com:rabbitmq/$(RABBITMQ_COMPONENT_REPO_NAME).git
212
213 # Current URL for the current project. If this is not a Git clone,
214 # default to the upstream Git repository.
215 ifneq ($(wildcard .git),)
216 git_origin_fetch_url := $(shell git config remote.origin.url)
217 git_origin_push_url := $(shell git config remote.origin.pushurl || git config remote.origin.url)
218 RABBITMQ_CURRENT_FETCH_URL ?= $(git_origin_fetch_url)
219 RABBITMQ_CURRENT_PUSH_URL ?= $(git_origin_push_url)
220 else
221 RABBITMQ_CURRENT_FETCH_URL ?= $(RABBITMQ_UPSTREAM_FETCH_URL)
222 RABBITMQ_CURRENT_PUSH_URL ?= $(RABBITMQ_UPSTREAM_PUSH_URL)
223 endif
224
225 # Macro to replace the following pattern:
226 # 1. /foo.git -> /bar.git
227 # 2. /foo -> /bar
228 # 3. /foo/ -> /bar/
229 subst_repo_name = $(patsubst %/$(1)/%,%/$(2)/%,$(patsubst %/$(1),%/$(2),$(patsubst %/$(1).git,%/$(2).git,$(3))))
230
231 # Macro to replace both the project's name (eg. "rabbit_common") and
232 # repository name (eg. "rabbitmq-common") by the target's equivalent.
233 #
234 # This macro is kept on one line because we don't want whitespaces in
235 # the returned value, as it's used in $(dep_fetch_git_rmq) in a shell
236 # single-quoted string.
237 dep_rmq_repo = $(if $(dep_$(2)),$(call subst_repo_name,$(PROJECT),$(2),$(call subst_repo_name,$(RABBITMQ_COMPONENT_REPO_NAME),$(call rmq_cmp_repo_name,$(2)),$(1))),$(pkg_$(1)_repo))
238
239 dep_rmq_commits = $(if $(dep_$(1)), \
240 $(wordlist 3,$(words $(dep_$(1))),$(dep_$(1))), \
241 $(pkg_$(1)_commit))
242
243 define dep_fetch_git_rmq
244 fetch_url1='$(call dep_rmq_repo,$(RABBITMQ_CURRENT_FETCH_URL),$(1))'; \
245 fetch_url2='$(call dep_rmq_repo,$(RABBITMQ_UPSTREAM_FETCH_URL),$(1))'; \
246 if test "$$$$fetch_url1" != '$(RABBITMQ_CURRENT_FETCH_URL)' && \
247 git clone -q -n -- "$$$$fetch_url1" $(DEPS_DIR)/$(call dep_name,$(1)); then \
248 fetch_url="$$$$fetch_url1"; \
249 push_url='$(call dep_rmq_repo,$(RABBITMQ_CURRENT_PUSH_URL),$(1))'; \
250 elif git clone -q -n -- "$$$$fetch_url2" $(DEPS_DIR)/$(call dep_name,$(1)); then \
251 fetch_url="$$$$fetch_url2"; \
252 push_url='$(call dep_rmq_repo,$(RABBITMQ_UPSTREAM_PUSH_URL),$(1))'; \
253 fi; \
254 cd $(DEPS_DIR)/$(call dep_name,$(1)) && ( \
255 $(foreach ref,$(call dep_rmq_commits,$(1)), \
256 git checkout -q $(ref) >/dev/null 2>&1 || \
257 ) \
258 (echo "error: no valid pathspec among: $(call dep_rmq_commits,$(1))" \
259 1>&2 && false) ) && \
260 (test "$$$$fetch_url" = "$$$$push_url" || \
261 git remote set-url --push origin "$$$$push_url")
262 endef
263
264 # --------------------------------------------------------------------
265 # Component distribution.
266 # --------------------------------------------------------------------
267
268 list-dist-deps::
269 @:
270
271 prepare-dist::
272 @:
273
274 # --------------------------------------------------------------------
275 # rabbitmq-components.mk checks.
276 # --------------------------------------------------------------------
277
278 # If this project is under the Umbrella project, we override $(DEPS_DIR)
279 # to point to the Umbrella's one. We also disable `make distclean` so
280 # $(DEPS_DIR) is not accidentally removed.
281
282 ifneq ($(wildcard ../../UMBRELLA.md),)
283 UNDER_UMBRELLA = 1
284 else ifneq ($(wildcard UMBRELLA.md),)
285 UNDER_UMBRELLA = 1
286 endif
287
288 ifeq ($(UNDER_UMBRELLA),1)
289 ifneq ($(PROJECT),rabbitmq_public_umbrella)
290 DEPS_DIR ?= $(abspath ..)
291 endif
292
293 ifneq ($(filter distclean distclean-deps,$(MAKECMDGOALS)),)
294 SKIP_DEPS = 1
295 endif
296 endif
297
298 UPSTREAM_RMQ_COMPONENTS_MK = $(DEPS_DIR)/rabbit_common/mk/rabbitmq-components.mk
299
300 check-rabbitmq-components.mk:
301 $(verbose) cmp -s rabbitmq-components.mk \
302 $(UPSTREAM_RMQ_COMPONENTS_MK) || \
303 (echo "error: rabbitmq-components.mk must be updated!" 1>&2; \
304 false)
305
306 ifeq ($(PROJECT),rabbit_common)
307 rabbitmq-components-mk:
308 @:
309 else
310 rabbitmq-components-mk:
311 $(gen_verbose) cp -a $(UPSTREAM_RMQ_COMPONENTS_MK) .
312 ifeq ($(DO_COMMIT),yes)
313 $(verbose) git diff --quiet rabbitmq-components.mk \
314 || git commit -m 'Update rabbitmq-components.mk' rabbitmq-components.mk
315 endif
316 endif
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_web_mqtt_app).
17
18 -behaviour(application).
19 -export([start/2, prep_stop/1, stop/1]).
20
21 %% Dummy supervisor - see Ulf Wiger's comment at
22 %% http://erlang.2086793.n4.nabble.com/initializing-library-applications-without-processes-td2094473.html
23 -behaviour(supervisor).
24 -export([init/1]).
25
26 %%----------------------------------------------------------------------------
27
28 -spec start(_, _) -> {ok, pid()}.
29 start(_Type, _StartArgs) ->
30 mqtt_init(),
31 supervisor:start_link({local, ?MODULE}, ?MODULE, []).
32
33 -spec prep_stop(term()) -> term().
34 prep_stop(State) ->
35 ranch:stop_listener(web_mqtt),
36 State.
37
38 -spec stop(_) -> ok.
39 stop(_State) ->
40 ok.
41
42 init([]) -> {ok, {{one_for_one, 1, 5}, []}}.
43
44 %%----------------------------------------------------------------------------
45
46 mqtt_init() ->
47 CowboyOpts0 = get_env(cowboy_opts, []),
48
49 Routes = cowboy_router:compile([{'_', [
50 {"/ws", rabbit_web_mqtt_handler, []}
51 ]}]),
52 CowboyOpts = [
53 {env, [{dispatch, Routes}]},
54 {middlewares, [cowboy_router, rabbit_web_mqtt_middleware, cowboy_handler]}
55 |CowboyOpts0],
56
57 TCPConf0 = [{connection_type, supervisor}|get_env(tcp_config, [])],
58 TCPConf = case proplists:get_value(port, TCPConf0) of
59 undefined -> [{port, 15675}|TCPConf0];
60 _ -> TCPConf0
61 end,
62 TCPPort = proplists:get_value(port, TCPConf),
63
64 {ok, _} = ranch:start_listener(web_mqtt, get_env(num_tcp_acceptors, 10),
65 ranch_tcp, TCPConf,
66 rabbit_web_mqtt_connection_sup, CowboyOpts),
67 listener_started('http/web-mqtt', TCPConf),
68 rabbit_log:info("rabbit_web_mqtt: listening for HTTP connections on ~s:~w~n",
69 ["0.0.0.0", TCPPort]),
70
71 case get_env(ssl_config, []) of
72 [] ->
73 ok;
74 SSLConf0 ->
75 rabbit_networking:ensure_ssl(),
76 SSLPort = proplists:get_value(port, SSLConf0),
77 SSLConf = [{connection_type, supervisor}|SSLConf0],
78
79 {ok, _} = ranch:start_listener(web_mqtt_secure, get_env(num_ssl_acceptors, 1),
80 ranch_ssl, SSLConf,
81 rabbit_web_mqtt_connection_sup, CowboyOpts),
82 listener_started('https/web-mqtt', TCPConf),
83 rabbit_log:info("rabbit_web_mqtt: listening for HTTPS connections on ~s:~w~n",
84 ["0.0.0.0", SSLPort])
85 end,
86 ok.
87
88 listener_started(Protocol, Listener) ->
89 Port = rabbit_misc:pget(port, Listener),
90 [rabbit_networking:tcp_listener_started(Protocol, Listener,
91 IPAddress, Port)
92 || {IPAddress, _Port, _Family}
93 <- rabbit_networking:tcp_listener_addresses(Port)],
94 ok.
95
96 get_env(Key, Default) ->
97 case application:get_env(rabbitmq_web_mqtt, Key) of
98 undefined -> Default;
99 {ok, V} -> V
100 end.
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_web_mqtt_connection_sup).
17
18 -behaviour(supervisor2).
19 -behaviour(ranch_protocol).
20
21 -include_lib("rabbit_common/include/rabbit.hrl").
22
23 -export([start_link/4, start_keepalive_link/0]).
24
25 -export([init/1]).
26
27 %%----------------------------------------------------------------------------
28
29 start_link(Ref, Sock, Transport, CowboyOpts0) ->
30 {ok, SupPid} = supervisor2:start_link(?MODULE, []),
31 {ok, KeepaliveSup} = supervisor2:start_child(
32 SupPid,
33 {rabbit_web_mqtt_keepalive_sup,
34 {?MODULE, start_keepalive_link, []},
35 intrinsic, infinity, supervisor, [rabbit_keepalive_sup]}),
36
37 %% In order for the Websocket handler to receive the KeepaliveSup
38 %% variable, we need to pass it first through the environment and
39 %% then have the middleware rabbit_web_mqtt_middleware place it
40 %% in the initial handler state.
41 {env, Env} = lists:keyfind(env, 1, CowboyOpts0),
42 CowboyOpts = lists:keyreplace(env, 1, CowboyOpts0,
43 {env, [{keepalive_sup, KeepaliveSup}|Env]}),
44
45 {ok, ReaderPid} = supervisor2:start_child(
46 SupPid,
47 {cowboy_protocol,
48 {cowboy_protocol, start_link, [Ref, Sock, Transport, CowboyOpts]},
49 intrinsic, ?WORKER_WAIT, worker, [cowboy_protocol]}),
50 {ok, SupPid, ReaderPid}.
51
52 start_keepalive_link() ->
53 supervisor2:start_link(?MODULE, []).
54
55 %%----------------------------------------------------------------------------
56
57 init([]) ->
58 {ok, {{one_for_all, 0, 1}, []}}.
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2012-2016 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_web_mqtt_handler).
17
18 -export([init/3]).
19 -export([websocket_init/3]).
20 -export([websocket_handle/3]).
21 -export([websocket_info/3]).
22 -export([websocket_terminate/3]).
23
24 -include_lib("amqp_client/include/amqp_client.hrl").
25
26 -record(state, {
27 conn_name,
28 keepalive,
29 keepalive_sup,
30 parse_state,
31 proc_state,
32 socket,
33 stats_timer,
34 connection
35 }).
36
37 init(_, _, _) ->
38 {upgrade, protocol, cowboy_websocket}.
39
40 websocket_init(_, Req, Opts) ->
41 process_flag(trap_exit, true),
42 {_, KeepaliveSup} = lists:keyfind(keepalive_sup, 1, Opts),
43 Sock = cowboy_req:get(socket, Req),
44 case rabbit_net:connection_string(Sock, inbound) of
45 {ok, ConnStr} ->
46 rabbit_log:log(connection, info, "accepting Web MQTT connection ~p (~s)~n", [self(), ConnStr]),
47 AdapterInfo0 = #amqp_adapter_info{additional_info=Extra}
48 = amqp_connection:socket_adapter_info(Sock, {'Web MQTT', "N/A"}),
49 %% Flow control is not supported for Web-MQTT connections.
50 AdapterInfo = AdapterInfo0#amqp_adapter_info{
51 additional_info=[{state, running}|Extra]},
52 ProcessorState = rabbit_mqtt_processor:initial_state(Sock,
53 rabbit_mqtt_reader:ssl_login_name(Sock),
54 AdapterInfo,
55 fun send_reply/2),
56 Req2 = case cowboy_req:header(<<"sec-websocket-protocol">>, Req) of
57 {undefined, Req1} ->
58 Req1;
59 {SecWsProtocol, Req1} ->
60 cowboy_req:set_resp_header(<<"sec-websocket-protocol">>, SecWsProtocol, Req1)
61 end,
62 Req3 = cowboy_req:compact(Req2),
63 {ok, Req3, rabbit_event:init_stats_timer(#state{
64 conn_name = ConnStr,
65 keepalive = {none, none},
66 keepalive_sup = KeepaliveSup,
67 parse_state = rabbit_mqtt_frame:initial_state(),
68 proc_state = ProcessorState,
69 socket = Sock
70 }, #state.stats_timer), hibernate};
71 _ ->
72 {shutdown, Req}
73 end.
74
75 websocket_handle({binary, Data}, Req, State) ->
76 handle_data(Data, Req, State);
77 websocket_handle(Frame, Req, State) ->
78 rabbit_log:log(connection, info, "Web MQTT: unexpected WebSocket frame ~p~n",
79 [Frame]),
80 {ok, Req, State, hibernate}.
81
82 websocket_info({#'basic.deliver'{}, #amqp_msg{}, _DeliveryCtx} = Delivery,
83 Req, State = #state{ proc_state = ProcState0 }) ->
84 case rabbit_mqtt_processor:amqp_callback(Delivery, ProcState0) of
85 {ok, ProcState} ->
86 {ok, Req, State #state { proc_state = ProcState }, hibernate};
87 {error, _, _} ->
88 {shutdown, Req, State}
89 end;
90 websocket_info(#'basic.ack'{} = Ack, Req, State = #state{ proc_state = ProcState0 }) ->
91 case rabbit_mqtt_processor:amqp_callback(Ack, ProcState0) of
92 {ok, ProcState} ->
93 {ok, Req, State #state { proc_state = ProcState }, hibernate};
94 {error, _, _} ->
95 {shutdown, Req, State}
96 end;
97 websocket_info(#'basic.consume_ok'{}, Req, State) ->
98 {ok, Req, State, hibernate};
99 websocket_info(#'basic.cancel'{}, Req, State) ->
100 {shutdown, Req, State};
101 websocket_info({reply, Data}, Req, State) ->
102 {reply, {binary, Data}, Req, State, hibernate};
103 websocket_info({'EXIT', _, _}, Req, State) ->
104 {shutdown, Req, State};
105 websocket_info({'$gen_cast', duplicate_id}, Req, State = #state{ proc_state = ProcState,
106 conn_name = ConnName }) ->
107 rabbit_log:log(connection, warning, "WEB-MQTT disconnecting duplicate client id ~p (~p)~n",
108 [rabbit_mqtt_processor:info(client_id, ProcState), ConnName]),
109 {shutdown, Req, State};
110 websocket_info({start_keepalives, Keepalive}, Req,
111 State = #state{ keepalive_sup = KeepaliveSup }) ->
112 Sock = cowboy_req:get(socket, Req),
113 %% Only the client has the responsibility for sending keepalives
114 SendFun = fun() -> ok end,
115 Parent = self(),
116 ReceiveFun = fun() -> Parent ! keepalive_timeout end,
117 Heartbeater = rabbit_heartbeat:start(
118 KeepaliveSup, Sock, 0, SendFun, Keepalive, ReceiveFun),
119 {ok, Req, State #state { keepalive = Heartbeater }};
120 websocket_info(keepalive_timeout, Req, State = #state {conn_name = ConnStr,
121 proc_state = PState}) ->
122 rabbit_log:log(connection, error, "closing WEB-MQTT connection ~p (keepalive timeout)~n", [ConnStr]),
123 rabbit_mqtt_processor:send_will(PState),
124 {shutdown, Req, State};
125 websocket_info(emit_stats, Req, State) ->
126 {ok, Req, emit_stats(State)};
127 websocket_info(Msg, Req, State) ->
128 rabbit_log:log(connection, info, "WEB-MQTT: unexpected message ~p~n",
129 [Msg]),
130 {ok, Req, State, hibernate}.
131
132 websocket_terminate(_, _, State = #state{ proc_state = ProcState,
133 conn_name = ConnName }) ->
134 maybe_emit_stats(State),
135 rabbit_log:log(connection, info, "closing WEB-MQTT connection ~p (~s)~n", [self(), ConnName]),
136 rabbit_mqtt_processor:close_connection(ProcState),
137 ok;
138 websocket_terminate(_, _, State) ->
139 maybe_emit_stats(State),
140 ok.
141
142 %% Internal.
143
144 handle_data(<<>>, Req, State) ->
145 {ok, Req, ensure_stats_timer(State), hibernate};
146 handle_data(Data, Req, State = #state{ parse_state = ParseState,
147 proc_state = ProcState,
148 conn_name = ConnStr }) ->
149 case rabbit_mqtt_frame:parse(Data, ParseState) of
150 {more, ParseState1} ->
151 {ok, Req, ensure_stats_timer(State #state{ parse_state = ParseState1 }), hibernate};
152 {ok, Frame, Rest} ->
153 case rabbit_mqtt_processor:process_frame(Frame, ProcState) of
154 {ok, ProcState1, ConnPid} ->
155 PS = rabbit_mqtt_frame:initial_state(),
156 handle_data(
157 Rest, Req,
158 State #state{ parse_state = PS,
159 proc_state = ProcState1,
160 connection = ConnPid });
161 {error, Reason, _} ->
162 rabbit_log:log(connection, info, "MQTT protocol error ~p for connection ~p~n",
163 [Reason, ConnStr]),
164 {shutdown, Req, State};
165 {error, Error} ->
166 rabbit_log:log(connection, error, "MQTT detected framing error '~p' for connection ~p~n",
167 [Error, ConnStr]),
168 {shutdown, Req, State};
169 {stop, _} ->
170 {shutdown, Req, State}
171 end;
172 {error, Error} ->
173 rabbit_log:log(connection, error, "MQTT detected framing error '~p' for connection ~p~n",
174 [ConnStr, Error]),
175 {shutdown, Req, State}
176 end.
177
178 send_reply(Frame, _) ->
179 self() ! {reply, rabbit_mqtt_frame:serialise(Frame)}.
180
181 ensure_stats_timer(State) ->
182 rabbit_event:ensure_stats_timer(State, #state.stats_timer, emit_stats).
183
184 maybe_emit_stats(State) ->
185 rabbit_event:if_enabled(State, #state.stats_timer,
186 fun() -> emit_stats(State) end).
187
188 emit_stats(State=#state{connection = C}) when C == none; C == undefined ->
189 %% Avoid emitting stats on terminate when the connection has not yet been
190 %% established, as this causes orphan entries on the stats database
191 State1 = rabbit_event:reset_stats_timer(State, #state.stats_timer),
192 State1;
193 emit_stats(State=#state{socket=Sock, connection=Conn}) ->
194 SockInfos = case rabbit_net:getstat(Sock,
195 [recv_oct, recv_cnt, send_oct, send_cnt, send_pend]) of
196 {ok, SI} -> SI;
197 {error, _} -> []
198 end,
199 Infos = [{pid, Conn}|SockInfos],
200 rabbit_core_metrics:connection_stats(Conn, Infos),
201 rabbit_event:notify(connection_stats, Infos),
202 State1 = rabbit_event:reset_stats_timer(State, #state.stats_timer),
203 State1.
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% Copyright (c) 2012-2016 Pivotal Software, Inc. All rights reserved.
13 %%
14
15 -module(rabbit_web_mqtt_middleware).
16 -behavior(cowboy_middleware).
17
18 -export([execute/2]).
19
20 execute(Req, Env) ->
21 {keepalive_sup, KeepaliveSup} = lists:keyfind(keepalive_sup, 1, Env),
22 case lists:keyfind(handler_opts, 1, Env) of
23 {_, Opts} when is_list(Opts) ->
24 {ok, Req, lists:keyreplace(handler_opts, 1, Env,
25 {handler_opts, [{keepalive_sup, KeepaliveSup}|Opts]})};
26 _ ->
27 {ok, Req, Env}
28 end.
0 # Contributor Code of Conduct
1
2 As contributors and maintainers of this project, and in the interest of fostering an open
3 and welcoming community, we pledge to respect all people who contribute through reporting
4 issues, posting feature requests, updating documentation, submitting pull requests or
5 patches, and other activities.
6
7 We are committed to making participation in this project a harassment-free experience for
8 everyone, regardless of level of experience, gender, gender identity and expression,
9 sexual orientation, disability, personal appearance, body size, race, ethnicity, age,
10 religion, or nationality.
11
12 Examples of unacceptable behavior by participants include:
13
14 * The use of sexualized language or imagery
15 * Personal attacks
16 * Trolling or insulting/derogatory comments
17 * Public or private harassment
18 * Publishing other's private information, such as physical or electronic addresses,
19 without explicit permission
20 * Other unethical or unprofessional conduct
21
22 Project maintainers have the right and responsibility to remove, edit, or reject comments,
23 commits, code, wiki edits, issues, and other contributions that are not aligned to this
24 Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors
25 that they deem inappropriate, threatening, offensive, or harmful.
26
27 By adopting this Code of Conduct, project maintainers commit themselves to fairly and
28 consistently applying these principles to every aspect of managing this project. Project
29 maintainers who do not follow or enforce the Code of Conduct may be permanently removed
30 from the project team.
31
32 This Code of Conduct applies both within project spaces and in public spaces when an
33 individual is representing the project or its community.
34
35 Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
36 contacting a project maintainer at [info@rabbitmq.com](mailto:info@rabbitmq.com). All complaints will
37 be reviewed and investigated and will result in a response that is deemed necessary and
38 appropriate to the circumstances. Maintainers are obligated to maintain confidentiality
39 with regard to the reporter of an incident.
40
41 This Code of Conduct is adapted from the
42 [Contributor Covenant](http://contributor-covenant.org), version 1.3.0, available at
43 [contributor-covenant.org/version/1/3/0/](http://contributor-covenant.org/version/1/3/0/)
0 ## Overview
1
2 RabbitMQ projects use pull requests to discuss, collaborate on and accept code contributions.
3 Pull requests is the primary place of discussing code changes.
4
5 ## How to Contribute
6
7 The process is fairly standard:
8
9 * Fork the repository or repositories you plan on contributing to
10 * Clone [RabbitMQ umbrella repository](https://github.com/rabbitmq/rabbitmq-public-umbrella)
11 * `cd umbrella`, `make co`
12 * Create a branch with a descriptive name in the relevant repositories
13 * Make your changes, run tests, commit with a [descriptive message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), push to your fork
14 * Submit pull requests with an explanation what has been changed and **why**
15 * Submit a filled out and signed [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) if needed (see below)
16 * Be patient. We will get to your pull request eventually
17
18 If what you are going to work on is a substantial change, please first ask the core team
19 of their opinion on [RabbitMQ mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users).
20
21
22 ## Code of Conduct
23
24 See [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md).
25
26
27 ## Contributor Agreement
28
29 If you want to contribute a non-trivial change, please submit a signed copy of our
30 [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) around the time
31 you submit your pull request. This will make it much easier (in some cases, possible)
32 for the RabbitMQ team at Pivotal to merge your contribution.
33
34
35 ## Where to Ask Questions
36
37 If something isn't clear, feel free to ask on our [mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users).
0 This package, the rabbitmq-web-mqtt-examples, is licensed under the
1 MPL. For the MPL, please see LICENSE-MPL-RabbitMQ.
2
3 priv/mqttws31.js is a part of the Paho project
4 (https://eclipse.org/paho/clients/js/) and is released under
5 the EPL and the EDL.
6
7 If you have any questions regarding licensing, please contact us at
8 info@rabbitmq.com.
0 MOZILLA PUBLIC LICENSE
1 Version 1.1
2
3 ---------------
4
5 1. Definitions.
6
7 1.0.1. "Commercial Use" means distribution or otherwise making the
8 Covered Code available to a third party.
9
10 1.1. "Contributor" means each entity that creates or contributes to
11 the creation of Modifications.
12
13 1.2. "Contributor Version" means the combination of the Original
14 Code, prior Modifications used by a Contributor, and the Modifications
15 made by that particular Contributor.
16
17 1.3. "Covered Code" means the Original Code or Modifications or the
18 combination of the Original Code and Modifications, in each case
19 including portions thereof.
20
21 1.4. "Electronic Distribution Mechanism" means a mechanism generally
22 accepted in the software development community for the electronic
23 transfer of data.
24
25 1.5. "Executable" means Covered Code in any form other than Source
26 Code.
27
28 1.6. "Initial Developer" means the individual or entity identified
29 as the Initial Developer in the Source Code notice required by Exhibit
30 A.
31
32 1.7. "Larger Work" means a work which combines Covered Code or
33 portions thereof with code not governed by the terms of this License.
34
35 1.8. "License" means this document.
36
37 1.8.1. "Licensable" means having the right to grant, to the maximum
38 extent possible, whether at the time of the initial grant or
39 subsequently acquired, any and all of the rights conveyed herein.
40
41 1.9. "Modifications" means any addition to or deletion from the
42 substance or structure of either the Original Code or any previous
43 Modifications. When Covered Code is released as a series of files, a
44 Modification is:
45 A. Any addition to or deletion from the contents of a file
46 containing Original Code or previous Modifications.
47
48 B. Any new file that contains any part of the Original Code or
49 previous Modifications.
50
51 1.10. "Original Code" means Source Code of computer software code
52 which is described in the Source Code notice required by Exhibit A as
53 Original Code, and which, at the time of its release under this
54 License is not already Covered Code governed by this License.
55
56 1.10.1. "Patent Claims" means any patent claim(s), now owned or
57 hereafter acquired, including without limitation, method, process,
58 and apparatus claims, in any patent Licensable by grantor.
59
60 1.11. "Source Code" means the preferred form of the Covered Code for
61 making modifications to it, including all modules it contains, plus
62 any associated interface definition files, scripts used to control
63 compilation and installation of an Executable, or source code
64 differential comparisons against either the Original Code or another
65 well known, available Covered Code of the Contributor's choice. The
66 Source Code can be in a compressed or archival form, provided the
67 appropriate decompression or de-archiving software is widely available
68 for no charge.
69
70 1.12. "You" (or "Your") means an individual or a legal entity
71 exercising rights under, and complying with all of the terms of, this
72 License or a future version of this License issued under Section 6.1.
73 For legal entities, "You" includes any entity which controls, is
74 controlled by, or is under common control with You. For purposes of
75 this definition, "control" means (a) the power, direct or indirect,
76 to cause the direction or management of such entity, whether by
77 contract or otherwise, or (b) ownership of more than fifty percent
78 (50%) of the outstanding shares or beneficial ownership of such
79 entity.
80
81 2. Source Code License.
82
83 2.1. The Initial Developer Grant.
84 The Initial Developer hereby grants You a world-wide, royalty-free,
85 non-exclusive license, subject to third party intellectual property
86 claims:
87 (a) under intellectual property rights (other than patent or
88 trademark) Licensable by Initial Developer to use, reproduce,
89 modify, display, perform, sublicense and distribute the Original
90 Code (or portions thereof) with or without Modifications, and/or
91 as part of a Larger Work; and
92
93 (b) under Patents Claims infringed by the making, using or
94 selling of Original Code, to make, have made, use, practice,
95 sell, and offer for sale, and/or otherwise dispose of the
96 Original Code (or portions thereof).
97
98 (c) the licenses granted in this Section 2.1(a) and (b) are
99 effective on the date Initial Developer first distributes
100 Original Code under the terms of this License.
101
102 (d) Notwithstanding Section 2.1(b) above, no patent license is
103 granted: 1) for code that You delete from the Original Code; 2)
104 separate from the Original Code; or 3) for infringements caused
105 by: i) the modification of the Original Code or ii) the
106 combination of the Original Code with other software or devices.
107
108 2.2. Contributor Grant.
109 Subject to third party intellectual property claims, each Contributor
110 hereby grants You a world-wide, royalty-free, non-exclusive license
111
112 (a) under intellectual property rights (other than patent or
113 trademark) Licensable by Contributor, to use, reproduce, modify,
114 display, perform, sublicense and distribute the Modifications
115 created by such Contributor (or portions thereof) either on an
116 unmodified basis, with other Modifications, as Covered Code
117 and/or as part of a Larger Work; and
118
119 (b) under Patent Claims infringed by the making, using, or
120 selling of Modifications made by that Contributor either alone
121 and/or in combination with its Contributor Version (or portions
122 of such combination), to make, use, sell, offer for sale, have
123 made, and/or otherwise dispose of: 1) Modifications made by that
124 Contributor (or portions thereof); and 2) the combination of
125 Modifications made by that Contributor with its Contributor
126 Version (or portions of such combination).
127
128 (c) the licenses granted in Sections 2.2(a) and 2.2(b) are
129 effective on the date Contributor first makes Commercial Use of
130 the Covered Code.
131
132 (d) Notwithstanding Section 2.2(b) above, no patent license is
133 granted: 1) for any code that Contributor has deleted from the
134 Contributor Version; 2) separate from the Contributor Version;
135 3) for infringements caused by: i) third party modifications of
136 Contributor Version or ii) the combination of Modifications made
137 by that Contributor with other software (except as part of the
138 Contributor Version) or other devices; or 4) under Patent Claims
139 infringed by Covered Code in the absence of Modifications made by
140 that Contributor.
141
142 3. Distribution Obligations.
143
144 3.1. Application of License.
145 The Modifications which You create or to which You contribute are
146 governed by the terms of this License, including without limitation
147 Section 2.2. The Source Code version of Covered Code may be
148 distributed only under the terms of this License or a future version
149 of this License released under Section 6.1, and You must include a
150 copy of this License with every copy of the Source Code You
151 distribute. You may not offer or impose any terms on any Source Code
152 version that alters or restricts the applicable version of this
153 License or the recipients' rights hereunder. However, You may include
154 an additional document offering the additional rights described in
155 Section 3.5.
156
157 3.2. Availability of Source Code.
158 Any Modification which You create or to which You contribute must be
159 made available in Source Code form under the terms of this License
160 either on the same media as an Executable version or via an accepted
161 Electronic Distribution Mechanism to anyone to whom you made an
162 Executable version available; and if made available via Electronic
163 Distribution Mechanism, must remain available for at least twelve (12)
164 months after the date it initially became available, or at least six
165 (6) months after a subsequent version of that particular Modification
166 has been made available to such recipients. You are responsible for
167 ensuring that the Source Code version remains available even if the
168 Electronic Distribution Mechanism is maintained by a third party.
169
170 3.3. Description of Modifications.
171 You must cause all Covered Code to which You contribute to contain a
172 file documenting the changes You made to create that Covered Code and
173 the date of any change. You must include a prominent statement that
174 the Modification is derived, directly or indirectly, from Original
175 Code provided by the Initial Developer and including the name of the
176 Initial Developer in (a) the Source Code, and (b) in any notice in an
177 Executable version or related documentation in which You describe the
178 origin or ownership of the Covered Code.
179
180 3.4. Intellectual Property Matters
181 (a) Third Party Claims.
182 If Contributor has knowledge that a license under a third party's
183 intellectual property rights is required to exercise the rights
184 granted by such Contributor under Sections 2.1 or 2.2,
185 Contributor must include a text file with the Source Code
186 distribution titled "LEGAL" which describes the claim and the
187 party making the claim in sufficient detail that a recipient will
188 know whom to contact. If Contributor obtains such knowledge after
189 the Modification is made available as described in Section 3.2,
190 Contributor shall promptly modify the LEGAL file in all copies
191 Contributor makes available thereafter and shall take other steps
192 (such as notifying appropriate mailing lists or newsgroups)
193 reasonably calculated to inform those who received the Covered
194 Code that new knowledge has been obtained.
195
196 (b) Contributor APIs.
197 If Contributor's Modifications include an application programming
198 interface and Contributor has knowledge of patent licenses which
199 are reasonably necessary to implement that API, Contributor must
200 also include this information in the LEGAL file.
201
202 (c) Representations.
203 Contributor represents that, except as disclosed pursuant to
204 Section 3.4(a) above, Contributor believes that Contributor's
205 Modifications are Contributor's original creation(s) and/or
206 Contributor has sufficient rights to grant the rights conveyed by
207 this License.
208
209 3.5. Required Notices.
210 You must duplicate the notice in Exhibit A in each file of the Source
211 Code. If it is not possible to put such notice in a particular Source
212 Code file due to its structure, then You must include such notice in a
213 location (such as a relevant directory) where a user would be likely
214 to look for such a notice. If You created one or more Modification(s)
215 You may add your name as a Contributor to the notice described in
216 Exhibit A. You must also duplicate this License in any documentation
217 for the Source Code where You describe recipients' rights or ownership
218 rights relating to Covered Code. You may choose to offer, and to
219 charge a fee for, warranty, support, indemnity or liability
220 obligations to one or more recipients of Covered Code. However, You
221 may do so only on Your own behalf, and not on behalf of the Initial
222 Developer or any Contributor. You must make it absolutely clear than
223 any such warranty, support, indemnity or liability obligation is
224 offered by You alone, and You hereby agree to indemnify the Initial
225 Developer and every Contributor for any liability incurred by the
226 Initial Developer or such Contributor as a result of warranty,
227 support, indemnity or liability terms You offer.
228
229 3.6. Distribution of Executable Versions.
230 You may distribute Covered Code in Executable form only if the
231 requirements of Section 3.1-3.5 have been met for that Covered Code,
232 and if You include a notice stating that the Source Code version of
233 the Covered Code is available under the terms of this License,
234 including a description of how and where You have fulfilled the
235 obligations of Section 3.2. The notice must be conspicuously included
236 in any notice in an Executable version, related documentation or
237 collateral in which You describe recipients' rights relating to the
238 Covered Code. You may distribute the Executable version of Covered
239 Code or ownership rights under a license of Your choice, which may
240 contain terms different from this License, provided that You are in
241 compliance with the terms of this License and that the license for the
242 Executable version does not attempt to limit or alter the recipient's
243 rights in the Source Code version from the rights set forth in this
244 License. If You distribute the Executable version under a different
245 license You must make it absolutely clear that any terms which differ
246 from this License are offered by You alone, not by the Initial
247 Developer or any Contributor. You hereby agree to indemnify the
248 Initial Developer and every Contributor for any liability incurred by
249 the Initial Developer or such Contributor as a result of any such
250 terms You offer.
251
252 3.7. Larger Works.
253 You may create a Larger Work by combining Covered Code with other code
254 not governed by the terms of this License and distribute the Larger
255 Work as a single product. In such a case, You must make sure the
256 requirements of this License are fulfilled for the Covered Code.
257
258 4. Inability to Comply Due to Statute or Regulation.
259
260 If it is impossible for You to comply with any of the terms of this
261 License with respect to some or all of the Covered Code due to
262 statute, judicial order, or regulation then You must: (a) comply with
263 the terms of this License to the maximum extent possible; and (b)
264 describe the limitations and the code they affect. Such description
265 must be included in the LEGAL file described in Section 3.4 and must
266 be included with all distributions of the Source Code. Except to the
267 extent prohibited by statute or regulation, such description must be
268 sufficiently detailed for a recipient of ordinary skill to be able to
269 understand it.
270
271 5. Application of this License.
272
273 This License applies to code to which the Initial Developer has
274 attached the notice in Exhibit A and to related Covered Code.
275
276 6. Versions of the License.
277
278 6.1. New Versions.
279 Netscape Communications Corporation ("Netscape") may publish revised
280 and/or new versions of the License from time to time. Each version
281 will be given a distinguishing version number.
282
283 6.2. Effect of New Versions.
284 Once Covered Code has been published under a particular version of the
285 License, You may always continue to use it under the terms of that
286 version. You may also choose to use such Covered Code under the terms
287 of any subsequent version of the License published by Netscape. No one
288 other than Netscape has the right to modify the terms applicable to
289 Covered Code created under this License.
290
291 6.3. Derivative Works.
292 If You create or use a modified version of this License (which you may
293 only do in order to apply it to code which is not already Covered Code
294 governed by this License), You must (a) rename Your license so that
295 the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
296 "MPL", "NPL" or any confusingly similar phrase do not appear in your
297 license (except to note that your license differs from this License)
298 and (b) otherwise make it clear that Your version of the license
299 contains terms which differ from the Mozilla Public License and
300 Netscape Public License. (Filling in the name of the Initial
301 Developer, Original Code or Contributor in the notice described in
302 Exhibit A shall not of themselves be deemed to be modifications of
303 this License.)
304
305 7. DISCLAIMER OF WARRANTY.
306
307 COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
308 WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
309 WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
310 DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
311 THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE
312 IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,
313 YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE
314 COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
315 OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
316 ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
317
318 8. TERMINATION.
319
320 8.1. This License and the rights granted hereunder will terminate
321 automatically if You fail to comply with terms herein and fail to cure
322 such breach within 30 days of becoming aware of the breach. All
323 sublicenses to the Covered Code which are properly granted shall
324 survive any termination of this License. Provisions which, by their
325 nature, must remain in effect beyond the termination of this License
326 shall survive.
327
328 8.2. If You initiate litigation by asserting a patent infringement
329 claim (excluding declatory judgment actions) against Initial Developer
330 or a Contributor (the Initial Developer or Contributor against whom
331 You file such action is referred to as "Participant") alleging that:
332
333 (a) such Participant's Contributor Version directly or indirectly
334 infringes any patent, then any and all rights granted by such
335 Participant to You under Sections 2.1 and/or 2.2 of this License
336 shall, upon 60 days notice from Participant terminate prospectively,
337 unless if within 60 days after receipt of notice You either: (i)
338 agree in writing to pay Participant a mutually agreeable reasonable
339 royalty for Your past and future use of Modifications made by such
340 Participant, or (ii) withdraw Your litigation claim with respect to
341 the Contributor Version against such Participant. If within 60 days
342 of notice, a reasonable royalty and payment arrangement are not
343 mutually agreed upon in writing by the parties or the litigation claim
344 is not withdrawn, the rights granted by Participant to You under
345 Sections 2.1 and/or 2.2 automatically terminate at the expiration of
346 the 60 day notice period specified above.
347
348 (b) any software, hardware, or device, other than such Participant's
349 Contributor Version, directly or indirectly infringes any patent, then
350 any rights granted to You by such Participant under Sections 2.1(b)
351 and 2.2(b) are revoked effective as of the date You first made, used,
352 sold, distributed, or had made, Modifications made by that
353 Participant.
354
355 8.3. If You assert a patent infringement claim against Participant
356 alleging that such Participant's Contributor Version directly or
357 indirectly infringes any patent where such claim is resolved (such as
358 by license or settlement) prior to the initiation of patent
359 infringement litigation, then the reasonable value of the licenses
360 granted by such Participant under Sections 2.1 or 2.2 shall be taken
361 into account in determining the amount or value of any payment or
362 license.
363
364 8.4. In the event of termination under Sections 8.1 or 8.2 above,
365 all end user license agreements (excluding distributors and resellers)
366 which have been validly granted by You or any distributor hereunder
367 prior to termination shall survive termination.
368
369 9. LIMITATION OF LIABILITY.
370
371 UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
372 (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
373 DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
374 OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
375 ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
376 CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
377 WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
378 COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
379 INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
380 LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
381 RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
382 PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
383 EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
384 THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
385
386 10. U.S. GOVERNMENT END USERS.
387
388 The Covered Code is a "commercial item," as that term is defined in
389 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
390 software" and "commercial computer software documentation," as such
391 terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
392 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
393 all U.S. Government End Users acquire Covered Code with only those
394 rights set forth herein.
395
396 11. MISCELLANEOUS.
397
398 This License represents the complete agreement concerning subject
399 matter hereof. If any provision of this License is held to be
400 unenforceable, such provision shall be reformed only to the extent
401 necessary to make it enforceable. This License shall be governed by
402 California law provisions (except to the extent applicable law, if
403 any, provides otherwise), excluding its conflict-of-law provisions.
404 With respect to disputes in which at least one party is a citizen of,
405 or an entity chartered or registered to do business in the United
406 States of America, any litigation relating to this License shall be
407 subject to the jurisdiction of the Federal Courts of the Northern
408 District of California, with venue lying in Santa Clara County,
409 California, with the losing party responsible for costs, including
410 without limitation, court costs and reasonable attorneys' fees and
411 expenses. The application of the United Nations Convention on
412 Contracts for the International Sale of Goods is expressly excluded.
413 Any law or regulation which provides that the language of a contract
414 shall be construed against the drafter shall not apply to this
415 License.
416
417 12. RESPONSIBILITY FOR CLAIMS.
418
419 As between Initial Developer and the Contributors, each party is
420 responsible for claims and damages arising, directly or indirectly,
421 out of its utilization of rights under this License and You agree to
422 work with Initial Developer and Contributors to distribute such
423 responsibility on an equitable basis. Nothing herein is intended or
424 shall be deemed to constitute any admission of liability.
425
426 13. MULTIPLE-LICENSED CODE.
427
428 Initial Developer may designate portions of the Covered Code as
429 "Multiple-Licensed". "Multiple-Licensed" means that the Initial
430 Developer permits you to utilize portions of the Covered Code under
431 Your choice of the NPL or the alternative licenses, if any, specified
432 by the Initial Developer in the file described in Exhibit A.
433
434 EXHIBIT A -Mozilla Public License.
435
436 ``The contents of this file are subject to the Mozilla Public License
437 Version 1.1 (the "License"); you may not use this file except in
438 compliance with the License. You may obtain a copy of the License at
439 http://www.mozilla.org/MPL/
440
441 Software distributed under the License is distributed on an "AS IS"
442 basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
443 License for the specific language governing rights and limitations
444 under the License.
445
446 The Original Code is RabbitMQ.
447
448 The Initial Developer of the Original Code is GoPivotal, Inc.
449 Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved.''
450
451 [NOTE: The text of this Exhibit A may differ slightly from the text of
452 the notices in the Source Code files of the Original Code. You should
453 use the text of this Exhibit A rather than the text found in the
454 Original Code Source Code for Your Modifications.]
0 PROJECT = rabbitmq_web_mqtt_examples
1 PROJECT_DESCRIPTION = Rabbit WEB-MQTT - examples
2 PROJECT_MOD = rabbit_web_mqtt_examples_app
3
4 define PROJECT_ENV
5 [
6 {listener, [{port, 15670}]}
7 ]
8 endef
9
10 DEPS = rabbit_common rabbit rabbitmq_web_dispatch rabbitmq_web_mqtt
11
12 DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
13 DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk
14
15 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
16 # reviewed and merged.
17
18 ERLANG_MK_REPO = https://github.com/rabbitmq/erlang.mk.git
19 ERLANG_MK_COMMIT = rabbitmq-tmp
20
21 include rabbitmq-components.mk
22 include erlang.mk
0 # RabbitMQ Web MQTT Examples
1
2 This project contains few basic examples of [RabbitMQ Web MQTT plugin](https://github.com/rabbitmq/rabbitmq-web-mqtt)
3 usage.
4
5 Once installed the server will bind to port 15670 and serve few static
6 HTML files on port 15670 (e.g. [http://127.0.0.1:15670](http://127.0.0.1:15670/)).
7
8 ## Installation
9
10 * [RabbitMQ plugin build instructions](http://www.rabbitmq.com/plugin-development.html).
11
12 * [RabbitMQ plugin installation](http://www.rabbitmq.com/plugins.html#installing-plugins).
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
1 #
2 # Permission to use, copy, modify, and/or distribute this software for any
3 # purpose with or without fee is hereby granted, provided that the above
4 # copyright notice and this permission notice appear in all copies.
5 #
6 # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
7 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
8 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
9 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
10 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
11 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
12 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
13
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
15
16 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
31
32 # Core configuration.
33
34 PROJECT ?= $(notdir $(CURDIR))
35 PROJECT := $(strip $(PROJECT))
36
37 PROJECT_VERSION ?= rolling
38 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
40
41 # Verbosity.
42
43 V ?= 0
44
45 verbose_0 = @
46 verbose_2 = set -x;
47 verbose = $(verbose_$(V))
48
49 gen_verbose_0 = @echo " GEN " $@;
50 gen_verbose_2 = set -x;
51 gen_verbose = $(gen_verbose_$(V))
52
53 # Temporary files directory.
54
55 ERLANG_MK_TMP ?= $(CURDIR)/.erlang.mk
56 export ERLANG_MK_TMP
57
58 # "erl" command.
59
60 ERL = erl +A0 -noinput -boot start_clean
61
62 # Platform detection.
63
64 ifeq ($(PLATFORM),)
65 UNAME_S := $(shell uname -s)
66
67 ifeq ($(UNAME_S),Linux)
68 PLATFORM = linux
69 else ifeq ($(UNAME_S),Darwin)
70 PLATFORM = darwin
71 else ifeq ($(UNAME_S),SunOS)
72 PLATFORM = solaris
73 else ifeq ($(UNAME_S),GNU)
74 PLATFORM = gnu
75 else ifeq ($(UNAME_S),FreeBSD)
76 PLATFORM = freebsd
77 else ifeq ($(UNAME_S),NetBSD)
78 PLATFORM = netbsd
79 else ifeq ($(UNAME_S),OpenBSD)
80 PLATFORM = openbsd
81 else ifeq ($(UNAME_S),DragonFly)
82 PLATFORM = dragonfly
83 else ifeq ($(shell uname -o),Msys)
84 PLATFORM = msys2
85 else
86 $(error Unable to detect platform. Please open a ticket with the output of uname -a.)
87 endif
88
89 export PLATFORM
90 endif
91
92 # Core targets.
93
94 all:: deps app rel
95
96 # Noop to avoid a Make warning when there's nothing to do.
97 rel::
98 $(verbose) :
99
100 relup:: deps app
101
102 check:: tests
103
104 clean:: clean-crashdump
105
106 clean-crashdump:
107 ifneq ($(wildcard erl_crash.dump),)
108 $(gen_verbose) rm -f erl_crash.dump
109 endif
110
111 distclean:: clean distclean-tmp
112
113 distclean-tmp:
114 $(gen_verbose) rm -rf $(ERLANG_MK_TMP)
115
116 help::
117 $(verbose) printf "%s\n" \
118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
120 "" \
121 "Usage: [V=1] $(MAKE) [target]..." \
122 "" \
123 "Core targets:" \
124 " all Run deps, app and rel targets in that order" \
125 " app Compile the project" \
126 " deps Fetch dependencies (if needed) and compile them" \
127 " fetch-deps Fetch dependencies recursively (if needed) without compiling them" \
128 " list-deps List dependencies recursively on stdout" \
129 " search q=... Search for a package in the built-in index" \
130 " rel Build a release for this project, if applicable" \
131 " docs Build the documentation for this project" \
132 " install-docs Install the man pages for this project" \
133 " check Compile and run all tests and analysis for this project" \
134 " tests Run the tests for this project" \
135 " clean Delete temporary and output files from most targets" \
136 " distclean Delete all temporary and output files" \
137 " help Display this help and exit" \
138 " erlang-mk Update erlang.mk to the latest version"
139
140 # Core functions.
141
142 empty :=
143 space := $(empty) $(empty)
144 tab := $(empty) $(empty)
145 comma := ,
146
147 define newline
148
149
150 endef
151
152 define comma_list
153 $(subst $(space),$(comma),$(strip $(1)))
154 endef
155
156 # Adding erlang.mk to make Erlang scripts who call init:get_plain_arguments() happy.
157 define erlang
158 $(ERL) $(2) -pz $(ERLANG_MK_TMP)/rebar/ebin -eval "$(subst $(newline),,$(subst ",\",$(1)))" -- erlang.mk
159 endef
160
161 ifeq ($(PLATFORM),msys2)
162 core_native_path = $(subst \,\\\\,$(shell cygpath -w $1))
163 else
164 core_native_path = $1
165 endif
166
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
168
169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
170
171 core_find = $(if $(wildcard $1),$(shell find $(1:%/=%) -type f -name $(subst *,\*,$2)))
172
173 core_lc = $(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$(1)))))))))))))))))))))))))))
174
175 core_ls = $(filter-out $(1),$(shell echo $(1)))
176
177 # @todo Use a solution that does not require using perl.
178 core_relpath = $(shell perl -e 'use File::Spec; print File::Spec->abs2rel(@ARGV) . "\n"' $1 $2)
179
180 # Automated update.
181
182 ERLANG_MK_REPO ?= https://github.com/ninenines/erlang.mk
183 ERLANG_MK_COMMIT ?=
184 ERLANG_MK_BUILD_CONFIG ?= build.config
185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
188 erlang-mk:
189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
190 ifdef ERLANG_MK_COMMIT
191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
192 endif
193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
196 rm -rf $(ERLANG_MK_BUILD_DIR)
197
198 # The erlang.mk package index is bundled in the default erlang.mk build.
199 # Search for the string "copyright" to skip to the rest of the code.
200
201 PACKAGES += aberth
202 pkg_aberth_name = aberth
203 pkg_aberth_description = Generic BERT-RPC server in Erlang
204 pkg_aberth_homepage = https://github.com/a13x/aberth
205 pkg_aberth_fetch = git
206 pkg_aberth_repo = https://github.com/a13x/aberth
207 pkg_aberth_commit = master
208
209 PACKAGES += active
210 pkg_active_name = active
211 pkg_active_description = Active development for Erlang: rebuild and reload source/binary files while the VM is running
212 pkg_active_homepage = https://github.com/proger/active
213 pkg_active_fetch = git
214 pkg_active_repo = https://github.com/proger/active
215 pkg_active_commit = master
216
217 PACKAGES += actordb_core
218 pkg_actordb_core_name = actordb_core
219 pkg_actordb_core_description = ActorDB main source
220 pkg_actordb_core_homepage = http://www.actordb.com/
221 pkg_actordb_core_fetch = git
222 pkg_actordb_core_repo = https://github.com/biokoda/actordb_core
223 pkg_actordb_core_commit = master
224
225 PACKAGES += actordb_thrift
226 pkg_actordb_thrift_name = actordb_thrift
227 pkg_actordb_thrift_description = Thrift API for ActorDB
228 pkg_actordb_thrift_homepage = http://www.actordb.com/
229 pkg_actordb_thrift_fetch = git
230 pkg_actordb_thrift_repo = https://github.com/biokoda/actordb_thrift
231 pkg_actordb_thrift_commit = master
232
233 PACKAGES += aleppo
234 pkg_aleppo_name = aleppo
235 pkg_aleppo_description = Alternative Erlang Pre-Processor
236 pkg_aleppo_homepage = https://github.com/ErlyORM/aleppo
237 pkg_aleppo_fetch = git
238 pkg_aleppo_repo = https://github.com/ErlyORM/aleppo
239 pkg_aleppo_commit = master
240
241 PACKAGES += alog
242 pkg_alog_name = alog
243 pkg_alog_description = Simply the best logging framework for Erlang
244 pkg_alog_homepage = https://github.com/siberian-fast-food/alogger
245 pkg_alog_fetch = git
246 pkg_alog_repo = https://github.com/siberian-fast-food/alogger
247 pkg_alog_commit = master
248
249 PACKAGES += amqp_client
250 pkg_amqp_client_name = amqp_client
251 pkg_amqp_client_description = RabbitMQ Erlang AMQP client
252 pkg_amqp_client_homepage = https://www.rabbitmq.com/erlang-client-user-guide.html
253 pkg_amqp_client_fetch = git
254 pkg_amqp_client_repo = https://github.com/rabbitmq/rabbitmq-erlang-client.git
255 pkg_amqp_client_commit = master
256
257 PACKAGES += annotations
258 pkg_annotations_name = annotations
259 pkg_annotations_description = Simple code instrumentation utilities
260 pkg_annotations_homepage = https://github.com/hyperthunk/annotations
261 pkg_annotations_fetch = git
262 pkg_annotations_repo = https://github.com/hyperthunk/annotations
263 pkg_annotations_commit = master
264
265 PACKAGES += antidote
266 pkg_antidote_name = antidote
267 pkg_antidote_description = Large-scale computation without synchronisation
268 pkg_antidote_homepage = https://syncfree.lip6.fr/
269 pkg_antidote_fetch = git
270 pkg_antidote_repo = https://github.com/SyncFree/antidote
271 pkg_antidote_commit = master
272
273 PACKAGES += apns
274 pkg_apns_name = apns
275 pkg_apns_description = Apple Push Notification Server for Erlang
276 pkg_apns_homepage = http://inaka.github.com/apns4erl
277 pkg_apns_fetch = git
278 pkg_apns_repo = https://github.com/inaka/apns4erl
279 pkg_apns_commit = master
280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
289 PACKAGES += azdht
290 pkg_azdht_name = azdht
291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
292 pkg_azdht_homepage = https://github.com/arcusfelis/azdht
293 pkg_azdht_fetch = git
294 pkg_azdht_repo = https://github.com/arcusfelis/azdht
295 pkg_azdht_commit = master
296
297 PACKAGES += backoff
298 pkg_backoff_name = backoff
299 pkg_backoff_description = Simple exponential backoffs in Erlang
300 pkg_backoff_homepage = https://github.com/ferd/backoff
301 pkg_backoff_fetch = git
302 pkg_backoff_repo = https://github.com/ferd/backoff
303 pkg_backoff_commit = master
304
305 PACKAGES += barrel_tcp
306 pkg_barrel_tcp_name = barrel_tcp
307 pkg_barrel_tcp_description = barrel is a generic TCP acceptor pool with low latency in Erlang.
308 pkg_barrel_tcp_homepage = https://github.com/benoitc-attic/barrel_tcp
309 pkg_barrel_tcp_fetch = git
310 pkg_barrel_tcp_repo = https://github.com/benoitc-attic/barrel_tcp
311 pkg_barrel_tcp_commit = master
312
313 PACKAGES += basho_bench
314 pkg_basho_bench_name = basho_bench
315 pkg_basho_bench_description = A load-generation and testing tool for basically whatever you can write a returning Erlang function for.
316 pkg_basho_bench_homepage = https://github.com/basho/basho_bench
317 pkg_basho_bench_fetch = git
318 pkg_basho_bench_repo = https://github.com/basho/basho_bench
319 pkg_basho_bench_commit = master
320
321 PACKAGES += bcrypt
322 pkg_bcrypt_name = bcrypt
323 pkg_bcrypt_description = Bcrypt Erlang / C library
324 pkg_bcrypt_homepage = https://github.com/riverrun/branglecrypt
325 pkg_bcrypt_fetch = git
326 pkg_bcrypt_repo = https://github.com/riverrun/branglecrypt
327 pkg_bcrypt_commit = master
328
329 PACKAGES += beam
330 pkg_beam_name = beam
331 pkg_beam_description = BEAM emulator written in Erlang
332 pkg_beam_homepage = https://github.com/tonyrog/beam
333 pkg_beam_fetch = git
334 pkg_beam_repo = https://github.com/tonyrog/beam
335 pkg_beam_commit = master
336
337 PACKAGES += beanstalk
338 pkg_beanstalk_name = beanstalk
339 pkg_beanstalk_description = An Erlang client for beanstalkd
340 pkg_beanstalk_homepage = https://github.com/tim/erlang-beanstalk
341 pkg_beanstalk_fetch = git
342 pkg_beanstalk_repo = https://github.com/tim/erlang-beanstalk
343 pkg_beanstalk_commit = master
344
345 PACKAGES += bear
346 pkg_bear_name = bear
347 pkg_bear_description = a set of statistics functions for erlang
348 pkg_bear_homepage = https://github.com/boundary/bear
349 pkg_bear_fetch = git
350 pkg_bear_repo = https://github.com/boundary/bear
351 pkg_bear_commit = master
352
353 PACKAGES += bertconf
354 pkg_bertconf_name = bertconf
355 pkg_bertconf_description = Make ETS tables out of statc BERT files that are auto-reloaded
356 pkg_bertconf_homepage = https://github.com/ferd/bertconf
357 pkg_bertconf_fetch = git
358 pkg_bertconf_repo = https://github.com/ferd/bertconf
359 pkg_bertconf_commit = master
360
361 PACKAGES += bifrost
362 pkg_bifrost_name = bifrost
363 pkg_bifrost_description = Erlang FTP Server Framework
364 pkg_bifrost_homepage = https://github.com/thorstadt/bifrost
365 pkg_bifrost_fetch = git
366 pkg_bifrost_repo = https://github.com/thorstadt/bifrost
367 pkg_bifrost_commit = master
368
369 PACKAGES += binpp
370 pkg_binpp_name = binpp
371 pkg_binpp_description = Erlang Binary Pretty Printer
372 pkg_binpp_homepage = https://github.com/jtendo/binpp
373 pkg_binpp_fetch = git
374 pkg_binpp_repo = https://github.com/jtendo/binpp
375 pkg_binpp_commit = master
376
377 PACKAGES += bisect
378 pkg_bisect_name = bisect
379 pkg_bisect_description = Ordered fixed-size binary dictionary in Erlang
380 pkg_bisect_homepage = https://github.com/knutin/bisect
381 pkg_bisect_fetch = git
382 pkg_bisect_repo = https://github.com/knutin/bisect
383 pkg_bisect_commit = master
384
385 PACKAGES += bitcask
386 pkg_bitcask_name = bitcask
387 pkg_bitcask_description = because you need another a key/value storage engine
388 pkg_bitcask_homepage = https://github.com/basho/bitcask
389 pkg_bitcask_fetch = git
390 pkg_bitcask_repo = https://github.com/basho/bitcask
391 pkg_bitcask_commit = develop
392
393 PACKAGES += bitstore
394 pkg_bitstore_name = bitstore
395 pkg_bitstore_description = A document based ontology development environment
396 pkg_bitstore_homepage = https://github.com/bdionne/bitstore
397 pkg_bitstore_fetch = git
398 pkg_bitstore_repo = https://github.com/bdionne/bitstore
399 pkg_bitstore_commit = master
400
401 PACKAGES += bootstrap
402 pkg_bootstrap_name = bootstrap
403 pkg_bootstrap_description = A simple, yet powerful Erlang cluster bootstrapping application.
404 pkg_bootstrap_homepage = https://github.com/schlagert/bootstrap
405 pkg_bootstrap_fetch = git
406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
407 pkg_bootstrap_commit = master
408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
417 PACKAGES += boss_db
418 pkg_boss_db_name = boss_db
419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
420 pkg_boss_db_homepage = https://github.com/ErlyORM/boss_db
421 pkg_boss_db_fetch = git
422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
423 pkg_boss_db_commit = master
424
425 PACKAGES += brod
426 pkg_brod_name = brod
427 pkg_brod_description = Kafka client in Erlang
428 pkg_brod_homepage = https://github.com/klarna/brod
429 pkg_brod_fetch = git
430 pkg_brod_repo = https://github.com/klarna/brod.git
431 pkg_brod_commit = master
432
433 PACKAGES += bson
434 pkg_bson_name = bson
435 pkg_bson_description = BSON documents in Erlang, see bsonspec.org
436 pkg_bson_homepage = https://github.com/comtihon/bson-erlang
437 pkg_bson_fetch = git
438 pkg_bson_repo = https://github.com/comtihon/bson-erlang
439 pkg_bson_commit = master
440
441 PACKAGES += bullet
442 pkg_bullet_name = bullet
443 pkg_bullet_description = Simple, reliable, efficient streaming for Cowboy.
444 pkg_bullet_homepage = http://ninenines.eu
445 pkg_bullet_fetch = git
446 pkg_bullet_repo = https://github.com/ninenines/bullet
447 pkg_bullet_commit = master
448
449 PACKAGES += cache
450 pkg_cache_name = cache
451 pkg_cache_description = Erlang in-memory cache
452 pkg_cache_homepage = https://github.com/fogfish/cache
453 pkg_cache_fetch = git
454 pkg_cache_repo = https://github.com/fogfish/cache
455 pkg_cache_commit = master
456
457 PACKAGES += cake
458 pkg_cake_name = cake
459 pkg_cake_description = Really simple terminal colorization
460 pkg_cake_homepage = https://github.com/darach/cake-erl
461 pkg_cake_fetch = git
462 pkg_cake_repo = https://github.com/darach/cake-erl
463 pkg_cake_commit = master
464
465 PACKAGES += carotene
466 pkg_carotene_name = carotene
467 pkg_carotene_description = Real-time server
468 pkg_carotene_homepage = https://github.com/carotene/carotene
469 pkg_carotene_fetch = git
470 pkg_carotene_repo = https://github.com/carotene/carotene
471 pkg_carotene_commit = master
472
473 PACKAGES += cberl
474 pkg_cberl_name = cberl
475 pkg_cberl_description = NIF based Erlang bindings for Couchbase
476 pkg_cberl_homepage = https://github.com/chitika/cberl
477 pkg_cberl_fetch = git
478 pkg_cberl_repo = https://github.com/chitika/cberl
479 pkg_cberl_commit = master
480
481 PACKAGES += cecho
482 pkg_cecho_name = cecho
483 pkg_cecho_description = An ncurses library for Erlang
484 pkg_cecho_homepage = https://github.com/mazenharake/cecho
485 pkg_cecho_fetch = git
486 pkg_cecho_repo = https://github.com/mazenharake/cecho
487 pkg_cecho_commit = master
488
489 PACKAGES += cferl
490 pkg_cferl_name = cferl
491 pkg_cferl_description = Rackspace / Open Stack Cloud Files Erlang Client
492 pkg_cferl_homepage = https://github.com/ddossot/cferl
493 pkg_cferl_fetch = git
494 pkg_cferl_repo = https://github.com/ddossot/cferl
495 pkg_cferl_commit = master
496
497 PACKAGES += chaos_monkey
498 pkg_chaos_monkey_name = chaos_monkey
499 pkg_chaos_monkey_description = This is The CHAOS MONKEY. It will kill your processes.
500 pkg_chaos_monkey_homepage = https://github.com/dLuna/chaos_monkey
501 pkg_chaos_monkey_fetch = git
502 pkg_chaos_monkey_repo = https://github.com/dLuna/chaos_monkey
503 pkg_chaos_monkey_commit = master
504
505 PACKAGES += check_node
506 pkg_check_node_name = check_node
507 pkg_check_node_description = Nagios Scripts for monitoring Riak
508 pkg_check_node_homepage = https://github.com/basho-labs/riak_nagios
509 pkg_check_node_fetch = git
510 pkg_check_node_repo = https://github.com/basho-labs/riak_nagios
511 pkg_check_node_commit = master
512
513 PACKAGES += chronos
514 pkg_chronos_name = chronos
515 pkg_chronos_description = Timer module for Erlang that makes it easy to abstact time out of the tests.
516 pkg_chronos_homepage = https://github.com/lehoff/chronos
517 pkg_chronos_fetch = git
518 pkg_chronos_repo = https://github.com/lehoff/chronos
519 pkg_chronos_commit = master
520
521 PACKAGES += chumak
522 pkg_chumak_name = chumak
523 pkg_chumak_description = Pure Erlang implementation of ZeroMQ Message Transport Protocol.
524 pkg_chumak_homepage = http://choven.ca
525 pkg_chumak_fetch = git
526 pkg_chumak_repo = https://github.com/chovencorp/chumak
527 pkg_chumak_commit = master
528
529 PACKAGES += cl
530 pkg_cl_name = cl
531 pkg_cl_description = OpenCL binding for Erlang
532 pkg_cl_homepage = https://github.com/tonyrog/cl
533 pkg_cl_fetch = git
534 pkg_cl_repo = https://github.com/tonyrog/cl
535 pkg_cl_commit = master
536
537 PACKAGES += classifier
538 pkg_classifier_name = classifier
539 pkg_classifier_description = An Erlang Bayesian Filter and Text Classifier
540 pkg_classifier_homepage = https://github.com/inaka/classifier
541 pkg_classifier_fetch = git
542 pkg_classifier_repo = https://github.com/inaka/classifier
543 pkg_classifier_commit = master
544
545 PACKAGES += clique
546 pkg_clique_name = clique
547 pkg_clique_description = CLI Framework for Erlang
548 pkg_clique_homepage = https://github.com/basho/clique
549 pkg_clique_fetch = git
550 pkg_clique_repo = https://github.com/basho/clique
551 pkg_clique_commit = develop
552
553 PACKAGES += cloudi_core
554 pkg_cloudi_core_name = cloudi_core
555 pkg_cloudi_core_description = CloudI internal service runtime
556 pkg_cloudi_core_homepage = http://cloudi.org/
557 pkg_cloudi_core_fetch = git
558 pkg_cloudi_core_repo = https://github.com/CloudI/cloudi_core
559 pkg_cloudi_core_commit = master
560
561 PACKAGES += cloudi_service_api_requests
562 pkg_cloudi_service_api_requests_name = cloudi_service_api_requests
563 pkg_cloudi_service_api_requests_description = CloudI Service API requests (JSON-RPC/Erlang-term support)
564 pkg_cloudi_service_api_requests_homepage = http://cloudi.org/
565 pkg_cloudi_service_api_requests_fetch = git
566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
567 pkg_cloudi_service_api_requests_commit = master
568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
585 PACKAGES += cloudi_service_db_cassandra_cql
586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
588 pkg_cloudi_service_db_cassandra_cql_homepage = http://cloudi.org/
589 pkg_cloudi_service_db_cassandra_cql_fetch = git
590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
591 pkg_cloudi_service_db_cassandra_cql_commit = master
592
593 PACKAGES += cloudi_service_db_couchdb
594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
596 pkg_cloudi_service_db_couchdb_homepage = http://cloudi.org/
597 pkg_cloudi_service_db_couchdb_fetch = git
598 pkg_cloudi_service_db_couchdb_repo = https://github.com/CloudI/cloudi_service_db_couchdb
599 pkg_cloudi_service_db_couchdb_commit = master
600
601 PACKAGES += cloudi_service_db_elasticsearch
602 pkg_cloudi_service_db_elasticsearch_name = cloudi_service_db_elasticsearch
603 pkg_cloudi_service_db_elasticsearch_description = elasticsearch CloudI Service
604 pkg_cloudi_service_db_elasticsearch_homepage = http://cloudi.org/
605 pkg_cloudi_service_db_elasticsearch_fetch = git
606 pkg_cloudi_service_db_elasticsearch_repo = https://github.com/CloudI/cloudi_service_db_elasticsearch
607 pkg_cloudi_service_db_elasticsearch_commit = master
608
609 PACKAGES += cloudi_service_db_memcached
610 pkg_cloudi_service_db_memcached_name = cloudi_service_db_memcached
611 pkg_cloudi_service_db_memcached_description = memcached CloudI Service
612 pkg_cloudi_service_db_memcached_homepage = http://cloudi.org/
613 pkg_cloudi_service_db_memcached_fetch = git
614 pkg_cloudi_service_db_memcached_repo = https://github.com/CloudI/cloudi_service_db_memcached
615 pkg_cloudi_service_db_memcached_commit = master
616
617 PACKAGES += cloudi_service_db_mysql
618 pkg_cloudi_service_db_mysql_name = cloudi_service_db_mysql
619 pkg_cloudi_service_db_mysql_description = MySQL CloudI Service
620 pkg_cloudi_service_db_mysql_homepage = http://cloudi.org/
621 pkg_cloudi_service_db_mysql_fetch = git
622 pkg_cloudi_service_db_mysql_repo = https://github.com/CloudI/cloudi_service_db_mysql
623 pkg_cloudi_service_db_mysql_commit = master
624
625 PACKAGES += cloudi_service_db_pgsql
626 pkg_cloudi_service_db_pgsql_name = cloudi_service_db_pgsql
627 pkg_cloudi_service_db_pgsql_description = PostgreSQL CloudI Service
628 pkg_cloudi_service_db_pgsql_homepage = http://cloudi.org/
629 pkg_cloudi_service_db_pgsql_fetch = git
630 pkg_cloudi_service_db_pgsql_repo = https://github.com/CloudI/cloudi_service_db_pgsql
631 pkg_cloudi_service_db_pgsql_commit = master
632
633 PACKAGES += cloudi_service_db_riak
634 pkg_cloudi_service_db_riak_name = cloudi_service_db_riak
635 pkg_cloudi_service_db_riak_description = Riak CloudI Service
636 pkg_cloudi_service_db_riak_homepage = http://cloudi.org/
637 pkg_cloudi_service_db_riak_fetch = git
638 pkg_cloudi_service_db_riak_repo = https://github.com/CloudI/cloudi_service_db_riak
639 pkg_cloudi_service_db_riak_commit = master
640
641 PACKAGES += cloudi_service_db_tokyotyrant
642 pkg_cloudi_service_db_tokyotyrant_name = cloudi_service_db_tokyotyrant
643 pkg_cloudi_service_db_tokyotyrant_description = Tokyo Tyrant CloudI Service
644 pkg_cloudi_service_db_tokyotyrant_homepage = http://cloudi.org/
645 pkg_cloudi_service_db_tokyotyrant_fetch = git
646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
647 pkg_cloudi_service_db_tokyotyrant_commit = master
648
649 PACKAGES += cloudi_service_filesystem
650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
652 pkg_cloudi_service_filesystem_homepage = http://cloudi.org/
653 pkg_cloudi_service_filesystem_fetch = git
654 pkg_cloudi_service_filesystem_repo = https://github.com/CloudI/cloudi_service_filesystem
655 pkg_cloudi_service_filesystem_commit = master
656
657 PACKAGES += cloudi_service_http_client
658 pkg_cloudi_service_http_client_name = cloudi_service_http_client
659 pkg_cloudi_service_http_client_description = HTTP client CloudI Service
660 pkg_cloudi_service_http_client_homepage = http://cloudi.org/
661 pkg_cloudi_service_http_client_fetch = git
662 pkg_cloudi_service_http_client_repo = https://github.com/CloudI/cloudi_service_http_client
663 pkg_cloudi_service_http_client_commit = master
664
665 PACKAGES += cloudi_service_http_cowboy
666 pkg_cloudi_service_http_cowboy_name = cloudi_service_http_cowboy
667 pkg_cloudi_service_http_cowboy_description = cowboy HTTP/HTTPS CloudI Service
668 pkg_cloudi_service_http_cowboy_homepage = http://cloudi.org/
669 pkg_cloudi_service_http_cowboy_fetch = git
670 pkg_cloudi_service_http_cowboy_repo = https://github.com/CloudI/cloudi_service_http_cowboy
671 pkg_cloudi_service_http_cowboy_commit = master
672
673 PACKAGES += cloudi_service_http_elli
674 pkg_cloudi_service_http_elli_name = cloudi_service_http_elli
675 pkg_cloudi_service_http_elli_description = elli HTTP CloudI Service
676 pkg_cloudi_service_http_elli_homepage = http://cloudi.org/
677 pkg_cloudi_service_http_elli_fetch = git
678 pkg_cloudi_service_http_elli_repo = https://github.com/CloudI/cloudi_service_http_elli
679 pkg_cloudi_service_http_elli_commit = master
680
681 PACKAGES += cloudi_service_map_reduce
682 pkg_cloudi_service_map_reduce_name = cloudi_service_map_reduce
683 pkg_cloudi_service_map_reduce_description = Map/Reduce CloudI Service
684 pkg_cloudi_service_map_reduce_homepage = http://cloudi.org/
685 pkg_cloudi_service_map_reduce_fetch = git
686 pkg_cloudi_service_map_reduce_repo = https://github.com/CloudI/cloudi_service_map_reduce
687 pkg_cloudi_service_map_reduce_commit = master
688
689 PACKAGES += cloudi_service_oauth1
690 pkg_cloudi_service_oauth1_name = cloudi_service_oauth1
691 pkg_cloudi_service_oauth1_description = OAuth v1.0 CloudI Service
692 pkg_cloudi_service_oauth1_homepage = http://cloudi.org/
693 pkg_cloudi_service_oauth1_fetch = git
694 pkg_cloudi_service_oauth1_repo = https://github.com/CloudI/cloudi_service_oauth1
695 pkg_cloudi_service_oauth1_commit = master
696
697 PACKAGES += cloudi_service_queue
698 pkg_cloudi_service_queue_name = cloudi_service_queue
699 pkg_cloudi_service_queue_description = Persistent Queue Service
700 pkg_cloudi_service_queue_homepage = http://cloudi.org/
701 pkg_cloudi_service_queue_fetch = git
702 pkg_cloudi_service_queue_repo = https://github.com/CloudI/cloudi_service_queue
703 pkg_cloudi_service_queue_commit = master
704
705 PACKAGES += cloudi_service_quorum
706 pkg_cloudi_service_quorum_name = cloudi_service_quorum
707 pkg_cloudi_service_quorum_description = CloudI Quorum Service
708 pkg_cloudi_service_quorum_homepage = http://cloudi.org/
709 pkg_cloudi_service_quorum_fetch = git
710 pkg_cloudi_service_quorum_repo = https://github.com/CloudI/cloudi_service_quorum
711 pkg_cloudi_service_quorum_commit = master
712
713 PACKAGES += cloudi_service_router
714 pkg_cloudi_service_router_name = cloudi_service_router
715 pkg_cloudi_service_router_description = CloudI Router Service
716 pkg_cloudi_service_router_homepage = http://cloudi.org/
717 pkg_cloudi_service_router_fetch = git
718 pkg_cloudi_service_router_repo = https://github.com/CloudI/cloudi_service_router
719 pkg_cloudi_service_router_commit = master
720
721 PACKAGES += cloudi_service_tcp
722 pkg_cloudi_service_tcp_name = cloudi_service_tcp
723 pkg_cloudi_service_tcp_description = TCP CloudI Service
724 pkg_cloudi_service_tcp_homepage = http://cloudi.org/
725 pkg_cloudi_service_tcp_fetch = git
726 pkg_cloudi_service_tcp_repo = https://github.com/CloudI/cloudi_service_tcp
727 pkg_cloudi_service_tcp_commit = master
728
729 PACKAGES += cloudi_service_timers
730 pkg_cloudi_service_timers_name = cloudi_service_timers
731 pkg_cloudi_service_timers_description = Timers CloudI Service
732 pkg_cloudi_service_timers_homepage = http://cloudi.org/
733 pkg_cloudi_service_timers_fetch = git
734 pkg_cloudi_service_timers_repo = https://github.com/CloudI/cloudi_service_timers
735 pkg_cloudi_service_timers_commit = master
736
737 PACKAGES += cloudi_service_udp
738 pkg_cloudi_service_udp_name = cloudi_service_udp
739 pkg_cloudi_service_udp_description = UDP CloudI Service
740 pkg_cloudi_service_udp_homepage = http://cloudi.org/
741 pkg_cloudi_service_udp_fetch = git
742 pkg_cloudi_service_udp_repo = https://github.com/CloudI/cloudi_service_udp
743 pkg_cloudi_service_udp_commit = master
744
745 PACKAGES += cloudi_service_validate
746 pkg_cloudi_service_validate_name = cloudi_service_validate
747 pkg_cloudi_service_validate_description = CloudI Validate Service
748 pkg_cloudi_service_validate_homepage = http://cloudi.org/
749 pkg_cloudi_service_validate_fetch = git
750 pkg_cloudi_service_validate_repo = https://github.com/CloudI/cloudi_service_validate
751 pkg_cloudi_service_validate_commit = master
752
753 PACKAGES += cloudi_service_zeromq
754 pkg_cloudi_service_zeromq_name = cloudi_service_zeromq
755 pkg_cloudi_service_zeromq_description = ZeroMQ CloudI Service
756 pkg_cloudi_service_zeromq_homepage = http://cloudi.org/
757 pkg_cloudi_service_zeromq_fetch = git
758 pkg_cloudi_service_zeromq_repo = https://github.com/CloudI/cloudi_service_zeromq
759 pkg_cloudi_service_zeromq_commit = master
760
761 PACKAGES += cluster_info
762 pkg_cluster_info_name = cluster_info
763 pkg_cluster_info_description = Fork of Hibari's nifty cluster_info OTP app
764 pkg_cluster_info_homepage = https://github.com/basho/cluster_info
765 pkg_cluster_info_fetch = git
766 pkg_cluster_info_repo = https://github.com/basho/cluster_info
767 pkg_cluster_info_commit = master
768
769 PACKAGES += color
770 pkg_color_name = color
771 pkg_color_description = ANSI colors for your Erlang
772 pkg_color_homepage = https://github.com/julianduque/erlang-color
773 pkg_color_fetch = git
774 pkg_color_repo = https://github.com/julianduque/erlang-color
775 pkg_color_commit = master
776
777 PACKAGES += confetti
778 pkg_confetti_name = confetti
779 pkg_confetti_description = Erlang configuration provider / application:get_env/2 on steroids
780 pkg_confetti_homepage = https://github.com/jtendo/confetti
781 pkg_confetti_fetch = git
782 pkg_confetti_repo = https://github.com/jtendo/confetti
783 pkg_confetti_commit = master
784
785 PACKAGES += couchbeam
786 pkg_couchbeam_name = couchbeam
787 pkg_couchbeam_description = Apache CouchDB client in Erlang
788 pkg_couchbeam_homepage = https://github.com/benoitc/couchbeam
789 pkg_couchbeam_fetch = git
790 pkg_couchbeam_repo = https://github.com/benoitc/couchbeam
791 pkg_couchbeam_commit = master
792
793 PACKAGES += covertool
794 pkg_covertool_name = covertool
795 pkg_covertool_description = Tool to convert Erlang cover data files into Cobertura XML reports
796 pkg_covertool_homepage = https://github.com/idubrov/covertool
797 pkg_covertool_fetch = git
798 pkg_covertool_repo = https://github.com/idubrov/covertool
799 pkg_covertool_commit = master
800
801 PACKAGES += cowboy
802 pkg_cowboy_name = cowboy
803 pkg_cowboy_description = Small, fast and modular HTTP server.
804 pkg_cowboy_homepage = http://ninenines.eu
805 pkg_cowboy_fetch = git
806 pkg_cowboy_repo = https://github.com/ninenines/cowboy
807 pkg_cowboy_commit = 1.0.4
808
809 PACKAGES += cowdb
810 pkg_cowdb_name = cowdb
811 pkg_cowdb_description = Pure Key/Value database library for Erlang Applications
812 pkg_cowdb_homepage = https://github.com/refuge/cowdb
813 pkg_cowdb_fetch = git
814 pkg_cowdb_repo = https://github.com/refuge/cowdb
815 pkg_cowdb_commit = master
816
817 PACKAGES += cowlib
818 pkg_cowlib_name = cowlib
819 pkg_cowlib_description = Support library for manipulating Web protocols.
820 pkg_cowlib_homepage = http://ninenines.eu
821 pkg_cowlib_fetch = git
822 pkg_cowlib_repo = https://github.com/ninenines/cowlib
823 pkg_cowlib_commit = 1.0.2
824
825 PACKAGES += cpg
826 pkg_cpg_name = cpg
827 pkg_cpg_description = CloudI Process Groups
828 pkg_cpg_homepage = https://github.com/okeuday/cpg
829 pkg_cpg_fetch = git
830 pkg_cpg_repo = https://github.com/okeuday/cpg
831 pkg_cpg_commit = master
832
833 PACKAGES += cqerl
834 pkg_cqerl_name = cqerl
835 pkg_cqerl_description = Native Erlang CQL client for Cassandra
836 pkg_cqerl_homepage = https://matehat.github.io/cqerl/
837 pkg_cqerl_fetch = git
838 pkg_cqerl_repo = https://github.com/matehat/cqerl
839 pkg_cqerl_commit = master
840
841 PACKAGES += cr
842 pkg_cr_name = cr
843 pkg_cr_description = Chain Replication
844 pkg_cr_homepage = https://synrc.com/apps/cr/doc/cr.htm
845 pkg_cr_fetch = git
846 pkg_cr_repo = https://github.com/spawnproc/cr
847 pkg_cr_commit = master
848
849 PACKAGES += cuttlefish
850 pkg_cuttlefish_name = cuttlefish
851 pkg_cuttlefish_description = never lose your childlike sense of wonder baby cuttlefish, promise me?
852 pkg_cuttlefish_homepage = https://github.com/basho/cuttlefish
853 pkg_cuttlefish_fetch = git
854 pkg_cuttlefish_repo = https://github.com/basho/cuttlefish
855 pkg_cuttlefish_commit = master
856
857 PACKAGES += damocles
858 pkg_damocles_name = damocles
859 pkg_damocles_description = Erlang library for generating adversarial network conditions for QAing distributed applications/systems on a single Linux box.
860 pkg_damocles_homepage = https://github.com/lostcolony/damocles
861 pkg_damocles_fetch = git
862 pkg_damocles_repo = https://github.com/lostcolony/damocles
863 pkg_damocles_commit = master
864
865 PACKAGES += debbie
866 pkg_debbie_name = debbie
867 pkg_debbie_description = .DEB Built In Erlang
868 pkg_debbie_homepage = https://github.com/crownedgrouse/debbie
869 pkg_debbie_fetch = git
870 pkg_debbie_repo = https://github.com/crownedgrouse/debbie
871 pkg_debbie_commit = master
872
873 PACKAGES += decimal
874 pkg_decimal_name = decimal
875 pkg_decimal_description = An Erlang decimal arithmetic library
876 pkg_decimal_homepage = https://github.com/tim/erlang-decimal
877 pkg_decimal_fetch = git
878 pkg_decimal_repo = https://github.com/tim/erlang-decimal
879 pkg_decimal_commit = master
880
881 PACKAGES += detergent
882 pkg_detergent_name = detergent
883 pkg_detergent_description = An emulsifying Erlang SOAP library
884 pkg_detergent_homepage = https://github.com/devinus/detergent
885 pkg_detergent_fetch = git
886 pkg_detergent_repo = https://github.com/devinus/detergent
887 pkg_detergent_commit = master
888
889 PACKAGES += detest
890 pkg_detest_name = detest
891 pkg_detest_description = Tool for running tests on a cluster of erlang nodes
892 pkg_detest_homepage = https://github.com/biokoda/detest
893 pkg_detest_fetch = git
894 pkg_detest_repo = https://github.com/biokoda/detest
895 pkg_detest_commit = master
896
897 PACKAGES += dh_date
898 pkg_dh_date_name = dh_date
899 pkg_dh_date_description = Date formatting / parsing library for erlang
900 pkg_dh_date_homepage = https://github.com/daleharvey/dh_date
901 pkg_dh_date_fetch = git
902 pkg_dh_date_repo = https://github.com/daleharvey/dh_date
903 pkg_dh_date_commit = master
904
905 PACKAGES += dirbusterl
906 pkg_dirbusterl_name = dirbusterl
907 pkg_dirbusterl_description = DirBuster successor in Erlang
908 pkg_dirbusterl_homepage = https://github.com/silentsignal/DirBustErl
909 pkg_dirbusterl_fetch = git
910 pkg_dirbusterl_repo = https://github.com/silentsignal/DirBustErl
911 pkg_dirbusterl_commit = master
912
913 PACKAGES += dispcount
914 pkg_dispcount_name = dispcount
915 pkg_dispcount_description = Erlang task dispatcher based on ETS counters.
916 pkg_dispcount_homepage = https://github.com/ferd/dispcount
917 pkg_dispcount_fetch = git
918 pkg_dispcount_repo = https://github.com/ferd/dispcount
919 pkg_dispcount_commit = master
920
921 PACKAGES += dlhttpc
922 pkg_dlhttpc_name = dlhttpc
923 pkg_dlhttpc_description = dispcount-based lhttpc fork for massive amounts of requests to limited endpoints
924 pkg_dlhttpc_homepage = https://github.com/ferd/dlhttpc
925 pkg_dlhttpc_fetch = git
926 pkg_dlhttpc_repo = https://github.com/ferd/dlhttpc
927 pkg_dlhttpc_commit = master
928
929 PACKAGES += dns
930 pkg_dns_name = dns
931 pkg_dns_description = Erlang DNS library
932 pkg_dns_homepage = https://github.com/aetrion/dns_erlang
933 pkg_dns_fetch = git
934 pkg_dns_repo = https://github.com/aetrion/dns_erlang
935 pkg_dns_commit = master
936
937 PACKAGES += dnssd
938 pkg_dnssd_name = dnssd
939 pkg_dnssd_description = Erlang interface to Apple's Bonjour D NS Service Discovery implementation
940 pkg_dnssd_homepage = https://github.com/benoitc/dnssd_erlang
941 pkg_dnssd_fetch = git
942 pkg_dnssd_repo = https://github.com/benoitc/dnssd_erlang
943 pkg_dnssd_commit = master
944
945 PACKAGES += dtl
946 pkg_dtl_name = dtl
947 pkg_dtl_description = Django Template Language: A full-featured port of the Django template engine to Erlang.
948 pkg_dtl_homepage = https://github.com/oinksoft/dtl
949 pkg_dtl_fetch = git
950 pkg_dtl_repo = https://github.com/oinksoft/dtl
951 pkg_dtl_commit = master
952
953 PACKAGES += dynamic_compile
954 pkg_dynamic_compile_name = dynamic_compile
955 pkg_dynamic_compile_description = compile and load erlang modules from string input
956 pkg_dynamic_compile_homepage = https://github.com/jkvor/dynamic_compile
957 pkg_dynamic_compile_fetch = git
958 pkg_dynamic_compile_repo = https://github.com/jkvor/dynamic_compile
959 pkg_dynamic_compile_commit = master
960
961 PACKAGES += e2
962 pkg_e2_name = e2
963 pkg_e2_description = Library to simply writing correct OTP applications.
964 pkg_e2_homepage = http://e2project.org
965 pkg_e2_fetch = git
966 pkg_e2_repo = https://github.com/gar1t/e2
967 pkg_e2_commit = master
968
969 PACKAGES += eamf
970 pkg_eamf_name = eamf
971 pkg_eamf_description = eAMF provides Action Message Format (AMF) support for Erlang
972 pkg_eamf_homepage = https://github.com/mrinalwadhwa/eamf
973 pkg_eamf_fetch = git
974 pkg_eamf_repo = https://github.com/mrinalwadhwa/eamf
975 pkg_eamf_commit = master
976
977 PACKAGES += eavro
978 pkg_eavro_name = eavro
979 pkg_eavro_description = Apache Avro encoder/decoder
980 pkg_eavro_homepage = https://github.com/SIfoxDevTeam/eavro
981 pkg_eavro_fetch = git
982 pkg_eavro_repo = https://github.com/SIfoxDevTeam/eavro
983 pkg_eavro_commit = master
984
985 PACKAGES += ecapnp
986 pkg_ecapnp_name = ecapnp
987 pkg_ecapnp_description = Cap'n Proto library for Erlang
988 pkg_ecapnp_homepage = https://github.com/kaos/ecapnp
989 pkg_ecapnp_fetch = git
990 pkg_ecapnp_repo = https://github.com/kaos/ecapnp
991 pkg_ecapnp_commit = master
992
993 PACKAGES += econfig
994 pkg_econfig_name = econfig
995 pkg_econfig_description = simple Erlang config handler using INI files
996 pkg_econfig_homepage = https://github.com/benoitc/econfig
997 pkg_econfig_fetch = git
998 pkg_econfig_repo = https://github.com/benoitc/econfig
999 pkg_econfig_commit = master
1000
1001 PACKAGES += edate
1002 pkg_edate_name = edate
1003 pkg_edate_description = date manipulation library for erlang
1004 pkg_edate_homepage = https://github.com/dweldon/edate
1005 pkg_edate_fetch = git
1006 pkg_edate_repo = https://github.com/dweldon/edate
1007 pkg_edate_commit = master
1008
1009 PACKAGES += edgar
1010 pkg_edgar_name = edgar
1011 pkg_edgar_description = Erlang Does GNU AR
1012 pkg_edgar_homepage = https://github.com/crownedgrouse/edgar
1013 pkg_edgar_fetch = git
1014 pkg_edgar_repo = https://github.com/crownedgrouse/edgar
1015 pkg_edgar_commit = master
1016
1017 PACKAGES += edis
1018 pkg_edis_name = edis
1019 pkg_edis_description = An Erlang implementation of Redis KV Store
1020 pkg_edis_homepage = http://inaka.github.com/edis/
1021 pkg_edis_fetch = git
1022 pkg_edis_repo = https://github.com/inaka/edis
1023 pkg_edis_commit = master
1024
1025 PACKAGES += edns
1026 pkg_edns_name = edns
1027 pkg_edns_description = Erlang/OTP DNS server
1028 pkg_edns_homepage = https://github.com/hcvst/erlang-dns
1029 pkg_edns_fetch = git
1030 pkg_edns_repo = https://github.com/hcvst/erlang-dns
1031 pkg_edns_commit = master
1032
1033 PACKAGES += edown
1034 pkg_edown_name = edown
1035 pkg_edown_description = EDoc extension for generating Github-flavored Markdown
1036 pkg_edown_homepage = https://github.com/uwiger/edown
1037 pkg_edown_fetch = git
1038 pkg_edown_repo = https://github.com/uwiger/edown
1039 pkg_edown_commit = master
1040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
1049 PACKAGES += eep_app
1050 pkg_eep_app_name = eep_app
1051 pkg_eep_app_description = Embedded Event Processing
1052 pkg_eep_app_homepage = https://github.com/darach/eep-erl
1053 pkg_eep_app_fetch = git
1054 pkg_eep_app_repo = https://github.com/darach/eep-erl
1055 pkg_eep_app_commit = master
1056
1057 PACKAGES += efene
1058 pkg_efene_name = efene
1059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
1060 pkg_efene_homepage = https://github.com/efene/efene
1061 pkg_efene_fetch = git
1062 pkg_efene_repo = https://github.com/efene/efene
1063 pkg_efene_commit = master
1064
1065 PACKAGES += egeoip
1066 pkg_egeoip_name = egeoip
1067 pkg_egeoip_description = Erlang IP Geolocation module, currently supporting the MaxMind GeoLite City Database.
1068 pkg_egeoip_homepage = https://github.com/mochi/egeoip
1069 pkg_egeoip_fetch = git
1070 pkg_egeoip_repo = https://github.com/mochi/egeoip
1071 pkg_egeoip_commit = master
1072
1073 PACKAGES += ehsa
1074 pkg_ehsa_name = ehsa
1075 pkg_ehsa_description = Erlang HTTP server basic and digest authentication modules
1076 pkg_ehsa_homepage = https://bitbucket.org/a12n/ehsa
1077 pkg_ehsa_fetch = hg
1078 pkg_ehsa_repo = https://bitbucket.org/a12n/ehsa
1079 pkg_ehsa_commit = default
1080
1081 PACKAGES += ej
1082 pkg_ej_name = ej
1083 pkg_ej_description = Helper module for working with Erlang terms representing JSON
1084 pkg_ej_homepage = https://github.com/seth/ej
1085 pkg_ej_fetch = git
1086 pkg_ej_repo = https://github.com/seth/ej
1087 pkg_ej_commit = master
1088
1089 PACKAGES += ejabberd
1090 pkg_ejabberd_name = ejabberd
1091 pkg_ejabberd_description = Robust, ubiquitous and massively scalable Jabber / XMPP Instant Messaging platform
1092 pkg_ejabberd_homepage = https://github.com/processone/ejabberd
1093 pkg_ejabberd_fetch = git
1094 pkg_ejabberd_repo = https://github.com/processone/ejabberd
1095 pkg_ejabberd_commit = master
1096
1097 PACKAGES += ejwt
1098 pkg_ejwt_name = ejwt
1099 pkg_ejwt_description = erlang library for JSON Web Token
1100 pkg_ejwt_homepage = https://github.com/artefactop/ejwt
1101 pkg_ejwt_fetch = git
1102 pkg_ejwt_repo = https://github.com/artefactop/ejwt
1103 pkg_ejwt_commit = master
1104
1105 PACKAGES += ekaf
1106 pkg_ekaf_name = ekaf
1107 pkg_ekaf_description = A minimal, high-performance Kafka client in Erlang.
1108 pkg_ekaf_homepage = https://github.com/helpshift/ekaf
1109 pkg_ekaf_fetch = git
1110 pkg_ekaf_repo = https://github.com/helpshift/ekaf
1111 pkg_ekaf_commit = master
1112
1113 PACKAGES += elarm
1114 pkg_elarm_name = elarm
1115 pkg_elarm_description = Alarm Manager for Erlang.
1116 pkg_elarm_homepage = https://github.com/esl/elarm
1117 pkg_elarm_fetch = git
1118 pkg_elarm_repo = https://github.com/esl/elarm
1119 pkg_elarm_commit = master
1120
1121 PACKAGES += eleveldb
1122 pkg_eleveldb_name = eleveldb
1123 pkg_eleveldb_description = Erlang LevelDB API
1124 pkg_eleveldb_homepage = https://github.com/basho/eleveldb
1125 pkg_eleveldb_fetch = git
1126 pkg_eleveldb_repo = https://github.com/basho/eleveldb
1127 pkg_eleveldb_commit = master
1128
1129 PACKAGES += elli
1130 pkg_elli_name = elli
1131 pkg_elli_description = Simple, robust and performant Erlang web server
1132 pkg_elli_homepage = https://github.com/knutin/elli
1133 pkg_elli_fetch = git
1134 pkg_elli_repo = https://github.com/knutin/elli
1135 pkg_elli_commit = master
1136
1137 PACKAGES += elvis
1138 pkg_elvis_name = elvis
1139 pkg_elvis_description = Erlang Style Reviewer
1140 pkg_elvis_homepage = https://github.com/inaka/elvis
1141 pkg_elvis_fetch = git
1142 pkg_elvis_repo = https://github.com/inaka/elvis
1143 pkg_elvis_commit = master
1144
1145 PACKAGES += emagick
1146 pkg_emagick_name = emagick
1147 pkg_emagick_description = Wrapper for Graphics/ImageMagick command line tool.
1148 pkg_emagick_homepage = https://github.com/kivra/emagick
1149 pkg_emagick_fetch = git
1150 pkg_emagick_repo = https://github.com/kivra/emagick
1151 pkg_emagick_commit = master
1152
1153 PACKAGES += emysql
1154 pkg_emysql_name = emysql
1155 pkg_emysql_description = Stable, pure Erlang MySQL driver.
1156 pkg_emysql_homepage = https://github.com/Eonblast/Emysql
1157 pkg_emysql_fetch = git
1158 pkg_emysql_repo = https://github.com/Eonblast/Emysql
1159 pkg_emysql_commit = master
1160
1161 PACKAGES += enm
1162 pkg_enm_name = enm
1163 pkg_enm_description = Erlang driver for nanomsg
1164 pkg_enm_homepage = https://github.com/basho/enm
1165 pkg_enm_fetch = git
1166 pkg_enm_repo = https://github.com/basho/enm
1167 pkg_enm_commit = master
1168
1169 PACKAGES += entop
1170 pkg_entop_name = entop
1171 pkg_entop_description = A top-like tool for monitoring an Erlang node
1172 pkg_entop_homepage = https://github.com/mazenharake/entop
1173 pkg_entop_fetch = git
1174 pkg_entop_repo = https://github.com/mazenharake/entop
1175 pkg_entop_commit = master
1176
1177 PACKAGES += epcap
1178 pkg_epcap_name = epcap
1179 pkg_epcap_description = Erlang packet capture interface using pcap
1180 pkg_epcap_homepage = https://github.com/msantos/epcap
1181 pkg_epcap_fetch = git
1182 pkg_epcap_repo = https://github.com/msantos/epcap
1183 pkg_epcap_commit = master
1184
1185 PACKAGES += eper
1186 pkg_eper_name = eper
1187 pkg_eper_description = Erlang performance and debugging tools.
1188 pkg_eper_homepage = https://github.com/massemanet/eper
1189 pkg_eper_fetch = git
1190 pkg_eper_repo = https://github.com/massemanet/eper
1191 pkg_eper_commit = master
1192
1193 PACKAGES += epgsql
1194 pkg_epgsql_name = epgsql
1195 pkg_epgsql_description = Erlang PostgreSQL client library.
1196 pkg_epgsql_homepage = https://github.com/epgsql/epgsql
1197 pkg_epgsql_fetch = git
1198 pkg_epgsql_repo = https://github.com/epgsql/epgsql
1199 pkg_epgsql_commit = master
1200
1201 PACKAGES += episcina
1202 pkg_episcina_name = episcina
1203 pkg_episcina_description = A simple non intrusive resource pool for connections
1204 pkg_episcina_homepage = https://github.com/erlware/episcina
1205 pkg_episcina_fetch = git
1206 pkg_episcina_repo = https://github.com/erlware/episcina
1207 pkg_episcina_commit = master
1208
1209 PACKAGES += eplot
1210 pkg_eplot_name = eplot
1211 pkg_eplot_description = A plot engine written in erlang.
1212 pkg_eplot_homepage = https://github.com/psyeugenic/eplot
1213 pkg_eplot_fetch = git
1214 pkg_eplot_repo = https://github.com/psyeugenic/eplot
1215 pkg_eplot_commit = master
1216
1217 PACKAGES += epocxy
1218 pkg_epocxy_name = epocxy
1219 pkg_epocxy_description = Erlang Patterns of Concurrency
1220 pkg_epocxy_homepage = https://github.com/duomark/epocxy
1221 pkg_epocxy_fetch = git
1222 pkg_epocxy_repo = https://github.com/duomark/epocxy
1223 pkg_epocxy_commit = master
1224
1225 PACKAGES += epubnub
1226 pkg_epubnub_name = epubnub
1227 pkg_epubnub_description = Erlang PubNub API
1228 pkg_epubnub_homepage = https://github.com/tsloughter/epubnub
1229 pkg_epubnub_fetch = git
1230 pkg_epubnub_repo = https://github.com/tsloughter/epubnub
1231 pkg_epubnub_commit = master
1232
1233 PACKAGES += eqm
1234 pkg_eqm_name = eqm
1235 pkg_eqm_description = Erlang pub sub with supply-demand channels
1236 pkg_eqm_homepage = https://github.com/loucash/eqm
1237 pkg_eqm_fetch = git
1238 pkg_eqm_repo = https://github.com/loucash/eqm
1239 pkg_eqm_commit = master
1240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
1249 PACKAGES += eredis_pool
1250 pkg_eredis_pool_name = eredis_pool
1251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
1252 pkg_eredis_pool_homepage = https://github.com/hiroeorz/eredis_pool
1253 pkg_eredis_pool_fetch = git
1254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
1255 pkg_eredis_pool_commit = master
1256
1257 PACKAGES += erl_streams
1258 pkg_erl_streams_name = erl_streams
1259 pkg_erl_streams_description = Streams in Erlang
1260 pkg_erl_streams_homepage = https://github.com/epappas/erl_streams
1261 pkg_erl_streams_fetch = git
1262 pkg_erl_streams_repo = https://github.com/epappas/erl_streams
1263 pkg_erl_streams_commit = master
1264
1265 PACKAGES += erlang_cep
1266 pkg_erlang_cep_name = erlang_cep
1267 pkg_erlang_cep_description = A basic CEP package written in erlang
1268 pkg_erlang_cep_homepage = https://github.com/danmacklin/erlang_cep
1269 pkg_erlang_cep_fetch = git
1270 pkg_erlang_cep_repo = https://github.com/danmacklin/erlang_cep
1271 pkg_erlang_cep_commit = master
1272
1273 PACKAGES += erlang_js
1274 pkg_erlang_js_name = erlang_js
1275 pkg_erlang_js_description = A linked-in driver for Erlang to Mozilla's Spidermonkey Javascript runtime.
1276 pkg_erlang_js_homepage = https://github.com/basho/erlang_js
1277 pkg_erlang_js_fetch = git
1278 pkg_erlang_js_repo = https://github.com/basho/erlang_js
1279 pkg_erlang_js_commit = master
1280
1281 PACKAGES += erlang_localtime
1282 pkg_erlang_localtime_name = erlang_localtime
1283 pkg_erlang_localtime_description = Erlang library for conversion from one local time to another
1284 pkg_erlang_localtime_homepage = https://github.com/dmitryme/erlang_localtime
1285 pkg_erlang_localtime_fetch = git
1286 pkg_erlang_localtime_repo = https://github.com/dmitryme/erlang_localtime
1287 pkg_erlang_localtime_commit = master
1288
1289 PACKAGES += erlang_smtp
1290 pkg_erlang_smtp_name = erlang_smtp
1291 pkg_erlang_smtp_description = Erlang SMTP and POP3 server code.
1292 pkg_erlang_smtp_homepage = https://github.com/tonyg/erlang-smtp
1293 pkg_erlang_smtp_fetch = git
1294 pkg_erlang_smtp_repo = https://github.com/tonyg/erlang-smtp
1295 pkg_erlang_smtp_commit = master
1296
1297 PACKAGES += erlang_term
1298 pkg_erlang_term_name = erlang_term
1299 pkg_erlang_term_description = Erlang Term Info
1300 pkg_erlang_term_homepage = https://github.com/okeuday/erlang_term
1301 pkg_erlang_term_fetch = git
1302 pkg_erlang_term_repo = https://github.com/okeuday/erlang_term
1303 pkg_erlang_term_commit = master
1304
1305 PACKAGES += erlastic_search
1306 pkg_erlastic_search_name = erlastic_search
1307 pkg_erlastic_search_description = An Erlang app for communicating with Elastic Search's rest interface.
1308 pkg_erlastic_search_homepage = https://github.com/tsloughter/erlastic_search
1309 pkg_erlastic_search_fetch = git
1310 pkg_erlastic_search_repo = https://github.com/tsloughter/erlastic_search
1311 pkg_erlastic_search_commit = master
1312
1313 PACKAGES += erlasticsearch
1314 pkg_erlasticsearch_name = erlasticsearch
1315 pkg_erlasticsearch_description = Erlang thrift interface to elastic_search
1316 pkg_erlasticsearch_homepage = https://github.com/dieswaytoofast/erlasticsearch
1317 pkg_erlasticsearch_fetch = git
1318 pkg_erlasticsearch_repo = https://github.com/dieswaytoofast/erlasticsearch
1319 pkg_erlasticsearch_commit = master
1320
1321 PACKAGES += erlbrake
1322 pkg_erlbrake_name = erlbrake
1323 pkg_erlbrake_description = Erlang Airbrake notification client
1324 pkg_erlbrake_homepage = https://github.com/kenpratt/erlbrake
1325 pkg_erlbrake_fetch = git
1326 pkg_erlbrake_repo = https://github.com/kenpratt/erlbrake
1327 pkg_erlbrake_commit = master
1328
1329 PACKAGES += erlcloud
1330 pkg_erlcloud_name = erlcloud
1331 pkg_erlcloud_description = Cloud Computing library for erlang (Amazon EC2, S3, SQS, SimpleDB, Mechanical Turk, ELB)
1332 pkg_erlcloud_homepage = https://github.com/gleber/erlcloud
1333 pkg_erlcloud_fetch = git
1334 pkg_erlcloud_repo = https://github.com/gleber/erlcloud
1335 pkg_erlcloud_commit = master
1336
1337 PACKAGES += erlcron
1338 pkg_erlcron_name = erlcron
1339 pkg_erlcron_description = Erlang cronish system
1340 pkg_erlcron_homepage = https://github.com/erlware/erlcron
1341 pkg_erlcron_fetch = git
1342 pkg_erlcron_repo = https://github.com/erlware/erlcron
1343 pkg_erlcron_commit = master
1344
1345 PACKAGES += erldb
1346 pkg_erldb_name = erldb
1347 pkg_erldb_description = ORM (Object-relational mapping) application implemented in Erlang
1348 pkg_erldb_homepage = http://erldb.org
1349 pkg_erldb_fetch = git
1350 pkg_erldb_repo = https://github.com/erldb/erldb
1351 pkg_erldb_commit = master
1352
1353 PACKAGES += erldis
1354 pkg_erldis_name = erldis
1355 pkg_erldis_description = redis erlang client library
1356 pkg_erldis_homepage = https://github.com/cstar/erldis
1357 pkg_erldis_fetch = git
1358 pkg_erldis_repo = https://github.com/cstar/erldis
1359 pkg_erldis_commit = master
1360
1361 PACKAGES += erldns
1362 pkg_erldns_name = erldns
1363 pkg_erldns_description = DNS server, in erlang.
1364 pkg_erldns_homepage = https://github.com/aetrion/erl-dns
1365 pkg_erldns_fetch = git
1366 pkg_erldns_repo = https://github.com/aetrion/erl-dns
1367 pkg_erldns_commit = master
1368
1369 PACKAGES += erldocker
1370 pkg_erldocker_name = erldocker
1371 pkg_erldocker_description = Docker Remote API client for Erlang
1372 pkg_erldocker_homepage = https://github.com/proger/erldocker
1373 pkg_erldocker_fetch = git
1374 pkg_erldocker_repo = https://github.com/proger/erldocker
1375 pkg_erldocker_commit = master
1376
1377 PACKAGES += erlfsmon
1378 pkg_erlfsmon_name = erlfsmon
1379 pkg_erlfsmon_description = Erlang filesystem event watcher for Linux and OSX
1380 pkg_erlfsmon_homepage = https://github.com/proger/erlfsmon
1381 pkg_erlfsmon_fetch = git
1382 pkg_erlfsmon_repo = https://github.com/proger/erlfsmon
1383 pkg_erlfsmon_commit = master
1384
1385 PACKAGES += erlgit
1386 pkg_erlgit_name = erlgit
1387 pkg_erlgit_description = Erlang convenience wrapper around git executable
1388 pkg_erlgit_homepage = https://github.com/gleber/erlgit
1389 pkg_erlgit_fetch = git
1390 pkg_erlgit_repo = https://github.com/gleber/erlgit
1391 pkg_erlgit_commit = master
1392
1393 PACKAGES += erlguten
1394 pkg_erlguten_name = erlguten
1395 pkg_erlguten_description = ErlGuten is a system for high-quality typesetting, written purely in Erlang.
1396 pkg_erlguten_homepage = https://github.com/richcarl/erlguten
1397 pkg_erlguten_fetch = git
1398 pkg_erlguten_repo = https://github.com/richcarl/erlguten
1399 pkg_erlguten_commit = master
1400
1401 PACKAGES += erlmc
1402 pkg_erlmc_name = erlmc
1403 pkg_erlmc_description = Erlang memcached binary protocol client
1404 pkg_erlmc_homepage = https://github.com/jkvor/erlmc
1405 pkg_erlmc_fetch = git
1406 pkg_erlmc_repo = https://github.com/jkvor/erlmc
1407 pkg_erlmc_commit = master
1408
1409 PACKAGES += erlmongo
1410 pkg_erlmongo_name = erlmongo
1411 pkg_erlmongo_description = Record based Erlang driver for MongoDB with gridfs support
1412 pkg_erlmongo_homepage = https://github.com/SergejJurecko/erlmongo
1413 pkg_erlmongo_fetch = git
1414 pkg_erlmongo_repo = https://github.com/SergejJurecko/erlmongo
1415 pkg_erlmongo_commit = master
1416
1417 PACKAGES += erlog
1418 pkg_erlog_name = erlog
1419 pkg_erlog_description = Prolog interpreter in and for Erlang
1420 pkg_erlog_homepage = https://github.com/rvirding/erlog
1421 pkg_erlog_fetch = git
1422 pkg_erlog_repo = https://github.com/rvirding/erlog
1423 pkg_erlog_commit = master
1424
1425 PACKAGES += erlpass
1426 pkg_erlpass_name = erlpass
1427 pkg_erlpass_description = A library to handle password hashing and changing in a safe manner, independent from any kind of storage whatsoever.
1428 pkg_erlpass_homepage = https://github.com/ferd/erlpass
1429 pkg_erlpass_fetch = git
1430 pkg_erlpass_repo = https://github.com/ferd/erlpass
1431 pkg_erlpass_commit = master
1432
1433 PACKAGES += erlport
1434 pkg_erlport_name = erlport
1435 pkg_erlport_description = ErlPort - connect Erlang to other languages
1436 pkg_erlport_homepage = https://github.com/hdima/erlport
1437 pkg_erlport_fetch = git
1438 pkg_erlport_repo = https://github.com/hdima/erlport
1439 pkg_erlport_commit = master
1440
1441 PACKAGES += erlsh
1442 pkg_erlsh_name = erlsh
1443 pkg_erlsh_description = Erlang shell tools
1444 pkg_erlsh_homepage = https://github.com/proger/erlsh
1445 pkg_erlsh_fetch = git
1446 pkg_erlsh_repo = https://github.com/proger/erlsh
1447 pkg_erlsh_commit = master
1448
1449 PACKAGES += erlsha2
1450 pkg_erlsha2_name = erlsha2
1451 pkg_erlsha2_description = SHA-224, SHA-256, SHA-384, SHA-512 implemented in Erlang NIFs.
1452 pkg_erlsha2_homepage = https://github.com/vinoski/erlsha2
1453 pkg_erlsha2_fetch = git
1454 pkg_erlsha2_repo = https://github.com/vinoski/erlsha2
1455 pkg_erlsha2_commit = master
1456
1457 PACKAGES += erlsom
1458 pkg_erlsom_name = erlsom
1459 pkg_erlsom_description = XML parser for Erlang
1460 pkg_erlsom_homepage = https://github.com/willemdj/erlsom
1461 pkg_erlsom_fetch = git
1462 pkg_erlsom_repo = https://github.com/willemdj/erlsom
1463 pkg_erlsom_commit = master
1464
1465 PACKAGES += erlubi
1466 pkg_erlubi_name = erlubi
1467 pkg_erlubi_description = Ubigraph Erlang Client (and Process Visualizer)
1468 pkg_erlubi_homepage = https://github.com/krestenkrab/erlubi
1469 pkg_erlubi_fetch = git
1470 pkg_erlubi_repo = https://github.com/krestenkrab/erlubi
1471 pkg_erlubi_commit = master
1472
1473 PACKAGES += erlvolt
1474 pkg_erlvolt_name = erlvolt
1475 pkg_erlvolt_description = VoltDB Erlang Client Driver
1476 pkg_erlvolt_homepage = https://github.com/VoltDB/voltdb-client-erlang
1477 pkg_erlvolt_fetch = git
1478 pkg_erlvolt_repo = https://github.com/VoltDB/voltdb-client-erlang
1479 pkg_erlvolt_commit = master
1480
1481 PACKAGES += erlware_commons
1482 pkg_erlware_commons_name = erlware_commons
1483 pkg_erlware_commons_description = Erlware Commons is an Erlware project focused on all aspects of reusable Erlang components.
1484 pkg_erlware_commons_homepage = https://github.com/erlware/erlware_commons
1485 pkg_erlware_commons_fetch = git
1486 pkg_erlware_commons_repo = https://github.com/erlware/erlware_commons
1487 pkg_erlware_commons_commit = master
1488
1489 PACKAGES += erlydtl
1490 pkg_erlydtl_name = erlydtl
1491 pkg_erlydtl_description = Django Template Language for Erlang.
1492 pkg_erlydtl_homepage = https://github.com/erlydtl/erlydtl
1493 pkg_erlydtl_fetch = git
1494 pkg_erlydtl_repo = https://github.com/erlydtl/erlydtl
1495 pkg_erlydtl_commit = master
1496
1497 PACKAGES += errd
1498 pkg_errd_name = errd
1499 pkg_errd_description = Erlang RRDTool library
1500 pkg_errd_homepage = https://github.com/archaelus/errd
1501 pkg_errd_fetch = git
1502 pkg_errd_repo = https://github.com/archaelus/errd
1503 pkg_errd_commit = master
1504
1505 PACKAGES += erserve
1506 pkg_erserve_name = erserve
1507 pkg_erserve_description = Erlang/Rserve communication interface
1508 pkg_erserve_homepage = https://github.com/del/erserve
1509 pkg_erserve_fetch = git
1510 pkg_erserve_repo = https://github.com/del/erserve
1511 pkg_erserve_commit = master
1512
1513 PACKAGES += erwa
1514 pkg_erwa_name = erwa
1515 pkg_erwa_description = A WAMP router and client written in Erlang.
1516 pkg_erwa_homepage = https://github.com/bwegh/erwa
1517 pkg_erwa_fetch = git
1518 pkg_erwa_repo = https://github.com/bwegh/erwa
1519 pkg_erwa_commit = master
1520
1521 PACKAGES += espec
1522 pkg_espec_name = espec
1523 pkg_espec_description = ESpec: Behaviour driven development framework for Erlang
1524 pkg_espec_homepage = https://github.com/lucaspiller/espec
1525 pkg_espec_fetch = git
1526 pkg_espec_repo = https://github.com/lucaspiller/espec
1527 pkg_espec_commit = master
1528
1529 PACKAGES += estatsd
1530 pkg_estatsd_name = estatsd
1531 pkg_estatsd_description = Erlang stats aggregation app that periodically flushes data to graphite
1532 pkg_estatsd_homepage = https://github.com/RJ/estatsd
1533 pkg_estatsd_fetch = git
1534 pkg_estatsd_repo = https://github.com/RJ/estatsd
1535 pkg_estatsd_commit = master
1536
1537 PACKAGES += etap
1538 pkg_etap_name = etap
1539 pkg_etap_description = etap is a simple erlang testing library that provides TAP compliant output.
1540 pkg_etap_homepage = https://github.com/ngerakines/etap
1541 pkg_etap_fetch = git
1542 pkg_etap_repo = https://github.com/ngerakines/etap
1543 pkg_etap_commit = master
1544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
1553 PACKAGES += etest_http
1554 pkg_etest_http_name = etest_http
1555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
1556 pkg_etest_http_homepage = https://github.com/wooga/etest_http
1557 pkg_etest_http_fetch = git
1558 pkg_etest_http_repo = https://github.com/wooga/etest_http
1559 pkg_etest_http_commit = master
1560
1561 PACKAGES += etoml
1562 pkg_etoml_name = etoml
1563 pkg_etoml_description = TOML language erlang parser
1564 pkg_etoml_homepage = https://github.com/kalta/etoml
1565 pkg_etoml_fetch = git
1566 pkg_etoml_repo = https://github.com/kalta/etoml
1567 pkg_etoml_commit = master
1568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
1577 PACKAGES += eunit_formatters
1578 pkg_eunit_formatters_name = eunit_formatters
1579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
1580 pkg_eunit_formatters_homepage = https://github.com/seancribbs/eunit_formatters
1581 pkg_eunit_formatters_fetch = git
1582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
1583 pkg_eunit_formatters_commit = master
1584
1585 PACKAGES += euthanasia
1586 pkg_euthanasia_name = euthanasia
1587 pkg_euthanasia_description = Merciful killer for your Erlang processes
1588 pkg_euthanasia_homepage = https://github.com/doubleyou/euthanasia
1589 pkg_euthanasia_fetch = git
1590 pkg_euthanasia_repo = https://github.com/doubleyou/euthanasia
1591 pkg_euthanasia_commit = master
1592
1593 PACKAGES += evum
1594 pkg_evum_name = evum
1595 pkg_evum_description = Spawn Linux VMs as Erlang processes in the Erlang VM
1596 pkg_evum_homepage = https://github.com/msantos/evum
1597 pkg_evum_fetch = git
1598 pkg_evum_repo = https://github.com/msantos/evum
1599 pkg_evum_commit = master
1600
1601 PACKAGES += exec
1602 pkg_exec_name = erlexec
1603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
1604 pkg_exec_homepage = http://saleyn.github.com/erlexec
1605 pkg_exec_fetch = git
1606 pkg_exec_repo = https://github.com/saleyn/erlexec
1607 pkg_exec_commit = master
1608
1609 PACKAGES += exml
1610 pkg_exml_name = exml
1611 pkg_exml_description = XML parsing library in Erlang
1612 pkg_exml_homepage = https://github.com/paulgray/exml
1613 pkg_exml_fetch = git
1614 pkg_exml_repo = https://github.com/paulgray/exml
1615 pkg_exml_commit = master
1616
1617 PACKAGES += exometer
1618 pkg_exometer_name = exometer
1619 pkg_exometer_description = Basic measurement objects and probe behavior
1620 pkg_exometer_homepage = https://github.com/Feuerlabs/exometer
1621 pkg_exometer_fetch = git
1622 pkg_exometer_repo = https://github.com/Feuerlabs/exometer
1623 pkg_exometer_commit = master
1624
1625 PACKAGES += exs1024
1626 pkg_exs1024_name = exs1024
1627 pkg_exs1024_description = Xorshift1024star pseudo random number generator for Erlang.
1628 pkg_exs1024_homepage = https://github.com/jj1bdx/exs1024
1629 pkg_exs1024_fetch = git
1630 pkg_exs1024_repo = https://github.com/jj1bdx/exs1024
1631 pkg_exs1024_commit = master
1632
1633 PACKAGES += exs64
1634 pkg_exs64_name = exs64
1635 pkg_exs64_description = Xorshift64star pseudo random number generator for Erlang.
1636 pkg_exs64_homepage = https://github.com/jj1bdx/exs64
1637 pkg_exs64_fetch = git
1638 pkg_exs64_repo = https://github.com/jj1bdx/exs64
1639 pkg_exs64_commit = master
1640
1641 PACKAGES += exsplus116
1642 pkg_exsplus116_name = exsplus116
1643 pkg_exsplus116_description = Xorshift116plus for Erlang
1644 pkg_exsplus116_homepage = https://github.com/jj1bdx/exsplus116
1645 pkg_exsplus116_fetch = git
1646 pkg_exsplus116_repo = https://github.com/jj1bdx/exsplus116
1647 pkg_exsplus116_commit = master
1648
1649 PACKAGES += exsplus128
1650 pkg_exsplus128_name = exsplus128
1651 pkg_exsplus128_description = Xorshift128plus pseudo random number generator for Erlang.
1652 pkg_exsplus128_homepage = https://github.com/jj1bdx/exsplus128
1653 pkg_exsplus128_fetch = git
1654 pkg_exsplus128_repo = https://github.com/jj1bdx/exsplus128
1655 pkg_exsplus128_commit = master
1656
1657 PACKAGES += ezmq
1658 pkg_ezmq_name = ezmq
1659 pkg_ezmq_description = zMQ implemented in Erlang
1660 pkg_ezmq_homepage = https://github.com/RoadRunnr/ezmq
1661 pkg_ezmq_fetch = git
1662 pkg_ezmq_repo = https://github.com/RoadRunnr/ezmq
1663 pkg_ezmq_commit = master
1664
1665 PACKAGES += ezmtp
1666 pkg_ezmtp_name = ezmtp
1667 pkg_ezmtp_description = ZMTP protocol in pure Erlang.
1668 pkg_ezmtp_homepage = https://github.com/a13x/ezmtp
1669 pkg_ezmtp_fetch = git
1670 pkg_ezmtp_repo = https://github.com/a13x/ezmtp
1671 pkg_ezmtp_commit = master
1672
1673 PACKAGES += fast_disk_log
1674 pkg_fast_disk_log_name = fast_disk_log
1675 pkg_fast_disk_log_description = Pool-based asynchronous Erlang disk logger
1676 pkg_fast_disk_log_homepage = https://github.com/lpgauth/fast_disk_log
1677 pkg_fast_disk_log_fetch = git
1678 pkg_fast_disk_log_repo = https://github.com/lpgauth/fast_disk_log
1679 pkg_fast_disk_log_commit = master
1680
1681 PACKAGES += feeder
1682 pkg_feeder_name = feeder
1683 pkg_feeder_description = Stream parse RSS and Atom formatted XML feeds.
1684 pkg_feeder_homepage = https://github.com/michaelnisi/feeder
1685 pkg_feeder_fetch = git
1686 pkg_feeder_repo = https://github.com/michaelnisi/feeder
1687 pkg_feeder_commit = master
1688
1689 PACKAGES += find_crate
1690 pkg_find_crate_name = find_crate
1691 pkg_find_crate_description = Find Rust libs and exes in Erlang application priv directory
1692 pkg_find_crate_homepage = https://github.com/goertzenator/find_crate
1693 pkg_find_crate_fetch = git
1694 pkg_find_crate_repo = https://github.com/goertzenator/find_crate
1695 pkg_find_crate_commit = master
1696
1697 PACKAGES += fix
1698 pkg_fix_name = fix
1699 pkg_fix_description = http://fixprotocol.org/ implementation.
1700 pkg_fix_homepage = https://github.com/maxlapshin/fix
1701 pkg_fix_fetch = git
1702 pkg_fix_repo = https://github.com/maxlapshin/fix
1703 pkg_fix_commit = master
1704
1705 PACKAGES += flower
1706 pkg_flower_name = flower
1707 pkg_flower_description = FlowER - a Erlang OpenFlow development platform
1708 pkg_flower_homepage = https://github.com/travelping/flower
1709 pkg_flower_fetch = git
1710 pkg_flower_repo = https://github.com/travelping/flower
1711 pkg_flower_commit = master
1712
1713 PACKAGES += fn
1714 pkg_fn_name = fn
1715 pkg_fn_description = Function utilities for Erlang
1716 pkg_fn_homepage = https://github.com/reiddraper/fn
1717 pkg_fn_fetch = git
1718 pkg_fn_repo = https://github.com/reiddraper/fn
1719 pkg_fn_commit = master
1720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
1729 PACKAGES += folsom_cowboy
1730 pkg_folsom_cowboy_name = folsom_cowboy
1731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
1732 pkg_folsom_cowboy_homepage = https://github.com/boundary/folsom_cowboy
1733 pkg_folsom_cowboy_fetch = git
1734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
1735 pkg_folsom_cowboy_commit = master
1736
1737 PACKAGES += folsomite
1738 pkg_folsomite_name = folsomite
1739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
1740 pkg_folsomite_homepage = https://github.com/campanja/folsomite
1741 pkg_folsomite_fetch = git
1742 pkg_folsomite_repo = https://github.com/campanja/folsomite
1743 pkg_folsomite_commit = master
1744
1745 PACKAGES += fs
1746 pkg_fs_name = fs
1747 pkg_fs_description = Erlang FileSystem Listener
1748 pkg_fs_homepage = https://github.com/synrc/fs
1749 pkg_fs_fetch = git
1750 pkg_fs_repo = https://github.com/synrc/fs
1751 pkg_fs_commit = master
1752
1753 PACKAGES += fuse
1754 pkg_fuse_name = fuse
1755 pkg_fuse_description = A Circuit Breaker for Erlang
1756 pkg_fuse_homepage = https://github.com/jlouis/fuse
1757 pkg_fuse_fetch = git
1758 pkg_fuse_repo = https://github.com/jlouis/fuse
1759 pkg_fuse_commit = master
1760
1761 PACKAGES += gcm
1762 pkg_gcm_name = gcm
1763 pkg_gcm_description = An Erlang application for Google Cloud Messaging
1764 pkg_gcm_homepage = https://github.com/pdincau/gcm-erlang
1765 pkg_gcm_fetch = git
1766 pkg_gcm_repo = https://github.com/pdincau/gcm-erlang
1767 pkg_gcm_commit = master
1768
1769 PACKAGES += gcprof
1770 pkg_gcprof_name = gcprof
1771 pkg_gcprof_description = Garbage Collection profiler for Erlang
1772 pkg_gcprof_homepage = https://github.com/knutin/gcprof
1773 pkg_gcprof_fetch = git
1774 pkg_gcprof_repo = https://github.com/knutin/gcprof
1775 pkg_gcprof_commit = master
1776
1777 PACKAGES += geas
1778 pkg_geas_name = geas
1779 pkg_geas_description = Guess Erlang Application Scattering
1780 pkg_geas_homepage = https://github.com/crownedgrouse/geas
1781 pkg_geas_fetch = git
1782 pkg_geas_repo = https://github.com/crownedgrouse/geas
1783 pkg_geas_commit = master
1784
1785 PACKAGES += geef
1786 pkg_geef_name = geef
1787 pkg_geef_description = Git NEEEEF (Erlang NIF)
1788 pkg_geef_homepage = https://github.com/carlosmn/geef
1789 pkg_geef_fetch = git
1790 pkg_geef_repo = https://github.com/carlosmn/geef
1791 pkg_geef_commit = master
1792
1793 PACKAGES += gen_coap
1794 pkg_gen_coap_name = gen_coap
1795 pkg_gen_coap_description = Generic Erlang CoAP Client/Server
1796 pkg_gen_coap_homepage = https://github.com/gotthardp/gen_coap
1797 pkg_gen_coap_fetch = git
1798 pkg_gen_coap_repo = https://github.com/gotthardp/gen_coap
1799 pkg_gen_coap_commit = master
1800
1801 PACKAGES += gen_cycle
1802 pkg_gen_cycle_name = gen_cycle
1803 pkg_gen_cycle_description = Simple, generic OTP behaviour for recurring tasks
1804 pkg_gen_cycle_homepage = https://github.com/aerosol/gen_cycle
1805 pkg_gen_cycle_fetch = git
1806 pkg_gen_cycle_repo = https://github.com/aerosol/gen_cycle
1807 pkg_gen_cycle_commit = develop
1808
1809 PACKAGES += gen_icmp
1810 pkg_gen_icmp_name = gen_icmp
1811 pkg_gen_icmp_description = Erlang interface to ICMP sockets
1812 pkg_gen_icmp_homepage = https://github.com/msantos/gen_icmp
1813 pkg_gen_icmp_fetch = git
1814 pkg_gen_icmp_repo = https://github.com/msantos/gen_icmp
1815 pkg_gen_icmp_commit = master
1816
1817 PACKAGES += gen_nb_server
1818 pkg_gen_nb_server_name = gen_nb_server
1819 pkg_gen_nb_server_description = OTP behavior for writing non-blocking servers
1820 pkg_gen_nb_server_homepage = https://github.com/kevsmith/gen_nb_server
1821 pkg_gen_nb_server_fetch = git
1822 pkg_gen_nb_server_repo = https://github.com/kevsmith/gen_nb_server
1823 pkg_gen_nb_server_commit = master
1824
1825 PACKAGES += gen_paxos
1826 pkg_gen_paxos_name = gen_paxos
1827 pkg_gen_paxos_description = An Erlang/OTP-style implementation of the PAXOS distributed consensus protocol
1828 pkg_gen_paxos_homepage = https://github.com/gburd/gen_paxos
1829 pkg_gen_paxos_fetch = git
1830 pkg_gen_paxos_repo = https://github.com/gburd/gen_paxos
1831 pkg_gen_paxos_commit = master
1832
1833 PACKAGES += gen_smtp
1834 pkg_gen_smtp_name = gen_smtp
1835 pkg_gen_smtp_description = A generic Erlang SMTP server and client that can be extended via callback modules
1836 pkg_gen_smtp_homepage = https://github.com/Vagabond/gen_smtp
1837 pkg_gen_smtp_fetch = git
1838 pkg_gen_smtp_repo = https://github.com/Vagabond/gen_smtp
1839 pkg_gen_smtp_commit = master
1840
1841 PACKAGES += gen_tracker
1842 pkg_gen_tracker_name = gen_tracker
1843 pkg_gen_tracker_description = supervisor with ets handling of children and their metadata
1844 pkg_gen_tracker_homepage = https://github.com/erlyvideo/gen_tracker
1845 pkg_gen_tracker_fetch = git
1846 pkg_gen_tracker_repo = https://github.com/erlyvideo/gen_tracker
1847 pkg_gen_tracker_commit = master
1848
1849 PACKAGES += gen_unix
1850 pkg_gen_unix_name = gen_unix
1851 pkg_gen_unix_description = Erlang Unix socket interface
1852 pkg_gen_unix_homepage = https://github.com/msantos/gen_unix
1853 pkg_gen_unix_fetch = git
1854 pkg_gen_unix_repo = https://github.com/msantos/gen_unix
1855 pkg_gen_unix_commit = master
1856
1857 PACKAGES += geode
1858 pkg_geode_name = geode
1859 pkg_geode_description = geohash/proximity lookup in pure, uncut erlang.
1860 pkg_geode_homepage = https://github.com/bradfordw/geode
1861 pkg_geode_fetch = git
1862 pkg_geode_repo = https://github.com/bradfordw/geode
1863 pkg_geode_commit = master
1864
1865 PACKAGES += getopt
1866 pkg_getopt_name = getopt
1867 pkg_getopt_description = Module to parse command line arguments using the GNU getopt syntax
1868 pkg_getopt_homepage = https://github.com/jcomellas/getopt
1869 pkg_getopt_fetch = git
1870 pkg_getopt_repo = https://github.com/jcomellas/getopt
1871 pkg_getopt_commit = master
1872
1873 PACKAGES += gettext
1874 pkg_gettext_name = gettext
1875 pkg_gettext_description = Erlang internationalization library.
1876 pkg_gettext_homepage = https://github.com/etnt/gettext
1877 pkg_gettext_fetch = git
1878 pkg_gettext_repo = https://github.com/etnt/gettext
1879 pkg_gettext_commit = master
1880
1881 PACKAGES += giallo
1882 pkg_giallo_name = giallo
1883 pkg_giallo_description = Small and flexible web framework on top of Cowboy
1884 pkg_giallo_homepage = https://github.com/kivra/giallo
1885 pkg_giallo_fetch = git
1886 pkg_giallo_repo = https://github.com/kivra/giallo
1887 pkg_giallo_commit = master
1888
1889 PACKAGES += gin
1890 pkg_gin_name = gin
1891 pkg_gin_description = The guards and for Erlang parse_transform
1892 pkg_gin_homepage = https://github.com/mad-cocktail/gin
1893 pkg_gin_fetch = git
1894 pkg_gin_repo = https://github.com/mad-cocktail/gin
1895 pkg_gin_commit = master
1896
1897 PACKAGES += gitty
1898 pkg_gitty_name = gitty
1899 pkg_gitty_description = Git access in erlang
1900 pkg_gitty_homepage = https://github.com/maxlapshin/gitty
1901 pkg_gitty_fetch = git
1902 pkg_gitty_repo = https://github.com/maxlapshin/gitty
1903 pkg_gitty_commit = master
1904
1905 PACKAGES += gold_fever
1906 pkg_gold_fever_name = gold_fever
1907 pkg_gold_fever_description = A Treasure Hunt for Erlangers
1908 pkg_gold_fever_homepage = https://github.com/inaka/gold_fever
1909 pkg_gold_fever_fetch = git
1910 pkg_gold_fever_repo = https://github.com/inaka/gold_fever
1911 pkg_gold_fever_commit = master
1912
1913 PACKAGES += gossiperl
1914 pkg_gossiperl_name = gossiperl
1915 pkg_gossiperl_description = Gossip middleware in Erlang
1916 pkg_gossiperl_homepage = http://gossiperl.com/
1917 pkg_gossiperl_fetch = git
1918 pkg_gossiperl_repo = https://github.com/gossiperl/gossiperl
1919 pkg_gossiperl_commit = master
1920
1921 PACKAGES += gpb
1922 pkg_gpb_name = gpb
1923 pkg_gpb_description = A Google Protobuf implementation for Erlang
1924 pkg_gpb_homepage = https://github.com/tomas-abrahamsson/gpb
1925 pkg_gpb_fetch = git
1926 pkg_gpb_repo = https://github.com/tomas-abrahamsson/gpb
1927 pkg_gpb_commit = master
1928
1929 PACKAGES += gproc
1930 pkg_gproc_name = gproc
1931 pkg_gproc_description = Extended process registry for Erlang
1932 pkg_gproc_homepage = https://github.com/uwiger/gproc
1933 pkg_gproc_fetch = git
1934 pkg_gproc_repo = https://github.com/uwiger/gproc
1935 pkg_gproc_commit = master
1936
1937 PACKAGES += grapherl
1938 pkg_grapherl_name = grapherl
1939 pkg_grapherl_description = Create graphs of Erlang systems and programs
1940 pkg_grapherl_homepage = https://github.com/eproxus/grapherl
1941 pkg_grapherl_fetch = git
1942 pkg_grapherl_repo = https://github.com/eproxus/grapherl
1943 pkg_grapherl_commit = master
1944
1945 PACKAGES += gun
1946 pkg_gun_name = gun
1947 pkg_gun_description = Asynchronous SPDY, HTTP and Websocket client written in Erlang.
1948 pkg_gun_homepage = http//ninenines.eu
1949 pkg_gun_fetch = git
1950 pkg_gun_repo = https://github.com/ninenines/gun
1951 pkg_gun_commit = master
1952
1953 PACKAGES += gut
1954 pkg_gut_name = gut
1955 pkg_gut_description = gut is a template printing, aka scaffolding, tool for Erlang. Like rails generate or yeoman
1956 pkg_gut_homepage = https://github.com/unbalancedparentheses/gut
1957 pkg_gut_fetch = git
1958 pkg_gut_repo = https://github.com/unbalancedparentheses/gut
1959 pkg_gut_commit = master
1960
1961 PACKAGES += hackney
1962 pkg_hackney_name = hackney
1963 pkg_hackney_description = simple HTTP client in Erlang
1964 pkg_hackney_homepage = https://github.com/benoitc/hackney
1965 pkg_hackney_fetch = git
1966 pkg_hackney_repo = https://github.com/benoitc/hackney
1967 pkg_hackney_commit = master
1968
1969 PACKAGES += hamcrest
1970 pkg_hamcrest_name = hamcrest
1971 pkg_hamcrest_description = Erlang port of Hamcrest
1972 pkg_hamcrest_homepage = https://github.com/hyperthunk/hamcrest-erlang
1973 pkg_hamcrest_fetch = git
1974 pkg_hamcrest_repo = https://github.com/hyperthunk/hamcrest-erlang
1975 pkg_hamcrest_commit = master
1976
1977 PACKAGES += hanoidb
1978 pkg_hanoidb_name = hanoidb
1979 pkg_hanoidb_description = Erlang LSM BTree Storage
1980 pkg_hanoidb_homepage = https://github.com/krestenkrab/hanoidb
1981 pkg_hanoidb_fetch = git
1982 pkg_hanoidb_repo = https://github.com/krestenkrab/hanoidb
1983 pkg_hanoidb_commit = master
1984
1985 PACKAGES += hottub
1986 pkg_hottub_name = hottub
1987 pkg_hottub_description = Permanent Erlang Worker Pool
1988 pkg_hottub_homepage = https://github.com/bfrog/hottub
1989 pkg_hottub_fetch = git
1990 pkg_hottub_repo = https://github.com/bfrog/hottub
1991 pkg_hottub_commit = master
1992
1993 PACKAGES += hpack
1994 pkg_hpack_name = hpack
1995 pkg_hpack_description = HPACK Implementation for Erlang
1996 pkg_hpack_homepage = https://github.com/joedevivo/hpack
1997 pkg_hpack_fetch = git
1998 pkg_hpack_repo = https://github.com/joedevivo/hpack
1999 pkg_hpack_commit = master
2000
2001 PACKAGES += hyper
2002 pkg_hyper_name = hyper
2003 pkg_hyper_description = Erlang implementation of HyperLogLog
2004 pkg_hyper_homepage = https://github.com/GameAnalytics/hyper
2005 pkg_hyper_fetch = git
2006 pkg_hyper_repo = https://github.com/GameAnalytics/hyper
2007 pkg_hyper_commit = master
2008
2009 PACKAGES += i18n
2010 pkg_i18n_name = i18n
2011 pkg_i18n_description = International components for unicode from Erlang (unicode, date, string, number, format, locale, localization, transliteration, icu4e)
2012 pkg_i18n_homepage = https://github.com/erlang-unicode/i18n
2013 pkg_i18n_fetch = git
2014 pkg_i18n_repo = https://github.com/erlang-unicode/i18n
2015 pkg_i18n_commit = master
2016
2017 PACKAGES += ibrowse
2018 pkg_ibrowse_name = ibrowse
2019 pkg_ibrowse_description = Erlang HTTP client
2020 pkg_ibrowse_homepage = https://github.com/cmullaparthi/ibrowse
2021 pkg_ibrowse_fetch = git
2022 pkg_ibrowse_repo = https://github.com/cmullaparthi/ibrowse
2023 pkg_ibrowse_commit = master
2024
2025 PACKAGES += ierlang
2026 pkg_ierlang_name = ierlang
2027 pkg_ierlang_description = An Erlang language kernel for IPython.
2028 pkg_ierlang_homepage = https://github.com/robbielynch/ierlang
2029 pkg_ierlang_fetch = git
2030 pkg_ierlang_repo = https://github.com/robbielynch/ierlang
2031 pkg_ierlang_commit = master
2032
2033 PACKAGES += iota
2034 pkg_iota_name = iota
2035 pkg_iota_description = iota (Inter-dependency Objective Testing Apparatus) - a tool to enforce clean separation of responsibilities in Erlang code
2036 pkg_iota_homepage = https://github.com/jpgneves/iota
2037 pkg_iota_fetch = git
2038 pkg_iota_repo = https://github.com/jpgneves/iota
2039 pkg_iota_commit = master
2040
2041 PACKAGES += irc_lib
2042 pkg_irc_lib_name = irc_lib
2043 pkg_irc_lib_description = Erlang irc client library
2044 pkg_irc_lib_homepage = https://github.com/OtpChatBot/irc_lib
2045 pkg_irc_lib_fetch = git
2046 pkg_irc_lib_repo = https://github.com/OtpChatBot/irc_lib
2047 pkg_irc_lib_commit = master
2048
2049 PACKAGES += ircd
2050 pkg_ircd_name = ircd
2051 pkg_ircd_description = A pluggable IRC daemon application/library for Erlang.
2052 pkg_ircd_homepage = https://github.com/tonyg/erlang-ircd
2053 pkg_ircd_fetch = git
2054 pkg_ircd_repo = https://github.com/tonyg/erlang-ircd
2055 pkg_ircd_commit = master
2056
2057 PACKAGES += iris
2058 pkg_iris_name = iris
2059 pkg_iris_description = Iris Erlang binding
2060 pkg_iris_homepage = https://github.com/project-iris/iris-erl
2061 pkg_iris_fetch = git
2062 pkg_iris_repo = https://github.com/project-iris/iris-erl
2063 pkg_iris_commit = master
2064
2065 PACKAGES += iso8601
2066 pkg_iso8601_name = iso8601
2067 pkg_iso8601_description = Erlang ISO 8601 date formatter/parser
2068 pkg_iso8601_homepage = https://github.com/seansawyer/erlang_iso8601
2069 pkg_iso8601_fetch = git
2070 pkg_iso8601_repo = https://github.com/seansawyer/erlang_iso8601
2071 pkg_iso8601_commit = master
2072
2073 PACKAGES += jamdb_sybase
2074 pkg_jamdb_sybase_name = jamdb_sybase
2075 pkg_jamdb_sybase_description = Erlang driver for SAP Sybase ASE
2076 pkg_jamdb_sybase_homepage = https://github.com/erlangbureau/jamdb_sybase
2077 pkg_jamdb_sybase_fetch = git
2078 pkg_jamdb_sybase_repo = https://github.com/erlangbureau/jamdb_sybase
2079 pkg_jamdb_sybase_commit = master
2080
2081 PACKAGES += jerg
2082 pkg_jerg_name = jerg
2083 pkg_jerg_description = JSON Schema to Erlang Records Generator
2084 pkg_jerg_homepage = https://github.com/ddossot/jerg
2085 pkg_jerg_fetch = git
2086 pkg_jerg_repo = https://github.com/ddossot/jerg
2087 pkg_jerg_commit = master
2088
2089 PACKAGES += jesse
2090 pkg_jesse_name = jesse
2091 pkg_jesse_description = jesse (JSon Schema Erlang) is an implementation of a json schema validator for Erlang.
2092 pkg_jesse_homepage = https://github.com/for-GET/jesse
2093 pkg_jesse_fetch = git
2094 pkg_jesse_repo = https://github.com/for-GET/jesse
2095 pkg_jesse_commit = master
2096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
2105 PACKAGES += jiffy_v
2106 pkg_jiffy_v_name = jiffy_v
2107 pkg_jiffy_v_description = JSON validation utility
2108 pkg_jiffy_v_homepage = https://github.com/shizzard/jiffy-v
2109 pkg_jiffy_v_fetch = git
2110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
2111 pkg_jiffy_v_commit = master
2112
2113 PACKAGES += jobs
2114 pkg_jobs_name = jobs
2115 pkg_jobs_description = a Job scheduler for load regulation
2116 pkg_jobs_homepage = https://github.com/esl/jobs
2117 pkg_jobs_fetch = git
2118 pkg_jobs_repo = https://github.com/esl/jobs
2119 pkg_jobs_commit = master
2120
2121 PACKAGES += joxa
2122 pkg_joxa_name = joxa
2123 pkg_joxa_description = A Modern Lisp for the Erlang VM
2124 pkg_joxa_homepage = https://github.com/joxa/joxa
2125 pkg_joxa_fetch = git
2126 pkg_joxa_repo = https://github.com/joxa/joxa
2127 pkg_joxa_commit = master
2128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
2137 PACKAGES += json_rec
2138 pkg_json_rec_name = json_rec
2139 pkg_json_rec_description = JSON to erlang record
2140 pkg_json_rec_homepage = https://github.com/justinkirby/json_rec
2141 pkg_json_rec_fetch = git
2142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
2143 pkg_json_rec_commit = master
2144
2145 PACKAGES += jsone
2146 pkg_jsone_name = jsone
2147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
2148 pkg_jsone_homepage = https://github.com/sile/jsone.git
2149 pkg_jsone_fetch = git
2150 pkg_jsone_repo = https://github.com/sile/jsone.git
2151 pkg_jsone_commit = master
2152
2153 PACKAGES += jsonerl
2154 pkg_jsonerl_name = jsonerl
2155 pkg_jsonerl_description = yet another but slightly different erlang <-> json encoder/decoder
2156 pkg_jsonerl_homepage = https://github.com/lambder/jsonerl
2157 pkg_jsonerl_fetch = git
2158 pkg_jsonerl_repo = https://github.com/lambder/jsonerl
2159 pkg_jsonerl_commit = master
2160
2161 PACKAGES += jsonpath
2162 pkg_jsonpath_name = jsonpath
2163 pkg_jsonpath_description = Fast Erlang JSON data retrieval and updates via javascript-like notation
2164 pkg_jsonpath_homepage = https://github.com/GeneStevens/jsonpath
2165 pkg_jsonpath_fetch = git
2166 pkg_jsonpath_repo = https://github.com/GeneStevens/jsonpath
2167 pkg_jsonpath_commit = master
2168
2169 PACKAGES += jsonx
2170 pkg_jsonx_name = jsonx
2171 pkg_jsonx_description = JSONX is an Erlang library for efficient decode and encode JSON, written in C.
2172 pkg_jsonx_homepage = https://github.com/iskra/jsonx
2173 pkg_jsonx_fetch = git
2174 pkg_jsonx_repo = https://github.com/iskra/jsonx
2175 pkg_jsonx_commit = master
2176
2177 PACKAGES += jsx
2178 pkg_jsx_name = jsx
2179 pkg_jsx_description = An Erlang application for consuming, producing and manipulating JSON.
2180 pkg_jsx_homepage = https://github.com/talentdeficit/jsx
2181 pkg_jsx_fetch = git
2182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
2183 pkg_jsx_commit = master
2184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
2193 PACKAGES += kafka_protocol
2194 pkg_kafka_protocol_name = kafka_protocol
2195 pkg_kafka_protocol_description = Kafka protocol Erlang library
2196 pkg_kafka_protocol_homepage = https://github.com/klarna/kafka_protocol
2197 pkg_kafka_protocol_fetch = git
2198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
2199 pkg_kafka_protocol_commit = master
2200
2201 PACKAGES += kai
2202 pkg_kai_name = kai
2203 pkg_kai_description = DHT storage by Takeshi Inoue
2204 pkg_kai_homepage = https://github.com/synrc/kai
2205 pkg_kai_fetch = git
2206 pkg_kai_repo = https://github.com/synrc/kai
2207 pkg_kai_commit = master
2208
2209 PACKAGES += katja
2210 pkg_katja_name = katja
2211 pkg_katja_description = A simple Riemann client written in Erlang.
2212 pkg_katja_homepage = https://github.com/nifoc/katja
2213 pkg_katja_fetch = git
2214 pkg_katja_repo = https://github.com/nifoc/katja
2215 pkg_katja_commit = master
2216
2217 PACKAGES += kdht
2218 pkg_kdht_name = kdht
2219 pkg_kdht_description = kdht is an erlang DHT implementation
2220 pkg_kdht_homepage = https://github.com/kevinlynx/kdht
2221 pkg_kdht_fetch = git
2222 pkg_kdht_repo = https://github.com/kevinlynx/kdht
2223 pkg_kdht_commit = master
2224
2225 PACKAGES += key2value
2226 pkg_key2value_name = key2value
2227 pkg_key2value_description = Erlang 2-way map
2228 pkg_key2value_homepage = https://github.com/okeuday/key2value
2229 pkg_key2value_fetch = git
2230 pkg_key2value_repo = https://github.com/okeuday/key2value
2231 pkg_key2value_commit = master
2232
2233 PACKAGES += keys1value
2234 pkg_keys1value_name = keys1value
2235 pkg_keys1value_description = Erlang set associative map for key lists
2236 pkg_keys1value_homepage = https://github.com/okeuday/keys1value
2237 pkg_keys1value_fetch = git
2238 pkg_keys1value_repo = https://github.com/okeuday/keys1value
2239 pkg_keys1value_commit = master
2240
2241 PACKAGES += kinetic
2242 pkg_kinetic_name = kinetic
2243 pkg_kinetic_description = Erlang Kinesis Client
2244 pkg_kinetic_homepage = https://github.com/AdRoll/kinetic
2245 pkg_kinetic_fetch = git
2246 pkg_kinetic_repo = https://github.com/AdRoll/kinetic
2247 pkg_kinetic_commit = master
2248
2249 PACKAGES += kjell
2250 pkg_kjell_name = kjell
2251 pkg_kjell_description = Erlang Shell
2252 pkg_kjell_homepage = https://github.com/karlll/kjell
2253 pkg_kjell_fetch = git
2254 pkg_kjell_repo = https://github.com/karlll/kjell
2255 pkg_kjell_commit = master
2256
2257 PACKAGES += kraken
2258 pkg_kraken_name = kraken
2259 pkg_kraken_description = Distributed Pubsub Server for Realtime Apps
2260 pkg_kraken_homepage = https://github.com/Asana/kraken
2261 pkg_kraken_fetch = git
2262 pkg_kraken_repo = https://github.com/Asana/kraken
2263 pkg_kraken_commit = master
2264
2265 PACKAGES += kucumberl
2266 pkg_kucumberl_name = kucumberl
2267 pkg_kucumberl_description = A pure-erlang, open-source, implementation of Cucumber
2268 pkg_kucumberl_homepage = https://github.com/openshine/kucumberl
2269 pkg_kucumberl_fetch = git
2270 pkg_kucumberl_repo = https://github.com/openshine/kucumberl
2271 pkg_kucumberl_commit = master
2272
2273 PACKAGES += kvc
2274 pkg_kvc_name = kvc
2275 pkg_kvc_description = KVC - Key Value Coding for Erlang data structures
2276 pkg_kvc_homepage = https://github.com/etrepum/kvc
2277 pkg_kvc_fetch = git
2278 pkg_kvc_repo = https://github.com/etrepum/kvc
2279 pkg_kvc_commit = master
2280
2281 PACKAGES += kvlists
2282 pkg_kvlists_name = kvlists
2283 pkg_kvlists_description = Lists of key-value pairs (decoded JSON) in Erlang
2284 pkg_kvlists_homepage = https://github.com/jcomellas/kvlists
2285 pkg_kvlists_fetch = git
2286 pkg_kvlists_repo = https://github.com/jcomellas/kvlists
2287 pkg_kvlists_commit = master
2288
2289 PACKAGES += kvs
2290 pkg_kvs_name = kvs
2291 pkg_kvs_description = Container and Iterator
2292 pkg_kvs_homepage = https://github.com/synrc/kvs
2293 pkg_kvs_fetch = git
2294 pkg_kvs_repo = https://github.com/synrc/kvs
2295 pkg_kvs_commit = master
2296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
2305 PACKAGES += lager_amqp_backend
2306 pkg_lager_amqp_backend_name = lager_amqp_backend
2307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
2308 pkg_lager_amqp_backend_homepage = https://github.com/jbrisbin/lager_amqp_backend
2309 pkg_lager_amqp_backend_fetch = git
2310 pkg_lager_amqp_backend_repo = https://github.com/jbrisbin/lager_amqp_backend
2311 pkg_lager_amqp_backend_commit = master
2312
2313 PACKAGES += lager_syslog
2314 pkg_lager_syslog_name = lager_syslog
2315 pkg_lager_syslog_description = Syslog backend for lager
2316 pkg_lager_syslog_homepage = https://github.com/basho/lager_syslog
2317 pkg_lager_syslog_fetch = git
2318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
2319 pkg_lager_syslog_commit = master
2320
2321 PACKAGES += lambdapad
2322 pkg_lambdapad_name = lambdapad
2323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
2324 pkg_lambdapad_homepage = https://github.com/gar1t/lambdapad
2325 pkg_lambdapad_fetch = git
2326 pkg_lambdapad_repo = https://github.com/gar1t/lambdapad
2327 pkg_lambdapad_commit = master
2328
2329 PACKAGES += lasp
2330 pkg_lasp_name = lasp
2331 pkg_lasp_description = A Language for Distributed, Eventually Consistent Computations
2332 pkg_lasp_homepage = http://lasp-lang.org/
2333 pkg_lasp_fetch = git
2334 pkg_lasp_repo = https://github.com/lasp-lang/lasp
2335 pkg_lasp_commit = master
2336
2337 PACKAGES += lasse
2338 pkg_lasse_name = lasse
2339 pkg_lasse_description = SSE handler for Cowboy
2340 pkg_lasse_homepage = https://github.com/inaka/lasse
2341 pkg_lasse_fetch = git
2342 pkg_lasse_repo = https://github.com/inaka/lasse
2343 pkg_lasse_commit = master
2344
2345 PACKAGES += ldap
2346 pkg_ldap_name = ldap
2347 pkg_ldap_description = LDAP server written in Erlang
2348 pkg_ldap_homepage = https://github.com/spawnproc/ldap
2349 pkg_ldap_fetch = git
2350 pkg_ldap_repo = https://github.com/spawnproc/ldap
2351 pkg_ldap_commit = master
2352
2353 PACKAGES += lethink
2354 pkg_lethink_name = lethink
2355 pkg_lethink_description = erlang driver for rethinkdb
2356 pkg_lethink_homepage = https://github.com/taybin/lethink
2357 pkg_lethink_fetch = git
2358 pkg_lethink_repo = https://github.com/taybin/lethink
2359 pkg_lethink_commit = master
2360
2361 PACKAGES += lfe
2362 pkg_lfe_name = lfe
2363 pkg_lfe_description = Lisp Flavoured Erlang (LFE)
2364 pkg_lfe_homepage = https://github.com/rvirding/lfe
2365 pkg_lfe_fetch = git
2366 pkg_lfe_repo = https://github.com/rvirding/lfe
2367 pkg_lfe_commit = master
2368
2369 PACKAGES += ling
2370 pkg_ling_name = ling
2371 pkg_ling_description = Erlang on Xen
2372 pkg_ling_homepage = https://github.com/cloudozer/ling
2373 pkg_ling_fetch = git
2374 pkg_ling_repo = https://github.com/cloudozer/ling
2375 pkg_ling_commit = master
2376
2377 PACKAGES += live
2378 pkg_live_name = live
2379 pkg_live_description = Automated module and configuration reloader.
2380 pkg_live_homepage = http://ninenines.eu
2381 pkg_live_fetch = git
2382 pkg_live_repo = https://github.com/ninenines/live
2383 pkg_live_commit = master
2384
2385 PACKAGES += lmq
2386 pkg_lmq_name = lmq
2387 pkg_lmq_description = Lightweight Message Queue
2388 pkg_lmq_homepage = https://github.com/iij/lmq
2389 pkg_lmq_fetch = git
2390 pkg_lmq_repo = https://github.com/iij/lmq
2391 pkg_lmq_commit = master
2392
2393 PACKAGES += locker
2394 pkg_locker_name = locker
2395 pkg_locker_description = Atomic distributed 'check and set' for short-lived keys
2396 pkg_locker_homepage = https://github.com/wooga/locker
2397 pkg_locker_fetch = git
2398 pkg_locker_repo = https://github.com/wooga/locker
2399 pkg_locker_commit = master
2400
2401 PACKAGES += locks
2402 pkg_locks_name = locks
2403 pkg_locks_description = A scalable, deadlock-resolving resource locker
2404 pkg_locks_homepage = https://github.com/uwiger/locks
2405 pkg_locks_fetch = git
2406 pkg_locks_repo = https://github.com/uwiger/locks
2407 pkg_locks_commit = master
2408
2409 PACKAGES += log4erl
2410 pkg_log4erl_name = log4erl
2411 pkg_log4erl_description = A logger for erlang in the spirit of Log4J.
2412 pkg_log4erl_homepage = https://github.com/ahmednawras/log4erl
2413 pkg_log4erl_fetch = git
2414 pkg_log4erl_repo = https://github.com/ahmednawras/log4erl
2415 pkg_log4erl_commit = master
2416
2417 PACKAGES += lol
2418 pkg_lol_name = lol
2419 pkg_lol_description = Lisp on erLang, and programming is fun again
2420 pkg_lol_homepage = https://github.com/b0oh/lol
2421 pkg_lol_fetch = git
2422 pkg_lol_repo = https://github.com/b0oh/lol
2423 pkg_lol_commit = master
2424
2425 PACKAGES += lucid
2426 pkg_lucid_name = lucid
2427 pkg_lucid_description = HTTP/2 server written in Erlang
2428 pkg_lucid_homepage = https://github.com/tatsuhiro-t/lucid
2429 pkg_lucid_fetch = git
2430 pkg_lucid_repo = https://github.com/tatsuhiro-t/lucid
2431 pkg_lucid_commit = master
2432
2433 PACKAGES += luerl
2434 pkg_luerl_name = luerl
2435 pkg_luerl_description = Lua in Erlang
2436 pkg_luerl_homepage = https://github.com/rvirding/luerl
2437 pkg_luerl_fetch = git
2438 pkg_luerl_repo = https://github.com/rvirding/luerl
2439 pkg_luerl_commit = develop
2440
2441 PACKAGES += luwak
2442 pkg_luwak_name = luwak
2443 pkg_luwak_description = Large-object storage interface for Riak
2444 pkg_luwak_homepage = https://github.com/basho/luwak
2445 pkg_luwak_fetch = git
2446 pkg_luwak_repo = https://github.com/basho/luwak
2447 pkg_luwak_commit = master
2448
2449 PACKAGES += lux
2450 pkg_lux_name = lux
2451 pkg_lux_description = Lux (LUcid eXpect scripting) simplifies test automation and provides an Expect-style execution of commands
2452 pkg_lux_homepage = https://github.com/hawk/lux
2453 pkg_lux_fetch = git
2454 pkg_lux_repo = https://github.com/hawk/lux
2455 pkg_lux_commit = master
2456
2457 PACKAGES += machi
2458 pkg_machi_name = machi
2459 pkg_machi_description = Machi file store
2460 pkg_machi_homepage = https://github.com/basho/machi
2461 pkg_machi_fetch = git
2462 pkg_machi_repo = https://github.com/basho/machi
2463 pkg_machi_commit = master
2464
2465 PACKAGES += mad
2466 pkg_mad_name = mad
2467 pkg_mad_description = Small and Fast Rebar Replacement
2468 pkg_mad_homepage = https://github.com/synrc/mad
2469 pkg_mad_fetch = git
2470 pkg_mad_repo = https://github.com/synrc/mad
2471 pkg_mad_commit = master
2472
2473 PACKAGES += marina
2474 pkg_marina_name = marina
2475 pkg_marina_description = Non-blocking Erlang Cassandra CQL3 client
2476 pkg_marina_homepage = https://github.com/lpgauth/marina
2477 pkg_marina_fetch = git
2478 pkg_marina_repo = https://github.com/lpgauth/marina
2479 pkg_marina_commit = master
2480
2481 PACKAGES += mavg
2482 pkg_mavg_name = mavg
2483 pkg_mavg_description = Erlang :: Exponential moving average library
2484 pkg_mavg_homepage = https://github.com/EchoTeam/mavg
2485 pkg_mavg_fetch = git
2486 pkg_mavg_repo = https://github.com/EchoTeam/mavg
2487 pkg_mavg_commit = master
2488
2489 PACKAGES += mc_erl
2490 pkg_mc_erl_name = mc_erl
2491 pkg_mc_erl_description = mc-erl is a server for Minecraft 1.4.7 written in Erlang.
2492 pkg_mc_erl_homepage = https://github.com/clonejo/mc-erl
2493 pkg_mc_erl_fetch = git
2494 pkg_mc_erl_repo = https://github.com/clonejo/mc-erl
2495 pkg_mc_erl_commit = master
2496
2497 PACKAGES += mcd
2498 pkg_mcd_name = mcd
2499 pkg_mcd_description = Fast memcached protocol client in pure Erlang
2500 pkg_mcd_homepage = https://github.com/EchoTeam/mcd
2501 pkg_mcd_fetch = git
2502 pkg_mcd_repo = https://github.com/EchoTeam/mcd
2503 pkg_mcd_commit = master
2504
2505 PACKAGES += mcerlang
2506 pkg_mcerlang_name = mcerlang
2507 pkg_mcerlang_description = The McErlang model checker for Erlang
2508 pkg_mcerlang_homepage = https://github.com/fredlund/McErlang
2509 pkg_mcerlang_fetch = git
2510 pkg_mcerlang_repo = https://github.com/fredlund/McErlang
2511 pkg_mcerlang_commit = master
2512
2513 PACKAGES += meck
2514 pkg_meck_name = meck
2515 pkg_meck_description = A mocking library for Erlang
2516 pkg_meck_homepage = https://github.com/eproxus/meck
2517 pkg_meck_fetch = git
2518 pkg_meck_repo = https://github.com/eproxus/meck
2519 pkg_meck_commit = master
2520
2521 PACKAGES += mekao
2522 pkg_mekao_name = mekao
2523 pkg_mekao_description = SQL constructor
2524 pkg_mekao_homepage = https://github.com/ddosia/mekao
2525 pkg_mekao_fetch = git
2526 pkg_mekao_repo = https://github.com/ddosia/mekao
2527 pkg_mekao_commit = master
2528
2529 PACKAGES += memo
2530 pkg_memo_name = memo
2531 pkg_memo_description = Erlang memoization server
2532 pkg_memo_homepage = https://github.com/tuncer/memo
2533 pkg_memo_fetch = git
2534 pkg_memo_repo = https://github.com/tuncer/memo
2535 pkg_memo_commit = master
2536
2537 PACKAGES += merge_index
2538 pkg_merge_index_name = merge_index
2539 pkg_merge_index_description = MergeIndex is an Erlang library for storing ordered sets on disk. It is very similar to an SSTable (in Google's Bigtable) or an HFile (in Hadoop).
2540 pkg_merge_index_homepage = https://github.com/basho/merge_index
2541 pkg_merge_index_fetch = git
2542 pkg_merge_index_repo = https://github.com/basho/merge_index
2543 pkg_merge_index_commit = master
2544
2545 PACKAGES += merl
2546 pkg_merl_name = merl
2547 pkg_merl_description = Metaprogramming in Erlang
2548 pkg_merl_homepage = https://github.com/richcarl/merl
2549 pkg_merl_fetch = git
2550 pkg_merl_repo = https://github.com/richcarl/merl
2551 pkg_merl_commit = master
2552
2553 PACKAGES += mimerl
2554 pkg_mimerl_name = mimerl
2555 pkg_mimerl_description = library to handle mimetypes
2556 pkg_mimerl_homepage = https://github.com/benoitc/mimerl
2557 pkg_mimerl_fetch = git
2558 pkg_mimerl_repo = https://github.com/benoitc/mimerl
2559 pkg_mimerl_commit = master
2560
2561 PACKAGES += mimetypes
2562 pkg_mimetypes_name = mimetypes
2563 pkg_mimetypes_description = Erlang MIME types library
2564 pkg_mimetypes_homepage = https://github.com/spawngrid/mimetypes
2565 pkg_mimetypes_fetch = git
2566 pkg_mimetypes_repo = https://github.com/spawngrid/mimetypes
2567 pkg_mimetypes_commit = master
2568
2569 PACKAGES += mixer
2570 pkg_mixer_name = mixer
2571 pkg_mixer_description = Mix in functions from other modules
2572 pkg_mixer_homepage = https://github.com/chef/mixer
2573 pkg_mixer_fetch = git
2574 pkg_mixer_repo = https://github.com/chef/mixer
2575 pkg_mixer_commit = master
2576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
2585 PACKAGES += mochiweb_xpath
2586 pkg_mochiweb_xpath_name = mochiweb_xpath
2587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
2588 pkg_mochiweb_xpath_homepage = https://github.com/retnuh/mochiweb_xpath
2589 pkg_mochiweb_xpath_fetch = git
2590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
2591 pkg_mochiweb_xpath_commit = master
2592
2593 PACKAGES += mockgyver
2594 pkg_mockgyver_name = mockgyver
2595 pkg_mockgyver_description = A mocking library for Erlang
2596 pkg_mockgyver_homepage = https://github.com/klajo/mockgyver
2597 pkg_mockgyver_fetch = git
2598 pkg_mockgyver_repo = https://github.com/klajo/mockgyver
2599 pkg_mockgyver_commit = master
2600
2601 PACKAGES += modlib
2602 pkg_modlib_name = modlib
2603 pkg_modlib_description = Web framework based on Erlang's inets httpd
2604 pkg_modlib_homepage = https://github.com/gar1t/modlib
2605 pkg_modlib_fetch = git
2606 pkg_modlib_repo = https://github.com/gar1t/modlib
2607 pkg_modlib_commit = master
2608
2609 PACKAGES += mongodb
2610 pkg_mongodb_name = mongodb
2611 pkg_mongodb_description = MongoDB driver for Erlang
2612 pkg_mongodb_homepage = https://github.com/comtihon/mongodb-erlang
2613 pkg_mongodb_fetch = git
2614 pkg_mongodb_repo = https://github.com/comtihon/mongodb-erlang
2615 pkg_mongodb_commit = master
2616
2617 PACKAGES += mongooseim
2618 pkg_mongooseim_name = mongooseim
2619 pkg_mongooseim_description = Jabber / XMPP server with focus on performance and scalability, by Erlang Solutions
2620 pkg_mongooseim_homepage = https://www.erlang-solutions.com/products/mongooseim-massively-scalable-ejabberd-platform
2621 pkg_mongooseim_fetch = git
2622 pkg_mongooseim_repo = https://github.com/esl/MongooseIM
2623 pkg_mongooseim_commit = master
2624
2625 PACKAGES += moyo
2626 pkg_moyo_name = moyo
2627 pkg_moyo_description = Erlang utility functions library
2628 pkg_moyo_homepage = https://github.com/dwango/moyo
2629 pkg_moyo_fetch = git
2630 pkg_moyo_repo = https://github.com/dwango/moyo
2631 pkg_moyo_commit = master
2632
2633 PACKAGES += msgpack
2634 pkg_msgpack_name = msgpack
2635 pkg_msgpack_description = MessagePack (de)serializer implementation for Erlang
2636 pkg_msgpack_homepage = https://github.com/msgpack/msgpack-erlang
2637 pkg_msgpack_fetch = git
2638 pkg_msgpack_repo = https://github.com/msgpack/msgpack-erlang
2639 pkg_msgpack_commit = master
2640
2641 PACKAGES += mu2
2642 pkg_mu2_name = mu2
2643 pkg_mu2_description = Erlang mutation testing tool
2644 pkg_mu2_homepage = https://github.com/ramsay-t/mu2
2645 pkg_mu2_fetch = git
2646 pkg_mu2_repo = https://github.com/ramsay-t/mu2
2647 pkg_mu2_commit = master
2648
2649 PACKAGES += mustache
2650 pkg_mustache_name = mustache
2651 pkg_mustache_description = Mustache template engine for Erlang.
2652 pkg_mustache_homepage = https://github.com/mojombo/mustache.erl
2653 pkg_mustache_fetch = git
2654 pkg_mustache_repo = https://github.com/mojombo/mustache.erl
2655 pkg_mustache_commit = master
2656
2657 PACKAGES += myproto
2658 pkg_myproto_name = myproto
2659 pkg_myproto_description = MySQL Server Protocol in Erlang
2660 pkg_myproto_homepage = https://github.com/altenwald/myproto
2661 pkg_myproto_fetch = git
2662 pkg_myproto_repo = https://github.com/altenwald/myproto
2663 pkg_myproto_commit = master
2664
2665 PACKAGES += mysql
2666 pkg_mysql_name = mysql
2667 pkg_mysql_description = Erlang MySQL Driver (from code.google.com)
2668 pkg_mysql_homepage = https://github.com/dizzyd/erlang-mysql-driver
2669 pkg_mysql_fetch = git
2670 pkg_mysql_repo = https://github.com/dizzyd/erlang-mysql-driver
2671 pkg_mysql_commit = master
2672
2673 PACKAGES += n2o
2674 pkg_n2o_name = n2o
2675 pkg_n2o_description = WebSocket Application Server
2676 pkg_n2o_homepage = https://github.com/5HT/n2o
2677 pkg_n2o_fetch = git
2678 pkg_n2o_repo = https://github.com/5HT/n2o
2679 pkg_n2o_commit = master
2680
2681 PACKAGES += nat_upnp
2682 pkg_nat_upnp_name = nat_upnp
2683 pkg_nat_upnp_description = Erlang library to map your internal port to an external using UNP IGD
2684 pkg_nat_upnp_homepage = https://github.com/benoitc/nat_upnp
2685 pkg_nat_upnp_fetch = git
2686 pkg_nat_upnp_repo = https://github.com/benoitc/nat_upnp
2687 pkg_nat_upnp_commit = master
2688
2689 PACKAGES += neo4j
2690 pkg_neo4j_name = neo4j
2691 pkg_neo4j_description = Erlang client library for Neo4J.
2692 pkg_neo4j_homepage = https://github.com/dmitriid/neo4j-erlang
2693 pkg_neo4j_fetch = git
2694 pkg_neo4j_repo = https://github.com/dmitriid/neo4j-erlang
2695 pkg_neo4j_commit = master
2696
2697 PACKAGES += neotoma
2698 pkg_neotoma_name = neotoma
2699 pkg_neotoma_description = Erlang library and packrat parser-generator for parsing expression grammars.
2700 pkg_neotoma_homepage = https://github.com/seancribbs/neotoma
2701 pkg_neotoma_fetch = git
2702 pkg_neotoma_repo = https://github.com/seancribbs/neotoma
2703 pkg_neotoma_commit = master
2704
2705 PACKAGES += newrelic
2706 pkg_newrelic_name = newrelic
2707 pkg_newrelic_description = Erlang library for sending metrics to New Relic
2708 pkg_newrelic_homepage = https://github.com/wooga/newrelic-erlang
2709 pkg_newrelic_fetch = git
2710 pkg_newrelic_repo = https://github.com/wooga/newrelic-erlang
2711 pkg_newrelic_commit = master
2712
2713 PACKAGES += nifty
2714 pkg_nifty_name = nifty
2715 pkg_nifty_description = Erlang NIF wrapper generator
2716 pkg_nifty_homepage = https://github.com/parapluu/nifty
2717 pkg_nifty_fetch = git
2718 pkg_nifty_repo = https://github.com/parapluu/nifty
2719 pkg_nifty_commit = master
2720
2721 PACKAGES += nitrogen_core
2722 pkg_nitrogen_core_name = nitrogen_core
2723 pkg_nitrogen_core_description = The core Nitrogen library.
2724 pkg_nitrogen_core_homepage = http://nitrogenproject.com/
2725 pkg_nitrogen_core_fetch = git
2726 pkg_nitrogen_core_repo = https://github.com/nitrogen/nitrogen_core
2727 pkg_nitrogen_core_commit = master
2728
2729 PACKAGES += nkbase
2730 pkg_nkbase_name = nkbase
2731 pkg_nkbase_description = NkBASE distributed database
2732 pkg_nkbase_homepage = https://github.com/Nekso/nkbase
2733 pkg_nkbase_fetch = git
2734 pkg_nkbase_repo = https://github.com/Nekso/nkbase
2735 pkg_nkbase_commit = develop
2736
2737 PACKAGES += nkdocker
2738 pkg_nkdocker_name = nkdocker
2739 pkg_nkdocker_description = Erlang Docker client
2740 pkg_nkdocker_homepage = https://github.com/Nekso/nkdocker
2741 pkg_nkdocker_fetch = git
2742 pkg_nkdocker_repo = https://github.com/Nekso/nkdocker
2743 pkg_nkdocker_commit = master
2744
2745 PACKAGES += nkpacket
2746 pkg_nkpacket_name = nkpacket
2747 pkg_nkpacket_description = Generic Erlang transport layer
2748 pkg_nkpacket_homepage = https://github.com/Nekso/nkpacket
2749 pkg_nkpacket_fetch = git
2750 pkg_nkpacket_repo = https://github.com/Nekso/nkpacket
2751 pkg_nkpacket_commit = master
2752
2753 PACKAGES += nksip
2754 pkg_nksip_name = nksip
2755 pkg_nksip_description = Erlang SIP application server
2756 pkg_nksip_homepage = https://github.com/kalta/nksip
2757 pkg_nksip_fetch = git
2758 pkg_nksip_repo = https://github.com/kalta/nksip
2759 pkg_nksip_commit = master
2760
2761 PACKAGES += nodefinder
2762 pkg_nodefinder_name = nodefinder
2763 pkg_nodefinder_description = automatic node discovery via UDP multicast
2764 pkg_nodefinder_homepage = https://github.com/erlanger/nodefinder
2765 pkg_nodefinder_fetch = git
2766 pkg_nodefinder_repo = https://github.com/okeuday/nodefinder
2767 pkg_nodefinder_commit = master
2768
2769 PACKAGES += nprocreg
2770 pkg_nprocreg_name = nprocreg
2771 pkg_nprocreg_description = Minimal Distributed Erlang Process Registry
2772 pkg_nprocreg_homepage = http://nitrogenproject.com/
2773 pkg_nprocreg_fetch = git
2774 pkg_nprocreg_repo = https://github.com/nitrogen/nprocreg
2775 pkg_nprocreg_commit = master
2776
2777 PACKAGES += oauth
2778 pkg_oauth_name = oauth
2779 pkg_oauth_description = An Erlang OAuth 1.0 implementation
2780 pkg_oauth_homepage = https://github.com/tim/erlang-oauth
2781 pkg_oauth_fetch = git
2782 pkg_oauth_repo = https://github.com/tim/erlang-oauth
2783 pkg_oauth_commit = master
2784
2785 PACKAGES += oauth2
2786 pkg_oauth2_name = oauth2
2787 pkg_oauth2_description = Erlang Oauth2 implementation
2788 pkg_oauth2_homepage = https://github.com/kivra/oauth2
2789 pkg_oauth2_fetch = git
2790 pkg_oauth2_repo = https://github.com/kivra/oauth2
2791 pkg_oauth2_commit = master
2792
2793 PACKAGES += octopus
2794 pkg_octopus_name = octopus
2795 pkg_octopus_description = Small and flexible pool manager written in Erlang
2796 pkg_octopus_homepage = https://github.com/erlangbureau/octopus
2797 pkg_octopus_fetch = git
2798 pkg_octopus_repo = https://github.com/erlangbureau/octopus
2799 pkg_octopus_commit = master
2800
2801 PACKAGES += of_protocol
2802 pkg_of_protocol_name = of_protocol
2803 pkg_of_protocol_description = OpenFlow Protocol Library for Erlang
2804 pkg_of_protocol_homepage = https://github.com/FlowForwarding/of_protocol
2805 pkg_of_protocol_fetch = git
2806 pkg_of_protocol_repo = https://github.com/FlowForwarding/of_protocol
2807 pkg_of_protocol_commit = master
2808
2809 PACKAGES += opencouch
2810 pkg_opencouch_name = couch
2811 pkg_opencouch_description = A embeddable document oriented database compatible with Apache CouchDB
2812 pkg_opencouch_homepage = https://github.com/benoitc/opencouch
2813 pkg_opencouch_fetch = git
2814 pkg_opencouch_repo = https://github.com/benoitc/opencouch
2815 pkg_opencouch_commit = master
2816
2817 PACKAGES += openflow
2818 pkg_openflow_name = openflow
2819 pkg_openflow_description = An OpenFlow controller written in pure erlang
2820 pkg_openflow_homepage = https://github.com/renatoaguiar/erlang-openflow
2821 pkg_openflow_fetch = git
2822 pkg_openflow_repo = https://github.com/renatoaguiar/erlang-openflow
2823 pkg_openflow_commit = master
2824
2825 PACKAGES += openid
2826 pkg_openid_name = openid
2827 pkg_openid_description = Erlang OpenID
2828 pkg_openid_homepage = https://github.com/brendonh/erl_openid
2829 pkg_openid_fetch = git
2830 pkg_openid_repo = https://github.com/brendonh/erl_openid
2831 pkg_openid_commit = master
2832
2833 PACKAGES += openpoker
2834 pkg_openpoker_name = openpoker
2835 pkg_openpoker_description = Genesis Texas hold'em Game Server
2836 pkg_openpoker_homepage = https://github.com/hpyhacking/openpoker
2837 pkg_openpoker_fetch = git
2838 pkg_openpoker_repo = https://github.com/hpyhacking/openpoker
2839 pkg_openpoker_commit = master
2840
2841 PACKAGES += pal
2842 pkg_pal_name = pal
2843 pkg_pal_description = Pragmatic Authentication Library
2844 pkg_pal_homepage = https://github.com/manifest/pal
2845 pkg_pal_fetch = git
2846 pkg_pal_repo = https://github.com/manifest/pal
2847 pkg_pal_commit = master
2848
2849 PACKAGES += parse_trans
2850 pkg_parse_trans_name = parse_trans
2851 pkg_parse_trans_description = Parse transform utilities for Erlang
2852 pkg_parse_trans_homepage = https://github.com/uwiger/parse_trans
2853 pkg_parse_trans_fetch = git
2854 pkg_parse_trans_repo = https://github.com/uwiger/parse_trans
2855 pkg_parse_trans_commit = master
2856
2857 PACKAGES += parsexml
2858 pkg_parsexml_name = parsexml
2859 pkg_parsexml_description = Simple DOM XML parser with convenient and very simple API
2860 pkg_parsexml_homepage = https://github.com/maxlapshin/parsexml
2861 pkg_parsexml_fetch = git
2862 pkg_parsexml_repo = https://github.com/maxlapshin/parsexml
2863 pkg_parsexml_commit = master
2864
2865 PACKAGES += pegjs
2866 pkg_pegjs_name = pegjs
2867 pkg_pegjs_description = An implementation of PEG.js grammar for Erlang.
2868 pkg_pegjs_homepage = https://github.com/dmitriid/pegjs
2869 pkg_pegjs_fetch = git
2870 pkg_pegjs_repo = https://github.com/dmitriid/pegjs
2871 pkg_pegjs_commit = master
2872
2873 PACKAGES += percept2
2874 pkg_percept2_name = percept2
2875 pkg_percept2_description = Concurrent profiling tool for Erlang
2876 pkg_percept2_homepage = https://github.com/huiqing/percept2
2877 pkg_percept2_fetch = git
2878 pkg_percept2_repo = https://github.com/huiqing/percept2
2879 pkg_percept2_commit = master
2880
2881 PACKAGES += pgsql
2882 pkg_pgsql_name = pgsql
2883 pkg_pgsql_description = Erlang PostgreSQL driver
2884 pkg_pgsql_homepage = https://github.com/semiocast/pgsql
2885 pkg_pgsql_fetch = git
2886 pkg_pgsql_repo = https://github.com/semiocast/pgsql
2887 pkg_pgsql_commit = master
2888
2889 PACKAGES += pkgx
2890 pkg_pkgx_name = pkgx
2891 pkg_pkgx_description = Build .deb packages from Erlang releases
2892 pkg_pkgx_homepage = https://github.com/arjan/pkgx
2893 pkg_pkgx_fetch = git
2894 pkg_pkgx_repo = https://github.com/arjan/pkgx
2895 pkg_pkgx_commit = master
2896
2897 PACKAGES += pkt
2898 pkg_pkt_name = pkt
2899 pkg_pkt_description = Erlang network protocol library
2900 pkg_pkt_homepage = https://github.com/msantos/pkt
2901 pkg_pkt_fetch = git
2902 pkg_pkt_repo = https://github.com/msantos/pkt
2903 pkg_pkt_commit = master
2904
2905 PACKAGES += plain_fsm
2906 pkg_plain_fsm_name = plain_fsm
2907 pkg_plain_fsm_description = A behaviour/support library for writing plain Erlang FSMs.
2908 pkg_plain_fsm_homepage = https://github.com/uwiger/plain_fsm
2909 pkg_plain_fsm_fetch = git
2910 pkg_plain_fsm_repo = https://github.com/uwiger/plain_fsm
2911 pkg_plain_fsm_commit = master
2912
2913 PACKAGES += plumtree
2914 pkg_plumtree_name = plumtree
2915 pkg_plumtree_description = Epidemic Broadcast Trees
2916 pkg_plumtree_homepage = https://github.com/helium/plumtree
2917 pkg_plumtree_fetch = git
2918 pkg_plumtree_repo = https://github.com/helium/plumtree
2919 pkg_plumtree_commit = master
2920
2921 PACKAGES += pmod_transform
2922 pkg_pmod_transform_name = pmod_transform
2923 pkg_pmod_transform_description = Parse transform for parameterized modules
2924 pkg_pmod_transform_homepage = https://github.com/erlang/pmod_transform
2925 pkg_pmod_transform_fetch = git
2926 pkg_pmod_transform_repo = https://github.com/erlang/pmod_transform
2927 pkg_pmod_transform_commit = master
2928
2929 PACKAGES += pobox
2930 pkg_pobox_name = pobox
2931 pkg_pobox_description = External buffer processes to protect against mailbox overflow in Erlang
2932 pkg_pobox_homepage = https://github.com/ferd/pobox
2933 pkg_pobox_fetch = git
2934 pkg_pobox_repo = https://github.com/ferd/pobox
2935 pkg_pobox_commit = master
2936
2937 PACKAGES += ponos
2938 pkg_ponos_name = ponos
2939 pkg_ponos_description = ponos is a simple yet powerful load generator written in erlang
2940 pkg_ponos_homepage = https://github.com/klarna/ponos
2941 pkg_ponos_fetch = git
2942 pkg_ponos_repo = https://github.com/klarna/ponos
2943 pkg_ponos_commit = master
2944
2945 PACKAGES += poolboy
2946 pkg_poolboy_name = poolboy
2947 pkg_poolboy_description = A hunky Erlang worker pool factory
2948 pkg_poolboy_homepage = https://github.com/devinus/poolboy
2949 pkg_poolboy_fetch = git
2950 pkg_poolboy_repo = https://github.com/devinus/poolboy
2951 pkg_poolboy_commit = master
2952
2953 PACKAGES += pooler
2954 pkg_pooler_name = pooler
2955 pkg_pooler_description = An OTP Process Pool Application
2956 pkg_pooler_homepage = https://github.com/seth/pooler
2957 pkg_pooler_fetch = git
2958 pkg_pooler_repo = https://github.com/seth/pooler
2959 pkg_pooler_commit = master
2960
2961 PACKAGES += pqueue
2962 pkg_pqueue_name = pqueue
2963 pkg_pqueue_description = Erlang Priority Queues
2964 pkg_pqueue_homepage = https://github.com/okeuday/pqueue
2965 pkg_pqueue_fetch = git
2966 pkg_pqueue_repo = https://github.com/okeuday/pqueue
2967 pkg_pqueue_commit = master
2968
2969 PACKAGES += procket
2970 pkg_procket_name = procket
2971 pkg_procket_description = Erlang interface to low level socket operations
2972 pkg_procket_homepage = http://blog.listincomprehension.com/search/label/procket
2973 pkg_procket_fetch = git
2974 pkg_procket_repo = https://github.com/msantos/procket
2975 pkg_procket_commit = master
2976
2977 PACKAGES += prop
2978 pkg_prop_name = prop
2979 pkg_prop_description = An Erlang code scaffolding and generator system.
2980 pkg_prop_homepage = https://github.com/nuex/prop
2981 pkg_prop_fetch = git
2982 pkg_prop_repo = https://github.com/nuex/prop
2983 pkg_prop_commit = master
2984
2985 PACKAGES += proper
2986 pkg_proper_name = proper
2987 pkg_proper_description = PropEr: a QuickCheck-inspired property-based testing tool for Erlang.
2988 pkg_proper_homepage = http://proper.softlab.ntua.gr
2989 pkg_proper_fetch = git
2990 pkg_proper_repo = https://github.com/manopapad/proper
2991 pkg_proper_commit = master
2992
2993 PACKAGES += props
2994 pkg_props_name = props
2995 pkg_props_description = Property structure library
2996 pkg_props_homepage = https://github.com/greyarea/props
2997 pkg_props_fetch = git
2998 pkg_props_repo = https://github.com/greyarea/props
2999 pkg_props_commit = master
3000
3001 PACKAGES += protobuffs
3002 pkg_protobuffs_name = protobuffs
3003 pkg_protobuffs_description = An implementation of Google's Protocol Buffers for Erlang, based on ngerakines/erlang_protobuffs.
3004 pkg_protobuffs_homepage = https://github.com/basho/erlang_protobuffs
3005 pkg_protobuffs_fetch = git
3006 pkg_protobuffs_repo = https://github.com/basho/erlang_protobuffs
3007 pkg_protobuffs_commit = master
3008
3009 PACKAGES += psycho
3010 pkg_psycho_name = psycho
3011 pkg_psycho_description = HTTP server that provides a WSGI-like interface for applications and middleware.
3012 pkg_psycho_homepage = https://github.com/gar1t/psycho
3013 pkg_psycho_fetch = git
3014 pkg_psycho_repo = https://github.com/gar1t/psycho
3015 pkg_psycho_commit = master
3016
3017 PACKAGES += purity
3018 pkg_purity_name = purity
3019 pkg_purity_description = A side-effect analyzer for Erlang
3020 pkg_purity_homepage = https://github.com/mpitid/purity
3021 pkg_purity_fetch = git
3022 pkg_purity_repo = https://github.com/mpitid/purity
3023 pkg_purity_commit = master
3024
3025 PACKAGES += push_service
3026 pkg_push_service_name = push_service
3027 pkg_push_service_description = Push service
3028 pkg_push_service_homepage = https://github.com/hairyhum/push_service
3029 pkg_push_service_fetch = git
3030 pkg_push_service_repo = https://github.com/hairyhum/push_service
3031 pkg_push_service_commit = master
3032
3033 PACKAGES += qdate
3034 pkg_qdate_name = qdate
3035 pkg_qdate_description = Date, time, and timezone parsing, formatting, and conversion for Erlang.
3036 pkg_qdate_homepage = https://github.com/choptastic/qdate
3037 pkg_qdate_fetch = git
3038 pkg_qdate_repo = https://github.com/choptastic/qdate
3039 pkg_qdate_commit = master
3040
3041 PACKAGES += qrcode
3042 pkg_qrcode_name = qrcode
3043 pkg_qrcode_description = QR Code encoder in Erlang
3044 pkg_qrcode_homepage = https://github.com/komone/qrcode
3045 pkg_qrcode_fetch = git
3046 pkg_qrcode_repo = https://github.com/komone/qrcode
3047 pkg_qrcode_commit = master
3048
3049 PACKAGES += quest
3050 pkg_quest_name = quest
3051 pkg_quest_description = Learn Erlang through this set of challenges. An interactive system for getting to know Erlang.
3052 pkg_quest_homepage = https://github.com/eriksoe/ErlangQuest
3053 pkg_quest_fetch = git
3054 pkg_quest_repo = https://github.com/eriksoe/ErlangQuest
3055 pkg_quest_commit = master
3056
3057 PACKAGES += quickrand
3058 pkg_quickrand_name = quickrand
3059 pkg_quickrand_description = Quick Erlang Random Number Generation
3060 pkg_quickrand_homepage = https://github.com/okeuday/quickrand
3061 pkg_quickrand_fetch = git
3062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
3063 pkg_quickrand_commit = master
3064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
3073 PACKAGES += rabbit_exchange_type_riak
3074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
3075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
3076 pkg_rabbit_exchange_type_riak_homepage = https://github.com/jbrisbin/riak-exchange
3077 pkg_rabbit_exchange_type_riak_fetch = git
3078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
3079 pkg_rabbit_exchange_type_riak_commit = master
3080
3081 PACKAGES += rack
3082 pkg_rack_name = rack
3083 pkg_rack_description = Rack handler for erlang
3084 pkg_rack_homepage = https://github.com/erlyvideo/rack
3085 pkg_rack_fetch = git
3086 pkg_rack_repo = https://github.com/erlyvideo/rack
3087 pkg_rack_commit = master
3088
3089 PACKAGES += radierl
3090 pkg_radierl_name = radierl
3091 pkg_radierl_description = RADIUS protocol stack implemented in Erlang.
3092 pkg_radierl_homepage = https://github.com/vances/radierl
3093 pkg_radierl_fetch = git
3094 pkg_radierl_repo = https://github.com/vances/radierl
3095 pkg_radierl_commit = master
3096
3097 PACKAGES += rafter
3098 pkg_rafter_name = rafter
3099 pkg_rafter_description = An Erlang library application which implements the Raft consensus protocol
3100 pkg_rafter_homepage = https://github.com/andrewjstone/rafter
3101 pkg_rafter_fetch = git
3102 pkg_rafter_repo = https://github.com/andrewjstone/rafter
3103 pkg_rafter_commit = master
3104
3105 PACKAGES += ranch
3106 pkg_ranch_name = ranch
3107 pkg_ranch_description = Socket acceptor pool for TCP protocols.
3108 pkg_ranch_homepage = http://ninenines.eu
3109 pkg_ranch_fetch = git
3110 pkg_ranch_repo = https://github.com/ninenines/ranch
3111 pkg_ranch_commit = 1.2.1
3112
3113 PACKAGES += rbeacon
3114 pkg_rbeacon_name = rbeacon
3115 pkg_rbeacon_description = LAN discovery and presence in Erlang.
3116 pkg_rbeacon_homepage = https://github.com/refuge/rbeacon
3117 pkg_rbeacon_fetch = git
3118 pkg_rbeacon_repo = https://github.com/refuge/rbeacon
3119 pkg_rbeacon_commit = master
3120
3121 PACKAGES += rebar
3122 pkg_rebar_name = rebar
3123 pkg_rebar_description = Erlang build tool that makes it easy to compile and test Erlang applications, port drivers and releases.
3124 pkg_rebar_homepage = http://www.rebar3.org
3125 pkg_rebar_fetch = git
3126 pkg_rebar_repo = https://github.com/rebar/rebar3
3127 pkg_rebar_commit = master
3128
3129 PACKAGES += rebus
3130 pkg_rebus_name = rebus
3131 pkg_rebus_description = A stupid simple, internal, pub/sub event bus written in- and for Erlang.
3132 pkg_rebus_homepage = https://github.com/olle/rebus
3133 pkg_rebus_fetch = git
3134 pkg_rebus_repo = https://github.com/olle/rebus
3135 pkg_rebus_commit = master
3136
3137 PACKAGES += rec2json
3138 pkg_rec2json_name = rec2json
3139 pkg_rec2json_description = Compile erlang record definitions into modules to convert them to/from json easily.
3140 pkg_rec2json_homepage = https://github.com/lordnull/rec2json
3141 pkg_rec2json_fetch = git
3142 pkg_rec2json_repo = https://github.com/lordnull/rec2json
3143 pkg_rec2json_commit = master
3144
3145 PACKAGES += recon
3146 pkg_recon_name = recon
3147 pkg_recon_description = Collection of functions and scripts to debug Erlang in production.
3148 pkg_recon_homepage = https://github.com/ferd/recon
3149 pkg_recon_fetch = git
3150 pkg_recon_repo = https://github.com/ferd/recon
3151 pkg_recon_commit = master
3152
3153 PACKAGES += record_info
3154 pkg_record_info_name = record_info
3155 pkg_record_info_description = Convert between record and proplist
3156 pkg_record_info_homepage = https://github.com/bipthelin/erlang-record_info
3157 pkg_record_info_fetch = git
3158 pkg_record_info_repo = https://github.com/bipthelin/erlang-record_info
3159 pkg_record_info_commit = master
3160
3161 PACKAGES += redgrid
3162 pkg_redgrid_name = redgrid
3163 pkg_redgrid_description = automatic Erlang node discovery via redis
3164 pkg_redgrid_homepage = https://github.com/jkvor/redgrid
3165 pkg_redgrid_fetch = git
3166 pkg_redgrid_repo = https://github.com/jkvor/redgrid
3167 pkg_redgrid_commit = master
3168
3169 PACKAGES += redo
3170 pkg_redo_name = redo
3171 pkg_redo_description = pipelined erlang redis client
3172 pkg_redo_homepage = https://github.com/jkvor/redo
3173 pkg_redo_fetch = git
3174 pkg_redo_repo = https://github.com/jkvor/redo
3175 pkg_redo_commit = master
3176
3177 PACKAGES += reload_mk
3178 pkg_reload_mk_name = reload_mk
3179 pkg_reload_mk_description = Live reload plugin for erlang.mk.
3180 pkg_reload_mk_homepage = https://github.com/bullno1/reload.mk
3181 pkg_reload_mk_fetch = git
3182 pkg_reload_mk_repo = https://github.com/bullno1/reload.mk
3183 pkg_reload_mk_commit = master
3184
3185 PACKAGES += reltool_util
3186 pkg_reltool_util_name = reltool_util
3187 pkg_reltool_util_description = Erlang reltool utility functionality application
3188 pkg_reltool_util_homepage = https://github.com/okeuday/reltool_util
3189 pkg_reltool_util_fetch = git
3190 pkg_reltool_util_repo = https://github.com/okeuday/reltool_util
3191 pkg_reltool_util_commit = master
3192
3193 PACKAGES += relx
3194 pkg_relx_name = relx
3195 pkg_relx_description = Sane, simple release creation for Erlang
3196 pkg_relx_homepage = https://github.com/erlware/relx
3197 pkg_relx_fetch = git
3198 pkg_relx_repo = https://github.com/erlware/relx
3199 pkg_relx_commit = master
3200
3201 PACKAGES += resource_discovery
3202 pkg_resource_discovery_name = resource_discovery
3203 pkg_resource_discovery_description = An application used to dynamically discover resources present in an Erlang node cluster.
3204 pkg_resource_discovery_homepage = http://erlware.org/
3205 pkg_resource_discovery_fetch = git
3206 pkg_resource_discovery_repo = https://github.com/erlware/resource_discovery
3207 pkg_resource_discovery_commit = master
3208
3209 PACKAGES += restc
3210 pkg_restc_name = restc
3211 pkg_restc_description = Erlang Rest Client
3212 pkg_restc_homepage = https://github.com/kivra/restclient
3213 pkg_restc_fetch = git
3214 pkg_restc_repo = https://github.com/kivra/restclient
3215 pkg_restc_commit = master
3216
3217 PACKAGES += rfc4627_jsonrpc
3218 pkg_rfc4627_jsonrpc_name = rfc4627_jsonrpc
3219 pkg_rfc4627_jsonrpc_description = Erlang RFC4627 (JSON) codec and JSON-RPC server implementation.
3220 pkg_rfc4627_jsonrpc_homepage = https://github.com/tonyg/erlang-rfc4627
3221 pkg_rfc4627_jsonrpc_fetch = git
3222 pkg_rfc4627_jsonrpc_repo = https://github.com/tonyg/erlang-rfc4627
3223 pkg_rfc4627_jsonrpc_commit = master
3224
3225 PACKAGES += riak_control
3226 pkg_riak_control_name = riak_control
3227 pkg_riak_control_description = Webmachine-based administration interface for Riak.
3228 pkg_riak_control_homepage = https://github.com/basho/riak_control
3229 pkg_riak_control_fetch = git
3230 pkg_riak_control_repo = https://github.com/basho/riak_control
3231 pkg_riak_control_commit = master
3232
3233 PACKAGES += riak_core
3234 pkg_riak_core_name = riak_core
3235 pkg_riak_core_description = Distributed systems infrastructure used by Riak.
3236 pkg_riak_core_homepage = https://github.com/basho/riak_core
3237 pkg_riak_core_fetch = git
3238 pkg_riak_core_repo = https://github.com/basho/riak_core
3239 pkg_riak_core_commit = master
3240
3241 PACKAGES += riak_dt
3242 pkg_riak_dt_name = riak_dt
3243 pkg_riak_dt_description = Convergent replicated datatypes in Erlang
3244 pkg_riak_dt_homepage = https://github.com/basho/riak_dt
3245 pkg_riak_dt_fetch = git
3246 pkg_riak_dt_repo = https://github.com/basho/riak_dt
3247 pkg_riak_dt_commit = master
3248
3249 PACKAGES += riak_ensemble
3250 pkg_riak_ensemble_name = riak_ensemble
3251 pkg_riak_ensemble_description = Multi-Paxos framework in Erlang
3252 pkg_riak_ensemble_homepage = https://github.com/basho/riak_ensemble
3253 pkg_riak_ensemble_fetch = git
3254 pkg_riak_ensemble_repo = https://github.com/basho/riak_ensemble
3255 pkg_riak_ensemble_commit = master
3256
3257 PACKAGES += riak_kv
3258 pkg_riak_kv_name = riak_kv
3259 pkg_riak_kv_description = Riak Key/Value Store
3260 pkg_riak_kv_homepage = https://github.com/basho/riak_kv
3261 pkg_riak_kv_fetch = git
3262 pkg_riak_kv_repo = https://github.com/basho/riak_kv
3263 pkg_riak_kv_commit = master
3264
3265 PACKAGES += riak_pg
3266 pkg_riak_pg_name = riak_pg
3267 pkg_riak_pg_description = Distributed process groups with riak_core.
3268 pkg_riak_pg_homepage = https://github.com/cmeiklejohn/riak_pg
3269 pkg_riak_pg_fetch = git
3270 pkg_riak_pg_repo = https://github.com/cmeiklejohn/riak_pg
3271 pkg_riak_pg_commit = master
3272
3273 PACKAGES += riak_pipe
3274 pkg_riak_pipe_name = riak_pipe
3275 pkg_riak_pipe_description = Riak Pipelines
3276 pkg_riak_pipe_homepage = https://github.com/basho/riak_pipe
3277 pkg_riak_pipe_fetch = git
3278 pkg_riak_pipe_repo = https://github.com/basho/riak_pipe
3279 pkg_riak_pipe_commit = master
3280
3281 PACKAGES += riak_sysmon
3282 pkg_riak_sysmon_name = riak_sysmon
3283 pkg_riak_sysmon_description = Simple OTP app for managing Erlang VM system_monitor event messages
3284 pkg_riak_sysmon_homepage = https://github.com/basho/riak_sysmon
3285 pkg_riak_sysmon_fetch = git
3286 pkg_riak_sysmon_repo = https://github.com/basho/riak_sysmon
3287 pkg_riak_sysmon_commit = master
3288
3289 PACKAGES += riak_test
3290 pkg_riak_test_name = riak_test
3291 pkg_riak_test_description = I'm in your cluster, testing your riaks
3292 pkg_riak_test_homepage = https://github.com/basho/riak_test
3293 pkg_riak_test_fetch = git
3294 pkg_riak_test_repo = https://github.com/basho/riak_test
3295 pkg_riak_test_commit = master
3296
3297 PACKAGES += riakc
3298 pkg_riakc_name = riakc
3299 pkg_riakc_description = Erlang clients for Riak.
3300 pkg_riakc_homepage = https://github.com/basho/riak-erlang-client
3301 pkg_riakc_fetch = git
3302 pkg_riakc_repo = https://github.com/basho/riak-erlang-client
3303 pkg_riakc_commit = master
3304
3305 PACKAGES += riakhttpc
3306 pkg_riakhttpc_name = riakhttpc
3307 pkg_riakhttpc_description = Riak Erlang client using the HTTP interface
3308 pkg_riakhttpc_homepage = https://github.com/basho/riak-erlang-http-client
3309 pkg_riakhttpc_fetch = git
3310 pkg_riakhttpc_repo = https://github.com/basho/riak-erlang-http-client
3311 pkg_riakhttpc_commit = master
3312
3313 PACKAGES += riaknostic
3314 pkg_riaknostic_name = riaknostic
3315 pkg_riaknostic_description = A diagnostic tool for Riak installations, to find common errors asap
3316 pkg_riaknostic_homepage = https://github.com/basho/riaknostic
3317 pkg_riaknostic_fetch = git
3318 pkg_riaknostic_repo = https://github.com/basho/riaknostic
3319 pkg_riaknostic_commit = master
3320
3321 PACKAGES += riakpool
3322 pkg_riakpool_name = riakpool
3323 pkg_riakpool_description = erlang riak client pool
3324 pkg_riakpool_homepage = https://github.com/dweldon/riakpool
3325 pkg_riakpool_fetch = git
3326 pkg_riakpool_repo = https://github.com/dweldon/riakpool
3327 pkg_riakpool_commit = master
3328
3329 PACKAGES += rivus_cep
3330 pkg_rivus_cep_name = rivus_cep
3331 pkg_rivus_cep_description = Complex event processing in Erlang
3332 pkg_rivus_cep_homepage = https://github.com/vascokk/rivus_cep
3333 pkg_rivus_cep_fetch = git
3334 pkg_rivus_cep_repo = https://github.com/vascokk/rivus_cep
3335 pkg_rivus_cep_commit = master
3336
3337 PACKAGES += rlimit
3338 pkg_rlimit_name = rlimit
3339 pkg_rlimit_description = Magnus Klaar's rate limiter code from etorrent
3340 pkg_rlimit_homepage = https://github.com/jlouis/rlimit
3341 pkg_rlimit_fetch = git
3342 pkg_rlimit_repo = https://github.com/jlouis/rlimit
3343 pkg_rlimit_commit = master
3344
3345 PACKAGES += rust_mk
3346 pkg_rust_mk_name = rust_mk
3347 pkg_rust_mk_description = Build Rust crates in an Erlang application
3348 pkg_rust_mk_homepage = https://github.com/goertzenator/rust.mk
3349 pkg_rust_mk_fetch = git
3350 pkg_rust_mk_repo = https://github.com/goertzenator/rust.mk
3351 pkg_rust_mk_commit = master
3352
3353 PACKAGES += safetyvalve
3354 pkg_safetyvalve_name = safetyvalve
3355 pkg_safetyvalve_description = A safety valve for your erlang node
3356 pkg_safetyvalve_homepage = https://github.com/jlouis/safetyvalve
3357 pkg_safetyvalve_fetch = git
3358 pkg_safetyvalve_repo = https://github.com/jlouis/safetyvalve
3359 pkg_safetyvalve_commit = master
3360
3361 PACKAGES += seestar
3362 pkg_seestar_name = seestar
3363 pkg_seestar_description = The Erlang client for Cassandra 1.2+ binary protocol
3364 pkg_seestar_homepage = https://github.com/iamaleksey/seestar
3365 pkg_seestar_fetch = git
3366 pkg_seestar_repo = https://github.com/iamaleksey/seestar
3367 pkg_seestar_commit = master
3368
3369 PACKAGES += service
3370 pkg_service_name = service
3371 pkg_service_description = A minimal Erlang behavior for creating CloudI internal services
3372 pkg_service_homepage = http://cloudi.org/
3373 pkg_service_fetch = git
3374 pkg_service_repo = https://github.com/CloudI/service
3375 pkg_service_commit = master
3376
3377 PACKAGES += setup
3378 pkg_setup_name = setup
3379 pkg_setup_description = Generic setup utility for Erlang-based systems
3380 pkg_setup_homepage = https://github.com/uwiger/setup
3381 pkg_setup_fetch = git
3382 pkg_setup_repo = https://github.com/uwiger/setup
3383 pkg_setup_commit = master
3384
3385 PACKAGES += sext
3386 pkg_sext_name = sext
3387 pkg_sext_description = Sortable Erlang Term Serialization
3388 pkg_sext_homepage = https://github.com/uwiger/sext
3389 pkg_sext_fetch = git
3390 pkg_sext_repo = https://github.com/uwiger/sext
3391 pkg_sext_commit = master
3392
3393 PACKAGES += sfmt
3394 pkg_sfmt_name = sfmt
3395 pkg_sfmt_description = SFMT pseudo random number generator for Erlang.
3396 pkg_sfmt_homepage = https://github.com/jj1bdx/sfmt-erlang
3397 pkg_sfmt_fetch = git
3398 pkg_sfmt_repo = https://github.com/jj1bdx/sfmt-erlang
3399 pkg_sfmt_commit = master
3400
3401 PACKAGES += sgte
3402 pkg_sgte_name = sgte
3403 pkg_sgte_description = A simple Erlang Template Engine
3404 pkg_sgte_homepage = https://github.com/filippo/sgte
3405 pkg_sgte_fetch = git
3406 pkg_sgte_repo = https://github.com/filippo/sgte
3407 pkg_sgte_commit = master
3408
3409 PACKAGES += sheriff
3410 pkg_sheriff_name = sheriff
3411 pkg_sheriff_description = Parse transform for type based validation.
3412 pkg_sheriff_homepage = http://ninenines.eu
3413 pkg_sheriff_fetch = git
3414 pkg_sheriff_repo = https://github.com/extend/sheriff
3415 pkg_sheriff_commit = master
3416
3417 PACKAGES += shotgun
3418 pkg_shotgun_name = shotgun
3419 pkg_shotgun_description = better than just a gun
3420 pkg_shotgun_homepage = https://github.com/inaka/shotgun
3421 pkg_shotgun_fetch = git
3422 pkg_shotgun_repo = https://github.com/inaka/shotgun
3423 pkg_shotgun_commit = master
3424
3425 PACKAGES += sidejob
3426 pkg_sidejob_name = sidejob
3427 pkg_sidejob_description = Parallel worker and capacity limiting library for Erlang
3428 pkg_sidejob_homepage = https://github.com/basho/sidejob
3429 pkg_sidejob_fetch = git
3430 pkg_sidejob_repo = https://github.com/basho/sidejob
3431 pkg_sidejob_commit = master
3432
3433 PACKAGES += sieve
3434 pkg_sieve_name = sieve
3435 pkg_sieve_description = sieve is a simple TCP routing proxy (layer 7) in erlang
3436 pkg_sieve_homepage = https://github.com/benoitc/sieve
3437 pkg_sieve_fetch = git
3438 pkg_sieve_repo = https://github.com/benoitc/sieve
3439 pkg_sieve_commit = master
3440
3441 PACKAGES += sighandler
3442 pkg_sighandler_name = sighandler
3443 pkg_sighandler_description = Handle UNIX signals in Er lang
3444 pkg_sighandler_homepage = https://github.com/jkingsbery/sighandler
3445 pkg_sighandler_fetch = git
3446 pkg_sighandler_repo = https://github.com/jkingsbery/sighandler
3447 pkg_sighandler_commit = master
3448
3449 PACKAGES += simhash
3450 pkg_simhash_name = simhash
3451 pkg_simhash_description = Simhashing for Erlang -- hashing algorithm to find near-duplicates in binary data.
3452 pkg_simhash_homepage = https://github.com/ferd/simhash
3453 pkg_simhash_fetch = git
3454 pkg_simhash_repo = https://github.com/ferd/simhash
3455 pkg_simhash_commit = master
3456
3457 PACKAGES += simple_bridge
3458 pkg_simple_bridge_name = simple_bridge
3459 pkg_simple_bridge_description = A simple, standardized interface library to Erlang HTTP Servers.
3460 pkg_simple_bridge_homepage = https://github.com/nitrogen/simple_bridge
3461 pkg_simple_bridge_fetch = git
3462 pkg_simple_bridge_repo = https://github.com/nitrogen/simple_bridge
3463 pkg_simple_bridge_commit = master
3464
3465 PACKAGES += simple_oauth2
3466 pkg_simple_oauth2_name = simple_oauth2
3467 pkg_simple_oauth2_description = Simple erlang OAuth2 client module for any http server framework (Google, Facebook, Yandex, Vkontakte are preconfigured)
3468 pkg_simple_oauth2_homepage = https://github.com/virtan/simple_oauth2
3469 pkg_simple_oauth2_fetch = git
3470 pkg_simple_oauth2_repo = https://github.com/virtan/simple_oauth2
3471 pkg_simple_oauth2_commit = master
3472
3473 PACKAGES += skel
3474 pkg_skel_name = skel
3475 pkg_skel_description = A Streaming Process-based Skeleton Library for Erlang
3476 pkg_skel_homepage = https://github.com/ParaPhrase/skel
3477 pkg_skel_fetch = git
3478 pkg_skel_repo = https://github.com/ParaPhrase/skel
3479 pkg_skel_commit = master
3480
3481 PACKAGES += slack
3482 pkg_slack_name = slack
3483 pkg_slack_description = Minimal slack notification OTP library.
3484 pkg_slack_homepage = https://github.com/DonBranson/slack
3485 pkg_slack_fetch = git
3486 pkg_slack_repo = https://github.com/DonBranson/slack.git
3487 pkg_slack_commit = master
3488
3489 PACKAGES += smother
3490 pkg_smother_name = smother
3491 pkg_smother_description = Extended code coverage metrics for Erlang.
3492 pkg_smother_homepage = https://ramsay-t.github.io/Smother/
3493 pkg_smother_fetch = git
3494 pkg_smother_repo = https://github.com/ramsay-t/Smother
3495 pkg_smother_commit = master
3496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
3505 PACKAGES += social
3506 pkg_social_name = social
3507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
3508 pkg_social_homepage = https://github.com/dvv/social
3509 pkg_social_fetch = git
3510 pkg_social_repo = https://github.com/dvv/social
3511 pkg_social_commit = master
3512
3513 PACKAGES += spapi_router
3514 pkg_spapi_router_name = spapi_router
3515 pkg_spapi_router_description = Partially-connected Erlang clustering
3516 pkg_spapi_router_homepage = https://github.com/spilgames/spapi-router
3517 pkg_spapi_router_fetch = git
3518 pkg_spapi_router_repo = https://github.com/spilgames/spapi-router
3519 pkg_spapi_router_commit = master
3520
3521 PACKAGES += sqerl
3522 pkg_sqerl_name = sqerl
3523 pkg_sqerl_description = An Erlang-flavoured SQL DSL
3524 pkg_sqerl_homepage = https://github.com/hairyhum/sqerl
3525 pkg_sqerl_fetch = git
3526 pkg_sqerl_repo = https://github.com/hairyhum/sqerl
3527 pkg_sqerl_commit = master
3528
3529 PACKAGES += srly
3530 pkg_srly_name = srly
3531 pkg_srly_description = Native Erlang Unix serial interface
3532 pkg_srly_homepage = https://github.com/msantos/srly
3533 pkg_srly_fetch = git
3534 pkg_srly_repo = https://github.com/msantos/srly
3535 pkg_srly_commit = master
3536
3537 PACKAGES += sshrpc
3538 pkg_sshrpc_name = sshrpc
3539 pkg_sshrpc_description = Erlang SSH RPC module (experimental)
3540 pkg_sshrpc_homepage = https://github.com/jj1bdx/sshrpc
3541 pkg_sshrpc_fetch = git
3542 pkg_sshrpc_repo = https://github.com/jj1bdx/sshrpc
3543 pkg_sshrpc_commit = master
3544
3545 PACKAGES += stable
3546 pkg_stable_name = stable
3547 pkg_stable_description = Library of assorted helpers for Cowboy web server.
3548 pkg_stable_homepage = https://github.com/dvv/stable
3549 pkg_stable_fetch = git
3550 pkg_stable_repo = https://github.com/dvv/stable
3551 pkg_stable_commit = master
3552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
3561 PACKAGES += statebox_riak
3562 pkg_statebox_riak_name = statebox_riak
3563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
3564 pkg_statebox_riak_homepage = https://github.com/mochi/statebox_riak
3565 pkg_statebox_riak_fetch = git
3566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
3567 pkg_statebox_riak_commit = master
3568
3569 PACKAGES += statman
3570 pkg_statman_name = statman
3571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
3572 pkg_statman_homepage = https://github.com/knutin/statman
3573 pkg_statman_fetch = git
3574 pkg_statman_repo = https://github.com/knutin/statman
3575 pkg_statman_commit = master
3576
3577 PACKAGES += statsderl
3578 pkg_statsderl_name = statsderl
3579 pkg_statsderl_description = StatsD client (erlang)
3580 pkg_statsderl_homepage = https://github.com/lpgauth/statsderl
3581 pkg_statsderl_fetch = git
3582 pkg_statsderl_repo = https://github.com/lpgauth/statsderl
3583 pkg_statsderl_commit = master
3584
3585 PACKAGES += stdinout_pool
3586 pkg_stdinout_pool_name = stdinout_pool
3587 pkg_stdinout_pool_description = stdinout_pool : stuff goes in, stuff goes out. there's never any miscommunication.
3588 pkg_stdinout_pool_homepage = https://github.com/mattsta/erlang-stdinout-pool
3589 pkg_stdinout_pool_fetch = git
3590 pkg_stdinout_pool_repo = https://github.com/mattsta/erlang-stdinout-pool
3591 pkg_stdinout_pool_commit = master
3592
3593 PACKAGES += stockdb
3594 pkg_stockdb_name = stockdb
3595 pkg_stockdb_description = Database for storing Stock Exchange quotes in erlang
3596 pkg_stockdb_homepage = https://github.com/maxlapshin/stockdb
3597 pkg_stockdb_fetch = git
3598 pkg_stockdb_repo = https://github.com/maxlapshin/stockdb
3599 pkg_stockdb_commit = master
3600
3601 PACKAGES += stripe
3602 pkg_stripe_name = stripe
3603 pkg_stripe_description = Erlang interface to the stripe.com API
3604 pkg_stripe_homepage = https://github.com/mattsta/stripe-erlang
3605 pkg_stripe_fetch = git
3606 pkg_stripe_repo = https://github.com/mattsta/stripe-erlang
3607 pkg_stripe_commit = v1
3608
3609 PACKAGES += supervisor3
3610 pkg_supervisor3_name = supervisor3
3611 pkg_supervisor3_description = OTP supervisor with additional strategies
3612 pkg_supervisor3_homepage = https://github.com/klarna/supervisor3
3613 pkg_supervisor3_fetch = git
3614 pkg_supervisor3_repo = https://github.com/klarna/supervisor3.git
3615 pkg_supervisor3_commit = master
3616
3617 PACKAGES += surrogate
3618 pkg_surrogate_name = surrogate
3619 pkg_surrogate_description = Proxy server written in erlang. Supports reverse proxy load balancing and forward proxy with http (including CONNECT), socks4, socks5, and transparent proxy modes.
3620 pkg_surrogate_homepage = https://github.com/skruger/Surrogate
3621 pkg_surrogate_fetch = git
3622 pkg_surrogate_repo = https://github.com/skruger/Surrogate
3623 pkg_surrogate_commit = master
3624
3625 PACKAGES += swab
3626 pkg_swab_name = swab
3627 pkg_swab_description = General purpose buffer handling module
3628 pkg_swab_homepage = https://github.com/crownedgrouse/swab
3629 pkg_swab_fetch = git
3630 pkg_swab_repo = https://github.com/crownedgrouse/swab
3631 pkg_swab_commit = master
3632
3633 PACKAGES += swarm
3634 pkg_swarm_name = swarm
3635 pkg_swarm_description = Fast and simple acceptor pool for Erlang
3636 pkg_swarm_homepage = https://github.com/jeremey/swarm
3637 pkg_swarm_fetch = git
3638 pkg_swarm_repo = https://github.com/jeremey/swarm
3639 pkg_swarm_commit = master
3640
3641 PACKAGES += switchboard
3642 pkg_switchboard_name = switchboard
3643 pkg_switchboard_description = A framework for processing email using worker plugins.
3644 pkg_switchboard_homepage = https://github.com/thusfresh/switchboard
3645 pkg_switchboard_fetch = git
3646 pkg_switchboard_repo = https://github.com/thusfresh/switchboard
3647 pkg_switchboard_commit = master
3648
3649 PACKAGES += syn
3650 pkg_syn_name = syn
3651 pkg_syn_description = A global Process Registry and Process Group manager for Erlang.
3652 pkg_syn_homepage = https://github.com/ostinelli/syn
3653 pkg_syn_fetch = git
3654 pkg_syn_repo = https://github.com/ostinelli/syn
3655 pkg_syn_commit = master
3656
3657 PACKAGES += sync
3658 pkg_sync_name = sync
3659 pkg_sync_description = On-the-fly recompiling and reloading in Erlang.
3660 pkg_sync_homepage = https://github.com/rustyio/sync
3661 pkg_sync_fetch = git
3662 pkg_sync_repo = https://github.com/rustyio/sync
3663 pkg_sync_commit = master
3664
3665 PACKAGES += syntaxerl
3666 pkg_syntaxerl_name = syntaxerl
3667 pkg_syntaxerl_description = Syntax checker for Erlang
3668 pkg_syntaxerl_homepage = https://github.com/ten0s/syntaxerl
3669 pkg_syntaxerl_fetch = git
3670 pkg_syntaxerl_repo = https://github.com/ten0s/syntaxerl
3671 pkg_syntaxerl_commit = master
3672
3673 PACKAGES += syslog
3674 pkg_syslog_name = syslog
3675 pkg_syslog_description = Erlang port driver for interacting with syslog via syslog(3)
3676 pkg_syslog_homepage = https://github.com/Vagabond/erlang-syslog
3677 pkg_syslog_fetch = git
3678 pkg_syslog_repo = https://github.com/Vagabond/erlang-syslog
3679 pkg_syslog_commit = master
3680
3681 PACKAGES += taskforce
3682 pkg_taskforce_name = taskforce
3683 pkg_taskforce_description = Erlang worker pools for controlled parallelisation of arbitrary tasks.
3684 pkg_taskforce_homepage = https://github.com/g-andrade/taskforce
3685 pkg_taskforce_fetch = git
3686 pkg_taskforce_repo = https://github.com/g-andrade/taskforce
3687 pkg_taskforce_commit = master
3688
3689 PACKAGES += tddreloader
3690 pkg_tddreloader_name = tddreloader
3691 pkg_tddreloader_description = Shell utility for recompiling, reloading, and testing code as it changes
3692 pkg_tddreloader_homepage = https://github.com/version2beta/tddreloader
3693 pkg_tddreloader_fetch = git
3694 pkg_tddreloader_repo = https://github.com/version2beta/tddreloader
3695 pkg_tddreloader_commit = master
3696
3697 PACKAGES += tempo
3698 pkg_tempo_name = tempo
3699 pkg_tempo_description = NIF-based date and time parsing and formatting for Erlang.
3700 pkg_tempo_homepage = https://github.com/selectel/tempo
3701 pkg_tempo_fetch = git
3702 pkg_tempo_repo = https://github.com/selectel/tempo
3703 pkg_tempo_commit = master
3704
3705 PACKAGES += ticktick
3706 pkg_ticktick_name = ticktick
3707 pkg_ticktick_description = Ticktick is an id generator for message service.
3708 pkg_ticktick_homepage = https://github.com/ericliang/ticktick
3709 pkg_ticktick_fetch = git
3710 pkg_ticktick_repo = https://github.com/ericliang/ticktick
3711 pkg_ticktick_commit = master
3712
3713 PACKAGES += tinymq
3714 pkg_tinymq_name = tinymq
3715 pkg_tinymq_description = TinyMQ - a diminutive, in-memory message queue
3716 pkg_tinymq_homepage = https://github.com/ChicagoBoss/tinymq
3717 pkg_tinymq_fetch = git
3718 pkg_tinymq_repo = https://github.com/ChicagoBoss/tinymq
3719 pkg_tinymq_commit = master
3720
3721 PACKAGES += tinymt
3722 pkg_tinymt_name = tinymt
3723 pkg_tinymt_description = TinyMT pseudo random number generator for Erlang.
3724 pkg_tinymt_homepage = https://github.com/jj1bdx/tinymt-erlang
3725 pkg_tinymt_fetch = git
3726 pkg_tinymt_repo = https://github.com/jj1bdx/tinymt-erlang
3727 pkg_tinymt_commit = master
3728
3729 PACKAGES += tirerl
3730 pkg_tirerl_name = tirerl
3731 pkg_tirerl_description = Erlang interface to Elastic Search
3732 pkg_tirerl_homepage = https://github.com/inaka/tirerl
3733 pkg_tirerl_fetch = git
3734 pkg_tirerl_repo = https://github.com/inaka/tirerl
3735 pkg_tirerl_commit = master
3736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
3745 PACKAGES += traffic_tools
3746 pkg_traffic_tools_name = traffic_tools
3747 pkg_traffic_tools_description = Simple traffic limiting library
3748 pkg_traffic_tools_homepage = https://github.com/systra/traffic_tools
3749 pkg_traffic_tools_fetch = git
3750 pkg_traffic_tools_repo = https://github.com/systra/traffic_tools
3751 pkg_traffic_tools_commit = master
3752
3753 PACKAGES += trails
3754 pkg_trails_name = trails
3755 pkg_trails_description = A couple of improvements over Cowboy Routes
3756 pkg_trails_homepage = http://inaka.github.io/cowboy-trails/
3757 pkg_trails_fetch = git
3758 pkg_trails_repo = https://github.com/inaka/cowboy-trails
3759 pkg_trails_commit = master
3760
3761 PACKAGES += trane
3762 pkg_trane_name = trane
3763 pkg_trane_description = SAX style broken HTML parser in Erlang
3764 pkg_trane_homepage = https://github.com/massemanet/trane
3765 pkg_trane_fetch = git
3766 pkg_trane_repo = https://github.com/massemanet/trane
3767 pkg_trane_commit = master
3768
3769 PACKAGES += transit
3770 pkg_transit_name = transit
3771 pkg_transit_description = transit format for erlang
3772 pkg_transit_homepage = https://github.com/isaiah/transit-erlang
3773 pkg_transit_fetch = git
3774 pkg_transit_repo = https://github.com/isaiah/transit-erlang
3775 pkg_transit_commit = master
3776
3777 PACKAGES += trie
3778 pkg_trie_name = trie
3779 pkg_trie_description = Erlang Trie Implementation
3780 pkg_trie_homepage = https://github.com/okeuday/trie
3781 pkg_trie_fetch = git
3782 pkg_trie_repo = https://github.com/okeuday/trie
3783 pkg_trie_commit = master
3784
3785 PACKAGES += triq
3786 pkg_triq_name = triq
3787 pkg_triq_description = Trifork QuickCheck
3788 pkg_triq_homepage = https://github.com/krestenkrab/triq
3789 pkg_triq_fetch = git
3790 pkg_triq_repo = https://github.com/krestenkrab/triq
3791 pkg_triq_commit = master
3792
3793 PACKAGES += tunctl
3794 pkg_tunctl_name = tunctl
3795 pkg_tunctl_description = Erlang TUN/TAP interface
3796 pkg_tunctl_homepage = https://github.com/msantos/tunctl
3797 pkg_tunctl_fetch = git
3798 pkg_tunctl_repo = https://github.com/msantos/tunctl
3799 pkg_tunctl_commit = master
3800
3801 PACKAGES += twerl
3802 pkg_twerl_name = twerl
3803 pkg_twerl_description = Erlang client for the Twitter Streaming API
3804 pkg_twerl_homepage = https://github.com/lucaspiller/twerl
3805 pkg_twerl_fetch = git
3806 pkg_twerl_repo = https://github.com/lucaspiller/twerl
3807 pkg_twerl_commit = oauth
3808
3809 PACKAGES += twitter_erlang
3810 pkg_twitter_erlang_name = twitter_erlang
3811 pkg_twitter_erlang_description = An Erlang twitter client
3812 pkg_twitter_erlang_homepage = https://github.com/ngerakines/erlang_twitter
3813 pkg_twitter_erlang_fetch = git
3814 pkg_twitter_erlang_repo = https://github.com/ngerakines/erlang_twitter
3815 pkg_twitter_erlang_commit = master
3816
3817 PACKAGES += ucol_nif
3818 pkg_ucol_nif_name = ucol_nif
3819 pkg_ucol_nif_description = ICU based collation Erlang module
3820 pkg_ucol_nif_homepage = https://github.com/refuge/ucol_nif
3821 pkg_ucol_nif_fetch = git
3822 pkg_ucol_nif_repo = https://github.com/refuge/ucol_nif
3823 pkg_ucol_nif_commit = master
3824
3825 PACKAGES += unicorn
3826 pkg_unicorn_name = unicorn
3827 pkg_unicorn_description = Generic configuration server
3828 pkg_unicorn_homepage = https://github.com/shizzard/unicorn
3829 pkg_unicorn_fetch = git
3830 pkg_unicorn_repo = https://github.com/shizzard/unicorn
3831 pkg_unicorn_commit = master
3832
3833 PACKAGES += unsplit
3834 pkg_unsplit_name = unsplit
3835 pkg_unsplit_description = Resolves conflicts in Mnesia after network splits
3836 pkg_unsplit_homepage = https://github.com/uwiger/unsplit
3837 pkg_unsplit_fetch = git
3838 pkg_unsplit_repo = https://github.com/uwiger/unsplit
3839 pkg_unsplit_commit = master
3840
3841 PACKAGES += uuid
3842 pkg_uuid_name = uuid
3843 pkg_uuid_description = Erlang UUID Implementation
3844 pkg_uuid_homepage = https://github.com/okeuday/uuid
3845 pkg_uuid_fetch = git
3846 pkg_uuid_repo = https://github.com/okeuday/uuid
3847 pkg_uuid_commit = master
3848
3849 PACKAGES += ux
3850 pkg_ux_name = ux
3851 pkg_ux_description = Unicode eXtention for Erlang (Strings, Collation)
3852 pkg_ux_homepage = https://github.com/erlang-unicode/ux
3853 pkg_ux_fetch = git
3854 pkg_ux_repo = https://github.com/erlang-unicode/ux
3855 pkg_ux_commit = master
3856
3857 PACKAGES += vert
3858 pkg_vert_name = vert
3859 pkg_vert_description = Erlang binding to libvirt virtualization API
3860 pkg_vert_homepage = https://github.com/msantos/erlang-libvirt
3861 pkg_vert_fetch = git
3862 pkg_vert_repo = https://github.com/msantos/erlang-libvirt
3863 pkg_vert_commit = master
3864
3865 PACKAGES += verx
3866 pkg_verx_name = verx
3867 pkg_verx_description = Erlang implementation of the libvirtd remote protocol
3868 pkg_verx_homepage = https://github.com/msantos/verx
3869 pkg_verx_fetch = git
3870 pkg_verx_repo = https://github.com/msantos/verx
3871 pkg_verx_commit = master
3872
3873 PACKAGES += vmq_acl
3874 pkg_vmq_acl_name = vmq_acl
3875 pkg_vmq_acl_description = Component of VerneMQ: A distributed MQTT message broker
3876 pkg_vmq_acl_homepage = https://verne.mq/
3877 pkg_vmq_acl_fetch = git
3878 pkg_vmq_acl_repo = https://github.com/erlio/vmq_acl
3879 pkg_vmq_acl_commit = master
3880
3881 PACKAGES += vmq_bridge
3882 pkg_vmq_bridge_name = vmq_bridge
3883 pkg_vmq_bridge_description = Component of VerneMQ: A distributed MQTT message broker
3884 pkg_vmq_bridge_homepage = https://verne.mq/
3885 pkg_vmq_bridge_fetch = git
3886 pkg_vmq_bridge_repo = https://github.com/erlio/vmq_bridge
3887 pkg_vmq_bridge_commit = master
3888
3889 PACKAGES += vmq_graphite
3890 pkg_vmq_graphite_name = vmq_graphite
3891 pkg_vmq_graphite_description = Component of VerneMQ: A distributed MQTT message broker
3892 pkg_vmq_graphite_homepage = https://verne.mq/
3893 pkg_vmq_graphite_fetch = git
3894 pkg_vmq_graphite_repo = https://github.com/erlio/vmq_graphite
3895 pkg_vmq_graphite_commit = master
3896
3897 PACKAGES += vmq_passwd
3898 pkg_vmq_passwd_name = vmq_passwd
3899 pkg_vmq_passwd_description = Component of VerneMQ: A distributed MQTT message broker
3900 pkg_vmq_passwd_homepage = https://verne.mq/
3901 pkg_vmq_passwd_fetch = git
3902 pkg_vmq_passwd_repo = https://github.com/erlio/vmq_passwd
3903 pkg_vmq_passwd_commit = master
3904
3905 PACKAGES += vmq_server
3906 pkg_vmq_server_name = vmq_server
3907 pkg_vmq_server_description = Component of VerneMQ: A distributed MQTT message broker
3908 pkg_vmq_server_homepage = https://verne.mq/
3909 pkg_vmq_server_fetch = git
3910 pkg_vmq_server_repo = https://github.com/erlio/vmq_server
3911 pkg_vmq_server_commit = master
3912
3913 PACKAGES += vmq_snmp
3914 pkg_vmq_snmp_name = vmq_snmp
3915 pkg_vmq_snmp_description = Component of VerneMQ: A distributed MQTT message broker
3916 pkg_vmq_snmp_homepage = https://verne.mq/
3917 pkg_vmq_snmp_fetch = git
3918 pkg_vmq_snmp_repo = https://github.com/erlio/vmq_snmp
3919 pkg_vmq_snmp_commit = master
3920
3921 PACKAGES += vmq_systree
3922 pkg_vmq_systree_name = vmq_systree
3923 pkg_vmq_systree_description = Component of VerneMQ: A distributed MQTT message broker
3924 pkg_vmq_systree_homepage = https://verne.mq/
3925 pkg_vmq_systree_fetch = git
3926 pkg_vmq_systree_repo = https://github.com/erlio/vmq_systree
3927 pkg_vmq_systree_commit = master
3928
3929 PACKAGES += vmstats
3930 pkg_vmstats_name = vmstats
3931 pkg_vmstats_description = tiny Erlang app that works in conjunction with statsderl in order to generate information on the Erlang VM for graphite logs.
3932 pkg_vmstats_homepage = https://github.com/ferd/vmstats
3933 pkg_vmstats_fetch = git
3934 pkg_vmstats_repo = https://github.com/ferd/vmstats
3935 pkg_vmstats_commit = master
3936
3937 PACKAGES += walrus
3938 pkg_walrus_name = walrus
3939 pkg_walrus_description = Walrus - Mustache-like Templating
3940 pkg_walrus_homepage = https://github.com/devinus/walrus
3941 pkg_walrus_fetch = git
3942 pkg_walrus_repo = https://github.com/devinus/walrus
3943 pkg_walrus_commit = master
3944
3945 PACKAGES += webmachine
3946 pkg_webmachine_name = webmachine
3947 pkg_webmachine_description = A REST-based system for building web applications.
3948 pkg_webmachine_homepage = https://github.com/basho/webmachine
3949 pkg_webmachine_fetch = git
3950 pkg_webmachine_repo = https://github.com/basho/webmachine
3951 pkg_webmachine_commit = master
3952
3953 PACKAGES += websocket_client
3954 pkg_websocket_client_name = websocket_client
3955 pkg_websocket_client_description = Erlang websocket client (ws and wss supported)
3956 pkg_websocket_client_homepage = https://github.com/jeremyong/websocket_client
3957 pkg_websocket_client_fetch = git
3958 pkg_websocket_client_repo = https://github.com/jeremyong/websocket_client
3959 pkg_websocket_client_commit = master
3960
3961 PACKAGES += worker_pool
3962 pkg_worker_pool_name = worker_pool
3963 pkg_worker_pool_description = a simple erlang worker pool
3964 pkg_worker_pool_homepage = https://github.com/inaka/worker_pool
3965 pkg_worker_pool_fetch = git
3966 pkg_worker_pool_repo = https://github.com/inaka/worker_pool
3967 pkg_worker_pool_commit = master
3968
3969 PACKAGES += wrangler
3970 pkg_wrangler_name = wrangler
3971 pkg_wrangler_description = Import of the Wrangler svn repository.
3972 pkg_wrangler_homepage = http://www.cs.kent.ac.uk/projects/wrangler/Home.html
3973 pkg_wrangler_fetch = git
3974 pkg_wrangler_repo = https://github.com/RefactoringTools/wrangler
3975 pkg_wrangler_commit = master
3976
3977 PACKAGES += wsock
3978 pkg_wsock_name = wsock
3979 pkg_wsock_description = Erlang library to build WebSocket clients and servers
3980 pkg_wsock_homepage = https://github.com/madtrick/wsock
3981 pkg_wsock_fetch = git
3982 pkg_wsock_repo = https://github.com/madtrick/wsock
3983 pkg_wsock_commit = master
3984
3985 PACKAGES += xhttpc
3986 pkg_xhttpc_name = xhttpc
3987 pkg_xhttpc_description = Extensible HTTP Client for Erlang
3988 pkg_xhttpc_homepage = https://github.com/seriyps/xhttpc
3989 pkg_xhttpc_fetch = git
3990 pkg_xhttpc_repo = https://github.com/seriyps/xhttpc
3991 pkg_xhttpc_commit = master
3992
3993 PACKAGES += xref_runner
3994 pkg_xref_runner_name = xref_runner
3995 pkg_xref_runner_description = Erlang Xref Runner (inspired in rebar xref)
3996 pkg_xref_runner_homepage = https://github.com/inaka/xref_runner
3997 pkg_xref_runner_fetch = git
3998 pkg_xref_runner_repo = https://github.com/inaka/xref_runner
3999 pkg_xref_runner_commit = master
4000
4001 PACKAGES += yamerl
4002 pkg_yamerl_name = yamerl
4003 pkg_yamerl_description = YAML 1.2 parser in pure Erlang
4004 pkg_yamerl_homepage = https://github.com/yakaz/yamerl
4005 pkg_yamerl_fetch = git
4006 pkg_yamerl_repo = https://github.com/yakaz/yamerl
4007 pkg_yamerl_commit = master
4008
4009 PACKAGES += yamler
4010 pkg_yamler_name = yamler
4011 pkg_yamler_description = libyaml-based yaml loader for Erlang
4012 pkg_yamler_homepage = https://github.com/goertzenator/yamler
4013 pkg_yamler_fetch = git
4014 pkg_yamler_repo = https://github.com/goertzenator/yamler
4015 pkg_yamler_commit = master
4016
4017 PACKAGES += yaws
4018 pkg_yaws_name = yaws
4019 pkg_yaws_description = Yaws webserver
4020 pkg_yaws_homepage = http://yaws.hyber.org
4021 pkg_yaws_fetch = git
4022 pkg_yaws_repo = https://github.com/klacke/yaws
4023 pkg_yaws_commit = master
4024
4025 PACKAGES += zab_engine
4026 pkg_zab_engine_name = zab_engine
4027 pkg_zab_engine_description = zab propotocol implement by erlang
4028 pkg_zab_engine_homepage = https://github.com/xinmingyao/zab_engine
4029 pkg_zab_engine_fetch = git
4030 pkg_zab_engine_repo = https://github.com/xinmingyao/zab_engine
4031 pkg_zab_engine_commit = master
4032
4033 PACKAGES += zabbix_sender
4034 pkg_zabbix_sender_name = zabbix_sender
4035 pkg_zabbix_sender_description = Zabbix trapper for sending data to Zabbix in pure Erlang
4036 pkg_zabbix_sender_homepage = https://github.com/stalkermn/zabbix_sender
4037 pkg_zabbix_sender_fetch = git
4038 pkg_zabbix_sender_repo = https://github.com/stalkermn/zabbix_sender.git
4039 pkg_zabbix_sender_commit = master
4040
4041 PACKAGES += zeta
4042 pkg_zeta_name = zeta
4043 pkg_zeta_description = HTTP access log parser in Erlang
4044 pkg_zeta_homepage = https://github.com/s1n4/zeta
4045 pkg_zeta_fetch = git
4046 pkg_zeta_repo = https://github.com/s1n4/zeta
4047 pkg_zeta_commit = master
4048
4049 PACKAGES += zippers
4050 pkg_zippers_name = zippers
4051 pkg_zippers_description = A library for functional zipper data structures in Erlang. Read more on zippers
4052 pkg_zippers_homepage = https://github.com/ferd/zippers
4053 pkg_zippers_fetch = git
4054 pkg_zippers_repo = https://github.com/ferd/zippers
4055 pkg_zippers_commit = master
4056
4057 PACKAGES += zlists
4058 pkg_zlists_name = zlists
4059 pkg_zlists_description = Erlang lazy lists library.
4060 pkg_zlists_homepage = https://github.com/vjache/erlang-zlists
4061 pkg_zlists_fetch = git
4062 pkg_zlists_repo = https://github.com/vjache/erlang-zlists
4063 pkg_zlists_commit = master
4064
4065 PACKAGES += zraft_lib
4066 pkg_zraft_lib_name = zraft_lib
4067 pkg_zraft_lib_description = Erlang raft consensus protocol implementation
4068 pkg_zraft_lib_homepage = https://github.com/dreyk/zraft_lib
4069 pkg_zraft_lib_fetch = git
4070 pkg_zraft_lib_repo = https://github.com/dreyk/zraft_lib
4071 pkg_zraft_lib_commit = master
4072
4073 PACKAGES += zucchini
4074 pkg_zucchini_name = zucchini
4075 pkg_zucchini_description = An Erlang INI parser
4076 pkg_zucchini_homepage = https://github.com/devinus/zucchini
4077 pkg_zucchini_fetch = git
4078 pkg_zucchini_repo = https://github.com/devinus/zucchini
4079 pkg_zucchini_commit = master
4080
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
4082 # This file is part of erlang.mk and subject to the terms of the ISC License.
4083
4084 .PHONY: search
4085
4086 define pkg_print
4087 $(verbose) printf "%s\n" \
4088 $(if $(call core_eq,$(1),$(pkg_$(1)_name)),,"Pkg name: $(1)") \
4089 "App name: $(pkg_$(1)_name)" \
4090 "Description: $(pkg_$(1)_description)" \
4091 "Home page: $(pkg_$(1)_homepage)" \
4092 "Fetch with: $(pkg_$(1)_fetch)" \
4093 "Repository: $(pkg_$(1)_repo)" \
4094 "Commit: $(pkg_$(1)_commit)" \
4095 ""
4096
4097 endef
4098
4099 search:
4100 ifdef q
4101 $(foreach p,$(PACKAGES), \
4102 $(if $(findstring $(call core_lc,$(q)),$(call core_lc,$(pkg_$(p)_name) $(pkg_$(p)_description))), \
4103 $(call pkg_print,$(p))))
4104 else
4105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
4106 endif
4107
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
4109 # This file is part of erlang.mk and subject to the terms of the ISC License.
4110
4111 .PHONY: distclean-deps clean-tmp-deps.log
4112
4113 # Configuration.
4114
4115 ifdef OTP_DEPS
4116 $(warning The variable OTP_DEPS is deprecated in favor of LOCAL_DEPS.)
4117 endif
4118
4119 IGNORE_DEPS ?=
4120 export IGNORE_DEPS
4121
4122 APPS_DIR ?= $(CURDIR)/apps
4123 export APPS_DIR
4124
4125 DEPS_DIR ?= $(CURDIR)/deps
4126 export DEPS_DIR
4127
4128 REBAR_DEPS_DIR = $(DEPS_DIR)
4129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
4150
4151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
4152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
4153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
4154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
4155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
4157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
4158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
4159
4160 ifeq ($(filter $(APPS_DIR) $(DEPS_DIR),$(subst :, ,$(ERL_LIBS))),)
4161 ifeq ($(ERL_LIBS),)
4162 ERL_LIBS = $(APPS_DIR):$(DEPS_DIR)
4163 else
4164 ERL_LIBS := $(ERL_LIBS):$(APPS_DIR):$(DEPS_DIR)
4165 endif
4166 endif
4167 export ERL_LIBS
4168
4169 export NO_AUTOPATCH
4170
4171 # Verbosity.
4172
4173 dep_verbose_0 = @echo " DEP " $(1);
4174 dep_verbose_2 = set -x;
4175 dep_verbose = $(dep_verbose_$(V))
4176
4177 # Core targets.
4178
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
4180 ifeq ($(IS_APP)$(IS_DEP),)
4181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
4182 endif
4183 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4184 # Create ebin directory for all apps to make sure Erlang recognizes them
4185 # as proper OTP applications when using -include_lib. This is a temporary
4186 # fix, a proper fix would be to compile apps/* in the right order.
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
4190 done
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
4196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
4197 :; \
4198 else \
4199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4200 $(MAKE) -C $$dep IS_APP=1; \
4201 fi \
4202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4207 endif
4208
4209 ifneq ($(SKIP_DEPS),)
4210 deps::
4211 else
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
4213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
4215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
4216 :; \
4217 else \
4218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
4219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4220 $(MAKE) -C $$dep IS_DEP=1; \
4221 else \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
4223 exit 2; \
4224 fi \
4225 fi \
4226 done
4227 endif
4228
4229 # Deps related targets.
4230
4231 # @todo rename GNUmakefile and makefile into Makefile first, if they exist
4232 # While Makefile file could be GNUmakefile or makefile,
4233 # in practice only Makefile is needed so far.
4234 define dep_autopatch
4235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
4237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4238 $(call dep_autopatch_erlang_mk,$(1)); \
4239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4243 $(call dep_autopatch2,$(1)); \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4245 $(call dep_autopatch2,$(1)); \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
4247 $(call dep_autopatch2,$(1)); \
4248 fi \
4249 else \
4250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
4251 $(call dep_autopatch_noop,$(1)); \
4252 else \
4253 $(call dep_autopatch2,$(1)); \
4254 fi \
4255 fi
4256 endef
4257
4258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
4262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
4263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
4264 fi; \
4265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4267 $(call dep_autopatch_fetch_rebar); \
4268 $(call dep_autopatch_rebar,$(1)); \
4269 else \
4270 $(call dep_autopatch_gen,$(1)); \
4271 fi
4272 endef
4273
4274 define dep_autopatch_noop
4275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
4276 endef
4277
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
4280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
4281 define dep_autopatch_erlang_mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
4287 endef
4288 else
4289 define dep_autopatch_erlang_mk
4290 :
4291 endef
4292 endif
4293
4294 define dep_autopatch_gen
4295 printf "%s\n" \
4296 "ERLC_OPTS = +debug_info" \
4297 "include ../../erlang.mk" > $(DEPS_DIR)/$(1)/Makefile
4298 endef
4299
4300 define dep_autopatch_fetch_rebar
4301 mkdir -p $(ERLANG_MK_TMP); \
4302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
4303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
4304 cd $(ERLANG_MK_TMP)/rebar; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
4306 $(MAKE); \
4307 cd -; \
4308 fi
4309 endef
4310
4311 define dep_autopatch_rebar
4312 if [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4313 mv $(DEPS_DIR)/$(1)/Makefile $(DEPS_DIR)/$(1)/Makefile.orig.mk; \
4314 fi; \
4315 $(call erlang,$(call dep_autopatch_rebar.erl,$(1))); \
4316 rm -f $(DEPS_DIR)/$(1)/ebin/$(1).app
4317 endef
4318
4319 define dep_autopatch_rebar.erl
4320 application:load(rebar),
4321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
4323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
4324 {ok, Conf0} -> Conf0;
4325 _ -> []
4326 end,
4327 {Conf, OsEnv} = fun() ->
4328 case filelib:is_file("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config.script)") of
4329 false -> {Conf1, []};
4330 true ->
4331 Bindings0 = erl_eval:new_bindings(),
4332 Bindings1 = erl_eval:add_binding('CONFIG', Conf1, Bindings0),
4333 Bindings = erl_eval:add_binding('SCRIPT', "$(call core_native_path,$(DEPS_DIR)/$1/rebar.config.script)", Bindings1),
4334 Before = os:getenv(),
4335 {ok, Conf2} = file:script("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config.script)", Bindings),
4336 {Conf2, lists:foldl(fun(E, Acc) -> lists:delete(E, Acc) end, os:getenv(), Before)}
4337 end
4338 end(),
4339 Write = fun (Text) ->
4340 file:write_file("$(call core_native_path,$(DEPS_DIR)/$1/Makefile)", Text, [append])
4341 end,
4342 Escape = fun (Text) ->
4343 re:replace(Text, "\\\\$$", "\$$$$", [global, {return, list}])
4344 end,
4345 Write("IGNORE_DEPS += edown eper eunit_formatters meck node_package "
4346 "rebar_lock_deps_plugin rebar_vsn_plugin reltool_util\n"),
4347 Write("C_SRC_DIR = /path/do/not/exist\n"),
4348 Write("C_SRC_TYPE = rebar\n"),
4349 Write("DRV_CFLAGS = -fPIC\nexport DRV_CFLAGS\n"),
4350 Write(["ERLANG_ARCH = ", rebar_utils:wordsize(), "\nexport ERLANG_ARCH\n"]),
4351 fun() ->
4352 Write("ERLC_OPTS = +debug_info\nexport ERLC_OPTS\n"),
4353 case lists:keyfind(erl_opts, 1, Conf) of
4354 false -> ok;
4355 {_, ErlOpts} ->
4356 lists:foreach(fun
4357 ({d, D}) ->
4358 Write("ERLC_OPTS += -D" ++ atom_to_list(D) ++ "=1\n");
4359 ({i, I}) ->
4360 Write(["ERLC_OPTS += -I ", I, "\n"]);
4361 ({platform_define, Regex, D}) ->
4362 case rebar_utils:is_arch(Regex) of
4363 true -> Write("ERLC_OPTS += -D" ++ atom_to_list(D) ++ "=1\n");
4364 false -> ok
4365 end;
4366 ({parse_transform, PT}) ->
4367 Write("ERLC_OPTS += +'{parse_transform, " ++ atom_to_list(PT) ++ "}'\n");
4368 (_) -> ok
4369 end, ErlOpts)
4370 end,
4371 Write("\n")
4372 end(),
4373 fun() ->
4374 File = case lists:keyfind(deps, 1, Conf) of
4375 false -> [];
4376 {_, Deps} ->
4377 [begin case case Dep of
4378 {N, S} when is_atom(N), is_list(S) -> {N, {hex, S}};
4379 {N, S} when is_tuple(S) -> {N, S};
4380 {N, _, S} -> {N, S};
4381 {N, _, S, _} -> {N, S};
4382 _ -> false
4383 end of
4384 false -> ok;
4385 {Name, Source} ->
4386 {Method, Repo, Commit} = case Source of
4387 {hex, V} -> {hex, V, undefined};
4388 {git, R} -> {git, R, master};
4389 {M, R, {branch, C}} -> {M, R, C};
4390 {M, R, {ref, C}} -> {M, R, C};
4391 {M, R, {tag, C}} -> {M, R, C};
4392 {M, R, C} -> {M, R, C}
4393 end,
4394 Write(io_lib:format("DEPS += ~s\ndep_~s = ~s ~s ~s~n", [Name, Name, Method, Repo, Commit]))
4395 end end || Dep <- Deps]
4396 end
4397 end(),
4398 fun() ->
4399 case lists:keyfind(erl_first_files, 1, Conf) of
4400 false -> ok;
4401 {_, Files} ->
4402 Names = [[" ", case lists:reverse(F) of
4403 "lre." ++ Elif -> lists:reverse(Elif);
4404 Elif -> lists:reverse(Elif)
4405 end] || "src/" ++ F <- Files],
4406 Write(io_lib:format("COMPILE_FIRST +=~s\n", [Names]))
4407 end
4408 end(),
4409 Write("\n\nrebar_dep: preprocess pre-deps deps pre-app app\n"),
4410 Write("\npreprocess::\n"),
4411 Write("\npre-deps::\n"),
4412 Write("\npre-app::\n"),
4413 PatchHook = fun(Cmd) ->
4414 case Cmd of
4415 "make -C" ++ Cmd1 -> "$$\(MAKE) -C" ++ Escape(Cmd1);
4416 "gmake -C" ++ Cmd1 -> "$$\(MAKE) -C" ++ Escape(Cmd1);
4417 "make " ++ Cmd1 -> "$$\(MAKE) -f Makefile.orig.mk " ++ Escape(Cmd1);
4418 "gmake " ++ Cmd1 -> "$$\(MAKE) -f Makefile.orig.mk " ++ Escape(Cmd1);
4419 _ -> Escape(Cmd)
4420 end
4421 end,
4422 fun() ->
4423 case lists:keyfind(pre_hooks, 1, Conf) of
4424 false -> ok;
4425 {_, Hooks} ->
4426 [case H of
4427 {'get-deps', Cmd} ->
4428 Write("\npre-deps::\n\t" ++ PatchHook(Cmd) ++ "\n");
4429 {compile, Cmd} ->
4430 Write("\npre-app::\n\tCC=$$\(CC) " ++ PatchHook(Cmd) ++ "\n");
4431 {Regex, compile, Cmd} ->
4432 case rebar_utils:is_arch(Regex) of
4433 true -> Write("\npre-app::\n\tCC=$$\(CC) " ++ PatchHook(Cmd) ++ "\n");
4434 false -> ok
4435 end;
4436 _ -> ok
4437 end || H <- Hooks]
4438 end
4439 end(),
4440 ShellToMk = fun(V) ->
4441 re:replace(re:replace(V, "(\\\\$$)(\\\\w*)", "\\\\1(\\\\2)", [global]),
4442 "-Werror\\\\b", "", [{return, list}, global])
4443 end,
4444 PortSpecs = fun() ->
4445 case lists:keyfind(port_specs, 1, Conf) of
4446 false ->
4447 case filelib:is_dir("$(call core_native_path,$(DEPS_DIR)/$1/c_src)") of
4448 false -> [];
4449 true ->
4450 [{"priv/" ++ proplists:get_value(so_name, Conf, "$(1)_drv.so"),
4451 proplists:get_value(port_sources, Conf, ["c_src/*.c"]), []}]
4452 end;
4453 {_, Specs} ->
4454 lists:flatten([case S of
4455 {Output, Input} -> {ShellToMk(Output), Input, []};
4456 {Regex, Output, Input} ->
4457 case rebar_utils:is_arch(Regex) of
4458 true -> {ShellToMk(Output), Input, []};
4459 false -> []
4460 end;
4461 {Regex, Output, Input, [{env, Env}]} ->
4462 case rebar_utils:is_arch(Regex) of
4463 true -> {ShellToMk(Output), Input, Env};
4464 false -> []
4465 end
4466 end || S <- Specs])
4467 end
4468 end(),
4469 PortSpecWrite = fun (Text) ->
4470 file:write_file("$(call core_native_path,$(DEPS_DIR)/$1/c_src/Makefile.erlang.mk)", Text, [append])
4471 end,
4472 case PortSpecs of
4473 [] -> ok;
4474 _ ->
4475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
4479 [code:lib_dir(erl_interface, lib)])),
4480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
4481 FilterEnv = fun(Env) ->
4482 lists:flatten([case E of
4483 {_, _} -> E;
4484 {Regex, K, V} ->
4485 case rebar_utils:is_arch(Regex) of
4486 true -> {K, V};
4487 false -> []
4488 end
4489 end || E <- Env])
4490 end,
4491 MergeEnv = fun(Env) ->
4492 lists:foldl(fun ({K, V}, Acc) ->
4493 case lists:keyfind(K, 1, Acc) of
4494 false -> [{K, rebar_utils:expand_env_variable(V, K, "")}|Acc];
4495 {_, V0} -> [{K, rebar_utils:expand_env_variable(V, K, V0)}|Acc]
4496 end
4497 end, [], Env)
4498 end,
4499 PortEnv = case lists:keyfind(port_env, 1, Conf) of
4500 false -> [];
4501 {_, PortEnv0} -> FilterEnv(PortEnv0)
4502 end,
4503 PortSpec = fun ({Output, Input0, Env}) ->
4504 filelib:ensure_dir("$(call core_native_path,$(DEPS_DIR)/$1/)" ++ Output),
4505 Input = [[" ", I] || I <- Input0],
4506 PortSpecWrite([
4507 [["\n", K, " = ", ShellToMk(V)] || {K, V} <- lists:reverse(MergeEnv(PortEnv))],
4508 case $(PLATFORM) of
4509 darwin -> "\n\nLDFLAGS += -flat_namespace -undefined suppress";
4510 _ -> ""
4511 end,
4512 "\n\nall:: ", Output, "\n\n",
4513 "%.o: %.c\n\t$$\(CC) -c -o $$\@ $$\< $$\(CFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
4519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
4520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
4521 case {filename:extension(Output), $(PLATFORM)} of
4522 {[], _} -> "\n";
4523 {_, darwin} -> "\n";
4524 _ -> " -shared\n"
4525 end])
4526 end,
4527 [PortSpec(S) || S <- PortSpecs]
4528 end,
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
4530 RunPlugin = fun(Plugin, Step) ->
4531 case erlang:function_exported(Plugin, Step, 2) of
4532 false -> ok;
4533 true ->
4534 c:cd("$(call core_native_path,$(DEPS_DIR)/$1/)"),
4535 Ret = Plugin:Step({config, "", Conf, dict:new(), dict:new(), dict:new(),
4536 dict:store(base_dir, "", dict:new())}, undefined),
4537 io:format("rebar plugin ~p step ~p ret ~p~n", [Plugin, Step, Ret])
4538 end
4539 end,
4540 fun() ->
4541 case lists:keyfind(plugins, 1, Conf) of
4542 false -> ok;
4543 {_, Plugins} ->
4544 [begin
4545 case lists:keyfind(deps, 1, Conf) of
4546 false -> ok;
4547 {_, Deps} ->
4548 case lists:keyfind(P, 1, Deps) of
4549 false -> ok;
4550 _ ->
4551 Path = "$(call core_native_path,$(DEPS_DIR)/)" ++ atom_to_list(P),
4552 io:format("~s", [os:cmd("$(MAKE) -C $(call core_native_path,$(DEPS_DIR)/$1) " ++ Path)]),
4553 io:format("~s", [os:cmd("$(MAKE) -C " ++ Path ++ " IS_DEP=1")]),
4554 code:add_patha(Path ++ "/ebin")
4555 end
4556 end
4557 end || P <- Plugins],
4558 [case code:load_file(P) of
4559 {module, P} -> ok;
4560 _ ->
4561 case lists:keyfind(plugin_dir, 1, Conf) of
4562 false -> ok;
4563 {_, PluginsDir} ->
4564 ErlFile = "$(call core_native_path,$(DEPS_DIR)/$1/)" ++ PluginsDir ++ "/" ++ atom_to_list(P) ++ ".erl",
4565 {ok, P, Bin} = compile:file(ErlFile, [binary]),
4566 {module, P} = code:load_binary(P, ErlFile, Bin)
4567 end
4568 end || P <- Plugins],
4569 [RunPlugin(P, preprocess) || P <- Plugins],
4570 [RunPlugin(P, pre_compile) || P <- Plugins],
4571 [RunPlugin(P, compile) || P <- Plugins]
4572 end
4573 end(),
4574 halt()
4575 endef
4576
4577 define dep_autopatch_appsrc_script.erl
4578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
4579 AppSrcScript = AppSrc ++ ".script",
4580 Bindings = erl_eval:new_bindings(),
4581 {ok, Conf} = file:script(AppSrcScript, Bindings),
4582 ok = file:write_file(AppSrc, io_lib:format("~p.~n", [Conf])),
4583 halt()
4584 endef
4585
4586 define dep_autopatch_appsrc.erl
4587 AppSrcOut = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
4588 AppSrcIn = case filelib:is_regular(AppSrcOut) of false -> "$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"; true -> AppSrcOut end,
4589 case filelib:is_regular(AppSrcIn) of
4590 false -> ok;
4591 true ->
4592 {ok, [{application, $(1), L0}]} = file:consult(AppSrcIn),
4593 L1 = lists:keystore(modules, 1, L0, {modules, []}),
4594 L2 = case lists:keyfind(vsn, 1, L1) of {_, git} -> lists:keyreplace(vsn, 1, L1, {vsn, "git"}); _ -> L1 end,
4595 L3 = case lists:keyfind(registered, 1, L2) of false -> [{registered, []}|L2]; _ -> L2 end,
4596 ok = file:write_file(AppSrcOut, io_lib:format("~p.~n", [{application, $(1), L3}])),
4597 case AppSrcOut of AppSrcIn -> ok; _ -> ok = file:delete(AppSrcIn) end
4598 end,
4599 halt()
4600 endef
4601
4602 define dep_fetch_git
4603 git clone -q -n -- $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1)); \
4604 cd $(DEPS_DIR)/$(call dep_name,$(1)) && git checkout -q $(call dep_commit,$(1));
4605 endef
4606
4607 define dep_fetch_git-submodule
4608 git submodule update --init -- $(DEPS_DIR)/$1;
4609 endef
4610
4611 define dep_fetch_hg
4612 hg clone -q -U $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1)); \
4613 cd $(DEPS_DIR)/$(call dep_name,$(1)) && hg update -q $(call dep_commit,$(1));
4614 endef
4615
4616 define dep_fetch_svn
4617 svn checkout -q $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
4618 endef
4619
4620 define dep_fetch_cp
4621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
4622 endef
4623
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
4626 endef
4627
4628 # Hex only has a package version. No need to look in the Erlang.mk packages.
4629 define dep_fetch_hex
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
4634 endef
4635
4636 define dep_fetch_fail
4637 echo "Error: Unknown or invalid dependency: $(1)." >&2; \
4638 exit 78;
4639 endef
4640
4641 # Kept for compatibility purposes with older Erlang.mk configuration.
4642 define dep_fetch_legacy
4643 $(warning WARNING: '$(1)' dependency configuration uses deprecated format.) \
4644 git clone -q -n -- $(word 1,$(dep_$(1))) $(DEPS_DIR)/$(1); \
4645 cd $(DEPS_DIR)/$(1) && git checkout -q $(if $(word 2,$(dep_$(1))),$(word 2,$(dep_$(1))),master);
4646 endef
4647
4648 define dep_fetch
4649 $(if $(dep_$(1)), \
4650 $(if $(dep_fetch_$(word 1,$(dep_$(1)))), \
4651 $(word 1,$(dep_$(1))), \
4652 $(if $(IS_DEP),legacy,fail)), \
4653 $(if $(filter $(1),$(PACKAGES)), \
4654 $(pkg_$(1)_fetch), \
4655 fail))
4656 endef
4657
4658 define dep_target
4659 $(DEPS_DIR)/$(call dep_name,$1):
4660 $(eval DEP_NAME := $(call dep_name,$1))
4661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
4662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
4664 exit 17; \
4665 fi
4666 $(verbose) mkdir -p $(DEPS_DIR)
4667 $(dep_verbose) $(call dep_fetch_$(strip $(call dep_fetch,$(1))),$(1))
4668 $(verbose) if [ -f $(DEPS_DIR)/$(1)/configure.ac -o -f $(DEPS_DIR)/$(1)/configure.in ] \
4669 && [ ! -f $(DEPS_DIR)/$(1)/configure ]; then \
4670 echo " AUTO " $(1); \
4671 cd $(DEPS_DIR)/$(1) && autoreconf -Wall -vif -I m4; \
4672 fi
4673 - $(verbose) if [ -f $(DEPS_DIR)/$(DEP_NAME)/configure ]; then \
4674 echo " CONF " $(DEP_STR); \
4675 cd $(DEPS_DIR)/$(DEP_NAME) && ./configure; \
4676 fi
4677 ifeq ($(filter $(1),$(NO_AUTOPATCH)),)
4678 $(verbose) if [ "$(1)" = "amqp_client" -a "$(RABBITMQ_CLIENT_PATCH)" ]; then \
4679 if [ ! -d $(DEPS_DIR)/rabbitmq-codegen ]; then \
4680 echo " PATCH Downloading rabbitmq-codegen"; \
4681 git clone https://github.com/rabbitmq/rabbitmq-codegen.git $(DEPS_DIR)/rabbitmq-codegen; \
4682 fi; \
4683 if [ ! -d $(DEPS_DIR)/rabbitmq-server ]; then \
4684 echo " PATCH Downloading rabbitmq-server"; \
4685 git clone https://github.com/rabbitmq/rabbitmq-server.git $(DEPS_DIR)/rabbitmq-server; \
4686 fi; \
4687 ln -s $(DEPS_DIR)/amqp_client/deps/rabbit_common-0.0.0 $(DEPS_DIR)/rabbit_common; \
4688 elif [ "$(1)" = "rabbit" -a "$(RABBITMQ_SERVER_PATCH)" ]; then \
4689 if [ ! -d $(DEPS_DIR)/rabbitmq-codegen ]; then \
4690 echo " PATCH Downloading rabbitmq-codegen"; \
4691 git clone https://github.com/rabbitmq/rabbitmq-codegen.git $(DEPS_DIR)/rabbitmq-codegen; \
4692 fi \
4693 else \
4694 $$(call dep_autopatch,$(DEP_NAME)) \
4695 fi
4696 endif
4697 endef
4698
4699 $(foreach dep,$(BUILD_DEPS) $(DEPS),$(eval $(call dep_target,$(dep))))
4700
4701 ifndef IS_APP
4702 clean:: clean-apps
4703
4704 clean-apps:
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
4707 done
4708
4709 distclean:: distclean-apps
4710
4711 distclean-apps:
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
4714 done
4715 endif
4716
4717 ifndef SKIP_DEPS
4718 distclean:: distclean-deps
4719
4720 distclean-deps:
4721 $(gen_verbose) rm -rf $(DEPS_DIR)
4722 endif
4723
4724 # Forward-declare variables used in core/deps-tools.mk. This is required
4725 # in case plugins use them.
4726
4727 ERLANG_MK_RECURSIVE_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-deps-list.log
4728 ERLANG_MK_RECURSIVE_DOC_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-doc-deps-list.log
4729 ERLANG_MK_RECURSIVE_REL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-rel-deps-list.log
4730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
4731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
4732
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
4734 # This file is part of erlang.mk and subject to the terms of the ISC License.
4735
4736 # Verbosity.
4737
4738 proto_verbose_0 = @echo " PROTO " $(filter %.proto,$(?F));
4739 proto_verbose = $(proto_verbose_$(V))
4740
4741 # Core targets.
4742
4743 define compile_proto
4744 $(verbose) mkdir -p ebin/ include/
4745 $(proto_verbose) $(call erlang,$(call compile_proto.erl,$(1)))
4746 $(proto_verbose) erlc +debug_info -o ebin/ ebin/*.erl
4747 $(verbose) rm ebin/*.erl
4748 endef
4749
4750 define compile_proto.erl
4751 [begin
4752 protobuffs_compile:generate_source(F,
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
4755 end || F <- string:tokens("$(1)", " ")],
4756 halt().
4757 endef
4758
4759 ifneq ($(wildcard src/),)
4760 ebin/$(PROJECT).app:: $(sort $(call core_find,src/,*.proto))
4761 $(if $(strip $?),$(call compile_proto,$?))
4762 endif
4763
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
4765 # This file is part of erlang.mk and subject to the terms of the ISC License.
4766
4767 .PHONY: clean-app
4768
4769 # Configuration.
4770
4771 ERLC_OPTS ?= -Werror +debug_info +warn_export_vars +warn_shadow_vars \
4772 +warn_obsolete_guard # +bin_opt_info +warn_export_all +warn_missing_spec
4773 COMPILE_FIRST ?=
4774 COMPILE_FIRST_PATHS = $(addprefix src/,$(addsuffix .erl,$(COMPILE_FIRST)))
4775 ERLC_EXCLUDE ?=
4776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
4777
4778 ERLC_ASN1_OPTS ?=
4779
4780 ERLC_MIB_OPTS ?=
4781 COMPILE_MIB_FIRST ?=
4782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
4783
4784 # Verbosity.
4785
4786 app_verbose_0 = @echo " APP " $(PROJECT);
4787 app_verbose_2 = set -x;
4788 app_verbose = $(app_verbose_$(V))
4789
4790 appsrc_verbose_0 = @echo " APP " $(PROJECT).app.src;
4791 appsrc_verbose_2 = set -x;
4792 appsrc_verbose = $(appsrc_verbose_$(V))
4793
4794 makedep_verbose_0 = @echo " DEPEND" $(PROJECT).d;
4795 makedep_verbose_2 = set -x;
4796 makedep_verbose = $(makedep_verbose_$(V))
4797
4798 erlc_verbose_0 = @echo " ERLC " $(filter-out $(patsubst %,%.erl,$(ERLC_EXCLUDE)),\
4799 $(filter %.erl %.core,$(?F)));
4800 erlc_verbose_2 = set -x;
4801 erlc_verbose = $(erlc_verbose_$(V))
4802
4803 xyrl_verbose_0 = @echo " XYRL " $(filter %.xrl %.yrl,$(?F));
4804 xyrl_verbose_2 = set -x;
4805 xyrl_verbose = $(xyrl_verbose_$(V))
4806
4807 asn1_verbose_0 = @echo " ASN1 " $(filter %.asn1,$(?F));
4808 asn1_verbose_2 = set -x;
4809 asn1_verbose = $(asn1_verbose_$(V))
4810
4811 mib_verbose_0 = @echo " MIB " $(filter %.bin %.mib,$(?F));
4812 mib_verbose_2 = set -x;
4813 mib_verbose = $(mib_verbose_$(V))
4814
4815 ifneq ($(wildcard src/),)
4816
4817 # Targets.
4818
4819 ifeq ($(wildcard ebin/test),)
4820 app:: deps $(PROJECT).d
4821 $(verbose) $(MAKE) --no-print-directory app-build
4822 else
4823 app:: clean deps $(PROJECT).d
4824 $(verbose) $(MAKE) --no-print-directory app-build
4825 endif
4826
4827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
4828 define app_file
4829 {application, '$(PROJECT)', [
4830 {description, "$(PROJECT_DESCRIPTION)"},
4831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
4832 {id$(comma)$(space)"$(1)"}$(comma))
4833 {modules, [$(call comma_list,$(2))]},
4834 {registered, []},
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
4837 ]}.
4838 endef
4839 else
4840 define app_file
4841 {application, '$(PROJECT)', [
4842 {description, "$(PROJECT_DESCRIPTION)"},
4843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
4844 {id$(comma)$(space)"$(1)"}$(comma))
4845 {modules, [$(call comma_list,$(2))]},
4846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
4847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
4850 ]}.
4851 endef
4852 endif
4853
4854 app-build: ebin/$(PROJECT).app
4855 $(verbose) :
4856
4857 # Source files.
4858
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
4863
4864 # ASN.1 files.
4865
4866 ifneq ($(wildcard asn1/),)
4867 ASN1_FILES = $(sort $(call core_find,asn1/,*.asn1))
4868 ERL_FILES += $(addprefix src/,$(patsubst %.asn1,%.erl,$(notdir $(ASN1_FILES))))
4869
4870 define compile_asn1
4871 $(verbose) mkdir -p include/
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
4873 $(verbose) mv asn1/*.erl src/
4874 $(verbose) mv asn1/*.hrl include/
4875 $(verbose) mv asn1/*.asn1db include/
4876 endef
4877
4878 $(PROJECT).d:: $(ASN1_FILES)
4879 $(if $(strip $?),$(call compile_asn1,$?))
4880 endif
4881
4882 # SNMP MIB files.
4883
4884 ifneq ($(wildcard mibs/),)
4885 MIB_FILES = $(sort $(call core_find,mibs/,*.mib))
4886
4887 $(PROJECT).d:: $(COMPILE_MIB_FIRST_PATHS) $(MIB_FILES)
4888 $(verbose) mkdir -p include/ priv/mibs/
4889 $(mib_verbose) erlc -v $(ERLC_MIB_OPTS) -o priv/mibs/ -I priv/mibs/ $?
4890 $(mib_verbose) erlc -o include/ -- $(addprefix priv/mibs/,$(patsubst %.mib,%.bin,$(notdir $?)))
4891 endif
4892
4893 # Leex and Yecc files.
4894
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
4896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
4897 ERL_FILES += $(XRL_ERL_FILES)
4898
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
4900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
4901 ERL_FILES += $(YRL_ERL_FILES)
4902
4903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
4905
4906 # Erlang and Core Erlang files.
4907
4908 define makedep.erl
4909 E = ets:new(makedep, [bag]),
4910 G = digraph:new([acyclic]),
4911 ErlFiles = lists:usort(string:tokens("$(ERL_FILES)", " ")),
4912 Modules = [{list_to_atom(filename:basename(F, ".erl")), F} || F <- ErlFiles],
4913 Add = fun (Mod, Dep) ->
4914 case lists:keyfind(Dep, 1, Modules) of
4915 false -> ok;
4916 {_, DepFile} ->
4917 {_, ModFile} = lists:keyfind(Mod, 1, Modules),
4918 ets:insert(E, {ModFile, DepFile}),
4919 digraph:add_vertex(G, Mod),
4920 digraph:add_vertex(G, Dep),
4921 digraph:add_edge(G, Mod, Dep)
4922 end
4923 end,
4924 AddHd = fun (F, Mod, DepFile) ->
4925 case file:open(DepFile, [read]) of
4926 {error, enoent} -> ok;
4927 {ok, Fd} ->
4928 F(F, Fd, Mod),
4929 {_, ModFile} = lists:keyfind(Mod, 1, Modules),
4930 ets:insert(E, {ModFile, DepFile})
4931 end
4932 end,
4933 Attr = fun
4934 (F, Mod, behavior, Dep) -> Add(Mod, Dep);
4935 (F, Mod, behaviour, Dep) -> Add(Mod, Dep);
4936 (F, Mod, compile, {parse_transform, Dep}) -> Add(Mod, Dep);
4937 (F, Mod, compile, Opts) when is_list(Opts) ->
4938 case proplists:get_value(parse_transform, Opts) of
4939 undefined -> ok;
4940 Dep -> Add(Mod, Dep)
4941 end;
4942 (F, Mod, include, Hrl) ->
4943 case filelib:is_file("include/" ++ Hrl) of
4944 true -> AddHd(F, Mod, "include/" ++ Hrl);
4945 false ->
4946 case filelib:is_file("src/" ++ Hrl) of
4947 true -> AddHd(F, Mod, "src/" ++ Hrl);
4948 false -> false
4949 end
4950 end;
4951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
4952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
4953 (F, Mod, import, {Imp, _}) ->
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
4960 false -> ok;
4961 true -> Add(Mod, Imp)
4962 end;
4963 (_, _, _, _) -> ok
4964 end,
4965 MakeDepend = fun(F, Fd, Mod) ->
4966 case io:parse_erl_form(Fd, undefined) of
4967 {ok, {attribute, _, Key, Value}, _} ->
4968 Attr(F, Mod, Key, Value),
4969 F(F, Fd, Mod);
4970 {eof, _} ->
4971 file:close(Fd);
4972 _ ->
4973 F(F, Fd, Mod)
4974 end
4975 end,
4976 [begin
4977 Mod = list_to_atom(filename:basename(F, ".erl")),
4978 {ok, Fd} = file:open(F, [read]),
4979 MakeDepend(MakeDepend, Fd, Mod)
4980 end || F <- ErlFiles],
4981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
4982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
4991 ok = file:write_file("$(1)", [
4992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
4994 ]),
4995 halt()
4996 endef
4997
4998 ifeq ($(if $(NO_MAKEDEP),$(wildcard $(PROJECT).d),),)
4999 $(PROJECT).d:: $(ERL_FILES) $(call core_find,include/,*.hrl) $(MAKEFILE_LIST)
5000 $(makedep_verbose) $(call erlang,$(call makedep.erl,$@))
5001 endif
5002
5003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
5004 # Rebuild everything when the Makefile changes.
5005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
5008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
5009 touch -c $(PROJECT).d; \
5010 fi
5011 $(verbose) touch $@
5012
5013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
5014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
5015 endif
5016
5017 include $(wildcard $(PROJECT).d)
5018
5019 ebin/$(PROJECT).app:: ebin/
5020
5021 ebin/:
5022 $(verbose) mkdir -p ebin/
5023
5024 define compile_erl
5025 $(erlc_verbose) erlc -v $(if $(IS_DEP),$(filter-out -Werror,$(ERLC_OPTS)),$(ERLC_OPTS)) -o ebin/ \
5026 -pa ebin/ -I include/ $(filter-out $(ERLC_EXCLUDE_PATHS),$(COMPILE_FIRST_PATHS) $(1))
5027 endef
5028
5029 ebin/$(PROJECT).app:: $(ERL_FILES) $(CORE_FILES) $(wildcard src/$(PROJECT).app.src)
5030 $(eval FILES_TO_COMPILE := $(filter-out src/$(PROJECT).app.src,$?))
5031 $(if $(strip $(FILES_TO_COMPILE)),$(call compile_erl,$(FILES_TO_COMPILE)))
5032 $(eval GITDESCRIBE := $(shell git describe --dirty --abbrev=7 --tags --always --first-parent 2>/dev/null || true))
5033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
5034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
5035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
5037 > ebin/$(PROJECT).app
5038 else
5039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
5040 echo "Empty modules entry not found in $(PROJECT).app.src. Please consult the erlang.mk README for instructions." >&2; \
5041 exit 1; \
5042 fi
5043 $(appsrc_verbose) cat src/$(PROJECT).app.src \
5044 | sed "s/{[[:space:]]*modules[[:space:]]*,[[:space:]]*\[\]}/{modules, \[$(call comma_list,$(MODULES))\]}/" \
5045 | sed "s/{id,[[:space:]]*\"git\"}/{id, \"$(subst /,\/,$(GITDESCRIBE))\"}/" \
5046 > ebin/$(PROJECT).app
5047 endif
5048
5049 clean:: clean-app
5050
5051 clean-app:
5052 $(gen_verbose) rm -rf $(PROJECT).d ebin/ priv/mibs/ $(XRL_ERL_FILES) $(YRL_ERL_FILES) \
5053 $(addprefix include/,$(patsubst %.mib,%.hrl,$(notdir $(MIB_FILES)))) \
5054 $(addprefix include/,$(patsubst %.asn1,%.hrl,$(notdir $(ASN1_FILES)))) \
5055 $(addprefix include/,$(patsubst %.asn1,%.asn1db,$(notdir $(ASN1_FILES)))) \
5056 $(addprefix src/,$(patsubst %.asn1,%.erl,$(notdir $(ASN1_FILES))))
5057
5058 endif
5059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
5061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
5062 # This file is part of erlang.mk and subject to the terms of the ISC License.
5063
5064 .PHONY: docs-deps
5065
5066 # Configuration.
5067
5068 ALL_DOC_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(DOC_DEPS))
5069
5070 # Targets.
5071
5072 $(foreach dep,$(DOC_DEPS),$(eval $(call dep_target,$(dep))))
5073
5074 ifneq ($(SKIP_DEPS),)
5075 doc-deps:
5076 else
5077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
5082 # This file is part of erlang.mk and subject to the terms of the ISC License.
5083
5084 .PHONY: rel-deps
5085
5086 # Configuration.
5087
5088 ALL_REL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(REL_DEPS))
5089
5090 # Targets.
5091
5092 $(foreach dep,$(REL_DEPS),$(eval $(call dep_target,$(dep))))
5093
5094 ifneq ($(SKIP_DEPS),)
5095 rel-deps:
5096 else
5097 rel-deps: $(ALL_REL_DEPS_DIRS)
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
5102 # This file is part of erlang.mk and subject to the terms of the ISC License.
5103
5104 .PHONY: test-deps test-dir test-build clean-test-dir
5105
5106 # Configuration.
5107
5108 TEST_DIR ?= $(CURDIR)/test
5109
5110 ALL_TEST_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(TEST_DEPS))
5111
5112 TEST_ERLC_OPTS ?= +debug_info +warn_export_vars +warn_shadow_vars +warn_obsolete_guard
5113 TEST_ERLC_OPTS += -DTEST=1
5114
5115 # Targets.
5116
5117 $(foreach dep,$(TEST_DEPS),$(eval $(call dep_target,$(dep))))
5118
5119 ifneq ($(SKIP_DEPS),)
5120 test-deps:
5121 else
5122 test-deps: $(ALL_TEST_DEPS_DIRS)
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5124 endif
5125
5126 ifneq ($(wildcard $(TEST_DIR)),)
5127 test-dir:
5128 $(gen_verbose) erlc -v $(TEST_ERLC_OPTS) -I include/ -o $(TEST_DIR) \
5129 $(call core_find,$(TEST_DIR)/,*.erl) -pa ebin/
5130 endif
5131
5132 ifeq ($(wildcard src),)
5133 test-build:: ERLC_OPTS=$(TEST_ERLC_OPTS)
5134 test-build:: clean deps test-deps
5135 $(verbose) $(MAKE) --no-print-directory test-dir ERLC_OPTS="$(TEST_ERLC_OPTS)"
5136 else
5137 ifeq ($(wildcard ebin/test),)
5138 test-build:: ERLC_OPTS=$(TEST_ERLC_OPTS)
5139 test-build:: clean deps test-deps $(PROJECT).d
5140 $(verbose) $(MAKE) --no-print-directory app-build test-dir ERLC_OPTS="$(TEST_ERLC_OPTS)"
5141 $(gen_verbose) touch ebin/test
5142 else
5143 test-build:: ERLC_OPTS=$(TEST_ERLC_OPTS)
5144 test-build:: deps test-deps $(PROJECT).d
5145 $(verbose) $(MAKE) --no-print-directory app-build test-dir ERLC_OPTS="$(TEST_ERLC_OPTS)"
5146 endif
5147
5148 clean:: clean-test-dir
5149
5150 clean-test-dir:
5151 ifneq ($(wildcard $(TEST_DIR)/*.beam),)
5152 $(gen_verbose) rm -f $(TEST_DIR)/*.beam
5153 endif
5154 endif
5155
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
5157 # This file is part of erlang.mk and subject to the terms of the ISC License.
5158
5159 .PHONY: rebar.config
5160
5161 # We strip out -Werror because we don't want to fail due to
5162 # warnings when used as a dependency.
5163
5164 compat_prepare_erlc_opts = $(shell echo "$1" | sed 's/, */,/g')
5165
5166 define compat_convert_erlc_opts
5167 $(if $(filter-out -Werror,$1),\
5168 $(if $(findstring +,$1),\
5169 $(shell echo $1 | cut -b 2-)))
5170 endef
5171
5172 define compat_erlc_opts_to_list
5173 [$(call comma_list,$(foreach o,$(call compat_prepare_erlc_opts,$1),$(call compat_convert_erlc_opts,$o)))]
5174 endef
5175
5176 define compat_rebar_config
5177 {deps, [
5178 $(call comma_list,$(foreach d,$(DEPS),\
5179 $(if $(filter hex,$(call dep_fetch,$d)),\
5180 {$(call dep_name,$d)$(comma)"$(call dep_repo,$d)"},\
5181 {$(call dep_name,$d)$(comma)".*"$(comma){git,"$(call dep_repo,$d)"$(comma)"$(call dep_commit,$d)"}})))
5182 ]}.
5183 {erl_opts, $(call compat_erlc_opts_to_list,$(ERLC_OPTS))}.
5184 endef
5185
5186 $(eval _compat_rebar_config = $$(compat_rebar_config))
5187 $(eval export _compat_rebar_config)
5188
5189 rebar.config:
5190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
5191
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
5193 # This file is part of erlang.mk and subject to the terms of the ISC License.
5194
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
5200
5201 docs:: asciidoc
5202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
5207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
5210
5211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
5212 asciidoc-guide:
5213 else
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
5215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
5216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
5227 asciidoc-manual:
5228 else
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
5262
5263 install-docs:: install-asciidoc
5264
5265 install-asciidoc: asciidoc-manual
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
5276 # This file is part of erlang.mk and subject to the terms of the ISC License.
5277
5278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
5279
5280 # Core targets.
5281
5282 help::
5283 $(verbose) printf "%s\n" "" \
5284 "Bootstrap targets:" \
5285 " bootstrap Generate a skeleton of an OTP application" \
5286 " bootstrap-lib Generate a skeleton of an OTP library" \
5287 " bootstrap-rel Generate the files needed to build a release" \
5288 " new-app in=NAME Create a new local OTP application NAME" \
5289 " new-lib in=NAME Create a new local OTP library NAME" \
5290 " new t=TPL n=NAME Generate a module NAME based on the template TPL" \
5291 " new t=T n=N in=APP Generate a module NAME based on the template TPL in APP" \
5292 " list-templates List available templates"
5293
5294 # Bootstrap templates.
5295
5296 define bs_appsrc
5297 {application, $p, [
5298 {description, ""},
5299 {vsn, "0.1.0"},
5300 {id, "git"},
5301 {modules, []},
5302 {registered, []},
5303 {applications, [
5304 kernel,
5305 stdlib
5306 ]},
5307 {mod, {$p_app, []}},
5308 {env, []}
5309 ]}.
5310 endef
5311
5312 define bs_appsrc_lib
5313 {application, $p, [
5314 {description, ""},
5315 {vsn, "0.1.0"},
5316 {id, "git"},
5317 {modules, []},
5318 {registered, []},
5319 {applications, [
5320 kernel,
5321 stdlib
5322 ]}
5323 ]}.
5324 endef
5325
5326 # To prevent autocompletion issues with ZSH, we add "include erlang.mk"
5327 # separately during the actual bootstrap.
5328 ifdef SP
5329 define bs_Makefile
5330 PROJECT = $p
5331 PROJECT_DESCRIPTION = New project
5332 PROJECT_VERSION = 0.1.0
5333
5334 # Whitespace to be used when creating files from templates.
5335 SP = $(SP)
5336
5337 endef
5338 else
5339 define bs_Makefile
5340 PROJECT = $p
5341 PROJECT_DESCRIPTION = New project
5342 PROJECT_VERSION = 0.1.0
5343
5344 endef
5345 endif
5346
5347 define bs_apps_Makefile
5348 PROJECT = $p
5349 PROJECT_DESCRIPTION = New project
5350 PROJECT_VERSION = 0.1.0
5351
5352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
5353 endef
5354
5355 define bs_app
5356 -module($p_app).
5357 -behaviour(application).
5358
5359 -export([start/2]).
5360 -export([stop/1]).
5361
5362 start(_Type, _Args) ->
5363 $p_sup:start_link().
5364
5365 stop(_State) ->
5366 ok.
5367 endef
5368
5369 define bs_relx_config
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
5371 {extended_start_script, true}.
5372 {sys_config, "rel/sys.config"}.
5373 {vm_args, "rel/vm.args"}.
5374 endef
5375
5376 define bs_sys_config
5377 [
5378 ].
5379 endef
5380
5381 define bs_vm_args
5382 -name $p@127.0.0.1
5383 -setcookie $p
5384 -heart
5385 endef
5386
5387 # Normal templates.
5388
5389 define tpl_supervisor
5390 -module($(n)).
5391 -behaviour(supervisor).
5392
5393 -export([start_link/0]).
5394 -export([init/1]).
5395
5396 start_link() ->
5397 supervisor:start_link({local, ?MODULE}, ?MODULE, []).
5398
5399 init([]) ->
5400 Procs = [],
5401 {ok, {{one_for_one, 1, 5}, Procs}}.
5402 endef
5403
5404 define tpl_gen_server
5405 -module($(n)).
5406 -behaviour(gen_server).
5407
5408 %% API.
5409 -export([start_link/0]).
5410
5411 %% gen_server.
5412 -export([init/1]).
5413 -export([handle_call/3]).
5414 -export([handle_cast/2]).
5415 -export([handle_info/2]).
5416 -export([terminate/2]).
5417 -export([code_change/3]).
5418
5419 -record(state, {
5420 }).
5421
5422 %% API.
5423
5424 -spec start_link() -> {ok, pid()}.
5425 start_link() ->
5426 gen_server:start_link(?MODULE, [], []).
5427
5428 %% gen_server.
5429
5430 init([]) ->
5431 {ok, #state{}}.
5432
5433 handle_call(_Request, _From, State) ->
5434 {reply, ignored, State}.
5435
5436 handle_cast(_Msg, State) ->
5437 {noreply, State}.
5438
5439 handle_info(_Info, State) ->
5440 {noreply, State}.
5441
5442 terminate(_Reason, _State) ->
5443 ok.
5444
5445 code_change(_OldVsn, State, _Extra) ->
5446 {ok, State}.
5447 endef
5448
5449 define tpl_module
5450 -module($(n)).
5451 -export([]).
5452 endef
5453
5454 define tpl_cowboy_http
5455 -module($(n)).
5456 -behaviour(cowboy_http_handler).
5457
5458 -export([init/3]).
5459 -export([handle/2]).
5460 -export([terminate/3]).
5461
5462 -record(state, {
5463 }).
5464
5465 init(_, Req, _Opts) ->
5466 {ok, Req, #state{}}.
5467
5468 handle(Req, State=#state{}) ->
5469 {ok, Req2} = cowboy_req:reply(200, Req),
5470 {ok, Req2, State}.
5471
5472 terminate(_Reason, _Req, _State) ->
5473 ok.
5474 endef
5475
5476 define tpl_gen_fsm
5477 -module($(n)).
5478 -behaviour(gen_fsm).
5479
5480 %% API.
5481 -export([start_link/0]).
5482
5483 %% gen_fsm.
5484 -export([init/1]).
5485 -export([state_name/2]).
5486 -export([handle_event/3]).
5487 -export([state_name/3]).
5488 -export([handle_sync_event/4]).
5489 -export([handle_info/3]).
5490 -export([terminate/3]).
5491 -export([code_change/4]).
5492
5493 -record(state, {
5494 }).
5495
5496 %% API.
5497
5498 -spec start_link() -> {ok, pid()}.
5499 start_link() ->
5500 gen_fsm:start_link(?MODULE, [], []).
5501
5502 %% gen_fsm.
5503
5504 init([]) ->
5505 {ok, state_name, #state{}}.
5506
5507 state_name(_Event, StateData) ->
5508 {next_state, state_name, StateData}.
5509
5510 handle_event(_Event, StateName, StateData) ->
5511 {next_state, StateName, StateData}.
5512
5513 state_name(_Event, _From, StateData) ->
5514 {reply, ignored, state_name, StateData}.
5515
5516 handle_sync_event(_Event, _From, StateName, StateData) ->
5517 {reply, ignored, StateName, StateData}.
5518
5519 handle_info(_Info, StateName, StateData) ->
5520 {next_state, StateName, StateData}.
5521
5522 terminate(_Reason, _StateName, _StateData) ->
5523 ok.
5524
5525 code_change(_OldVsn, StateName, StateData, _Extra) ->
5526 {ok, StateName, StateData}.
5527 endef
5528
5529 define tpl_cowboy_loop
5530 -module($(n)).
5531 -behaviour(cowboy_loop_handler).
5532
5533 -export([init/3]).
5534 -export([info/3]).
5535 -export([terminate/3]).
5536
5537 -record(state, {
5538 }).
5539
5540 init(_, Req, _Opts) ->
5541 {loop, Req, #state{}, 5000, hibernate}.
5542
5543 info(_Info, Req, State) ->
5544 {loop, Req, State, hibernate}.
5545
5546 terminate(_Reason, _Req, _State) ->
5547 ok.
5548 endef
5549
5550 define tpl_cowboy_rest
5551 -module($(n)).
5552
5553 -export([init/3]).
5554 -export([content_types_provided/2]).
5555 -export([get_html/2]).
5556
5557 init(_, _Req, _Opts) ->
5558 {upgrade, protocol, cowboy_rest}.
5559
5560 content_types_provided(Req, State) ->
5561 {[{{<<"text">>, <<"html">>, '*'}, get_html}], Req, State}.
5562
5563 get_html(Req, State) ->
5564 {<<"<html><body>This is REST!</body></html>">>, Req, State}.
5565 endef
5566
5567 define tpl_cowboy_ws
5568 -module($(n)).
5569 -behaviour(cowboy_websocket_handler).
5570
5571 -export([init/3]).
5572 -export([websocket_init/3]).
5573 -export([websocket_handle/3]).
5574 -export([websocket_info/3]).
5575 -export([websocket_terminate/3]).
5576
5577 -record(state, {
5578 }).
5579
5580 init(_, _, _) ->
5581 {upgrade, protocol, cowboy_websocket}.
5582
5583 websocket_init(_, Req, _Opts) ->
5584 Req2 = cowboy_req:compact(Req),
5585 {ok, Req2, #state{}}.
5586
5587 websocket_handle({text, Data}, Req, State) ->
5588 {reply, {text, Data}, Req, State};
5589 websocket_handle({binary, Data}, Req, State) ->
5590 {reply, {binary, Data}, Req, State};
5591 websocket_handle(_Frame, Req, State) ->
5592 {ok, Req, State}.
5593
5594 websocket_info(_Info, Req, State) ->
5595 {ok, Req, State}.
5596
5597 websocket_terminate(_Reason, _Req, _State) ->
5598 ok.
5599 endef
5600
5601 define tpl_ranch_protocol
5602 -module($(n)).
5603 -behaviour(ranch_protocol).
5604
5605 -export([start_link/4]).
5606 -export([init/4]).
5607
5608 -type opts() :: [].
5609 -export_type([opts/0]).
5610
5611 -record(state, {
5612 socket :: inet:socket(),
5613 transport :: module()
5614 }).
5615
5616 start_link(Ref, Socket, Transport, Opts) ->
5617 Pid = spawn_link(?MODULE, init, [Ref, Socket, Transport, Opts]),
5618 {ok, Pid}.
5619
5620 -spec init(ranch:ref(), inet:socket(), module(), opts()) -> ok.
5621 init(Ref, Socket, Transport, _Opts) ->
5622 ok = ranch:accept_ack(Ref),
5623 loop(#state{socket=Socket, transport=Transport}).
5624
5625 loop(State) ->
5626 loop(State).
5627 endef
5628
5629 # Plugin-specific targets.
5630
5631 define render_template
5632 $(verbose) printf -- '$(subst $(newline),\n,$(subst %,%%,$(subst ','\'',$(subst $(tab),$(WS),$(call $(1))))))\n' > $(2)
5633 endef
5634
5635 ifndef WS
5636 ifdef SP
5637 WS = $(subst a,,a $(wordlist 1,$(SP),a a a a a a a a a a a a a a a a a a a a))
5638 else
5639 WS = $(tab)
5640 endif
5641 endif
5642
5643 bootstrap:
5644 ifneq ($(wildcard src/),)
5645 $(error Error: src/ directory already exists)
5646 endif
5647 $(eval p := $(PROJECT))
5648 $(eval n := $(PROJECT)_sup)
5649 $(call render_template,bs_Makefile,Makefile)
5650 $(verbose) echo "include erlang.mk" >> Makefile
5651 $(verbose) mkdir src/
5652 ifdef LEGACY
5653 $(call render_template,bs_appsrc,src/$(PROJECT).app.src)
5654 endif
5655 $(call render_template,bs_app,src/$(PROJECT)_app.erl)
5656 $(call render_template,tpl_supervisor,src/$(PROJECT)_sup.erl)
5657
5658 bootstrap-lib:
5659 ifneq ($(wildcard src/),)
5660 $(error Error: src/ directory already exists)
5661 endif
5662 $(eval p := $(PROJECT))
5663 $(call render_template,bs_Makefile,Makefile)
5664 $(verbose) echo "include erlang.mk" >> Makefile
5665 $(verbose) mkdir src/
5666 ifdef LEGACY
5667 $(call render_template,bs_appsrc_lib,src/$(PROJECT).app.src)
5668 endif
5669
5670 bootstrap-rel:
5671 ifneq ($(wildcard relx.config),)
5672 $(error Error: relx.config already exists)
5673 endif
5674 ifneq ($(wildcard rel/),)
5675 $(error Error: rel/ directory already exists)
5676 endif
5677 $(eval p := $(PROJECT))
5678 $(call render_template,bs_relx_config,relx.config)
5679 $(verbose) mkdir rel/
5680 $(call render_template,bs_sys_config,rel/sys.config)
5681 $(call render_template,bs_vm_args,rel/vm.args)
5682
5683 new-app:
5684 ifndef in
5685 $(error Usage: $(MAKE) new-app in=APP)
5686 endif
5687 ifneq ($(wildcard $(APPS_DIR)/$in),)
5688 $(error Error: Application $in already exists)
5689 endif
5690 $(eval p := $(in))
5691 $(eval n := $(in)_sup)
5692 $(verbose) mkdir -p $(APPS_DIR)/$p/src/
5693 $(call render_template,bs_apps_Makefile,$(APPS_DIR)/$p/Makefile)
5694 ifdef LEGACY
5695 $(call render_template,bs_appsrc,$(APPS_DIR)/$p/src/$p.app.src)
5696 endif
5697 $(call render_template,bs_app,$(APPS_DIR)/$p/src/$p_app.erl)
5698 $(call render_template,tpl_supervisor,$(APPS_DIR)/$p/src/$p_sup.erl)
5699
5700 new-lib:
5701 ifndef in
5702 $(error Usage: $(MAKE) new-lib in=APP)
5703 endif
5704 ifneq ($(wildcard $(APPS_DIR)/$in),)
5705 $(error Error: Application $in already exists)
5706 endif
5707 $(eval p := $(in))
5708 $(verbose) mkdir -p $(APPS_DIR)/$p/src/
5709 $(call render_template,bs_apps_Makefile,$(APPS_DIR)/$p/Makefile)
5710 ifdef LEGACY
5711 $(call render_template,bs_appsrc_lib,$(APPS_DIR)/$p/src/$p.app.src)
5712 endif
5713
5714 new:
5715 ifeq ($(wildcard src/)$(in),)
5716 $(error Error: src/ directory does not exist)
5717 endif
5718 ifndef t
5719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
5720 endif
5721 ifndef n
5722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
5723 endif
5724 ifdef in
5725 $(verbose) $(MAKE) -C $(APPS_DIR)/$(in)/ new t=$t n=$n in=
5726 else
5727 $(call render_template,tpl_$(t),src/$(n).erl)
5728 endif
5729
5730 list-templates:
5731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
5732
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
5734 # This file is part of erlang.mk and subject to the terms of the ISC License.
5735
5736 .PHONY: clean-c_src distclean-c_src-env
5737
5738 # Configuration.
5739
5740 C_SRC_DIR ?= $(CURDIR)/c_src
5741 C_SRC_ENV ?= $(C_SRC_DIR)/env.mk
5742 C_SRC_OUTPUT ?= $(CURDIR)/priv/$(PROJECT)
5743 C_SRC_TYPE ?= shared
5744
5745 # System type and C compiler/flags.
5746
5747 ifeq ($(PLATFORM),msys2)
5748 C_SRC_OUTPUT_EXECUTABLE_EXTENSION ?= .exe
5749 C_SRC_OUTPUT_SHARED_EXTENSION ?= .dll
5750 else
5751 C_SRC_OUTPUT_EXECUTABLE_EXTENSION ?=
5752 C_SRC_OUTPUT_SHARED_EXTENSION ?= .so
5753 endif
5754
5755 ifeq ($(C_SRC_TYPE),shared)
5756 C_SRC_OUTPUT_FILE = $(C_SRC_OUTPUT)$(C_SRC_OUTPUT_SHARED_EXTENSION)
5757 else
5758 C_SRC_OUTPUT_FILE = $(C_SRC_OUTPUT)$(C_SRC_OUTPUT_EXECUTABLE_EXTENSION)
5759 endif
5760
5761 ifeq ($(PLATFORM),msys2)
5762 # We hardcode the compiler used on MSYS2. The default CC=cc does
5763 # not produce working code. The "gcc" MSYS2 package also doesn't.
5764 CC = /mingw64/bin/gcc
5765 export CC
5766 CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes
5767 CXXFLAGS ?= -O3 -finline-functions -Wall
5768 else ifeq ($(PLATFORM),darwin)
5769 CC ?= cc
5770 CFLAGS ?= -O3 -std=c99 -arch x86_64 -finline-functions -Wall -Wmissing-prototypes
5771 CXXFLAGS ?= -O3 -arch x86_64 -finline-functions -Wall
5772 LDFLAGS ?= -arch x86_64 -flat_namespace -undefined suppress
5773 else ifeq ($(PLATFORM),freebsd)
5774 CC ?= cc
5775 CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes
5776 CXXFLAGS ?= -O3 -finline-functions -Wall
5777 else ifeq ($(PLATFORM),linux)
5778 CC ?= gcc
5779 CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes
5780 CXXFLAGS ?= -O3 -finline-functions -Wall
5781 endif
5782
5783 ifneq ($(PLATFORM),msys2)
5784 CFLAGS += -fPIC
5785 CXXFLAGS += -fPIC
5786 endif
5787
5788 CFLAGS += -I"$(ERTS_INCLUDE_DIR)" -I"$(ERL_INTERFACE_INCLUDE_DIR)"
5789 CXXFLAGS += -I"$(ERTS_INCLUDE_DIR)" -I"$(ERL_INTERFACE_INCLUDE_DIR)"
5790
5791 LDLIBS += -L"$(ERL_INTERFACE_LIB_DIR)" -lerl_interface -lei
5792
5793 # Verbosity.
5794
5795 c_verbose_0 = @echo " C " $(?F);
5796 c_verbose = $(c_verbose_$(V))
5797
5798 cpp_verbose_0 = @echo " CPP " $(?F);
5799 cpp_verbose = $(cpp_verbose_$(V))
5800
5801 link_verbose_0 = @echo " LD " $(@F);
5802 link_verbose = $(link_verbose_$(V))
5803
5804 # Targets.
5805
5806 ifeq ($(wildcard $(C_SRC_DIR)),)
5807 else ifneq ($(wildcard $(C_SRC_DIR)/Makefile),)
5808 app:: app-c_src
5809
5810 test-build:: app-c_src
5811
5812 app-c_src:
5813 $(MAKE) -C $(C_SRC_DIR)
5814
5815 clean::
5816 $(MAKE) -C $(C_SRC_DIR) clean
5817
5818 else
5819
5820 ifeq ($(SOURCES),)
5821 SOURCES := $(sort $(foreach pat,*.c *.C *.cc *.cpp,$(call core_find,$(C_SRC_DIR)/,$(pat))))
5822 endif
5823 OBJECTS = $(addsuffix .o, $(basename $(SOURCES)))
5824
5825 COMPILE_C = $(c_verbose) $(CC) $(CFLAGS) $(CPPFLAGS) -c
5826 COMPILE_CPP = $(cpp_verbose) $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c
5827
5828 app:: $(C_SRC_ENV) $(C_SRC_OUTPUT_FILE)
5829
5830 test-build:: $(C_SRC_ENV) $(C_SRC_OUTPUT_FILE)
5831
5832 $(C_SRC_OUTPUT_FILE): $(OBJECTS)
5833 $(verbose) mkdir -p priv/
5834 $(link_verbose) $(CC) $(OBJECTS) \
5835 $(LDFLAGS) $(if $(filter $(C_SRC_TYPE),shared),-shared) $(LDLIBS) \
5836 -o $(C_SRC_OUTPUT_FILE)
5837
5838 %.o: %.c
5839 $(COMPILE_C) $(OUTPUT_OPTION) $<
5840
5841 %.o: %.cc
5842 $(COMPILE_CPP) $(OUTPUT_OPTION) $<
5843
5844 %.o: %.C
5845 $(COMPILE_CPP) $(OUTPUT_OPTION) $<
5846
5847 %.o: %.cpp
5848 $(COMPILE_CPP) $(OUTPUT_OPTION) $<
5849
5850 clean:: clean-c_src
5851
5852 clean-c_src:
5853 $(gen_verbose) rm -f $(C_SRC_OUTPUT_FILE) $(OBJECTS)
5854
5855 endif
5856
5857 ifneq ($(wildcard $(C_SRC_DIR)),)
5858 $(C_SRC_ENV):
5859 $(verbose) $(ERL) -eval "file:write_file(\"$(call core_native_path,$(C_SRC_ENV))\", \
5860 io_lib:format( \
5861 \"ERTS_INCLUDE_DIR ?= ~s/erts-~s/include/~n\" \
5862 \"ERL_INTERFACE_INCLUDE_DIR ?= ~s~n\" \
5863 \"ERL_INTERFACE_LIB_DIR ?= ~s~n\", \
5864 [code:root_dir(), erlang:system_info(version), \
5865 code:lib_dir(erl_interface, include), \
5866 code:lib_dir(erl_interface, lib)])), \
5867 halt()."
5868
5869 distclean:: distclean-c_src-env
5870
5871 distclean-c_src-env:
5872 $(gen_verbose) rm -f $(C_SRC_ENV)
5873
5874 -include $(C_SRC_ENV)
5875 endif
5876
5877 # Templates.
5878
5879 define bs_c_nif
5880 #include "erl_nif.h"
5881
5882 static int loads = 0;
5883
5884 static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
5885 {
5886 /* Initialize private data. */
5887 *priv_data = NULL;
5888
5889 loads++;
5890
5891 return 0;
5892 }
5893
5894 static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info)
5895 {
5896 /* Convert the private data to the new version. */
5897 *priv_data = *old_priv_data;
5898
5899 loads++;
5900
5901 return 0;
5902 }
5903
5904 static void unload(ErlNifEnv* env, void* priv_data)
5905 {
5906 if (loads == 1) {
5907 /* Destroy the private data. */
5908 }
5909
5910 loads--;
5911 }
5912
5913 static ERL_NIF_TERM hello(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
5914 {
5915 if (enif_is_atom(env, argv[0])) {
5916 return enif_make_tuple2(env,
5917 enif_make_atom(env, "hello"),
5918 argv[0]);
5919 }
5920
5921 return enif_make_tuple2(env,
5922 enif_make_atom(env, "error"),
5923 enif_make_atom(env, "badarg"));
5924 }
5925
5926 static ErlNifFunc nif_funcs[] = {
5927 {"hello", 1, hello}
5928 };
5929
5930 ERL_NIF_INIT($n, nif_funcs, load, NULL, upgrade, unload)
5931 endef
5932
5933 define bs_erl_nif
5934 -module($n).
5935
5936 -export([hello/1]).
5937
5938 -on_load(on_load/0).
5939 on_load() ->
5940 PrivDir = case code:priv_dir(?MODULE) of
5941 {error, _} ->
5942 AppPath = filename:dirname(filename:dirname(code:which(?MODULE))),
5943 filename:join(AppPath, "priv");
5944 Path ->
5945 Path
5946 end,
5947 erlang:load_nif(filename:join(PrivDir, atom_to_list(?MODULE)), 0).
5948
5949 hello(_) ->
5950 erlang:nif_error({not_loaded, ?MODULE}).
5951 endef
5952
5953 new-nif:
5954 ifneq ($(wildcard $(C_SRC_DIR)/$n.c),)
5955 $(error Error: $(C_SRC_DIR)/$n.c already exists)
5956 endif
5957 ifneq ($(wildcard src/$n.erl),)
5958 $(error Error: src/$n.erl already exists)
5959 endif
5960 ifdef in
5961 $(verbose) $(MAKE) -C $(APPS_DIR)/$(in)/ new-nif n=$n in=
5962 else
5963 $(verbose) mkdir -p $(C_SRC_DIR) src/
5964 $(call render_template,bs_c_nif,$(C_SRC_DIR)/$n.c)
5965 $(call render_template,bs_erl_nif,src/$n.erl)
5966 endif
5967
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
5969 # This file is part of erlang.mk and subject to the terms of the ISC License.
5970
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
5973 CI_OTP ?=
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
5986 ci::
5987 else
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
6007
6008 ci-setup::
6009
6010 ci-extra::
6011
6012 ci_verbose_0 = @echo " CI " $(1);
6013 ci_verbose = $(ci_verbose_$(V))
6014
6015 define ci_target
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
6018 $(ci_verbose) \
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
6030
6031 define ci_otp_target
6032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
6033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
6035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
6036 endif
6037 endef
6038
6039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
6040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
6052 $(KERL):
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
6056 $(verbose) chmod +x $(KERL)
6057
6058 help::
6059 $(verbose) printf "%s\n" "" \
6060 "Continuous Integration targets:" \
6061 " ci Run '$(MAKE) tests' on all configured Erlang versions." \
6062 "" \
6063 "The CI_OTP variable must be defined with the Erlang versions" \
6064 "that must be tested. For example: CI_OTP = OTP-17.3.4 OTP-17.5.3"
6065
6066 distclean:: distclean-kerl
6067
6068 distclean-kerl:
6069 $(gen_verbose) rm -rf $(KERL)
6070 endif
6071
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
6073 # This file is part of erlang.mk and subject to the terms of the ISC License.
6074
6075 .PHONY: ct apps-ct distclean-ct
6076
6077 # Configuration.
6078
6079 CT_OPTS ?=
6080
6081 ifneq ($(wildcard $(TEST_DIR)),)
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
6088
6089 # Core targets.
6090
6091 tests:: ct
6092
6093 distclean:: distclean-ct
6094
6095 help::
6096 $(verbose) printf "%s\n" "" \
6097 "Common_test targets:" \
6098 " ct Run all the common_test suites for this project" \
6099 "" \
6100 "All your common_test suites have their associated targets." \
6101 "A suite named http_SUITE can be ran using the ct-http target."
6102
6103 # Plugin-specific targets.
6104
6105 CT_RUN = ct_run \
6106 -no_auto_compile \
6107 -noinput \
6108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
6109 -dir $(TEST_DIR) \
6110 -logdir $(CT_LOGS_DIR)
6111
6112 ifeq ($(CT_SUITES),)
6113 ct: $(if $(IS_APP),,apps-ct)
6114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
6117 ct: test-build $(if $(IS_APP),,apps-ct)
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
6119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
6121 endif
6122
6123 ifneq ($(ALL_APPS_DIRS),)
6124 define ct_app_target
6125 apps-ct-$1:
6126 $(MAKE) -C $1 ct IS_APP=1
6127 endef
6128
6129 $(foreach app,$(ALL_APPS_DIRS),$(eval $(call ct_app_target,$(app))))
6130
6131 apps-ct: test-build $(addprefix apps-ct-,$(ALL_APPS_DIRS))
6132 endif
6133
6134 ifndef t
6135 CT_EXTRA =
6136 else
6137 ifeq (,$(findstring :,$t))
6138 CT_EXTRA = -group $t
6139 else
6140 t_words = $(subst :, ,$t)
6141 CT_EXTRA = -group $(firstword $(t_words)) -case $(lastword $(t_words))
6142 endif
6143 endif
6144
6145 define ct_suite_target
6146 ct-$(1): test-build
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
6148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
6149 endef
6150
6151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
6152
6153 distclean-ct:
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
6157 # This file is part of erlang.mk and subject to the terms of the ISC License.
6158
6159 .PHONY: plt distclean-plt dialyze
6160
6161 # Configuration.
6162
6163 DIALYZER_PLT ?= $(CURDIR)/.$(PROJECT).plt
6164 export DIALYZER_PLT
6165
6166 PLT_APPS ?=
6167 DIALYZER_DIRS ?= --src -r $(wildcard src) $(ALL_APPS_DIRS)
6168 DIALYZER_OPTS ?= -Werror_handling -Wrace_conditions -Wunmatched_returns # -Wunderspecs
6169
6170 # Core targets.
6171
6172 check:: dialyze
6173
6174 distclean:: distclean-plt
6175
6176 help::
6177 $(verbose) printf "%s\n" "" \
6178 "Dialyzer targets:" \
6179 " plt Build a PLT file for this project" \
6180 " dialyze Analyze the project using Dialyzer"
6181
6182 # Plugin-specific targets.
6183
6184 define filter_opts.erl
6185 Opts = init:get_plain_arguments(),
6186 {Filtered, _} = lists:foldl(fun
6187 (O, {Os, true}) -> {[O|Os], false};
6188 (O = "-D", {Os, _}) -> {[O|Os], true};
6189 (O = [\\$$-, \\$$D, _ | _], {Os, _}) -> {[O|Os], false};
6190 (O = "-I", {Os, _}) -> {[O|Os], true};
6191 (O = [\\$$-, \\$$I, _ | _], {Os, _}) -> {[O|Os], false};
6192 (O = "-pa", {Os, _}) -> {[O|Os], true};
6193 (_, Acc) -> Acc
6194 end, {[], false}, Opts),
6195 io:format("~s~n", [string:join(lists:reverse(Filtered), " ")]),
6196 halt().
6197 endef
6198
6199 $(DIALYZER_PLT): deps app
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
6204
6205 plt: $(DIALYZER_PLT)
6206
6207 distclean-plt:
6208 $(gen_verbose) rm -f $(DIALYZER_PLT)
6209
6210 ifneq ($(wildcard $(DIALYZER_PLT)),)
6211 dialyze:
6212 else
6213 dialyze: $(DIALYZER_PLT)
6214 endif
6215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
6216
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
6218 # This file is part of erlang.mk and subject to the terms of the ISC License.
6219
6220 .PHONY: distclean-edoc edoc
6221
6222 # Configuration.
6223
6224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
6235
6236 # Core targets.
6237
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
6239 docs:: edoc
6240 endif
6241
6242 distclean:: distclean-edoc
6243
6244 # Plugin-specific targets.
6245
6246 edoc: distclean-edoc doc-deps
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
6248
6249 distclean-edoc:
6250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
6251
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
6253 # This file is part of erlang.mk and subject to the terms of the ISC License.
6254
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
6320
6321 # Configuration.
6322
6323 ESCRIPT_NAME ?= $(PROJECT)
6324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
6325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
6332
6333 # Core targets.
6334
6335 distclean:: distclean-escript
6336
6337 help::
6338 $(verbose) printf "%s\n" "" \
6339 "Escript targets:" \
6340 " escript Build an executable escript archive" \
6341
6342 # Plugin-specific targets.
6343
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
6360
6361 distclean-escript:
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
6365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6367
6368 .PHONY: eunit apps-eunit
6369
6370 # Configuration
6371
6372 EUNIT_OPTS ?=
6373 EUNIT_ERL_OPTS ?=
6374
6375 # Core targets.
6376
6377 tests:: eunit
6378
6379 help::
6380 $(verbose) printf "%s\n" "" \
6381 "EUnit targets:" \
6382 " eunit Run all the EUnit tests for this project"
6383
6384 # Plugin-specific targets.
6385
6386 define eunit.erl
6387 case "$(COVER)" of
6388 "" -> ok;
6389 _ ->
6390 case cover:compile_beam_directory("ebin") of
6391 {error, _} -> halt(1);
6392 _ -> ok
6393 end
6394 end,
6395 case eunit:test($1, [$(EUNIT_OPTS)]) of
6396 ok -> ok;
6397 error -> halt(2)
6398 end,
6399 case "$(COVER)" of
6400 "" -> ok;
6401 _ ->
6402 cover:export("eunit.coverdata")
6403 end,
6404 halt()
6405 endef
6406
6407 EUNIT_ERL_OPTS += -pa $(TEST_DIR) $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(CURDIR)/ebin
6408
6409 ifdef t
6410 ifeq (,$(findstring :,$(t)))
6411 eunit: test-build
6412 $(gen_verbose) $(call erlang,$(call eunit.erl,['$(t)']),$(EUNIT_ERL_OPTS))
6413 else
6414 eunit: test-build
6415 $(gen_verbose) $(call erlang,$(call eunit.erl,fun $(t)/0),$(EUNIT_ERL_OPTS))
6416 endif
6417 else
6418 EUNIT_EBIN_MODS = $(notdir $(basename $(ERL_FILES) $(BEAM_FILES)))
6419 EUNIT_TEST_MODS = $(notdir $(basename $(call core_find,$(TEST_DIR)/,*.erl)))
6420
6421 EUNIT_MODS = $(foreach mod,$(EUNIT_EBIN_MODS) $(filter-out \
6422 $(patsubst %,%_tests,$(EUNIT_EBIN_MODS)),$(EUNIT_TEST_MODS)),'$(mod)')
6423
6424 eunit: test-build $(if $(IS_APP),,apps-eunit)
6425 $(gen_verbose) $(call erlang,$(call eunit.erl,[$(call comma_list,$(EUNIT_MODS))]),$(EUNIT_ERL_OPTS))
6426
6427 ifneq ($(ALL_APPS_DIRS),)
6428 apps-eunit:
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
6436 # This file is part of erlang.mk and subject to the terms of the ISC License.
6437
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
6439
6440 # Configuration.
6441
6442 RELX ?= $(ERLANG_MK_TMP)/relx
6443 RELX_CONFIG ?= $(CURDIR)/relx.config
6444
6445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
6446 RELX_OPTS ?=
6447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
6454
6455 ifeq ($(firstword $(RELX_OPTS)),-o)
6456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
6457 else
6458 RELX_OPTS += -o $(RELX_OUTPUT_DIR)
6459 endif
6460
6461 # Core targets.
6462
6463 ifeq ($(IS_DEP),)
6464 ifneq ($(wildcard $(RELX_CONFIG)),)
6465 rel:: relx-rel
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
6472
6473 # Plugin-specific targets.
6474
6475 $(RELX):
6476 $(gen_verbose) $(call core_http_get,$(RELX),$(RELX_URL))
6477 $(verbose) chmod +x $(RELX)
6478
6479 relx-rel: $(RELX) rel-deps app
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
6484
6485 distclean-relx-rel:
6486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6487
6488 # Run target.
6489
6490 ifeq ($(wildcard $(RELX_CONFIG)),)
6491 run:
6492 else
6493
6494 define get_relx_release.erl
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
6504 halt(0).
6505 endef
6506
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
6514
6515 run: all
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
6517
6518 help::
6519 $(verbose) printf "%s\n" "" \
6520 "Relx targets:" \
6521 " run Compile the project, build the release and run it"
6522
6523 endif
6524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
6526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6528
6529 .PHONY: shell
6530
6531 # Configuration.
6532
6533 SHELL_ERL ?= erl
6534 SHELL_PATHS ?= $(CURDIR)/ebin $(APPS_DIR)/*/ebin $(DEPS_DIR)/*/ebin
6535 SHELL_OPTS ?=
6536
6537 ALL_SHELL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(SHELL_DEPS))
6538
6539 # Core targets
6540
6541 help::
6542 $(verbose) printf "%s\n" "" \
6543 "Shell targets:" \
6544 " shell Run an erlang shell with SHELL_OPTS or reasonable default"
6545
6546 # Plugin-specific targets.
6547
6548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
6549
6550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6552
6553 shell: build-shell-deps
6554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
6555
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
6571 # This file is part of erlang.mk and subject to the terms of the ISC License.
6572
6573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
6574 .PHONY: triq
6575
6576 # Targets.
6577
6578 tests:: triq
6579
6580 define triq_check.erl
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
6582 try
6583 case $(1) of
6584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
6585 module -> triq:check($(2));
6586 function -> triq:check($(2))
6587 end
6588 of
6589 true -> halt(0);
6590 _ -> halt(1)
6591 catch error:undef ->
6592 io:format("Undefined property or module~n"),
6593 halt(0)
6594 end.
6595 endef
6596
6597 ifdef t
6598 ifeq (,$(findstring :,$(t)))
6599 triq: test-build
6600 $(verbose) $(call erlang,$(call triq_check.erl,module,$(t)))
6601 else
6602 triq: test-build
6603 $(verbose) echo Testing $(t)/0
6604 $(verbose) $(call erlang,$(call triq_check.erl,function,$(t)()))
6605 endif
6606 else
6607 triq: test-build
6608 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename $(wildcard ebin/*.beam))))))
6609 $(gen_verbose) $(call erlang,$(call triq_check.erl,all,undefined,$(MODULES)))
6610 endif
6611 endif
6612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6614 # Copyright (c) 2015, Erlang Solutions Ltd.
6615 # This file is part of erlang.mk and subject to the terms of the ISC License.
6616
6617 .PHONY: xref distclean-xref
6618
6619 # Configuration.
6620
6621 ifeq ($(XREF_CONFIG),)
6622 XREFR_ARGS :=
6623 else
6624 XREFR_ARGS := -c $(XREF_CONFIG)
6625 endif
6626
6627 XREFR ?= $(CURDIR)/xrefr
6628 export XREFR
6629
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
6631
6632 # Core targets.
6633
6634 help::
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
6638
6639 distclean:: distclean-xref
6640
6641 # Plugin-specific targets.
6642
6643 $(XREFR):
6644 $(gen_verbose) $(call core_http_get,$(XREFR),$(XREFR_URL))
6645 $(verbose) chmod +x $(XREFR)
6646
6647 xref: deps app $(XREFR)
6648 $(gen_verbose) $(XREFR) $(XREFR_ARGS)
6649
6650 distclean-xref:
6651 $(gen_verbose) rm -rf $(XREFR)
6652
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6655 # This file is part of erlang.mk and subject to the terms of the ISC License.
6656
6657 COVER_REPORT_DIR = cover
6658
6659 # Hook in coverage to ct
6660
6661 ifdef COVER
6662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
6664 # All modules in 'ebin'
6665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
6666
6667 test-build:: $(TEST_DIR)/ct.cover.spec
6668
6669 $(TEST_DIR)/ct.cover.spec:
6670 $(verbose) echo Cover mods: $(COVER_MODS)
6671 $(gen_verbose) printf "%s\n" \
6672 '{incl_mods,[$(subst $(space),$(comma),$(COVER_MODS))]}.' \
6673 '{export,"$(CURDIR)/ct.coverdata"}.' > $@
6674
6675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
6676 endif
6677 endif
6678 endif
6679
6680 # Core targets
6681
6682 ifdef COVER
6683 ifneq ($(COVER_REPORT_DIR),)
6684 tests::
6685 $(verbose) $(MAKE) --no-print-directory cover-report
6686 endif
6687 endif
6688
6689 clean:: coverdata-clean
6690
6691 ifneq ($(COVER_REPORT_DIR),)
6692 distclean:: cover-report-clean
6693 endif
6694
6695 help::
6696 $(verbose) printf "%s\n" "" \
6697 "Cover targets:" \
6698 " cover-report Generate a HTML coverage report from previously collected" \
6699 " cover data." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
6701 "" \
6702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
6703 "target tests additionally generates a HTML coverage report from the combined" \
6704 "coverdata files from each of these testing tools. HTML reports can be disabled" \
6705 "by setting COVER_REPORT_DIR to empty."
6706
6707 # Plugin specific targets
6708
6709 COVERDATA = $(filter-out all.coverdata,$(wildcard *.coverdata))
6710
6711 .PHONY: coverdata-clean
6712 coverdata-clean:
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
6714
6715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
6721 all.coverdata: $(COVERDATA)
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
6723
6724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
6725 # empty if you want the coverdata files but not the HTML report.
6726 ifneq ($(COVER_REPORT_DIR),)
6727
6728 .PHONY: cover-report-clean cover-report
6729
6730 cover-report-clean:
6731 $(gen_verbose) rm -rf $(COVER_REPORT_DIR)
6732
6733 ifeq ($(COVERDATA),)
6734 cover-report:
6735 else
6736
6737 # Modules which include eunit.hrl always contain one line without coverage
6738 # because eunit defines test/0 which is never called. We compensate for this.
6739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
6742
6743 define cover_report.erl
6744 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6745 Ms = cover:imported_modules(),
6746 [cover:analyse_to_file(M, "$(COVER_REPORT_DIR)/" ++ atom_to_list(M)
6747 ++ ".COVER.html", [html]) || M <- Ms],
6748 Report = [begin {ok, R} = cover:analyse(M, module), R end || M <- Ms],
6749 EunitHrlMods = [$(EUNIT_HRL_MODS)],
6750 Report1 = [{M, {Y, case lists:member(M, EunitHrlMods) of
6751 true -> N - 1; false -> N end}} || {M, {Y, N}} <- Report],
6752 TotalY = lists:sum([Y || {_, {Y, _}} <- Report1]),
6753 TotalN = lists:sum([N || {_, {_, N}} <- Report1]),
6754 Perc = fun(Y, N) -> case Y + N of 0 -> 100; S -> round(100 * Y / S) end end,
6755 TotalPerc = Perc(TotalY, TotalN),
6756 {ok, F} = file:open("$(COVER_REPORT_DIR)/index.html", [write]),
6757 io:format(F, "<!DOCTYPE html><html>~n"
6758 "<head><meta charset=\"UTF-8\">~n"
6759 "<title>Coverage report</title></head>~n"
6760 "<body>~n", []),
6761 io:format(F, "<h1>Coverage</h1>~n<p>Total: ~p%</p>~n", [TotalPerc]),
6762 io:format(F, "<table><tr><th>Module</th><th>Coverage</th></tr>~n", []),
6763 [io:format(F, "<tr><td><a href=\"~p.COVER.html\">~p</a></td>"
6764 "<td>~p%</td></tr>~n",
6765 [M, M, Perc(Y, N)]) || {M, {Y, N}} <- Report1],
6766 How = "$(subst $(space),$(comma)$(space),$(basename $(COVERDATA)))",
6767 Date = "$(shell date -u "+%Y-%m-%dT%H:%M:%SZ")",
6768 io:format(F, "</table>~n"
6769 "<p>Generated using ~s and erlang.mk on ~s.</p>~n"
6770 "</body></html>", [How, Date]),
6771 halt().
6772 endef
6773
6774 cover-report:
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
6776 $(gen_verbose) $(call erlang,$(cover_report.erl))
6777
6778 endif
6779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
6839
6840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6842 # This file is part of erlang.mk and subject to the terms of the ISC License.
6843
6844 # Fetch dependencies recursively (without building them).
6845
6846 .PHONY: fetch-deps fetch-doc-deps fetch-rel-deps fetch-test-deps \
6847 fetch-shell-deps
6848
6849 .PHONY: $(ERLANG_MK_RECURSIVE_DEPS_LIST) \
6850 $(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST) \
6851 $(ERLANG_MK_RECURSIVE_REL_DEPS_LIST) \
6852 $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST) \
6853 $(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST)
6854
6855 fetch-deps: $(ERLANG_MK_RECURSIVE_DEPS_LIST)
6856 fetch-doc-deps: $(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST)
6857 fetch-rel-deps: $(ERLANG_MK_RECURSIVE_REL_DEPS_LIST)
6858 fetch-test-deps: $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST)
6859 fetch-shell-deps: $(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST)
6860
6861 ifneq ($(SKIP_DEPS),)
6862 $(ERLANG_MK_RECURSIVE_DEPS_LIST) \
6863 $(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST) \
6864 $(ERLANG_MK_RECURSIVE_REL_DEPS_LIST) \
6865 $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST) \
6866 $(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST):
6867 $(verbose) :> $@
6868 else
6869 # By default, we fetch "normal" dependencies. They are also included no
6870 # matter the type of requested dependencies.
6871 #
6872 # $(ALL_DEPS_DIRS) includes $(BUILD_DEPS).
6873
6874 $(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_DEPS_DIRS)
6875 $(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST): $(ALL_DEPS_DIRS) $(ALL_DOC_DEPS_DIRS)
6876 $(ERLANG_MK_RECURSIVE_REL_DEPS_LIST): $(ALL_DEPS_DIRS) $(ALL_REL_DEPS_DIRS)
6877 $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST): $(ALL_DEPS_DIRS) $(ALL_TEST_DEPS_DIRS)
6878 $(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST): $(ALL_DEPS_DIRS) $(ALL_SHELL_DEPS_DIRS)
6879
6880 # Allow to use fetch-deps and $(DEP_TYPES) to fetch multiple types of
6881 # dependencies with a single target.
6882 ifneq ($(filter doc,$(DEP_TYPES)),)
6883 $(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_DOC_DEPS_DIRS)
6884 endif
6885 ifneq ($(filter rel,$(DEP_TYPES)),)
6886 $(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_REL_DEPS_DIRS)
6887 endif
6888 ifneq ($(filter test,$(DEP_TYPES)),)
6889 $(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_TEST_DEPS_DIRS)
6890 endif
6891 ifneq ($(filter shell,$(DEP_TYPES)),)
6892 $(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_SHELL_DEPS_DIRS)
6893 endif
6894
6895 ERLANG_MK_RECURSIVE_TMP_LIST := $(abspath $(ERLANG_MK_TMP)/recursive-tmp-deps.log)
6896
6897 $(ERLANG_MK_RECURSIVE_DEPS_LIST) \
6898 $(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST) \
6899 $(ERLANG_MK_RECURSIVE_REL_DEPS_LIST) \
6900 $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST) \
6901 $(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST):
6902 ifeq ($(IS_APP)$(IS_DEP),)
6903 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
6905 endif
6906 ifndef IS_APP
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
6908 $(MAKE) -C $$dep $@ \
6909 IS_APP=1 \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
6911 done
6912 endif
6913 $(verbose) set -e; for dep in $^ ; do \
6914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
6915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
6917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
6918 $(MAKE) -C $$dep fetch-deps \
6919 IS_DEP=1 \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
6921 fi \
6922 fi \
6923 done
6924 ifeq ($(IS_APP)$(IS_DEP),)
6925 $(verbose) sort < $(ERLANG_MK_RECURSIVE_TMP_LIST) | uniq > $@
6926 $(verbose) rm $(ERLANG_MK_RECURSIVE_TMP_LIST)
6927 endif
6928 endif # ifneq ($(SKIP_DEPS),)
6929
6930 # List dependencies recursively.
6931
6932 .PHONY: list-deps list-doc-deps list-rel-deps list-test-deps \
6933 list-shell-deps
6934
6935 list-deps: $(ERLANG_MK_RECURSIVE_DEPS_LIST)
6936 list-doc-deps: $(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST)
6937 list-rel-deps: $(ERLANG_MK_RECURSIVE_REL_DEPS_LIST)
6938 list-test-deps: $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST)
6939 list-shell-deps: $(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST)
6940
6941 list-deps list-doc-deps list-rel-deps list-test-deps list-shell-deps:
6942 $(verbose) cat $^
0 <!doctype html>
1 <html><head>
2 <script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
3 <script src="//cdn.jsdelivr.net/sockjs/1.0.3/sockjs.min.js"></script>
4 <script src="mqttws31.js" type="text/javascript"></script>
5
6 <style>
7 #cnvs {
8 border: none;
9 -moz-border-radius: 4px;
10 cursor: url(pencil.cur),crosshair;
11 position: absolute;
12 overflow: hidden;
13 width: 100%;
14 height: 100%;
15 }
16 #cnvs:active {
17 cursor: url(pencil.cur),crosshair;
18 }
19 body {
20 overflow: hidden;
21 }
22 </style>
23 <title>RabbitMQ Web MQTT Examples: Bunny Drawing</title>
24 <link href="main.css" rel="stylesheet" type="text/css"/>
25 </head><body lang="en">
26 <h1><a href="index.html">RabbitMQ Web MQTT Examples</a> > Bunny Drawing</h1>
27 <canvas id="cnvs"></canvas>
28 <script>
29 var send; var draw;
30 send = draw = function(){};
31
32 var lines = [];
33
34 var canvas = document.getElementById('cnvs');
35
36 if (canvas.getContext) {
37 var ctx = canvas.getContext('2d');
38
39 var img = new Image();
40 img.onload = function() {
41 ctx.drawImage(img, 230, 160);
42 };
43 img.src = 'bunny.png';
44
45 draw = function(p) {
46 ctx.beginPath();
47 ctx.moveTo(p.x1, p.y1);
48 ctx.lineTo(p.x2, p.y2);
49 ctx.stroke();
50 ctx.drawImage(img, 230, 160);
51 };
52
53 var do_resize = function() {
54 canvas.width = window.innerWidth;
55 canvas.height = window.innerHeight;
56
57 ctx.font = "bold 20px sans-serif";
58 ctx.fillStyle = "#444";
59 ctx.fillText("Draw wings on the bunny!", 260, 100);
60 ctx.font = "normal 16px sans-serif";
61 ctx.fillStyle = "#888";
62 ctx.fillText("(For more fun open a second browser)", 255, 130);
63
64 ctx.drawImage(img, 230, 160);
65
66 ctx.strokeStyle = "#fa0";
67 ctx.lineWidth = "10";
68 ctx.lineCap = "round";
69
70 $.map(lines, function (p) {
71 draw(p);
72 });
73 };
74
75 $(window).resize(do_resize);
76 $(do_resize);
77
78
79 var pos = $('#cnvs').position();
80 var prev = null;
81 $('#cnvs').mousedown(function(evt) {
82 evt.preventDefault();
83 evt.stopPropagation();
84 $('#cnvs').bind('mousemove', function(e) {
85 var curr = {x:e.pageX-pos.left, y:e.pageY-pos.top};
86 if (!prev) {
87 prev = curr;
88 return;
89 }
90 if (Math.sqrt(Math.pow(prev.x - curr.x, 2) +
91 Math.pow(prev.y - curr.y, 2)) > 8) {
92 var p = {x1:prev.x, y1:prev.y, x2:curr.x, y2:curr.y}
93 lines.push(p);
94 draw(p);
95 send(JSON.stringify(p));
96 prev = curr;
97 }
98 });
99 });
100 $('html').mouseup(function() {
101 prev = null;
102 $('#cnvs').unbind('mousemove');
103 });
104 }
105 else {
106 document.write("Sorry - this demo requires a browser with canvas tag support.");
107 }
108
109 // MQTT boilerplate
110
111 var debug = function(){
112 if (window.console && console.log && console.log.apply) {
113 console.log.apply(console, arguments);
114 }
115 };
116
117 var wsbroker = location.hostname; //mqtt websocket enabled broker
118 var wsport = 15675; // port for above
119
120 var client = new Paho.MQTT.Client(wsbroker, wsport, "/ws",
121 "myclientid_" + parseInt(Math.random() * 100, 10));
122
123 client.onConnectionLost = function (responseObject) {
124 debug("CONNECTION LOST - " + responseObject.errorMessage);
125 };
126
127 client.onMessageArrived = function (message) {
128 debug("RECEIVE ON " + message.destinationName + " PAYLOAD " + message.payloadString);
129
130 var p = JSON.parse(message.payloadString);
131 lines.push(p);
132 draw(p, true);
133 };
134
135 var options = {
136 timeout: 3,
137 onSuccess: function () {
138 debug("CONNECTION SUCCESS");
139 client.subscribe('/topic/bunny', {qos: 1});
140 },
141 onFailure: function (message) {
142 debug("CONNECTION FAILURE - " + message.errorMessage);
143 }
144 };
145
146 if (location.protocol == "https:") {
147 options.useSSL = true;
148 }
149
150 debug("CONNECT TO " + wsbroker + ":" + wsport);
151 client.connect(options);
152
153 $('#first input').focus(function() {
154 if (!has_had_focus) {
155 has_had_focus = true;
156 $(this).val("");
157 }
158 });
159
160 send = function(data) {
161 message = new Paho.MQTT.Message(data);
162 message.destinationName = "/topic/bunny";
163 debug("SEND ON " + message.destinationName + " PAYLOAD " + data);
164 client.send(message);
165 };
166
167 </script>
168 </body></html>
0 <!DOCTYPE html>
1 <html>
2 <head>
3 <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
4 <title>RabbitMQ Web MQTT Example</title>
5 <script src="//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
6 <script src="mqttws31.js" type="text/javascript"></script>
7 <style>
8 .box {
9 width: 440px;
10 float: left;
11 margin: 0 20px 0 20px;
12 }
13
14 .box div, .box input {
15 border: 1px solid;
16 -moz-border-radius: 4px;
17 border-radius: 4px;
18 width: 100%;
19 padding: 5px;
20 margin: 3px 0 10px 0;
21 }
22
23 .box div {
24 border-color: grey;
25 height: 300px;
26 overflow: auto;
27 }
28
29 div code {
30 display: block;
31 }
32
33 #first div code {
34 -moz-border-radius: 2px;
35 border-radius: 2px;
36 border: 1px solid #eee;
37 margin-bottom: 5px;
38 }
39
40 #second div {
41 font-size: 0.8em;
42 }
43 </style>
44 <link href="main.css" rel="stylesheet" type="text/css"/>
45 </head>
46 <body lang="en">
47 <h1>RabbitMQ Web MQTT Example</h1>
48
49 <div id="first" class="box">
50 <h2>Received</h2>
51 <div></div>
52 <form><input autocomplete="off" value="Type here..."></input></form>
53 </div>
54
55 <div id="second" class="box">
56 <h2>Logs</h2>
57 <div></div>
58 </div>
59
60 <script>
61 var has_had_focus = false;
62 var pipe = function(el_name, send) {
63 var div = $(el_name + ' div');
64 var inp = $(el_name + ' input');
65 var form = $(el_name + ' form');
66
67 var print = function(m, p) {
68 p = (p === undefined) ? '' : JSON.stringify(p);
69 div.append($("<code>").text(m + ' ' + p));
70 div.scrollTop(div.scrollTop() + 10000);
71 };
72
73 if (send) {
74 form.submit(function() {
75 send(inp.val());
76 inp.val('');
77 return false;
78 });
79 }
80 return print;
81 };
82
83 var print_first = pipe('#first', function(data) {
84 message = new Paho.MQTT.Message(data);
85 message.destinationName = "/topic/test";
86 debug("SEND ON " + message.destinationName + " PAYLOAD " + data);
87 client.send(message);
88 });
89
90 var debug = pipe('#second');
91
92 var wsbroker = location.hostname; //mqtt websocket enabled broker
93 var wsport = 15675; // port for above
94
95 var client = new Paho.MQTT.Client(wsbroker, wsport, "/ws",
96 "myclientid_" + parseInt(Math.random() * 100, 10));
97
98 client.onConnectionLost = function (responseObject) {
99 debug("CONNECTION LOST - " + responseObject.errorMessage);
100 };
101
102 client.onMessageArrived = function (message) {
103 debug("RECEIVE ON " + message.destinationName + " PAYLOAD " + message.payloadString);
104 print_first(message.payloadString);
105 };
106
107 var options = {
108 timeout: 3,
109 onSuccess: function () {
110 debug("CONNECTION SUCCESS");
111 client.subscribe('/topic/test', {qos: 1});
112 },
113 onFailure: function (message) {
114 debug("CONNECTION FAILURE - " + message.errorMessage);
115 }
116 };
117
118 if (location.protocol == "https:") {
119 options.useSSL = true;
120 }
121
122 debug("CONNECT TO " + wsbroker + ":" + wsport);
123 client.connect(options);
124
125 $('#first input').focus(function() {
126 if (!has_had_focus) {
127 has_had_focus = true;
128 $(this).val("");
129 }
130 });
131 </script>
132 </body>
133 </html>
0 <!doctype html>
1 <html lang="en">
2 <head>
3 <meta charset="utf-8">
4 <title>RabbitMQ Web MQTT Examples</title>
5 <link href="main.css" rel="stylesheet" type="text/css"/>
6 </head>
7 <body>
8 <h1>RabbitMQ Web MQTT Examples</h1>
9 <ul class="menu">
10 <li><a href="echo.html">Simple Echo Server</a></li>
11 <li><a href="bunny.html">Bunny Drawing</a></li>
12 </ul>
13 </body>
14 </html>
0 body {
1 font-family: "Arial";
2 color: #444;
3 }
4
5 h1, h2 {
6 color: #f60;
7 font-weight: normal;
8 }
9
10 h1 {
11 font-size: 1.5em;
12 }
13
14 h2 {
15 font-size: 1.2em;
16 margin: 0;
17 }
18
19 a {
20 color: #f60;
21 border: 1px solid #fda;
22 background: #fff0e0;
23 border-radius: 3px; -moz-border-radius: 3px;
24 padding: 2px;
25 text-decoration: none;
26 /* font-weight: bold; */
27 }
28
29 ul.menu {
30 list-style-type: none;
31 padding: 0;
32 margin: 0;
33 }
34
35 ul.menu li {
36 padding: 5px 0;
37 }
0 /*******************************************************************************
1 * Copyright (c) 2013 IBM Corp.
2 *
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * and Eclipse Distribution License v1.0 which accompany this distribution.
6 *
7 * The Eclipse Public License is available at
8 * http://www.eclipse.org/legal/epl-v10.html
9 * and the Eclipse Distribution License is available at
10 * http://www.eclipse.org/org/documents/edl-v10.php.
11 *
12 * Contributors:
13 * Andrew Banks - initial API and implementation and initial documentation
14 *******************************************************************************/
15
16
17 // Only expose a single object name in the global namespace.
18 // Everything must go through this module. Global Paho.MQTT module
19 // only has a single public function, client, which returns
20 // a Paho.MQTT client object given connection details.
21
22 /**
23 * Send and receive messages using web browsers.
24 * <p>
25 * This programming interface lets a JavaScript client application use the MQTT V3.1 or
26 * V3.1.1 protocol to connect to an MQTT-supporting messaging server.
27 *
28 * The function supported includes:
29 * <ol>
30 * <li>Connecting to and disconnecting from a server. The server is identified by its host name and port number.
31 * <li>Specifying options that relate to the communications link with the server,
32 * for example the frequency of keep-alive heartbeats, and whether SSL/TLS is required.
33 * <li>Subscribing to and receiving messages from MQTT Topics.
34 * <li>Publishing messages to MQTT Topics.
35 * </ol>
36 * <p>
37 * The API consists of two main objects:
38 * <dl>
39 * <dt><b>{@link Paho.MQTT.Client}</b></dt>
40 * <dd>This contains methods that provide the functionality of the API,
41 * including provision of callbacks that notify the application when a message
42 * arrives from or is delivered to the messaging server,
43 * or when the status of its connection to the messaging server changes.</dd>
44 * <dt><b>{@link Paho.MQTT.Message}</b></dt>
45 * <dd>This encapsulates the payload of the message along with various attributes
46 * associated with its delivery, in particular the destination to which it has
47 * been (or is about to be) sent.</dd>
48 * </dl>
49 * <p>
50 * The programming interface validates parameters passed to it, and will throw
51 * an Error containing an error message intended for developer use, if it detects
52 * an error with any parameter.
53 * <p>
54 * Example:
55 *
56 * <code><pre>
57 client = new Paho.MQTT.Client(location.hostname, Number(location.port), "clientId");
58 client.onConnectionLost = onConnectionLost;
59 client.onMessageArrived = onMessageArrived;
60 client.connect({onSuccess:onConnect});
61
62 function onConnect() {
63 // Once a connection has been made, make a subscription and send a message.
64 console.log("onConnect");
65 client.subscribe("/World");
66 message = new Paho.MQTT.Message("Hello");
67 message.destinationName = "/World";
68 client.send(message);
69 };
70 function onConnectionLost(responseObject) {
71 if (responseObject.errorCode !== 0)
72 console.log("onConnectionLost:"+responseObject.errorMessage);
73 };
74 function onMessageArrived(message) {
75 console.log("onMessageArrived:"+message.payloadString);
76 client.disconnect();
77 };
78 * </pre></code>
79 * @namespace Paho.MQTT
80 */
81
82 if (typeof Paho === "undefined") {
83 Paho = {};
84 }
85
86 Paho.MQTT = (function (global) {
87
88 // Private variables below, these are only visible inside the function closure
89 // which is used to define the module.
90
91 var version = "@VERSION@";
92 var buildLevel = "@BUILDLEVEL@";
93
94 /**
95 * Unique message type identifiers, with associated
96 * associated integer values.
97 * @private
98 */
99 var MESSAGE_TYPE = {
100 CONNECT: 1,
101 CONNACK: 2,
102 PUBLISH: 3,
103 PUBACK: 4,
104 PUBREC: 5,
105 PUBREL: 6,
106 PUBCOMP: 7,
107 SUBSCRIBE: 8,
108 SUBACK: 9,
109 UNSUBSCRIBE: 10,
110 UNSUBACK: 11,
111 PINGREQ: 12,
112 PINGRESP: 13,
113 DISCONNECT: 14
114 };
115
116 // Collection of utility methods used to simplify module code
117 // and promote the DRY pattern.
118
119 /**
120 * Validate an object's parameter names to ensure they
121 * match a list of expected variables name for this option
122 * type. Used to ensure option object passed into the API don't
123 * contain erroneous parameters.
124 * @param {Object} obj - User options object
125 * @param {Object} keys - valid keys and types that may exist in obj.
126 * @throws {Error} Invalid option parameter found.
127 * @private
128 */
129 var validate = function(obj, keys) {
130 for (var key in obj) {
131 if (obj.hasOwnProperty(key)) {
132 if (keys.hasOwnProperty(key)) {
133 if (typeof obj[key] !== keys[key])
134 throw new Error(format(ERROR.INVALID_TYPE, [typeof obj[key], key]));
135 } else {
136 var errorStr = "Unknown property, " + key + ". Valid properties are:";
137 for (var key in keys)
138 if (keys.hasOwnProperty(key))
139 errorStr = errorStr+" "+key;
140 throw new Error(errorStr);
141 }
142 }
143 }
144 };
145
146 /**
147 * Return a new function which runs the user function bound
148 * to a fixed scope.
149 * @param {function} User function
150 * @param {object} Function scope
151 * @return {function} User function bound to another scope
152 * @private
153 */
154 var scope = function (f, scope) {
155 return function () {
156 return f.apply(scope, arguments);
157 };
158 };
159
160 /**
161 * Unique message type identifiers, with associated
162 * associated integer values.
163 * @private
164 */
165 var ERROR = {
166 OK: {code:0, text:"AMQJSC0000I OK."},
167 CONNECT_TIMEOUT: {code:1, text:"AMQJSC0001E Connect timed out."},
168 SUBSCRIBE_TIMEOUT: {code:2, text:"AMQJS0002E Subscribe timed out."},
169 UNSUBSCRIBE_TIMEOUT: {code:3, text:"AMQJS0003E Unsubscribe timed out."},
170 PING_TIMEOUT: {code:4, text:"AMQJS0004E Ping timed out."},
171 INTERNAL_ERROR: {code:5, text:"AMQJS0005E Internal error. Error Message: {0}, Stack trace: {1}"},
172 CONNACK_RETURNCODE: {code:6, text:"AMQJS0006E Bad Connack return code:{0} {1}."},
173 SOCKET_ERROR: {code:7, text:"AMQJS0007E Socket error:{0}."},
174 SOCKET_CLOSE: {code:8, text:"AMQJS0008I Socket closed."},
175 MALFORMED_UTF: {code:9, text:"AMQJS0009E Malformed UTF data:{0} {1} {2}."},
176 UNSUPPORTED: {code:10, text:"AMQJS0010E {0} is not supported by this browser."},
177 INVALID_STATE: {code:11, text:"AMQJS0011E Invalid state {0}."},
178 INVALID_TYPE: {code:12, text:"AMQJS0012E Invalid type {0} for {1}."},
179 INVALID_ARGUMENT: {code:13, text:"AMQJS0013E Invalid argument {0} for {1}."},
180 UNSUPPORTED_OPERATION: {code:14, text:"AMQJS0014E Unsupported operation."},
181 INVALID_STORED_DATA: {code:15, text:"AMQJS0015E Invalid data in local storage key={0} value={1}."},
182 INVALID_MQTT_MESSAGE_TYPE: {code:16, text:"AMQJS0016E Invalid MQTT message type {0}."},
183 MALFORMED_UNICODE: {code:17, text:"AMQJS0017E Malformed Unicode string:{0} {1}."},
184 };
185
186 /** CONNACK RC Meaning. */
187 var CONNACK_RC = {
188 0:"Connection Accepted",
189 1:"Connection Refused: unacceptable protocol version",
190 2:"Connection Refused: identifier rejected",
191 3:"Connection Refused: server unavailable",
192 4:"Connection Refused: bad user name or password",
193 5:"Connection Refused: not authorized"
194 };
195
196 /**
197 * Format an error message text.
198 * @private
199 * @param {error} ERROR.KEY value above.
200 * @param {substitutions} [array] substituted into the text.
201 * @return the text with the substitutions made.
202 */
203 var format = function(error, substitutions) {
204 var text = error.text;
205 if (substitutions) {
206 var field,start;
207 for (var i=0; i<substitutions.length; i++) {
208 field = "{"+i+"}";
209 start = text.indexOf(field);
210 if(start > 0) {
211 var part1 = text.substring(0,start);
212 var part2 = text.substring(start+field.length);
213 text = part1+substitutions[i]+part2;
214 }
215 }
216 }
217 return text;
218 };
219
220 //MQTT protocol and version 6 M Q I s d p 3
221 var MqttProtoIdentifierv3 = [0x00,0x06,0x4d,0x51,0x49,0x73,0x64,0x70,0x03];
222 //MQTT proto/version for 311 4 M Q T T 4
223 var MqttProtoIdentifierv4 = [0x00,0x04,0x4d,0x51,0x54,0x54,0x04];
224
225 /**
226 * Construct an MQTT wire protocol message.
227 * @param type MQTT packet type.
228 * @param options optional wire message attributes.
229 *
230 * Optional properties
231 *
232 * messageIdentifier: message ID in the range [0..65535]
233 * payloadMessage: Application Message - PUBLISH only
234 * connectStrings: array of 0 or more Strings to be put into the CONNECT payload
235 * topics: array of strings (SUBSCRIBE, UNSUBSCRIBE)
236 * requestQoS: array of QoS values [0..2]
237 *
238 * "Flag" properties
239 * cleanSession: true if present / false if absent (CONNECT)
240 * willMessage: true if present / false if absent (CONNECT)
241 * isRetained: true if present / false if absent (CONNECT)
242 * userName: true if present / false if absent (CONNECT)
243 * password: true if present / false if absent (CONNECT)
244 * keepAliveInterval: integer [0..65535] (CONNECT)
245 *
246 * @private
247 * @ignore
248 */
249 var WireMessage = function (type, options) {
250 this.type = type;
251 for (var name in options) {
252 if (options.hasOwnProperty(name)) {
253 this[name] = options[name];
254 }
255 }
256 };
257
258 WireMessage.prototype.encode = function() {
259 // Compute the first byte of the fixed header
260 var first = ((this.type & 0x0f) << 4);
261
262 /*
263 * Now calculate the length of the variable header + payload by adding up the lengths
264 * of all the component parts
265 */
266
267 var remLength = 0;
268 var topicStrLength = new Array();
269 var destinationNameLength = 0;
270
271 // if the message contains a messageIdentifier then we need two bytes for that
272 if (this.messageIdentifier != undefined)
273 remLength += 2;
274
275 switch(this.type) {
276 // If this a Connect then we need to include 12 bytes for its header
277 case MESSAGE_TYPE.CONNECT:
278 switch(this.mqttVersion) {
279 case 3:
280 remLength += MqttProtoIdentifierv3.length + 3;
281 break;
282 case 4:
283 remLength += MqttProtoIdentifierv4.length + 3;
284 break;
285 }
286
287 remLength += UTF8Length(this.clientId) + 2;
288 if (this.willMessage != undefined) {
289 remLength += UTF8Length(this.willMessage.destinationName) + 2;
290 // Will message is always a string, sent as UTF-8 characters with a preceding length.
291 var willMessagePayloadBytes = this.willMessage.payloadBytes;
292 if (!(willMessagePayloadBytes instanceof Uint8Array))
293 willMessagePayloadBytes = new Uint8Array(payloadBytes);
294 remLength += willMessagePayloadBytes.byteLength +2;
295 }
296 if (this.userName != undefined)
297 remLength += UTF8Length(this.userName) + 2;
298 if (this.password != undefined)
299 remLength += UTF8Length(this.password) + 2;
300 break;
301
302 // Subscribe, Unsubscribe can both contain topic strings
303 case MESSAGE_TYPE.SUBSCRIBE:
304 first |= 0x02; // Qos = 1;
305 for ( var i = 0; i < this.topics.length; i++) {
306 topicStrLength[i] = UTF8Length(this.topics[i]);
307 remLength += topicStrLength[i] + 2;
308 }
309 remLength += this.requestedQos.length; // 1 byte for each topic's Qos
310 // QoS on Subscribe only
311 break;
312
313 case MESSAGE_TYPE.UNSUBSCRIBE:
314 first |= 0x02; // Qos = 1;
315 for ( var i = 0; i < this.topics.length; i++) {
316 topicStrLength[i] = UTF8Length(this.topics[i]);
317 remLength += topicStrLength[i] + 2;
318 }
319 break;
320
321 case MESSAGE_TYPE.PUBREL:
322 first |= 0x02; // Qos = 1;
323 break;
324
325 case MESSAGE_TYPE.PUBLISH:
326 if (this.payloadMessage.duplicate) first |= 0x08;
327 first = first |= (this.payloadMessage.qos << 1);
328 if (this.payloadMessage.retained) first |= 0x01;
329 destinationNameLength = UTF8Length(this.payloadMessage.destinationName);
330 remLength += destinationNameLength + 2;
331 var payloadBytes = this.payloadMessage.payloadBytes;
332 remLength += payloadBytes.byteLength;
333 if (payloadBytes instanceof ArrayBuffer)
334 payloadBytes = new Uint8Array(payloadBytes);
335 else if (!(payloadBytes instanceof Uint8Array))
336 payloadBytes = new Uint8Array(payloadBytes.buffer);
337 break;
338
339 case MESSAGE_TYPE.DISCONNECT:
340 break;
341
342 default:
343 ;
344 }
345
346 // Now we can allocate a buffer for the message
347
348 var mbi = encodeMBI(remLength); // Convert the length to MQTT MBI format
349 var pos = mbi.length + 1; // Offset of start of variable header
350 var buffer = new ArrayBuffer(remLength + pos);
351 var byteStream = new Uint8Array(buffer); // view it as a sequence of bytes
352
353 //Write the fixed header into the buffer
354 byteStream[0] = first;
355 byteStream.set(mbi,1);
356
357 // If this is a PUBLISH then the variable header starts with a topic
358 if (this.type == MESSAGE_TYPE.PUBLISH)
359 pos = writeString(this.payloadMessage.destinationName, destinationNameLength, byteStream, pos);
360 // If this is a CONNECT then the variable header contains the protocol name/version, flags and keepalive time
361
362 else if (this.type == MESSAGE_TYPE.CONNECT) {
363 switch (this.mqttVersion) {
364 case 3:
365 byteStream.set(MqttProtoIdentifierv3, pos);
366 pos += MqttProtoIdentifierv3.length;
367 break;
368 case 4:
369 byteStream.set(MqttProtoIdentifierv4, pos);
370 pos += MqttProtoIdentifierv4.length;
371 break;
372 }
373 var connectFlags = 0;
374 if (this.cleanSession)
375 connectFlags = 0x02;
376 if (this.willMessage != undefined ) {
377 connectFlags |= 0x04;
378 connectFlags |= (this.willMessage.qos<<3);
379 if (this.willMessage.retained) {
380 connectFlags |= 0x20;
381 }
382 }
383 if (this.userName != undefined)
384 connectFlags |= 0x80;
385 if (this.password != undefined)
386 connectFlags |= 0x40;
387 byteStream[pos++] = connectFlags;
388 pos = writeUint16 (this.keepAliveInterval, byteStream, pos);
389 }
390
391 // Output the messageIdentifier - if there is one
392 if (this.messageIdentifier != undefined)
393 pos = writeUint16 (this.messageIdentifier, byteStream, pos);
394
395 switch(this.type) {
396 case MESSAGE_TYPE.CONNECT:
397 pos = writeString(this.clientId, UTF8Length(this.clientId), byteStream, pos);
398 if (this.willMessage != undefined) {
399 pos = writeString(this.willMessage.destinationName, UTF8Length(this.willMessage.destinationName), byteStream, pos);
400 pos = writeUint16(willMessagePayloadBytes.byteLength, byteStream, pos);
401 byteStream.set(willMessagePayloadBytes, pos);
402 pos += willMessagePayloadBytes.byteLength;
403
404 }
405 if (this.userName != undefined)
406 pos = writeString(this.userName, UTF8Length(this.userName), byteStream, pos);
407 if (this.password != undefined)
408 pos = writeString(this.password, UTF8Length(this.password), byteStream, pos);
409 break;
410
411 case MESSAGE_TYPE.PUBLISH:
412 // PUBLISH has a text or binary payload, if text do not add a 2 byte length field, just the UTF characters.
413 byteStream.set(payloadBytes, pos);
414
415 break;
416
417 // case MESSAGE_TYPE.PUBREC:
418 // case MESSAGE_TYPE.PUBREL:
419 // case MESSAGE_TYPE.PUBCOMP:
420 // break;
421
422 case MESSAGE_TYPE.SUBSCRIBE:
423 // SUBSCRIBE has a list of topic strings and request QoS
424 for (var i=0; i<this.topics.length; i++) {
425 pos = writeString(this.topics[i], topicStrLength[i], byteStream, pos);
426 byteStream[pos++] = this.requestedQos[i];
427 }
428 break;
429
430 case MESSAGE_TYPE.UNSUBSCRIBE:
431 // UNSUBSCRIBE has a list of topic strings
432 for (var i=0; i<this.topics.length; i++)
433 pos = writeString(this.topics[i], topicStrLength[i], byteStream, pos);
434 break;
435
436 default:
437 // Do nothing.
438 }
439
440 return buffer;
441 }
442
443 function decodeMessage(input,pos) {
444 var startingPos = pos;
445 var first = input[pos];
446 var type = first >> 4;
447 var messageInfo = first &= 0x0f;
448 pos += 1;
449
450
451 // Decode the remaining length (MBI format)
452
453 var digit;
454 var remLength = 0;
455 var multiplier = 1;
456 do {
457 if (pos == input.length) {
458 return [null,startingPos];
459 }
460 digit = input[pos++];
461 remLength += ((digit & 0x7F) * multiplier);
462 multiplier *= 128;
463 } while ((digit & 0x80) != 0);
464
465 var endPos = pos+remLength;
466 if (endPos > input.length) {
467 return [null,startingPos];
468 }
469
470 var wireMessage = new WireMessage(type);
471 switch(type) {
472 case MESSAGE_TYPE.CONNACK:
473 var connectAcknowledgeFlags = input[pos++];
474 if (connectAcknowledgeFlags & 0x01)
475 wireMessage.sessionPresent = true;
476 wireMessage.returnCode = input[pos++];
477 break;
478
479 case MESSAGE_TYPE.PUBLISH:
480 var qos = (messageInfo >> 1) & 0x03;
481
482 var len = readUint16(input, pos);
483 pos += 2;
484 var topicName = parseUTF8(input, pos, len);
485 pos += len;
486 // If QoS 1 or 2 there will be a messageIdentifier
487 if (qos > 0) {
488 wireMessage.messageIdentifier = readUint16(input, pos);
489 pos += 2;
490 }
491
492 var message = new Paho.MQTT.Message(input.subarray(pos, endPos));
493 if ((messageInfo & 0x01) == 0x01)
494 message.retained = true;
495 if ((messageInfo & 0x08) == 0x08)
496 message.duplicate = true;
497 message.qos = qos;
498 message.destinationName = topicName;
499 wireMessage.payloadMessage = message;
500 break;
501
502 case MESSAGE_TYPE.PUBACK:
503 case MESSAGE_TYPE.PUBREC:
504 case MESSAGE_TYPE.PUBREL:
505 case MESSAGE_TYPE.PUBCOMP:
506 case MESSAGE_TYPE.UNSUBACK:
507 wireMessage.messageIdentifier = readUint16(input, pos);
508 break;
509
510 case MESSAGE_TYPE.SUBACK:
511 wireMessage.messageIdentifier = readUint16(input, pos);
512 pos += 2;
513 wireMessage.returnCode = input.subarray(pos, endPos);
514 break;
515
516 default:
517 ;
518 }
519
520 return [wireMessage,endPos];
521 }
522
523 function writeUint16(input, buffer, offset) {
524 buffer[offset++] = input >> 8; //MSB
525 buffer[offset++] = input % 256; //LSB
526 return offset;
527 }
528
529 function writeString(input, utf8Length, buffer, offset) {
530 offset = writeUint16(utf8Length, buffer, offset);
531 stringToUTF8(input, buffer, offset);
532 return offset + utf8Length;
533 }
534
535 function readUint16(buffer, offset) {
536 return 256*buffer[offset] + buffer[offset+1];
537 }
538
539 /**
540 * Encodes an MQTT Multi-Byte Integer
541 * @private
542 */
543 function encodeMBI(number) {
544 var output = new Array(1);
545 var numBytes = 0;
546
547 do {
548 var digit = number % 128;
549 number = number >> 7;
550 if (number > 0) {
551 digit |= 0x80;
552 }
553 output[numBytes++] = digit;
554 } while ( (number > 0) && (numBytes<4) );
555
556 return output;
557 }
558
559 /**
560 * Takes a String and calculates its length in bytes when encoded in UTF8.
561 * @private
562 */
563 function UTF8Length(input) {
564 var output = 0;
565 for (var i = 0; i<input.length; i++)
566 {
567 var charCode = input.charCodeAt(i);
568 if (charCode > 0x7FF)
569 {
570 // Surrogate pair means its a 4 byte character
571 if (0xD800 <= charCode && charCode <= 0xDBFF)
572 {
573 i++;
574 output++;
575 }
576 output +=3;
577 }
578 else if (charCode > 0x7F)
579 output +=2;
580 else
581 output++;
582 }
583 return output;
584 }
585
586 /**
587 * Takes a String and writes it into an array as UTF8 encoded bytes.
588 * @private
589 */
590 function stringToUTF8(input, output, start) {
591 var pos = start;
592 for (var i = 0; i<input.length; i++) {
593 var charCode = input.charCodeAt(i);
594
595 // Check for a surrogate pair.
596 if (0xD800 <= charCode && charCode <= 0xDBFF) {
597 var lowCharCode = input.charCodeAt(++i);
598 if (isNaN(lowCharCode)) {
599 throw new Error(format(ERROR.MALFORMED_UNICODE, [charCode, lowCharCode]));
600 }
601 charCode = ((charCode - 0xD800)<<10) + (lowCharCode - 0xDC00) + 0x10000;
602
603 }
604
605 if (charCode <= 0x7F) {
606 output[pos++] = charCode;
607 } else if (charCode <= 0x7FF) {
608 output[pos++] = charCode>>6 & 0x1F | 0xC0;
609 output[pos++] = charCode & 0x3F | 0x80;
610 } else if (charCode <= 0xFFFF) {
611 output[pos++] = charCode>>12 & 0x0F | 0xE0;
612 output[pos++] = charCode>>6 & 0x3F | 0x80;
613 output[pos++] = charCode & 0x3F | 0x80;
614 } else {
615 output[pos++] = charCode>>18 & 0x07 | 0xF0;
616 output[pos++] = charCode>>12 & 0x3F | 0x80;
617 output[pos++] = charCode>>6 & 0x3F | 0x80;
618 output[pos++] = charCode & 0x3F | 0x80;
619 };
620 }
621 return output;
622 }
623
624 function parseUTF8(input, offset, length) {
625 var output = "";
626 var utf16;
627 var pos = offset;
628
629 while (pos < offset+length)
630 {
631 var byte1 = input[pos++];
632 if (byte1 < 128)
633 utf16 = byte1;
634 else
635 {
636 var byte2 = input[pos++]-128;
637 if (byte2 < 0)
638 throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16),""]));
639 if (byte1 < 0xE0) // 2 byte character
640 utf16 = 64*(byte1-0xC0) + byte2;
641 else
642 {
643 var byte3 = input[pos++]-128;
644 if (byte3 < 0)
645 throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16), byte3.toString(16)]));
646 if (byte1 < 0xF0) // 3 byte character
647 utf16 = 4096*(byte1-0xE0) + 64*byte2 + byte3;
648 else
649 {
650 var byte4 = input[pos++]-128;
651 if (byte4 < 0)
652 throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16), byte3.toString(16), byte4.toString(16)]));
653 if (byte1 < 0xF8) // 4 byte character
654 utf16 = 262144*(byte1-0xF0) + 4096*byte2 + 64*byte3 + byte4;
655 else // longer encodings are not supported
656 throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16), byte3.toString(16), byte4.toString(16)]));
657 }
658 }
659 }
660
661 if (utf16 > 0xFFFF) // 4 byte character - express as a surrogate pair
662 {
663 utf16 -= 0x10000;
664 output += String.fromCharCode(0xD800 + (utf16 >> 10)); // lead character
665 utf16 = 0xDC00 + (utf16 & 0x3FF); // trail character
666 }
667 output += String.fromCharCode(utf16);
668 }
669 return output;
670 }
671
672 /**
673 * Repeat keepalive requests, monitor responses.
674 * @ignore
675 */
676 var Pinger = function(client, window, keepAliveInterval) {
677 this._client = client;
678 this._window = window;
679 this._keepAliveInterval = keepAliveInterval*1000;
680 this.isReset = false;
681
682 var pingReq = new WireMessage(MESSAGE_TYPE.PINGREQ).encode();
683
684 var doTimeout = function (pinger) {
685 return function () {
686 return doPing.apply(pinger);
687 };
688 };
689
690 /** @ignore */
691 var doPing = function() {
692 if (!this.isReset) {
693 this._client._trace("Pinger.doPing", "Timed out");
694 this._client._disconnected( ERROR.PING_TIMEOUT.code , format(ERROR.PING_TIMEOUT));
695 } else {
696 this.isReset = false;
697 this._client._trace("Pinger.doPing", "send PINGREQ");
698 this._client.socket.send(pingReq);
699 this.timeout = this._window.setTimeout(doTimeout(this), this._keepAliveInterval);
700 }
701 }
702
703 this.reset = function() {
704 this.isReset = true;
705 this._window.clearTimeout(this.timeout);
706 if (this._keepAliveInterval > 0)
707 this.timeout = setTimeout(doTimeout(this), this._keepAliveInterval);
708 }
709
710 this.cancel = function() {
711 this._window.clearTimeout(this.timeout);
712 }
713 };
714
715 /**
716 * Monitor request completion.
717 * @ignore
718 */
719 var Timeout = function(client, window, timeoutSeconds, action, args) {
720 this._window = window;
721 if (!timeoutSeconds)
722 timeoutSeconds = 30;
723
724 var doTimeout = function (action, client, args) {
725 return function () {
726 return action.apply(client, args);
727 };
728 };
729 this.timeout = setTimeout(doTimeout(action, client, args), timeoutSeconds * 1000);
730
731 this.cancel = function() {
732 this._window.clearTimeout(this.timeout);
733 }
734 };
735
736 /*
737 * Internal implementation of the Websockets MQTT V3.1 client.
738 *
739 * @name Paho.MQTT.ClientImpl @constructor
740 * @param {String} host the DNS nameof the webSocket host.
741 * @param {Number} port the port number for that host.
742 * @param {String} clientId the MQ client identifier.
743 */
744 var ClientImpl = function (uri, host, port, path, clientId) {
745 // Check dependencies are satisfied in this browser.
746 if (!("WebSocket" in global && global["WebSocket"] !== null)) {
747 throw new Error(format(ERROR.UNSUPPORTED, ["WebSocket"]));
748 }
749 if (!("localStorage" in global && global["localStorage"] !== null)) {
750 throw new Error(format(ERROR.UNSUPPORTED, ["localStorage"]));
751 }
752 if (!("ArrayBuffer" in global && global["ArrayBuffer"] !== null)) {
753 throw new Error(format(ERROR.UNSUPPORTED, ["ArrayBuffer"]));
754 }
755 this._trace("Paho.MQTT.Client", uri, host, port, path, clientId);
756
757 this.host = host;
758 this.port = port;
759 this.path = path;
760 this.uri = uri;
761 this.clientId = clientId;
762
763 // Local storagekeys are qualified with the following string.
764 // The conditional inclusion of path in the key is for backward
765 // compatibility to when the path was not configurable and assumed to
766 // be /mqtt
767 this._localKey=host+":"+port+(path!="/mqtt"?":"+path:"")+":"+clientId+":";
768
769 // Create private instance-only message queue
770 // Internal queue of messages to be sent, in sending order.
771 this._msg_queue = [];
772
773 // Messages we have sent and are expecting a response for, indexed by their respective message ids.
774 this._sentMessages = {};
775
776 // Messages we have received and acknowleged and are expecting a confirm message for
777 // indexed by their respective message ids.
778 this._receivedMessages = {};
779
780 // Internal list of callbacks to be executed when messages
781 // have been successfully sent over web socket, e.g. disconnect
782 // when it doesn't have to wait for ACK, just message is dispatched.
783 this._notify_msg_sent = {};
784
785 // Unique identifier for SEND messages, incrementing
786 // counter as messages are sent.
787 this._message_identifier = 1;
788
789 // Used to determine the transmission sequence of stored sent messages.
790 this._sequence = 0;
791
792
793 // Load the local state, if any, from the saved version, only restore state relevant to this client.
794 for (var key in localStorage)
795 if ( key.indexOf("Sent:"+this._localKey) == 0
796 || key.indexOf("Received:"+this._localKey) == 0)
797 this.restore(key);
798 };
799
800 // Messaging Client public instance members.
801 ClientImpl.prototype.host;
802 ClientImpl.prototype.port;
803 ClientImpl.prototype.path;
804 ClientImpl.prototype.uri;
805 ClientImpl.prototype.clientId;
806
807 // Messaging Client private instance members.
808 ClientImpl.prototype.socket;
809 /* true once we have received an acknowledgement to a CONNECT packet. */
810 ClientImpl.prototype.connected = false;
811 /* The largest message identifier allowed, may not be larger than 2**16 but
812 * if set smaller reduces the maximum number of outbound messages allowed.
813 */
814 ClientImpl.prototype.maxMessageIdentifier = 65536;
815 ClientImpl.prototype.connectOptions;
816 ClientImpl.prototype.hostIndex;
817 ClientImpl.prototype.onConnectionLost;
818 ClientImpl.prototype.onMessageDelivered;
819 ClientImpl.prototype.onMessageArrived;
820 ClientImpl.prototype.traceFunction;
821 ClientImpl.prototype._msg_queue = null;
822 ClientImpl.prototype._connectTimeout;
823 /* The sendPinger monitors how long we allow before we send data to prove to the server that we are alive. */
824 ClientImpl.prototype.sendPinger = null;
825 /* The receivePinger monitors how long we allow before we require evidence that the server is alive. */
826 ClientImpl.prototype.receivePinger = null;
827
828 ClientImpl.prototype.receiveBuffer = null;
829
830 ClientImpl.prototype._traceBuffer = null;
831 ClientImpl.prototype._MAX_TRACE_ENTRIES = 100;
832
833 ClientImpl.prototype.connect = function (connectOptions) {
834 var connectOptionsMasked = this._traceMask(connectOptions, "password");
835 this._trace("Client.connect", connectOptionsMasked, this.socket, this.connected);
836
837 if (this.connected)
838 throw new Error(format(ERROR.INVALID_STATE, ["already connected"]));
839 if (this.socket)
840 throw new Error(format(ERROR.INVALID_STATE, ["already connected"]));
841
842 this.connectOptions = connectOptions;
843
844 if (connectOptions.uris) {
845 this.hostIndex = 0;
846 this._doConnect(connectOptions.uris[0]);
847 } else {
848 this._doConnect(this.uri);
849 }
850
851 };
852
853 ClientImpl.prototype.subscribe = function (filter, subscribeOptions) {
854 this._trace("Client.subscribe", filter, subscribeOptions);
855
856 if (!this.connected)
857 throw new Error(format(ERROR.INVALID_STATE, ["not connected"]));
858
859 var wireMessage = new WireMessage(MESSAGE_TYPE.SUBSCRIBE);
860 wireMessage.topics=[filter];
861 if (subscribeOptions.qos != undefined)
862 wireMessage.requestedQos = [subscribeOptions.qos];
863 else
864 wireMessage.requestedQos = [0];
865
866 if (subscribeOptions.onSuccess) {
867 wireMessage.onSuccess = function(grantedQos) {subscribeOptions.onSuccess({invocationContext:subscribeOptions.invocationContext,grantedQos:grantedQos});};
868 }
869
870 if (subscribeOptions.onFailure) {
871 wireMessage.onFailure = function(errorCode) {subscribeOptions.onFailure({invocationContext:subscribeOptions.invocationContext,errorCode:errorCode});};
872 }
873
874 if (subscribeOptions.timeout) {
875 wireMessage.timeOut = new Timeout(this, window, subscribeOptions.timeout, subscribeOptions.onFailure
876 , [{invocationContext:subscribeOptions.invocationContext,
877 errorCode:ERROR.SUBSCRIBE_TIMEOUT.code,
878 errorMessage:format(ERROR.SUBSCRIBE_TIMEOUT)}]);
879 }
880
881 // All subscriptions return a SUBACK.
882 this._requires_ack(wireMessage);
883 this._schedule_message(wireMessage);
884 };
885
886 /** @ignore */
887 ClientImpl.prototype.unsubscribe = function(filter, unsubscribeOptions) {
888 this._trace("Client.unsubscribe", filter, unsubscribeOptions);
889
890 if (!this.connected)
891 throw new Error(format(ERROR.INVALID_STATE, ["not connected"]));
892
893 var wireMessage = new WireMessage(MESSAGE_TYPE.UNSUBSCRIBE);
894 wireMessage.topics = [filter];
895
896 if (unsubscribeOptions.onSuccess) {
897 wireMessage.callback = function() {unsubscribeOptions.onSuccess({invocationContext:unsubscribeOptions.invocationContext});};
898 }
899 if (unsubscribeOptions.timeout) {
900 wireMessage.timeOut = new Timeout(this, window, unsubscribeOptions.timeout, unsubscribeOptions.onFailure
901 , [{invocationContext:unsubscribeOptions.invocationContext,
902 errorCode:ERROR.UNSUBSCRIBE_TIMEOUT.code,
903 errorMessage:format(ERROR.UNSUBSCRIBE_TIMEOUT)}]);
904 }
905
906 // All unsubscribes return a SUBACK.
907 this._requires_ack(wireMessage);
908 this._schedule_message(wireMessage);
909 };
910
911 ClientImpl.prototype.send = function (message) {
912 this._trace("Client.send", message);
913
914 if (!this.connected)
915 throw new Error(format(ERROR.INVALID_STATE, ["not connected"]));
916
917 wireMessage = new WireMessage(MESSAGE_TYPE.PUBLISH);
918 wireMessage.payloadMessage = message;
919
920 if (message.qos > 0)
921 this._requires_ack(wireMessage);
922 else if (this.onMessageDelivered)
923 this._notify_msg_sent[wireMessage] = this.onMessageDelivered(wireMessage.payloadMessage);
924 this._schedule_message(wireMessage);
925 };
926
927 ClientImpl.prototype.disconnect = function () {
928 this._trace("Client.disconnect");
929
930 if (!this.socket)
931 throw new Error(format(ERROR.INVALID_STATE, ["not connecting or connected"]));
932
933 wireMessage = new WireMessage(MESSAGE_TYPE.DISCONNECT);
934
935 // Run the disconnected call back as soon as the message has been sent,
936 // in case of a failure later on in the disconnect processing.
937 // as a consequence, the _disconected call back may be run several times.
938 this._notify_msg_sent[wireMessage] = scope(this._disconnected, this);
939
940 this._schedule_message(wireMessage);
941 };
942
943 ClientImpl.prototype.getTraceLog = function () {
944 if ( this._traceBuffer !== null ) {
945 this._trace("Client.getTraceLog", new Date());
946 this._trace("Client.getTraceLog in flight messages", this._sentMessages.length);
947 for (var key in this._sentMessages)
948 this._trace("_sentMessages ",key, this._sentMessages[key]);
949 for (var key in this._receivedMessages)
950 this._trace("_receivedMessages ",key, this._receivedMessages[key]);
951
952 return this._traceBuffer;
953 }
954 };
955
956 ClientImpl.prototype.startTrace = function () {
957 if ( this._traceBuffer === null ) {
958 this._traceBuffer = [];
959 }
960 this._trace("Client.startTrace", new Date(), version);
961 };
962
963 ClientImpl.prototype.stopTrace = function () {
964 delete this._traceBuffer;
965 };
966
967 ClientImpl.prototype._doConnect = function (wsurl) {
968 // When the socket is open, this client will send the CONNECT WireMessage using the saved parameters.
969 if (this.connectOptions.useSSL) {
970 var uriParts = wsurl.split(":");
971 uriParts[0] = "wss";
972 wsurl = uriParts.join(":");
973 }
974 this.connected = false;
975 if (this.connectOptions.mqttVersion < 4) {
976 this.socket = new WebSocket(wsurl, ["mqttv3.1"]);
977 } else {
978 this.socket = new WebSocket(wsurl, ["mqtt"]);
979 }
980 this.socket.binaryType = 'arraybuffer';
981
982 this.socket.onopen = scope(this._on_socket_open, this);
983 this.socket.onmessage = scope(this._on_socket_message, this);
984 this.socket.onerror = scope(this._on_socket_error, this);
985 this.socket.onclose = scope(this._on_socket_close, this);
986
987 this.sendPinger = new Pinger(this, window, this.connectOptions.keepAliveInterval);
988 this.receivePinger = new Pinger(this, window, this.connectOptions.keepAliveInterval);
989
990 this._connectTimeout = new Timeout(this, window, this.connectOptions.timeout, this._disconnected, [ERROR.CONNECT_TIMEOUT.code, format(ERROR.CONNECT_TIMEOUT)]);
991 };
992
993
994 // Schedule a new message to be sent over the WebSockets
995 // connection. CONNECT messages cause WebSocket connection
996 // to be started. All other messages are queued internally
997 // until this has happened. When WS connection starts, process
998 // all outstanding messages.
999 ClientImpl.prototype._schedule_message = function (message) {
1000 this._msg_queue.push(message);
1001 // Process outstanding messages in the queue if we have an open socket, and have received CONNACK.
1002 if (this.connected) {
1003 this._process_queue();
1004 }
1005 };
1006
1007 ClientImpl.prototype.store = function(prefix, wireMessage) {
1008 var storedMessage = {type:wireMessage.type, messageIdentifier:wireMessage.messageIdentifier, version:1};
1009
1010 switch(wireMessage.type) {
1011 case MESSAGE_TYPE.PUBLISH:
1012 if(wireMessage.pubRecReceived)
1013 storedMessage.pubRecReceived = true;
1014
1015 // Convert the payload to a hex string.
1016 storedMessage.payloadMessage = {};
1017 var hex = "";
1018 var messageBytes = wireMessage.payloadMessage.payloadBytes;
1019 for (var i=0; i<messageBytes.length; i++) {
1020 if (messageBytes[i] <= 0xF)
1021 hex = hex+"0"+messageBytes[i].toString(16);
1022 else
1023 hex = hex+messageBytes[i].toString(16);
1024 }
1025 storedMessage.payloadMessage.payloadHex = hex;
1026
1027 storedMessage.payloadMessage.qos = wireMessage.payloadMessage.qos;
1028 storedMessage.payloadMessage.destinationName = wireMessage.payloadMessage.destinationName;
1029 if (wireMessage.payloadMessage.duplicate)
1030 storedMessage.payloadMessage.duplicate = true;
1031 if (wireMessage.payloadMessage.retained)
1032 storedMessage.payloadMessage.retained = true;
1033
1034 // Add a sequence number to sent messages.
1035 if ( prefix.indexOf("Sent:") == 0 ) {
1036 if ( wireMessage.sequence === undefined )
1037 wireMessage.sequence = ++this._sequence;
1038 storedMessage.sequence = wireMessage.sequence;
1039 }
1040 break;
1041
1042 default:
1043 throw Error(format(ERROR.INVALID_STORED_DATA, [key, storedMessage]));
1044 }
1045 localStorage.setItem(prefix+this._localKey+wireMessage.messageIdentifier, JSON.stringify(storedMessage));
1046 };
1047
1048 ClientImpl.prototype.restore = function(key) {
1049 var value = localStorage.getItem(key);
1050 var storedMessage = JSON.parse(value);
1051
1052 var wireMessage = new WireMessage(storedMessage.type, storedMessage);
1053
1054 switch(storedMessage.type) {
1055 case MESSAGE_TYPE.PUBLISH:
1056 // Replace the payload message with a Message object.
1057 var hex = storedMessage.payloadMessage.payloadHex;
1058 var buffer = new ArrayBuffer((hex.length)/2);
1059 var byteStream = new Uint8Array(buffer);
1060 var i = 0;
1061 while (hex.length >= 2) {
1062 var x = parseInt(hex.substring(0, 2), 16);
1063 hex = hex.substring(2, hex.length);
1064 byteStream[i++] = x;
1065 }
1066 var payloadMessage = new Paho.MQTT.Message(byteStream);
1067
1068 payloadMessage.qos = storedMessage.payloadMessage.qos;
1069 payloadMessage.destinationName = storedMessage.payloadMessage.destinationName;
1070 if (storedMessage.payloadMessage.duplicate)
1071 payloadMessage.duplicate = true;
1072 if (storedMessage.payloadMessage.retained)
1073 payloadMessage.retained = true;
1074 wireMessage.payloadMessage = payloadMessage;
1075
1076 break;
1077
1078 default:
1079 throw Error(format(ERROR.INVALID_STORED_DATA, [key, value]));
1080 }
1081
1082 if (key.indexOf("Sent:"+this._localKey) == 0) {
1083 wireMessage.payloadMessage.duplicate = true;
1084 this._sentMessages[wireMessage.messageIdentifier] = wireMessage;
1085 } else if (key.indexOf("Received:"+this._localKey) == 0) {
1086 this._receivedMessages[wireMessage.messageIdentifier] = wireMessage;
1087 }
1088 };
1089
1090 ClientImpl.prototype._process_queue = function () {
1091 var message = null;
1092 // Process messages in order they were added
1093 var fifo = this._msg_queue.reverse();
1094
1095 // Send all queued messages down socket connection
1096 while ((message = fifo.pop())) {
1097 this._socket_send(message);
1098 // Notify listeners that message was successfully sent
1099 if (this._notify_msg_sent[message]) {
1100 this._notify_msg_sent[message]();
1101 delete this._notify_msg_sent[message];
1102 }
1103 }
1104 };
1105
1106 /**
1107 * Expect an ACK response for this message. Add message to the set of in progress
1108 * messages and set an unused identifier in this message.
1109 * @ignore
1110 */
1111 ClientImpl.prototype._requires_ack = function (wireMessage) {
1112 var messageCount = Object.keys(this._sentMessages).length;
1113 if (messageCount > this.maxMessageIdentifier)
1114 throw Error ("Too many messages:"+messageCount);
1115
1116 while(this._sentMessages[this._message_identifier] !== undefined) {
1117 this._message_identifier++;
1118 }
1119 wireMessage.messageIdentifier = this._message_identifier;
1120 this._sentMessages[wireMessage.messageIdentifier] = wireMessage;
1121 if (wireMessage.type === MESSAGE_TYPE.PUBLISH) {
1122 this.store("Sent:", wireMessage);
1123 }
1124 if (this._message_identifier === this.maxMessageIdentifier) {
1125 this._message_identifier = 1;
1126 }
1127 };
1128
1129 /**
1130 * Called when the underlying websocket has been opened.
1131 * @ignore
1132 */
1133 ClientImpl.prototype._on_socket_open = function () {
1134 // Create the CONNECT message object.
1135 var wireMessage = new WireMessage(MESSAGE_TYPE.CONNECT, this.connectOptions);
1136 wireMessage.clientId = this.clientId;
1137 this._socket_send(wireMessage);
1138 };
1139
1140 /**
1141 * Called when the underlying websocket has received a complete packet.
1142 * @ignore
1143 */
1144 ClientImpl.prototype._on_socket_message = function (event) {
1145 this._trace("Client._on_socket_message", event.data);
1146 // Reset the receive ping timer, we now have evidence the server is alive.
1147 this.receivePinger.reset();
1148 var messages = this._deframeMessages(event.data);
1149 for (var i = 0; i < messages.length; i+=1) {
1150 this._handleMessage(messages[i]);
1151 }
1152 }
1153
1154 ClientImpl.prototype._deframeMessages = function(data) {
1155 var byteArray = new Uint8Array(data);
1156 if (this.receiveBuffer) {
1157 var newData = new Uint8Array(this.receiveBuffer.length+byteArray.length);
1158 newData.set(this.receiveBuffer);
1159 newData.set(byteArray,this.receiveBuffer.length);
1160 byteArray = newData;
1161 delete this.receiveBuffer;
1162 }
1163 try {
1164 var offset = 0;
1165 var messages = [];
1166 while(offset < byteArray.length) {
1167 var result = decodeMessage(byteArray,offset);
1168 var wireMessage = result[0];
1169 offset = result[1];
1170 if (wireMessage !== null) {
1171 messages.push(wireMessage);
1172 } else {
1173 break;
1174 }
1175 }
1176 if (offset < byteArray.length) {
1177 this.receiveBuffer = byteArray.subarray(offset);
1178 }
1179 } catch (error) {
1180 this._disconnected(ERROR.INTERNAL_ERROR.code , format(ERROR.INTERNAL_ERROR, [error.message,error.stack.toString()]));
1181 return;
1182 }
1183 return messages;
1184 }
1185
1186 ClientImpl.prototype._handleMessage = function(wireMessage) {
1187
1188 this._trace("Client._handleMessage", wireMessage);
1189
1190 try {
1191 switch(wireMessage.type) {
1192 case MESSAGE_TYPE.CONNACK:
1193 this._connectTimeout.cancel();
1194
1195 // If we have started using clean session then clear up the local state.
1196 if (this.connectOptions.cleanSession) {
1197 for (var key in this._sentMessages) {
1198 var sentMessage = this._sentMessages[key];
1199 localStorage.removeItem("Sent:"+this._localKey+sentMessage.messageIdentifier);
1200 }
1201 this._sentMessages = {};
1202
1203 for (var key in this._receivedMessages) {
1204 var receivedMessage = this._receivedMessages[key];
1205 localStorage.removeItem("Received:"+this._localKey+receivedMessage.messageIdentifier);
1206 }
1207 this._receivedMessages = {};
1208 }
1209 // Client connected and ready for business.
1210 if (wireMessage.returnCode === 0) {
1211 this.connected = true;
1212 // Jump to the end of the list of uris and stop looking for a good host.
1213 if (this.connectOptions.uris)
1214 this.hostIndex = this.connectOptions.uris.length;
1215 } else {
1216 this._disconnected(ERROR.CONNACK_RETURNCODE.code , format(ERROR.CONNACK_RETURNCODE, [wireMessage.returnCode, CONNACK_RC[wireMessage.returnCode]]));
1217 break;
1218 }
1219
1220 // Resend messages.
1221 var sequencedMessages = new Array();
1222 for (var msgId in this._sentMessages) {
1223 if (this._sentMessages.hasOwnProperty(msgId))
1224 sequencedMessages.push(this._sentMessages[msgId]);
1225 }
1226
1227 // Sort sentMessages into the original sent order.
1228 var sequencedMessages = sequencedMessages.sort(function(a,b) {return a.sequence - b.sequence;} );
1229 for (var i=0, len=sequencedMessages.length; i<len; i++) {
1230 var sentMessage = sequencedMessages[i];
1231 if (sentMessage.type == MESSAGE_TYPE.PUBLISH && sentMessage.pubRecReceived) {
1232 var pubRelMessage = new WireMessage(MESSAGE_TYPE.PUBREL, {messageIdentifier:sentMessage.messageIdentifier});
1233 this._schedule_message(pubRelMessage);
1234 } else {
1235 this._schedule_message(sentMessage);
1236 };
1237 }
1238
1239 // Execute the connectOptions.onSuccess callback if there is one.
1240 if (this.connectOptions.onSuccess) {
1241 this.connectOptions.onSuccess({invocationContext:this.connectOptions.invocationContext});
1242 }
1243
1244 // Process all queued messages now that the connection is established.
1245 this._process_queue();
1246 break;
1247
1248 case MESSAGE_TYPE.PUBLISH:
1249 this._receivePublish(wireMessage);
1250 break;
1251
1252 case MESSAGE_TYPE.PUBACK:
1253 var sentMessage = this._sentMessages[wireMessage.messageIdentifier];
1254 // If this is a re flow of a PUBACK after we have restarted receivedMessage will not exist.
1255 if (sentMessage) {
1256 delete this._sentMessages[wireMessage.messageIdentifier];
1257 localStorage.removeItem("Sent:"+this._localKey+wireMessage.messageIdentifier);
1258 if (this.onMessageDelivered)
1259 this.onMessageDelivered(sentMessage.payloadMessage);
1260 }
1261 break;
1262
1263 case MESSAGE_TYPE.PUBREC:
1264 var sentMessage = this._sentMessages[wireMessage.messageIdentifier];
1265 // If this is a re flow of a PUBREC after we have restarted receivedMessage will not exist.
1266 if (sentMessage) {
1267 sentMessage.pubRecReceived = true;
1268 var pubRelMessage = new WireMessage(MESSAGE_TYPE.PUBREL, {messageIdentifier:wireMessage.messageIdentifier});
1269 this.store("Sent:", sentMessage);
1270 this._schedule_message(pubRelMessage);
1271 }
1272 break;
1273
1274 case MESSAGE_TYPE.PUBREL:
1275 var receivedMessage = this._receivedMessages[wireMessage.messageIdentifier];
1276 localStorage.removeItem("Received:"+this._localKey+wireMessage.messageIdentifier);
1277 // If this is a re flow of a PUBREL after we have restarted receivedMessage will not exist.
1278 if (receivedMessage) {
1279 this._receiveMessage(receivedMessage);
1280 delete this._receivedMessages[wireMessage.messageIdentifier];
1281 }
1282 // Always flow PubComp, we may have previously flowed PubComp but the server lost it and restarted.
1283 var pubCompMessage = new WireMessage(MESSAGE_TYPE.PUBCOMP, {messageIdentifier:wireMessage.messageIdentifier});
1284 this._schedule_message(pubCompMessage);
1285 break;
1286
1287 case MESSAGE_TYPE.PUBCOMP:
1288 var sentMessage = this._sentMessages[wireMessage.messageIdentifier];
1289 delete this._sentMessages[wireMessage.messageIdentifier];
1290 localStorage.removeItem("Sent:"+this._localKey+wireMessage.messageIdentifier);
1291 if (this.onMessageDelivered)
1292 this.onMessageDelivered(sentMessage.payloadMessage);
1293 break;
1294
1295 case MESSAGE_TYPE.SUBACK:
1296 var sentMessage = this._sentMessages[wireMessage.messageIdentifier];
1297 if (sentMessage) {
1298 if(sentMessage.timeOut)
1299 sentMessage.timeOut.cancel();
1300 // This will need to be fixed when we add multiple topic support
1301 if (wireMessage.returnCode[0] === 0x80) {
1302 if (sentMessage.onFailure) {
1303 sentMessage.onFailure(wireMessage.returnCode);
1304 }
1305 } else if (sentMessage.onSuccess) {
1306 sentMessage.onSuccess(wireMessage.returnCode);
1307 }
1308 delete this._sentMessages[wireMessage.messageIdentifier];
1309 }
1310 break;
1311
1312 case MESSAGE_TYPE.UNSUBACK:
1313 var sentMessage = this._sentMessages[wireMessage.messageIdentifier];
1314 if (sentMessage) {
1315 if (sentMessage.timeOut)
1316 sentMessage.timeOut.cancel();
1317 if (sentMessage.callback) {
1318 sentMessage.callback();
1319 }
1320 delete this._sentMessages[wireMessage.messageIdentifier];
1321 }
1322
1323 break;
1324
1325 case MESSAGE_TYPE.PINGRESP:
1326 /* The sendPinger or receivePinger may have sent a ping, the receivePinger has already been reset. */
1327 this.sendPinger.reset();
1328 break;
1329
1330 case MESSAGE_TYPE.DISCONNECT:
1331 // Clients do not expect to receive disconnect packets.
1332 this._disconnected(ERROR.INVALID_MQTT_MESSAGE_TYPE.code , format(ERROR.INVALID_MQTT_MESSAGE_TYPE, [wireMessage.type]));
1333 break;
1334
1335 default:
1336 this._disconnected(ERROR.INVALID_MQTT_MESSAGE_TYPE.code , format(ERROR.INVALID_MQTT_MESSAGE_TYPE, [wireMessage.type]));
1337 };
1338 } catch (error) {
1339 this._disconnected(ERROR.INTERNAL_ERROR.code , format(ERROR.INTERNAL_ERROR, [error.message,error.stack.toString()]));
1340 return;
1341 }
1342 };
1343
1344 /** @ignore */
1345 ClientImpl.prototype._on_socket_error = function (error) {
1346 this._disconnected(ERROR.SOCKET_ERROR.code , format(ERROR.SOCKET_ERROR, [error.data]));
1347 };
1348
1349 /** @ignore */
1350 ClientImpl.prototype._on_socket_close = function () {
1351 this._disconnected(ERROR.SOCKET_CLOSE.code , format(ERROR.SOCKET_CLOSE));
1352 };
1353
1354 /** @ignore */
1355 ClientImpl.prototype._socket_send = function (wireMessage) {
1356
1357 if (wireMessage.type == 1) {
1358 var wireMessageMasked = this._traceMask(wireMessage, "password");
1359 this._trace("Client._socket_send", wireMessageMasked);
1360 }
1361 else this._trace("Client._socket_send", wireMessage);
1362
1363 this.socket.send(wireMessage.encode());
1364 /* We have proved to the server we are alive. */
1365 this.sendPinger.reset();
1366 };
1367
1368 /** @ignore */
1369 ClientImpl.prototype._receivePublish = function (wireMessage) {
1370 switch(wireMessage.payloadMessage.qos) {
1371 case "undefined":
1372 case 0:
1373 this._receiveMessage(wireMessage);
1374 break;
1375
1376 case 1:
1377 var pubAckMessage = new WireMessage(MESSAGE_TYPE.PUBACK, {messageIdentifier:wireMessage.messageIdentifier});
1378 this._schedule_message(pubAckMessage);
1379 this._receiveMessage(wireMessage);
1380 break;
1381
1382 case 2:
1383 this._receivedMessages[wireMessage.messageIdentifier] = wireMessage;
1384 this.store("Received:", wireMessage);
1385 var pubRecMessage = new WireMessage(MESSAGE_TYPE.PUBREC, {messageIdentifier:wireMessage.messageIdentifier});
1386 this._schedule_message(pubRecMessage);
1387
1388 break;
1389
1390 default:
1391 throw Error("Invaild qos="+wireMmessage.payloadMessage.qos);
1392 };
1393 };
1394
1395 /** @ignore */
1396 ClientImpl.prototype._receiveMessage = function (wireMessage) {
1397 if (this.onMessageArrived) {
1398 this.onMessageArrived(wireMessage.payloadMessage);
1399 }
1400 };
1401
1402 /**
1403 * Client has disconnected either at its own request or because the server
1404 * or network disconnected it. Remove all non-durable state.
1405 * @param {errorCode} [number] the error number.
1406 * @param {errorText} [string] the error text.
1407 * @ignore
1408 */
1409 ClientImpl.prototype._disconnected = function (errorCode, errorText) {
1410 this._trace("Client._disconnected", errorCode, errorText);
1411
1412 this.sendPinger.cancel();
1413 this.receivePinger.cancel();
1414 if (this._connectTimeout)
1415 this._connectTimeout.cancel();
1416 // Clear message buffers.
1417 this._msg_queue = [];
1418 this._notify_msg_sent = {};
1419
1420 if (this.socket) {
1421 // Cancel all socket callbacks so that they cannot be driven again by this socket.
1422 this.socket.onopen = null;
1423 this.socket.onmessage = null;
1424 this.socket.onerror = null;
1425 this.socket.onclose = null;
1426 if (this.socket.readyState === 1)
1427 this.socket.close();
1428 delete this.socket;
1429 }
1430
1431 if (this.connectOptions.uris && this.hostIndex < this.connectOptions.uris.length-1) {
1432 // Try the next host.
1433 this.hostIndex++;
1434 this._doConnect(this.connectOptions.uris[this.hostIndex]);
1435
1436 } else {
1437
1438 if (errorCode === undefined) {
1439 errorCode = ERROR.OK.code;
1440 errorText = format(ERROR.OK);
1441 }
1442
1443 // Run any application callbacks last as they may attempt to reconnect and hence create a new socket.
1444 if (this.connected) {
1445 this.connected = false;
1446 // Execute the connectionLostCallback if there is one, and we were connected.
1447 if (this.onConnectionLost)
1448 this.onConnectionLost({errorCode:errorCode, errorMessage:errorText});
1449 } else {
1450 // Otherwise we never had a connection, so indicate that the connect has failed.
1451 if (this.connectOptions.mqttVersion === 4 && this.connectOptions.mqttVersionExplicit === false) {
1452 this._trace("Failed to connect V4, dropping back to V3")
1453 this.connectOptions.mqttVersion = 3;
1454 if (this.connectOptions.uris) {
1455 this.hostIndex = 0;
1456 this._doConnect(this.connectOptions.uris[0]);
1457 } else {
1458 this._doConnect(this.uri);
1459 }
1460 } else if(this.connectOptions.onFailure) {
1461 this.connectOptions.onFailure({invocationContext:this.connectOptions.invocationContext, errorCode:errorCode, errorMessage:errorText});
1462 }
1463 }
1464 }
1465 };
1466
1467 /** @ignore */
1468 ClientImpl.prototype._trace = function () {
1469 // Pass trace message back to client's callback function
1470 if (this.traceFunction) {
1471 for (var i in arguments)
1472 {
1473 if (typeof arguments[i] !== "undefined")
1474 arguments[i] = JSON.stringify(arguments[i]);
1475 }
1476 var record = Array.prototype.slice.call(arguments).join("");
1477 this.traceFunction ({severity: "Debug", message: record });
1478 }
1479
1480 //buffer style trace
1481 if ( this._traceBuffer !== null ) {
1482 for (var i = 0, max = arguments.length; i < max; i++) {
1483 if ( this._traceBuffer.length == this._MAX_TRACE_ENTRIES ) {
1484 this._traceBuffer.shift();
1485 }
1486 if (i === 0) this._traceBuffer.push(arguments[i]);
1487 else if (typeof arguments[i] === "undefined" ) this._traceBuffer.push(arguments[i]);
1488 else this._traceBuffer.push(" "+JSON.stringify(arguments[i]));
1489 };
1490 };
1491 };
1492
1493 /** @ignore */
1494 ClientImpl.prototype._traceMask = function (traceObject, masked) {
1495 var traceObjectMasked = {};
1496 for (var attr in traceObject) {
1497 if (traceObject.hasOwnProperty(attr)) {
1498 if (attr == masked)
1499 traceObjectMasked[attr] = "******";
1500 else
1501 traceObjectMasked[attr] = traceObject[attr];
1502 }
1503 }
1504 return traceObjectMasked;
1505 };
1506
1507 // ------------------------------------------------------------------------
1508 // Public Programming interface.
1509 // ------------------------------------------------------------------------
1510
1511 /**
1512 * The JavaScript application communicates to the server using a {@link Paho.MQTT.Client} object.
1513 * <p>
1514 * Most applications will create just one Client object and then call its connect() method,
1515 * however applications can create more than one Client object if they wish.
1516 * In this case the combination of host, port and clientId attributes must be different for each Client object.
1517 * <p>
1518 * The send, subscribe and unsubscribe methods are implemented as asynchronous JavaScript methods
1519 * (even though the underlying protocol exchange might be synchronous in nature).
1520 * This means they signal their completion by calling back to the application,
1521 * via Success or Failure callback functions provided by the application on the method in question.
1522 * Such callbacks are called at most once per method invocation and do not persist beyond the lifetime
1523 * of the script that made the invocation.
1524 * <p>
1525 * In contrast there are some callback functions, most notably <i>onMessageArrived</i>,
1526 * that are defined on the {@link Paho.MQTT.Client} object.
1527 * These may get called multiple times, and aren't directly related to specific method invocations made by the client.
1528 *
1529 * @name Paho.MQTT.Client
1530 *
1531 * @constructor
1532 *
1533 * @param {string} host - the address of the messaging server, as a fully qualified WebSocket URI, as a DNS name or dotted decimal IP address.
1534 * @param {number} port - the port number to connect to - only required if host is not a URI
1535 * @param {string} path - the path on the host to connect to - only used if host is not a URI. Default: '/mqtt'.
1536 * @param {string} clientId - the Messaging client identifier, between 1 and 23 characters in length.
1537 *
1538 * @property {string} host - <i>read only</i> the server's DNS hostname or dotted decimal IP address.
1539 * @property {number} port - <i>read only</i> the server's port.
1540 * @property {string} path - <i>read only</i> the server's path.
1541 * @property {string} clientId - <i>read only</i> used when connecting to the server.
1542 * @property {function} onConnectionLost - called when a connection has been lost.
1543 * after a connect() method has succeeded.
1544 * Establish the call back used when a connection has been lost. The connection may be
1545 * lost because the client initiates a disconnect or because the server or network
1546 * cause the client to be disconnected. The disconnect call back may be called without
1547 * the connectionComplete call back being invoked if, for example the client fails to
1548 * connect.
1549 * A single response object parameter is passed to the onConnectionLost callback containing the following fields:
1550 * <ol>
1551 * <li>errorCode
1552 * <li>errorMessage
1553 * </ol>
1554 * @property {function} onMessageDelivered called when a message has been delivered.
1555 * All processing that this Client will ever do has been completed. So, for example,
1556 * in the case of a Qos=2 message sent by this client, the PubComp flow has been received from the server
1557 * and the message has been removed from persistent storage before this callback is invoked.
1558 * Parameters passed to the onMessageDelivered callback are:
1559 * <ol>
1560 * <li>{@link Paho.MQTT.Message} that was delivered.
1561 * </ol>
1562 * @property {function} onMessageArrived called when a message has arrived in this Paho.MQTT.client.
1563 * Parameters passed to the onMessageArrived callback are:
1564 * <ol>
1565 * <li>{@link Paho.MQTT.Message} that has arrived.
1566 * </ol>
1567 */
1568 var Client = function (host, port, path, clientId) {
1569
1570 var uri;
1571
1572 if (typeof host !== "string")
1573 throw new Error(format(ERROR.INVALID_TYPE, [typeof host, "host"]));
1574
1575 if (arguments.length == 2) {
1576 // host: must be full ws:// uri
1577 // port: clientId
1578 clientId = port;
1579 uri = host;
1580 var match = uri.match(/^(wss?):\/\/((\[(.+)\])|([^\/]+?))(:(\d+))?(\/.*)$/);
1581 if (match) {
1582 host = match[4]||match[2];
1583 port = parseInt(match[7]);
1584 path = match[8];
1585 } else {
1586 throw new Error(format(ERROR.INVALID_ARGUMENT,[host,"host"]));
1587 }
1588 } else {
1589 if (arguments.length == 3) {
1590 clientId = path;
1591 path = "/mqtt";
1592 }
1593 if (typeof port !== "number" || port < 0)
1594 throw new Error(format(ERROR.INVALID_TYPE, [typeof port, "port"]));
1595 if (typeof path !== "string")
1596 throw new Error(format(ERROR.INVALID_TYPE, [typeof path, "path"]));
1597
1598 var ipv6AddSBracket = (host.indexOf(":") != -1 && host.slice(0,1) != "[" && host.slice(-1) != "]");
1599 uri = "ws://"+(ipv6AddSBracket?"["+host+"]":host)+":"+port+path;
1600 }
1601
1602 var clientIdLength = 0;
1603 for (var i = 0; i<clientId.length; i++) {
1604 var charCode = clientId.charCodeAt(i);
1605 if (0xD800 <= charCode && charCode <= 0xDBFF) {
1606 i++; // Surrogate pair.
1607 }
1608 clientIdLength++;
1609 }
1610 if (typeof clientId !== "string" || clientIdLength > 65535)
1611 throw new Error(format(ERROR.INVALID_ARGUMENT, [clientId, "clientId"]));
1612
1613 var client = new ClientImpl(uri, host, port, path, clientId);
1614 this._getHost = function() { return host; };
1615 this._setHost = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); };
1616
1617 this._getPort = function() { return port; };
1618 this._setPort = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); };
1619
1620 this._getPath = function() { return path; };
1621 this._setPath = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); };
1622
1623 this._getURI = function() { return uri; };
1624 this._setURI = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); };
1625
1626 this._getClientId = function() { return client.clientId; };
1627 this._setClientId = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); };
1628
1629 this._getOnConnectionLost = function() { return client.onConnectionLost; };
1630 this._setOnConnectionLost = function(newOnConnectionLost) {
1631 if (typeof newOnConnectionLost === "function")
1632 client.onConnectionLost = newOnConnectionLost;
1633 else
1634 throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnConnectionLost, "onConnectionLost"]));
1635 };
1636
1637 this._getOnMessageDelivered = function() { return client.onMessageDelivered; };
1638 this._setOnMessageDelivered = function(newOnMessageDelivered) {
1639 if (typeof newOnMessageDelivered === "function")
1640 client.onMessageDelivered = newOnMessageDelivered;
1641 else
1642 throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnMessageDelivered, "onMessageDelivered"]));
1643 };
1644
1645 this._getOnMessageArrived = function() { return client.onMessageArrived; };
1646 this._setOnMessageArrived = function(newOnMessageArrived) {
1647 if (typeof newOnMessageArrived === "function")
1648 client.onMessageArrived = newOnMessageArrived;
1649 else
1650 throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnMessageArrived, "onMessageArrived"]));
1651 };
1652
1653 this._getTrace = function() { return client.traceFunction; };
1654 this._setTrace = function(trace) {
1655 if(typeof trace === "function"){
1656 client.traceFunction = trace;
1657 }else{
1658 throw new Error(format(ERROR.INVALID_TYPE, [typeof trace, "onTrace"]));
1659 }
1660 };
1661
1662 /**
1663 * Connect this Messaging client to its server.
1664 *
1665 * @name Paho.MQTT.Client#connect
1666 * @function
1667 * @param {Object} connectOptions - attributes used with the connection.
1668 * @param {number} connectOptions.timeout - If the connect has not succeeded within this
1669 * number of seconds, it is deemed to have failed.
1670 * The default is 30 seconds.
1671 * @param {string} connectOptions.userName - Authentication username for this connection.
1672 * @param {string} connectOptions.password - Authentication password for this connection.
1673 * @param {Paho.MQTT.Message} connectOptions.willMessage - sent by the server when the client
1674 * disconnects abnormally.
1675 * @param {Number} connectOptions.keepAliveInterval - the server disconnects this client if
1676 * there is no activity for this number of seconds.
1677 * The default value of 60 seconds is assumed if not set.
1678 * @param {boolean} connectOptions.cleanSession - if true(default) the client and server
1679 * persistent state is deleted on successful connect.
1680 * @param {boolean} connectOptions.useSSL - if present and true, use an SSL Websocket connection.
1681 * @param {object} connectOptions.invocationContext - passed to the onSuccess callback or onFailure callback.
1682 * @param {function} connectOptions.onSuccess - called when the connect acknowledgement
1683 * has been received from the server.
1684 * A single response object parameter is passed to the onSuccess callback containing the following fields:
1685 * <ol>
1686 * <li>invocationContext as passed in to the onSuccess method in the connectOptions.
1687 * </ol>
1688 * @config {function} [onFailure] called when the connect request has failed or timed out.
1689 * A single response object parameter is passed to the onFailure callback containing the following fields:
1690 * <ol>
1691 * <li>invocationContext as passed in to the onFailure method in the connectOptions.
1692 * <li>errorCode a number indicating the nature of the error.
1693 * <li>errorMessage text describing the error.
1694 * </ol>
1695 * @config {Array} [hosts] If present this contains either a set of hostnames or fully qualified
1696 * WebSocket URIs (ws://example.com:1883/mqtt), that are tried in order in place
1697 * of the host and port paramater on the construtor. The hosts are tried one at at time in order until
1698 * one of then succeeds.
1699 * @config {Array} [ports] If present the set of ports matching the hosts. If hosts contains URIs, this property
1700 * is not used.
1701 * @throws {InvalidState} if the client is not in disconnected state. The client must have received connectionLost
1702 * or disconnected before calling connect for a second or subsequent time.
1703 */
1704 this.connect = function (connectOptions) {
1705 connectOptions = connectOptions || {} ;
1706 validate(connectOptions, {timeout:"number",
1707 userName:"string",
1708 password:"string",
1709 willMessage:"object",
1710 keepAliveInterval:"number",
1711 cleanSession:"boolean",
1712 useSSL:"boolean",
1713 invocationContext:"object",
1714 onSuccess:"function",
1715 onFailure:"function",
1716 hosts:"object",
1717 ports:"object",
1718 mqttVersion:"number"});
1719
1720 // If no keep alive interval is set, assume 60 seconds.
1721 if (connectOptions.keepAliveInterval === undefined)
1722 connectOptions.keepAliveInterval = 60;
1723
1724 if (connectOptions.mqttVersion > 4 || connectOptions.mqttVersion < 3) {
1725 throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.mqttVersion, "connectOptions.mqttVersion"]));
1726 }
1727
1728 if (connectOptions.mqttVersion === undefined) {
1729 connectOptions.mqttVersionExplicit = false;
1730 connectOptions.mqttVersion = 4;
1731 } else {
1732 connectOptions.mqttVersionExplicit = true;
1733 }
1734
1735 //Check that if password is set, so is username
1736 if (connectOptions.password === undefined && connectOptions.userName !== undefined)
1737 throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.password, "connectOptions.password"]))
1738
1739 if (connectOptions.willMessage) {
1740 if (!(connectOptions.willMessage instanceof Message))
1741 throw new Error(format(ERROR.INVALID_TYPE, [connectOptions.willMessage, "connectOptions.willMessage"]));
1742 // The will message must have a payload that can be represented as a string.
1743 // Cause the willMessage to throw an exception if this is not the case.
1744 connectOptions.willMessage.stringPayload;
1745
1746 if (typeof connectOptions.willMessage.destinationName === "undefined")
1747 throw new Error(format(ERROR.INVALID_TYPE, [typeof connectOptions.willMessage.destinationName, "connectOptions.willMessage.destinationName"]));
1748 }
1749 if (typeof connectOptions.cleanSession === "undefined")
1750 connectOptions.cleanSession = true;
1751 if (connectOptions.hosts) {
1752
1753 if (!(connectOptions.hosts instanceof Array) )
1754 throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.hosts, "connectOptions.hosts"]));
1755 if (connectOptions.hosts.length <1 )
1756 throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.hosts, "connectOptions.hosts"]));
1757
1758 var usingURIs = false;
1759 for (var i = 0; i<connectOptions.hosts.length; i++) {
1760 if (typeof connectOptions.hosts[i] !== "string")
1761 throw new Error(format(ERROR.INVALID_TYPE, [typeof connectOptions.hosts[i], "connectOptions.hosts["+i+"]"]));
1762 if (/^(wss?):\/\/((\[(.+)\])|([^\/]+?))(:(\d+))?(\/.*)$/.test(connectOptions.hosts[i])) {
1763 if (i == 0) {
1764 usingURIs = true;
1765 } else if (!usingURIs) {
1766 throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.hosts[i], "connectOptions.hosts["+i+"]"]));
1767 }
1768 } else if (usingURIs) {
1769 throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.hosts[i], "connectOptions.hosts["+i+"]"]));
1770 }
1771 }
1772
1773 if (!usingURIs) {
1774 if (!connectOptions.ports)
1775 throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.ports, "connectOptions.ports"]));
1776 if (!(connectOptions.ports instanceof Array) )
1777 throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.ports, "connectOptions.ports"]));
1778 if (connectOptions.hosts.length != connectOptions.ports.length)
1779 throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.ports, "connectOptions.ports"]));
1780
1781 connectOptions.uris = [];
1782
1783 for (var i = 0; i<connectOptions.hosts.length; i++) {
1784 if (typeof connectOptions.ports[i] !== "number" || connectOptions.ports[i] < 0)
1785 throw new Error(format(ERROR.INVALID_TYPE, [typeof connectOptions.ports[i], "connectOptions.ports["+i+"]"]));
1786 var host = connectOptions.hosts[i];
1787 var port = connectOptions.ports[i];
1788
1789 var ipv6 = (host.indexOf(":") != -1);
1790 uri = "ws://"+(ipv6?"["+host+"]":host)+":"+port+path;
1791 connectOptions.uris.push(uri);
1792 }
1793 } else {
1794 connectOptions.uris = connectOptions.hosts;
1795 }
1796 }
1797
1798 client.connect(connectOptions);
1799 };
1800
1801 /**
1802 * Subscribe for messages, request receipt of a copy of messages sent to the destinations described by the filter.
1803 *
1804 * @name Paho.MQTT.Client#subscribe
1805 * @function
1806 * @param {string} filter describing the destinations to receive messages from.
1807 * <br>
1808 * @param {object} subscribeOptions - used to control the subscription
1809 *
1810 * @param {number} subscribeOptions.qos - the maiximum qos of any publications sent
1811 * as a result of making this subscription.
1812 * @param {object} subscribeOptions.invocationContext - passed to the onSuccess callback
1813 * or onFailure callback.
1814 * @param {function} subscribeOptions.onSuccess - called when the subscribe acknowledgement
1815 * has been received from the server.
1816 * A single response object parameter is passed to the onSuccess callback containing the following fields:
1817 * <ol>
1818 * <li>invocationContext if set in the subscribeOptions.
1819 * </ol>
1820 * @param {function} subscribeOptions.onFailure - called when the subscribe request has failed or timed out.
1821 * A single response object parameter is passed to the onFailure callback containing the following fields:
1822 * <ol>
1823 * <li>invocationContext - if set in the subscribeOptions.
1824 * <li>errorCode - a number indicating the nature of the error.
1825 * <li>errorMessage - text describing the error.
1826 * </ol>
1827 * @param {number} subscribeOptions.timeout - which, if present, determines the number of
1828 * seconds after which the onFailure calback is called.
1829 * The presence of a timeout does not prevent the onSuccess
1830 * callback from being called when the subscribe completes.
1831 * @throws {InvalidState} if the client is not in connected state.
1832 */
1833 this.subscribe = function (filter, subscribeOptions) {
1834 if (typeof filter !== "string")
1835 throw new Error("Invalid argument:"+filter);
1836 subscribeOptions = subscribeOptions || {} ;
1837 validate(subscribeOptions, {qos:"number",
1838 invocationContext:"object",
1839 onSuccess:"function",
1840 onFailure:"function",
1841 timeout:"number"
1842 });
1843 if (subscribeOptions.timeout && !subscribeOptions.onFailure)
1844 throw new Error("subscribeOptions.timeout specified with no onFailure callback.");
1845 if (typeof subscribeOptions.qos !== "undefined"
1846 && !(subscribeOptions.qos === 0 || subscribeOptions.qos === 1 || subscribeOptions.qos === 2 ))
1847 throw new Error(format(ERROR.INVALID_ARGUMENT, [subscribeOptions.qos, "subscribeOptions.qos"]));
1848 client.subscribe(filter, subscribeOptions);
1849 };
1850
1851 /**
1852 * Unsubscribe for messages, stop receiving messages sent to destinations described by the filter.
1853 *
1854 * @name Paho.MQTT.Client#unsubscribe
1855 * @function
1856 * @param {string} filter - describing the destinations to receive messages from.
1857 * @param {object} unsubscribeOptions - used to control the subscription
1858 * @param {object} unsubscribeOptions.invocationContext - passed to the onSuccess callback
1859 or onFailure callback.
1860 * @param {function} unsubscribeOptions.onSuccess - called when the unsubscribe acknowledgement has been received from the server.
1861 * A single response object parameter is passed to the
1862 * onSuccess callback containing the following fields:
1863 * <ol>
1864 * <li>invocationContext - if set in the unsubscribeOptions.
1865 * </ol>
1866 * @param {function} unsubscribeOptions.onFailure called when the unsubscribe request has failed or timed out.
1867 * A single response object parameter is passed to the onFailure callback containing the following fields:
1868 * <ol>
1869 * <li>invocationContext - if set in the unsubscribeOptions.
1870 * <li>errorCode - a number indicating the nature of the error.
1871 * <li>errorMessage - text describing the error.
1872 * </ol>
1873 * @param {number} unsubscribeOptions.timeout - which, if present, determines the number of seconds
1874 * after which the onFailure callback is called. The presence of
1875 * a timeout does not prevent the onSuccess callback from being
1876 * called when the unsubscribe completes
1877 * @throws {InvalidState} if the client is not in connected state.
1878 */
1879 this.unsubscribe = function (filter, unsubscribeOptions) {
1880 if (typeof filter !== "string")
1881 throw new Error("Invalid argument:"+filter);
1882 unsubscribeOptions = unsubscribeOptions || {} ;
1883 validate(unsubscribeOptions, {invocationContext:"object",
1884 onSuccess:"function",
1885 onFailure:"function",
1886 timeout:"number"
1887 });
1888 if (unsubscribeOptions.timeout && !unsubscribeOptions.onFailure)
1889 throw new Error("unsubscribeOptions.timeout specified with no onFailure callback.");
1890 client.unsubscribe(filter, unsubscribeOptions);
1891 };
1892
1893 /**
1894 * Send a message to the consumers of the destination in the Message.
1895 *
1896 * @name Paho.MQTT.Client#send
1897 * @function
1898 * @param {string|Paho.MQTT.Message} topic - <b>mandatory</b> The name of the destination to which the message is to be sent.
1899 * - If it is the only parameter, used as Paho.MQTT.Message object.
1900 * @param {String|ArrayBuffer} payload - The message data to be sent.
1901 * @param {number} qos The Quality of Service used to deliver the message.
1902 * <dl>
1903 * <dt>0 Best effort (default).
1904 * <dt>1 At least once.
1905 * <dt>2 Exactly once.
1906 * </dl>
1907 * @param {Boolean} retained If true, the message is to be retained by the server and delivered
1908 * to both current and future subscriptions.
1909 * If false the server only delivers the message to current subscribers, this is the default for new Messages.
1910 * A received message has the retained boolean set to true if the message was published
1911 * with the retained boolean set to true
1912 * and the subscrption was made after the message has been published.
1913 * @throws {InvalidState} if the client is not connected.
1914 */
1915 this.send = function (topic,payload,qos,retained) {
1916 var message ;
1917
1918 if(arguments.length == 0){
1919 throw new Error("Invalid argument."+"length");
1920
1921 }else if(arguments.length == 1) {
1922
1923 if (!(topic instanceof Message) && (typeof topic !== "string"))
1924 throw new Error("Invalid argument:"+ typeof topic);
1925
1926 message = topic;
1927 if (typeof message.destinationName === "undefined")
1928 throw new Error(format(ERROR.INVALID_ARGUMENT,[message.destinationName,"Message.destinationName"]));
1929 client.send(message);
1930
1931 }else {
1932 //parameter checking in Message object
1933 message = new Message(payload);
1934 message.destinationName = topic;
1935 if(arguments.length >= 3)
1936 message.qos = qos;
1937 if(arguments.length >= 4)
1938 message.retained = retained;
1939 client.send(message);
1940 }
1941 };
1942
1943 /**
1944 * Normal disconnect of this Messaging client from its server.
1945 *
1946 * @name Paho.MQTT.Client#disconnect
1947 * @function
1948 * @throws {InvalidState} if the client is already disconnected.
1949 */
1950 this.disconnect = function () {
1951 client.disconnect();
1952 };
1953
1954 /**
1955 * Get the contents of the trace log.
1956 *
1957 * @name Paho.MQTT.Client#getTraceLog
1958 * @function
1959 * @return {Object[]} tracebuffer containing the time ordered trace records.
1960 */
1961 this.getTraceLog = function () {
1962 return client.getTraceLog();
1963 }
1964
1965 /**
1966 * Start tracing.
1967 *
1968 * @name Paho.MQTT.Client#startTrace
1969 * @function
1970 */
1971 this.startTrace = function () {
1972 client.startTrace();
1973 };
1974
1975 /**
1976 * Stop tracing.
1977 *
1978 * @name Paho.MQTT.Client#stopTrace
1979 * @function
1980 */
1981 this.stopTrace = function () {
1982 client.stopTrace();
1983 };
1984
1985 this.isConnected = function() {
1986 return client.connected;
1987 };
1988 };
1989
1990 Client.prototype = {
1991 get host() { return this._getHost(); },
1992 set host(newHost) { this._setHost(newHost); },
1993
1994 get port() { return this._getPort(); },
1995 set port(newPort) { this._setPort(newPort); },
1996
1997 get path() { return this._getPath(); },
1998 set path(newPath) { this._setPath(newPath); },
1999
2000 get clientId() { return this._getClientId(); },
2001 set clientId(newClientId) { this._setClientId(newClientId); },
2002
2003 get onConnectionLost() { return this._getOnConnectionLost(); },
2004 set onConnectionLost(newOnConnectionLost) { this._setOnConnectionLost(newOnConnectionLost); },
2005
2006 get onMessageDelivered() { return this._getOnMessageDelivered(); },
2007 set onMessageDelivered(newOnMessageDelivered) { this._setOnMessageDelivered(newOnMessageDelivered); },
2008
2009 get onMessageArrived() { return this._getOnMessageArrived(); },
2010 set onMessageArrived(newOnMessageArrived) { this._setOnMessageArrived(newOnMessageArrived); },
2011
2012 get trace() { return this._getTrace(); },
2013 set trace(newTraceFunction) { this._setTrace(newTraceFunction); }
2014
2015 };
2016
2017 /**
2018 * An application message, sent or received.
2019 * <p>
2020 * All attributes may be null, which implies the default values.
2021 *
2022 * @name Paho.MQTT.Message
2023 * @constructor
2024 * @param {String|ArrayBuffer} payload The message data to be sent.
2025 * <p>
2026 * @property {string} payloadString <i>read only</i> The payload as a string if the payload consists of valid UTF-8 characters.
2027 * @property {ArrayBuffer} payloadBytes <i>read only</i> The payload as an ArrayBuffer.
2028 * <p>
2029 * @property {string} destinationName <b>mandatory</b> The name of the destination to which the message is to be sent
2030 * (for messages about to be sent) or the name of the destination from which the message has been received.
2031 * (for messages received by the onMessage function).
2032 * <p>
2033 * @property {number} qos The Quality of Service used to deliver the message.
2034 * <dl>
2035 * <dt>0 Best effort (default).
2036 * <dt>1 At least once.
2037 * <dt>2 Exactly once.
2038 * </dl>
2039 * <p>
2040 * @property {Boolean} retained If true, the message is to be retained by the server and delivered
2041 * to both current and future subscriptions.
2042 * If false the server only delivers the message to current subscribers, this is the default for new Messages.
2043 * A received message has the retained boolean set to true if the message was published
2044 * with the retained boolean set to true
2045 * and the subscrption was made after the message has been published.
2046 * <p>
2047 * @property {Boolean} duplicate <i>read only</i> If true, this message might be a duplicate of one which has already been received.
2048 * This is only set on messages received from the server.
2049 *
2050 */
2051 var Message = function (newPayload) {
2052 var payload;
2053 if ( typeof newPayload === "string"
2054 || newPayload instanceof ArrayBuffer
2055 || newPayload instanceof Int8Array
2056 || newPayload instanceof Uint8Array
2057 || newPayload instanceof Int16Array
2058 || newPayload instanceof Uint16Array
2059 || newPayload instanceof Int32Array
2060 || newPayload instanceof Uint32Array
2061 || newPayload instanceof Float32Array
2062 || newPayload instanceof Float64Array
2063 ) {
2064 payload = newPayload;
2065 } else {
2066 throw (format(ERROR.INVALID_ARGUMENT, [newPayload, "newPayload"]));
2067 }
2068
2069 this._getPayloadString = function () {
2070 if (typeof payload === "string")
2071 return payload;
2072 else
2073 return parseUTF8(payload, 0, payload.length);
2074 };
2075
2076 this._getPayloadBytes = function() {
2077 if (typeof payload === "string") {
2078 var buffer = new ArrayBuffer(UTF8Length(payload));
2079 var byteStream = new Uint8Array(buffer);
2080 stringToUTF8(payload, byteStream, 0);
2081
2082 return byteStream;
2083 } else {
2084 return payload;
2085 };
2086 };
2087
2088 var destinationName = undefined;
2089 this._getDestinationName = function() { return destinationName; };
2090 this._setDestinationName = function(newDestinationName) {
2091 if (typeof newDestinationName === "string")
2092 destinationName = newDestinationName;
2093 else
2094 throw new Error(format(ERROR.INVALID_ARGUMENT, [newDestinationName, "newDestinationName"]));
2095 };
2096
2097 var qos = 0;
2098 this._getQos = function() { return qos; };
2099 this._setQos = function(newQos) {
2100 if (newQos === 0 || newQos === 1 || newQos === 2 )
2101 qos = newQos;
2102 else
2103 throw new Error("Invalid argument:"+newQos);
2104 };
2105
2106 var retained = false;
2107 this._getRetained = function() { return retained; };
2108 this._setRetained = function(newRetained) {
2109 if (typeof newRetained === "boolean")
2110 retained = newRetained;
2111 else
2112 throw new Error(format(ERROR.INVALID_ARGUMENT, [newRetained, "newRetained"]));
2113 };
2114
2115 var duplicate = false;
2116 this._getDuplicate = function() { return duplicate; };
2117 this._setDuplicate = function(newDuplicate) { duplicate = newDuplicate; };
2118 };
2119
2120 Message.prototype = {
2121 get payloadString() { return this._getPayloadString(); },
2122 get payloadBytes() { return this._getPayloadBytes(); },
2123
2124 get destinationName() { return this._getDestinationName(); },
2125 set destinationName(newDestinationName) { this._setDestinationName(newDestinationName); },
2126
2127 get qos() { return this._getQos(); },
2128 set qos(newQos) { this._setQos(newQos); },
2129
2130 get retained() { return this._getRetained(); },
2131 set retained(newRetained) { this._setRetained(newRetained); },
2132
2133 get duplicate() { return this._getDuplicate(); },
2134 set duplicate(newDuplicate) { this._setDuplicate(newDuplicate); }
2135 };
2136
2137 // Module contents.
2138 return {
2139 Client: Client,
2140 Message: Message
2141 };
2142 })(window);
0 ifeq ($(.DEFAULT_GOAL),)
1 # Define default goal to `all` because this file defines some targets
2 # before the inclusion of erlang.mk leading to the wrong target becoming
3 # the default.
4 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
26 endif
27
28 # --------------------------------------------------------------------
29 # RabbitMQ components.
30 # --------------------------------------------------------------------
31
32 # For RabbitMQ repositories, we want to checkout branches which match
33 # the parent project. For instance, if the parent project is on a
34 # release tag, dependencies must be on the same release tag. If the
35 # parent project is on a topic branch, dependencies must be on the same
36 # topic branch or fallback to `stable` or `master` whichever was the
37 # base of the topic branch.
38
39 dep_amqp_client = git_rmq rabbitmq-erlang-client $(current_rmq_ref) $(base_rmq_ref) master
40 dep_rabbit = git_rmq rabbitmq-server $(current_rmq_ref) $(base_rmq_ref) master
41 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
42 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
43 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
45 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
46 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
47 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
48 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
49 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
51 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
52 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
54 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
55 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
56 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
57 dep_rabbitmq_event_exchange = git_rmq rabbitmq-event-exchange $(current_rmq_ref) $(base_rmq_ref) master
58 dep_rabbitmq_federation = git_rmq rabbitmq-federation $(current_rmq_ref) $(base_rmq_ref) master
59 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
60 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
61 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
63 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
64 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
65 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
66 dep_rabbitmq_management_agent = git_rmq rabbitmq-management-agent $(current_rmq_ref) $(base_rmq_ref) master
67 dep_rabbitmq_management_exchange = git_rmq rabbitmq-management-exchange $(current_rmq_ref) $(base_rmq_ref) master
68 dep_rabbitmq_management_themes = git_rmq rabbitmq-management-themes $(current_rmq_ref) $(base_rmq_ref) master
69 dep_rabbitmq_management_visualiser = git_rmq rabbitmq-management-visualiser $(current_rmq_ref) $(base_rmq_ref) master
70 dep_rabbitmq_message_timestamp = git_rmq rabbitmq-message-timestamp $(current_rmq_ref) $(base_rmq_ref) master
71 dep_rabbitmq_metronome = git_rmq rabbitmq-metronome $(current_rmq_ref) $(base_rmq_ref) master
72 dep_rabbitmq_mqtt = git_rmq rabbitmq-mqtt $(current_rmq_ref) $(base_rmq_ref) master
73 dep_rabbitmq_objc_client = git_rmq rabbitmq-objc-client $(current_rmq_ref) $(base_rmq_ref) master
74 dep_rabbitmq_recent_history_exchange = git_rmq rabbitmq-recent-history-exchange $(current_rmq_ref) $(base_rmq_ref) master
75 dep_rabbitmq_routing_node_stamp = git_rmq rabbitmq-routing-node-stamp $(current_rmq_ref) $(base_rmq_ref) master
76 dep_rabbitmq_rtopic_exchange = git_rmq rabbitmq-rtopic-exchange $(current_rmq_ref) $(base_rmq_ref) master
77 dep_rabbitmq_server_release = git_rmq rabbitmq-server-release $(current_rmq_ref) $(base_rmq_ref) master
78 dep_rabbitmq_sharding = git_rmq rabbitmq-sharding $(current_rmq_ref) $(base_rmq_ref) master
79 dep_rabbitmq_shovel = git_rmq rabbitmq-shovel $(current_rmq_ref) $(base_rmq_ref) master
80 dep_rabbitmq_shovel_management = git_rmq rabbitmq-shovel-management $(current_rmq_ref) $(base_rmq_ref) master
81 dep_rabbitmq_stomp = git_rmq rabbitmq-stomp $(current_rmq_ref) $(base_rmq_ref) master
82 dep_rabbitmq_toke = git_rmq rabbitmq-toke $(current_rmq_ref) $(base_rmq_ref) master
83 dep_rabbitmq_top = git_rmq rabbitmq-top $(current_rmq_ref) $(base_rmq_ref) master
84 dep_rabbitmq_tracing = git_rmq rabbitmq-tracing $(current_rmq_ref) $(base_rmq_ref) master
85 dep_rabbitmq_trust_store = git_rmq rabbitmq-trust-store $(current_rmq_ref) $(base_rmq_ref) master
86 dep_rabbitmq_test = git_rmq rabbitmq-test $(current_rmq_ref) $(base_rmq_ref) master
87 dep_rabbitmq_web_dispatch = git_rmq rabbitmq-web-dispatch $(current_rmq_ref) $(base_rmq_ref) master
88 dep_rabbitmq_web_stomp = git_rmq rabbitmq-web-stomp $(current_rmq_ref) $(base_rmq_ref) master
89 dep_rabbitmq_web_stomp_examples = git_rmq rabbitmq-web-stomp-examples $(current_rmq_ref) $(base_rmq_ref) master
90 dep_rabbitmq_web_mqtt = git_rmq rabbitmq-web-mqtt $(current_rmq_ref) $(base_rmq_ref) master
91 dep_rabbitmq_web_mqtt_examples = git_rmq rabbitmq-web-mqtt-examples $(current_rmq_ref) $(base_rmq_ref) master
92 dep_rabbitmq_website = git_rmq rabbitmq-website $(current_rmq_ref) $(base_rmq_ref) live master
93 dep_sockjs = git_rmq sockjs-erlang $(current_rmq_ref) $(base_rmq_ref) master
94 dep_toke = git_rmq toke $(current_rmq_ref) $(base_rmq_ref) master
95
96 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
97
98 # Third-party dependencies version pinning.
99 #
100 # We do that in this file, which is copied in all projects, to ensure
101 # all projects use the same versions. It avoids conflicts and makes it
102 # possible to work with rabbitmq-public-umbrella.
103
104 dep_cowboy_commit = 1.1.0
105 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
106 dep_ranch_commit = 1.3.1
107 dep_webmachine_commit = 1.10.8p2
108 dep_ranch_proxy_protocol = git git://github.com/heroku/ranch_proxy_protocol.git 1.4.1
109
110 RABBITMQ_COMPONENTS = amqp_client \
111 rabbit \
112 rabbit_common \
113 rabbitmq_amqp1_0 \
114 rabbitmq_auth_backend_amqp \
115 rabbitmq_auth_backend_cache \
116 rabbitmq_auth_backend_http \
117 rabbitmq_auth_backend_ldap \
118 rabbitmq_auth_mechanism_ssl \
119 rabbitmq_boot_steps_visualiser \
120 rabbitmq_clusterer \
121 rabbitmq_cli \
122 rabbitmq_codegen \
123 rabbitmq_consistent_hash_exchange \
124 rabbitmq_ct_client_helpers \
125 rabbitmq_ct_helpers \
126 rabbitmq_delayed_message_exchange \
127 rabbitmq_dotnet_client \
128 rabbitmq_event_exchange \
129 rabbitmq_federation \
130 rabbitmq_federation_management \
131 rabbitmq_java_client \
132 rabbitmq_jms_client \
133 rabbitmq_jms_cts \
134 rabbitmq_jms_topic_exchange \
135 rabbitmq_lvc \
136 rabbitmq_management \
137 rabbitmq_management_agent \
138 rabbitmq_management_exchange \
139 rabbitmq_management_themes \
140 rabbitmq_management_visualiser \
141 rabbitmq_message_timestamp \
142 rabbitmq_metronome \
143 rabbitmq_mqtt \
144 rabbitmq_objc_client \
145 rabbitmq_recent_history_exchange \
146 rabbitmq_routing_node_stamp \
147 rabbitmq_rtopic_exchange \
148 rabbitmq_server_release \
149 rabbitmq_sharding \
150 rabbitmq_shovel \
151 rabbitmq_shovel_management \
152 rabbitmq_stomp \
153 rabbitmq_toke \
154 rabbitmq_top \
155 rabbitmq_tracing \
156 rabbitmq_trust_store \
157 rabbitmq_web_dispatch \
158 rabbitmq_web_mqtt \
159 rabbitmq_web_mqtt_examples \
160 rabbitmq_web_stomp \
161 rabbitmq_web_stomp_examples \
162 rabbitmq_website
163
164 # Several components have a custom erlang.mk/build.config, mainly
165 # to disable eunit. Therefore, we can't use the top-level project's
166 # erlang.mk copy.
167 NO_AUTOPATCH += $(RABBITMQ_COMPONENTS)
168
169 ifeq ($(origin current_rmq_ref),undefined)
170 ifneq ($(wildcard .git),)
171 current_rmq_ref := $(shell (\
172 ref=$$(git branch --list | awk '/^\* \(.*detached / {ref=$$0; sub(/.*detached [^ ]+ /, "", ref); sub(/\)$$/, "", ref); print ref; exit;} /^\* / {ref=$$0; sub(/^\* /, "", ref); print ref; exit}');\
173 if test "$$(git rev-parse --short HEAD)" != "$$ref"; then echo "$$ref"; fi))
174 else
175 current_rmq_ref := master
176 endif
177 endif
178 export current_rmq_ref
179
180 ifeq ($(origin base_rmq_ref),undefined)
181 ifneq ($(wildcard .git),)
182 base_rmq_ref := $(shell \
183 (git rev-parse --verify -q stable >/dev/null && \
184 git merge-base --is-ancestor $$(git merge-base master HEAD) stable && \
185 echo stable) || \
186 echo master)
187 else
188 base_rmq_ref := master
189 endif
190 endif
191 export base_rmq_ref
192
193 # Repository URL selection.
194 #
195 # First, we infer other components' location from the current project
196 # repository URL, if it's a Git repository:
197 # - We take the "origin" remote URL as the base
198 # - The current project name and repository name is replaced by the
199 # target's properties:
200 # eg. rabbitmq-common is replaced by rabbitmq-codegen
201 # eg. rabbit_common is replaced by rabbitmq_codegen
202 #
203 # If cloning from this computed location fails, we fallback to RabbitMQ
204 # upstream which is GitHub.
205
206 # Maccro to transform eg. "rabbit_common" to "rabbitmq-common".
207 rmq_cmp_repo_name = $(word 2,$(dep_$(1)))
208
209 # Upstream URL for the current project.
210 RABBITMQ_COMPONENT_REPO_NAME := $(call rmq_cmp_repo_name,$(PROJECT))
211 RABBITMQ_UPSTREAM_FETCH_URL ?= https://github.com/rabbitmq/$(RABBITMQ_COMPONENT_REPO_NAME).git
212 RABBITMQ_UPSTREAM_PUSH_URL ?= git@github.com:rabbitmq/$(RABBITMQ_COMPONENT_REPO_NAME).git
213
214 # Current URL for the current project. If this is not a Git clone,
215 # default to the upstream Git repository.
216 ifneq ($(wildcard .git),)
217 git_origin_fetch_url := $(shell git config remote.origin.url)
218 git_origin_push_url := $(shell git config remote.origin.pushurl || git config remote.origin.url)
219 RABBITMQ_CURRENT_FETCH_URL ?= $(git_origin_fetch_url)
220 RABBITMQ_CURRENT_PUSH_URL ?= $(git_origin_push_url)
221 else
222 RABBITMQ_CURRENT_FETCH_URL ?= $(RABBITMQ_UPSTREAM_FETCH_URL)
223 RABBITMQ_CURRENT_PUSH_URL ?= $(RABBITMQ_UPSTREAM_PUSH_URL)
224 endif
225
226 # Macro to replace the following pattern:
227 # 1. /foo.git -> /bar.git
228 # 2. /foo -> /bar
229 # 3. /foo/ -> /bar/
230 subst_repo_name = $(patsubst %/$(1)/%,%/$(2)/%,$(patsubst %/$(1),%/$(2),$(patsubst %/$(1).git,%/$(2).git,$(3))))
231
232 # Macro to replace both the project's name (eg. "rabbit_common") and
233 # repository name (eg. "rabbitmq-common") by the target's equivalent.
234 #
235 # This macro is kept on one line because we don't want whitespaces in
236 # the returned value, as it's used in $(dep_fetch_git_rmq) in a shell
237 # single-quoted string.
238 dep_rmq_repo = $(if $(dep_$(2)),$(call subst_repo_name,$(PROJECT),$(2),$(call subst_repo_name,$(RABBITMQ_COMPONENT_REPO_NAME),$(call rmq_cmp_repo_name,$(2)),$(1))),$(pkg_$(1)_repo))
239
240 dep_rmq_commits = $(if $(dep_$(1)), \
241 $(wordlist 3,$(words $(dep_$(1))),$(dep_$(1))), \
242 $(pkg_$(1)_commit))
243
244 define dep_fetch_git_rmq
245 fetch_url1='$(call dep_rmq_repo,$(RABBITMQ_CURRENT_FETCH_URL),$(1))'; \
246 fetch_url2='$(call dep_rmq_repo,$(RABBITMQ_UPSTREAM_FETCH_URL),$(1))'; \
247 if test "$$$$fetch_url1" != '$(RABBITMQ_CURRENT_FETCH_URL)' && \
248 git clone -q -n -- "$$$$fetch_url1" $(DEPS_DIR)/$(call dep_name,$(1)); then \
249 fetch_url="$$$$fetch_url1"; \
250 push_url='$(call dep_rmq_repo,$(RABBITMQ_CURRENT_PUSH_URL),$(1))'; \
251 elif git clone -q -n -- "$$$$fetch_url2" $(DEPS_DIR)/$(call dep_name,$(1)); then \
252 fetch_url="$$$$fetch_url2"; \
253 push_url='$(call dep_rmq_repo,$(RABBITMQ_UPSTREAM_PUSH_URL),$(1))'; \
254 fi; \
255 cd $(DEPS_DIR)/$(call dep_name,$(1)) && ( \
256 $(foreach ref,$(call dep_rmq_commits,$(1)), \
257 git checkout -q $(ref) >/dev/null 2>&1 || \
258 ) \
259 (echo "error: no valid pathspec among: $(call dep_rmq_commits,$(1))" \
260 1>&2 && false) ) && \
261 (test "$$$$fetch_url" = "$$$$push_url" || \
262 git remote set-url --push origin "$$$$push_url")
263 endef
264
265 # --------------------------------------------------------------------
266 # Component distribution.
267 # --------------------------------------------------------------------
268
269 list-dist-deps::
270 @:
271
272 prepare-dist::
273 @:
274
275 # --------------------------------------------------------------------
276 # rabbitmq-components.mk checks.
277 # --------------------------------------------------------------------
278
279 # If this project is under the Umbrella project, we override $(DEPS_DIR)
280 # to point to the Umbrella's one. We also disable `make distclean` so
281 # $(DEPS_DIR) is not accidentally removed.
282
283 ifneq ($(wildcard ../../UMBRELLA.md),)
284 UNDER_UMBRELLA = 1
285 else ifneq ($(wildcard UMBRELLA.md),)
286 UNDER_UMBRELLA = 1
287 endif
288
289 ifeq ($(UNDER_UMBRELLA),1)
290 ifneq ($(PROJECT),rabbitmq_public_umbrella)
291 DEPS_DIR ?= $(abspath ..)
292 endif
293
294 ifneq ($(filter distclean distclean-deps,$(MAKECMDGOALS)),)
295 SKIP_DEPS = 1
296 endif
297 endif
298
299 UPSTREAM_RMQ_COMPONENTS_MK = $(DEPS_DIR)/rabbit_common/mk/rabbitmq-components.mk
300
301 check-rabbitmq-components.mk:
302 $(verbose) cmp -s rabbitmq-components.mk \
303 $(UPSTREAM_RMQ_COMPONENTS_MK) || \
304 (echo "error: rabbitmq-components.mk must be updated!" 1>&2; \
305 false)
306
307 ifeq ($(PROJECT),rabbit_common)
308 rabbitmq-components-mk:
309 @:
310 else
311 rabbitmq-components-mk:
312 $(gen_verbose) cp -a $(UPSTREAM_RMQ_COMPONENTS_MK) .
313 ifeq ($(DO_COMMIT),yes)
314 $(verbose) git diff --quiet rabbitmq-components.mk \
315 || git commit -m 'Update rabbitmq-components.mk' rabbitmq-components.mk
316 endif
317 endif
0 %% The contents of this file are subject to the Mozilla Public License
1 %% Version 1.1 (the "License"); you may not use this file except in
2 %% compliance with the License. You may obtain a copy of the License
3 %% at http://www.mozilla.org/MPL/
4 %%
5 %% Software distributed under the License is distributed on an "AS IS"
6 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
7 %% the License for the specific language governing rights and
8 %% limitations under the License.
9 %%
10 %% The Original Code is RabbitMQ.
11 %%
12 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
14 %%
15
16 -module(rabbit_web_mqtt_examples_app).
17
18 -behaviour(application).
19 -export([start/2,stop/1]).
20
21 %% Dummy supervisor - see Ulf Wiger's comment at
22 %% http://erlang.2086793.n4.nabble.com/initializing-library-applications-without-processes-td2094473.html
23 -behaviour(supervisor).
24 -export([init/1]).
25
26 start(_Type, _StartArgs) ->
27 {ok, Listener} = application:get_env(rabbitmq_web_mqtt_examples, listener),
28 {ok, _} = rabbit_web_dispatch:register_static_context(
29 web_mqtt_examples, Listener, "web-mqtt-examples", ?MODULE,
30 "priv", "WEB-MQTT: examples"),
31 supervisor:start_link({local, ?MODULE}, ?MODULE, []).
32
33 stop(_State) ->
34 rabbit_web_dispatch:unregister_context(web_mqtt_examples),
35 ok.
36
37 init([]) -> {ok, {{one_for_one, 3, 10}, []}}.
00 PROJECT = rabbitmq_web_stomp
1 PROJECT_DESCRIPTION = Rabbit WEB-STOMP - WebSockets to Stomp adapter
2 PROJECT_MOD = rabbit_ws_app
3
4 define PROJECT_ENV
5 [
6 {port, 15674},
7 {tcp_config, []},
8 {num_tcp_acceptors, 10},
9 {ssl_config, []},
10 {num_ssl_acceptors, 1},
11 {cowboy_opts, []},
12 {sockjs_opts, []},
13 {ws_frame, text},
14 {use_http_auth, false}
15 ]
16 endef
117
218 DEPS = cowboy sockjs rabbit_common rabbit rabbitmq_stomp
3 TEST_DEPS = rabbitmq_ct_helpers
19 TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers
420 dep_cowboy_commit = 1.0.3
521
22 DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
623 DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk
724
825 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
0 RabbitMQ Web STOMP plugin
1 =========================
0 # RabbitMQ Web STOMP plugin
21
3 This project is a simple bridge between "RabbitMQ-stomp" plugin and
4 SockJS.
2 This project is a minimalistic bridge between the [RabbitMQ STOMP plugin](http://rabbitmq.com/stomp.html) and
3 WebSockets (directly or via SockJS emulation).
54
6 Once started the plugin opens a SockJS endpoint on prefix "/stomp" on
7 port 15674, for example a valid SockJS endpoint url may look like:
8 "http://127.0.0.1:15674/stomp".
5 ## RabbitMQ Version Requirements
96
10 Once the server is started you should be able to establish a SockJS
11 connection to this url. You will be able to communicate using the
12 usual STOMP protocol over it. For example, a page using Jeff Mesnil's
13 "stomp-websocket" project and SockJS may look like this:
7 The most recent version of this plugin requires RabbitMQ `3.6.1` or later.
8
9 ## Installation and Binary Builds
10
11 This plugin is now available from the [RabbitMQ community plugins page](http://www.rabbitmq.com/community-plugins.html).
12 Please consult the docs on [how to install RabbitMQ plugins](http://www.rabbitmq.com/plugins.html#installing-plugins).
13
14 ## Documentation
15
16 Please refer to the [RabbitMQ Web STOMP guide](http://www.rabbitmq.com/web-stomp.html).
17
18 ## Building from Source
19
20 See [Plugin Development guide](http://www.rabbitmq.com/plugin-development.html).
21
22 TL;DR: running
23
24 make dist
25
26 will build the plugin and put build artifacts under the `./plugins` directory.
1427
1528
16 <script src="sockjs-0.3.min.js"></script>
17 <script src="stomp.js"></script>
18 <script>
19 Stomp.WebSocketClass = SockJS;
29 ## Copyright and License
2030
21 var client = Stomp.client('http://127.0.0.1:15674/stomp');
22 var on_connect = function() {
23 console.log('connected');
24 };
25 var on_error = function() {
26 console.log('error');
27 };
28 client.connect('guest', 'guest', on_connect, on_error, '/');
29 [...]
31 (c) Pivotal Software Inc, 2007-2017
3032
31 See the "RabbitMQ-Web-Stomp-examples" plugin for more details.
32
33
34 Installation
35 ------------
36
37 Generic build instructions are at:
38
39 * http://www.rabbitmq.com/plugin-development.html
40
41 Instructions on how to install a plugin into RabbitMQ broker:
42
43 * http://www.rabbitmq.com/plugins.html#installing-plugins
44
33 Released under the MPL, the same license as RabbitMQ.
0 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
11 #
22 # Permission to use, copy, modify, and/or distribute this software for any
33 # purpose with or without fee is hereby granted, provided that the above
1111 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1212 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1313
14 .PHONY: all app apps deps search rel docs install-docs check tests clean distclean help erlang-mk
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
1515
1616 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17
18 ERLANG_MK_VERSION = 2.0.0-pre.2-144-g647ffd1
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
1931
2032 # Core configuration.
2133
2436
2537 PROJECT_VERSION ?= rolling
2638 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
2740
2841 # Verbosity.
2942
8497 rel::
8598 $(verbose) :
8699
100 relup:: deps app
101
87102 check:: tests
88103
89104 clean:: clean-crashdump
101116 help::
102117 $(verbose) printf "%s\n" \
103118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
104 "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
105120 "" \
106121 "Usage: [V=1] $(MAKE) [target]..." \
107122 "" \
149164 core_native_path = $1
150165 endif
151166
152 ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
153 define core_http_get
154 wget --no-check-certificate -O $(1) $(2)|| rm $(1)
155 endef
156 else
157 define core_http_get.erl
158 ssl:start(),
159 inets:start(),
160 case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
161 {ok, {{_, 200, _}, _, Body}} ->
162 case file:write_file("$(1)", Body) of
163 ok -> ok;
164 {error, R1} -> halt(R1)
165 end;
166 {error, R2} ->
167 halt(R2)
168 end,
169 halt(0).
170 endef
171
172 define core_http_get
173 $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
174 endef
175 endif
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
176168
177169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
178170
192184 ERLANG_MK_BUILD_CONFIG ?= build.config
193185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
194186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
195188 erlang-mk:
196189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
197190 ifdef ERLANG_MK_COMMIT
198191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
199192 endif
200193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
201 $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
202195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
203196 rm -rf $(ERLANG_MK_BUILD_DIR)
204197
285278 pkg_apns_repo = https://github.com/inaka/apns4erl
286279 pkg_apns_commit = master
287280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
288289 PACKAGES += azdht
289290 pkg_azdht_name = azdht
290291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
405406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
406407 pkg_bootstrap_commit = master
407408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
408417 PACKAGES += boss_db
409418 pkg_boss_db_name = boss_db
410419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
413422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
414423 pkg_boss_db_commit = master
415424
416 PACKAGES += boss
417 pkg_boss_name = boss
418 pkg_boss_description = Erlang web MVC, now featuring Comet
419 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
420 pkg_boss_fetch = git
421 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
422 pkg_boss_commit = master
423
424425 PACKAGES += brod
425426 pkg_brod_name = brod
426427 pkg_brod_description = Kafka client in Erlang
565566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
566567 pkg_cloudi_service_api_requests_commit = master
567568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
568585 PACKAGES += cloudi_service_db_cassandra_cql
569586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
570587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
573590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
574591 pkg_cloudi_service_db_cassandra_cql_commit = master
575592
576 PACKAGES += cloudi_service_db_cassandra
577 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
579 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
580 pkg_cloudi_service_db_cassandra_fetch = git
581 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
582 pkg_cloudi_service_db_cassandra_commit = master
583
584593 PACKAGES += cloudi_service_db_couchdb
585594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
586595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
637646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
638647 pkg_cloudi_service_db_tokyotyrant_commit = master
639648
640 PACKAGES += cloudi_service_db
641 pkg_cloudi_service_db_name = cloudi_service_db
642 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
643 pkg_cloudi_service_db_homepage = http://cloudi.org/
644 pkg_cloudi_service_db_fetch = git
645 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
646 pkg_cloudi_service_db_commit = master
647
648649 PACKAGES += cloudi_service_filesystem
649650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
650651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
10371038 pkg_edown_repo = https://github.com/uwiger/edown
10381039 pkg_edown_commit = master
10391040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
10401049 PACKAGES += eep_app
10411050 pkg_eep_app_name = eep_app
10421051 pkg_eep_app_description = Embedded Event Processing
10451054 pkg_eep_app_repo = https://github.com/darach/eep-erl
10461055 pkg_eep_app_commit = master
10471056
1048 PACKAGES += eep
1049 pkg_eep_name = eep
1050 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1051 pkg_eep_homepage = https://github.com/virtan/eep
1052 pkg_eep_fetch = git
1053 pkg_eep_repo = https://github.com/virtan/eep
1054 pkg_eep_commit = master
1055
10561057 PACKAGES += efene
10571058 pkg_efene_name = efene
10581059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
12371238 pkg_eqm_repo = https://github.com/loucash/eqm
12381239 pkg_eqm_commit = master
12391240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
12401249 PACKAGES += eredis_pool
12411250 pkg_eredis_pool_name = eredis_pool
12421251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
12451254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
12461255 pkg_eredis_pool_commit = master
12471256
1248 PACKAGES += eredis
1249 pkg_eredis_name = eredis
1250 pkg_eredis_description = Erlang Redis client
1251 pkg_eredis_homepage = https://github.com/wooga/eredis
1252 pkg_eredis_fetch = git
1253 pkg_eredis_repo = https://github.com/wooga/eredis
1254 pkg_eredis_commit = master
1255
12561257 PACKAGES += erl_streams
12571258 pkg_erl_streams_name = erl_streams
12581259 pkg_erl_streams_description = Streams in Erlang
15411542 pkg_etap_repo = https://github.com/ngerakines/etap
15421543 pkg_etap_commit = master
15431544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
15441553 PACKAGES += etest_http
15451554 pkg_etest_http_name = etest_http
15461555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
15491558 pkg_etest_http_repo = https://github.com/wooga/etest_http
15501559 pkg_etest_http_commit = master
15511560
1552 PACKAGES += etest
1553 pkg_etest_name = etest
1554 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1555 pkg_etest_homepage = https://github.com/wooga/etest
1556 pkg_etest_fetch = git
1557 pkg_etest_repo = https://github.com/wooga/etest
1558 pkg_etest_commit = master
1559
15601561 PACKAGES += etoml
15611562 pkg_etoml_name = etoml
15621563 pkg_etoml_description = TOML language erlang parser
15651566 pkg_etoml_repo = https://github.com/kalta/etoml
15661567 pkg_etoml_commit = master
15671568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
15681577 PACKAGES += eunit_formatters
15691578 pkg_eunit_formatters_name = eunit_formatters
15701579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
15731582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
15741583 pkg_eunit_formatters_commit = master
15751584
1576 PACKAGES += eunit
1577 pkg_eunit_name = eunit
1578 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1579 pkg_eunit_homepage = https://github.com/richcarl/eunit
1580 pkg_eunit_fetch = git
1581 pkg_eunit_repo = https://github.com/richcarl/eunit
1582 pkg_eunit_commit = master
1583
15841585 PACKAGES += euthanasia
15851586 pkg_euthanasia_name = euthanasia
15861587 pkg_euthanasia_description = Merciful killer for your Erlang processes
15981599 pkg_evum_commit = master
15991600
16001601 PACKAGES += exec
1601 pkg_exec_name = exec
1602 pkg_exec_name = erlexec
16021603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
16031604 pkg_exec_homepage = http://saleyn.github.com/erlexec
16041605 pkg_exec_fetch = git
17171718 pkg_fn_repo = https://github.com/reiddraper/fn
17181719 pkg_fn_commit = master
17191720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
17201729 PACKAGES += folsom_cowboy
17211730 pkg_folsom_cowboy_name = folsom_cowboy
17221731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
17251734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
17261735 pkg_folsom_cowboy_commit = master
17271736
1728 PACKAGES += folsom
1729 pkg_folsom_name = folsom
1730 pkg_folsom_description = Expose Erlang Events and Metrics
1731 pkg_folsom_homepage = https://github.com/boundary/folsom
1732 pkg_folsom_fetch = git
1733 pkg_folsom_repo = https://github.com/boundary/folsom
1734 pkg_folsom_commit = master
1735
17361737 PACKAGES += folsomite
17371738 pkg_folsomite_name = folsomite
17381739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
20932094 pkg_jesse_repo = https://github.com/for-GET/jesse
20942095 pkg_jesse_commit = master
20952096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
20962105 PACKAGES += jiffy_v
20972106 pkg_jiffy_v_name = jiffy_v
20982107 pkg_jiffy_v_description = JSON validation utility
21012110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
21022111 pkg_jiffy_v_commit = master
21032112
2104 PACKAGES += jiffy
2105 pkg_jiffy_name = jiffy
2106 pkg_jiffy_description = JSON NIFs for Erlang.
2107 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2108 pkg_jiffy_fetch = git
2109 pkg_jiffy_repo = https://github.com/davisp/jiffy
2110 pkg_jiffy_commit = master
2111
21122113 PACKAGES += jobs
21132114 pkg_jobs_name = jobs
21142115 pkg_jobs_description = a Job scheduler for load regulation
21252126 pkg_joxa_repo = https://github.com/joxa/joxa
21262127 pkg_joxa_commit = master
21272128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
21282137 PACKAGES += json_rec
21292138 pkg_json_rec_name = json_rec
21302139 pkg_json_rec_description = JSON to erlang record
21332142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
21342143 pkg_json_rec_commit = master
21352144
2136 PACKAGES += json
2137 pkg_json_name = json
2138 pkg_json_description = a high level json library for erlang (17.0+)
2139 pkg_json_homepage = https://github.com/talentdeficit/json
2140 pkg_json_fetch = git
2141 pkg_json_repo = https://github.com/talentdeficit/json
2142 pkg_json_commit = master
2143
21442145 PACKAGES += jsone
21452146 pkg_jsone_name = jsone
21462147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
21812182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
21822183 pkg_jsx_commit = master
21832184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
21842193 PACKAGES += kafka_protocol
21852194 pkg_kafka_protocol_name = kafka_protocol
21862195 pkg_kafka_protocol_description = Kafka protocol Erlang library
21892198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
21902199 pkg_kafka_protocol_commit = master
21912200
2192 PACKAGES += kafka
2193 pkg_kafka_name = kafka
2194 pkg_kafka_description = Kafka consumer and producer in Erlang
2195 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2196 pkg_kafka_fetch = git
2197 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2198 pkg_kafka_commit = master
2199
22002201 PACKAGES += kai
22012202 pkg_kai_name = kai
22022203 pkg_kai_description = DHT storage by Takeshi Inoue
22932294 pkg_kvs_repo = https://github.com/synrc/kvs
22942295 pkg_kvs_commit = master
22952296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
22962305 PACKAGES += lager_amqp_backend
22972306 pkg_lager_amqp_backend_name = lager_amqp_backend
22982307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
23092318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
23102319 pkg_lager_syslog_commit = master
23112320
2312 PACKAGES += lager
2313 pkg_lager_name = lager
2314 pkg_lager_description = A logging framework for Erlang/OTP.
2315 pkg_lager_homepage = https://github.com/basho/lager
2316 pkg_lager_fetch = git
2317 pkg_lager_repo = https://github.com/basho/lager
2318 pkg_lager_commit = master
2319
23202321 PACKAGES += lambdapad
23212322 pkg_lambdapad_name = lambdapad
23222323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
25732574 pkg_mixer_repo = https://github.com/chef/mixer
25742575 pkg_mixer_commit = master
25752576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
25762585 PACKAGES += mochiweb_xpath
25772586 pkg_mochiweb_xpath_name = mochiweb_xpath
25782587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
25812590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
25822591 pkg_mochiweb_xpath_commit = master
25832592
2584 PACKAGES += mochiweb
2585 pkg_mochiweb_name = mochiweb
2586 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2587 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2588 pkg_mochiweb_fetch = git
2589 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2590 pkg_mochiweb_commit = master
2591
25922593 PACKAGES += mockgyver
25932594 pkg_mockgyver_name = mockgyver
25942595 pkg_mockgyver_description = A mocking library for Erlang
30613062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
30623063 pkg_quickrand_commit = master
30633064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
30643073 PACKAGES += rabbit_exchange_type_riak
30653074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
30663075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
30693078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
30703079 pkg_rabbit_exchange_type_riak_commit = master
30713080
3072 PACKAGES += rabbit
3073 pkg_rabbit_name = rabbit
3074 pkg_rabbit_description = RabbitMQ Server
3075 pkg_rabbit_homepage = https://www.rabbitmq.com/
3076 pkg_rabbit_fetch = git
3077 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3078 pkg_rabbit_commit = master
3079
30803081 PACKAGES += rack
30813082 pkg_rack_name = rack
30823083 pkg_rack_description = Rack handler for erlang
34933494 pkg_smother_repo = https://github.com/ramsay-t/Smother
34943495 pkg_smother_commit = master
34953496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
34963505 PACKAGES += social
34973506 pkg_social_name = social
34983507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
35413550 pkg_stable_repo = https://github.com/dvv/stable
35423551 pkg_stable_commit = master
35433552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
35443561 PACKAGES += statebox_riak
35453562 pkg_statebox_riak_name = statebox_riak
35463563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
35493566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
35503567 pkg_statebox_riak_commit = master
35513568
3552 PACKAGES += statebox
3553 pkg_statebox_name = statebox
3554 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3555 pkg_statebox_homepage = https://github.com/mochi/statebox
3556 pkg_statebox_fetch = git
3557 pkg_statebox_repo = https://github.com/mochi/statebox
3558 pkg_statebox_commit = master
3559
35603569 PACKAGES += statman
35613570 pkg_statman_name = statman
35623571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
37253734 pkg_tirerl_repo = https://github.com/inaka/tirerl
37263735 pkg_tirerl_commit = master
37273736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
37283745 PACKAGES += traffic_tools
37293746 pkg_traffic_tools_name = traffic_tools
37303747 pkg_traffic_tools_description = Simple traffic limiting library
40614078 pkg_zucchini_repo = https://github.com/devinus/zucchini
40624079 pkg_zucchini_commit = master
40634080
4064 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
40654082 # This file is part of erlang.mk and subject to the terms of the ISC License.
40664083
40674084 .PHONY: search
40884105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
40894106 endif
40904107
4091 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
40924109 # This file is part of erlang.mk and subject to the terms of the ISC License.
40934110
4094 .PHONY: distclean-deps
4111 .PHONY: distclean-deps clean-tmp-deps.log
40954112
40964113 # Configuration.
40974114
41104127
41114128 REBAR_DEPS_DIR = $(DEPS_DIR)
41124129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
41134150
41144151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
41154152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
41164153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
41174154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
41184155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
41194157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
41204158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
41214159
41384176
41394177 # Core targets.
41404178
4141 ifdef IS_APP
4142 apps::
4143 else
4144 apps:: $(ALL_APPS_DIRS)
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
41454180 ifeq ($(IS_APP)$(IS_DEP),)
41464181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
41474182 endif
41494184 # Create ebin directory for all apps to make sure Erlang recognizes them
41504185 # as proper OTP applications when using -include_lib. This is a temporary
41514186 # fix, a proper fix would be to compile apps/* in the right order.
4152 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4153 mkdir -p $$dep/ebin || exit $$?; \
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
41544190 done
4155 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
41564196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
41574197 :; \
41584198 else \
41594199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4160 $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
4200 $(MAKE) -C $$dep IS_APP=1; \
41614201 fi \
41624202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
41634207 endif
41644208
41654209 ifneq ($(SKIP_DEPS),)
41664210 deps::
41674211 else
4168 deps:: $(ALL_DEPS_DIRS) apps
4169 ifeq ($(IS_APP)$(IS_DEP),)
4170 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4171 endif
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
41724213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4173 $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
41744215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
41754216 :; \
41764217 else \
41774218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
41784219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4179 $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
4220 $(MAKE) -C $$dep IS_DEP=1; \
41804221 else \
4181 echo "Error: No Makefile to build dependency $$dep."; \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
41824223 exit 2; \
41834224 fi \
41844225 fi \
41924233 # in practice only Makefile is needed so far.
41934234 define dep_autopatch
41944235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
41954237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
41964238 $(call dep_autopatch_erlang_mk,$(1)); \
41974239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4198 if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
41994243 $(call dep_autopatch2,$(1)); \
4200 elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
42014245 $(call dep_autopatch2,$(1)); \
4202 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
42034247 $(call dep_autopatch2,$(1)); \
4204 else \
4205 $(call erlang,$(call dep_autopatch_app.erl,$(1))); \
42064248 fi \
42074249 else \
42084250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
42144256 endef
42154257
42164258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
42174262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
42184263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
42194264 fi; \
42204265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4221 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
42224267 $(call dep_autopatch_fetch_rebar); \
42234268 $(call dep_autopatch_rebar,$(1)); \
42244269 else \
42304275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
42314276 endef
42324277
4233 # Overwrite erlang.mk with the current file by default.
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
42344280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
42354281 define dep_autopatch_erlang_mk
4236 echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \
4237 > $(DEPS_DIR)/$1/erlang.mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
42384287 endef
42394288 else
42404289 define dep_autopatch_erlang_mk
42534302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
42544303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
42554304 cd $(ERLANG_MK_TMP)/rebar; \
4256 git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
42574306 $(MAKE); \
42584307 cd -; \
42594308 fi
42704319 define dep_autopatch_rebar.erl
42714320 application:load(rebar),
42724321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
42734323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
42744324 {ok, Conf0} -> Conf0;
42754325 _ -> []
44234473 [] -> ok;
44244474 _ ->
44254475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4426 PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
44274477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4428 PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L \\"~s\\" -lerl_interface -lei\n",
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
44294479 [code:lib_dir(erl_interface, lib)])),
44304480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
44314481 FilterEnv = fun(Env) ->
44644514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44654515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44664516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4467 [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
44684518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
44694519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
44704520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
44764526 end,
44774527 [PortSpec(S) || S <- PortSpecs]
44784528 end,
4479 Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"),
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
44804530 RunPlugin = fun(Plugin, Step) ->
44814531 case erlang:function_exported(Plugin, Step, 2) of
44824532 false -> ok;
45244574 halt()
45254575 endef
45264576
4527 define dep_autopatch_app.erl
4528 UpdateModules = fun(App) ->
4529 case filelib:is_regular(App) of
4530 false -> ok;
4531 true ->
4532 {ok, [{application, '$(1)', L0}]} = file:consult(App),
4533 Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true,
4534 fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
4535 L = lists:keystore(modules, 1, L0, {modules, Mods}),
4536 ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}]))
4537 end
4538 end,
4539 UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"),
4540 halt()
4541 endef
4542
45434577 define dep_autopatch_appsrc_script.erl
45444578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
45454579 AppSrcScript = AppSrc ++ ".script",
45874621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
45884622 endef
45894623
4590 define dep_fetch_hex.erl
4591 ssl:start(),
4592 inets:start(),
4593 {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
4594 {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
4595 [], [{body_format, binary}]),
4596 {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
4597 {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
4598 ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
4599 halt()
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
46004626 endef
46014627
46024628 # Hex only has a package version. No need to look in the Erlang.mk packages.
46034629 define dep_fetch_hex
4604 $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
46054634 endef
46064635
46074636 define dep_fetch_fail
46314660 $(eval DEP_NAME := $(call dep_name,$1))
46324661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
46334662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4634 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
46354664 exit 17; \
46364665 fi
46374666 $(verbose) mkdir -p $(DEPS_DIR)
46734702 clean:: clean-apps
46744703
46754704 clean-apps:
4676 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4677 $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
46784707 done
46794708
46804709 distclean:: distclean-apps
46814710
46824711 distclean-apps:
4683 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4684 $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
46854714 done
46864715 endif
46874716
47014730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
47024731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
47034732
4704 # External plugins.
4705
4706 DEP_PLUGINS ?=
4707
4708 define core_dep_plugin
4709 -include $(DEPS_DIR)/$(1)
4710
4711 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4712 endef
4713
4714 $(foreach p,$(DEP_PLUGINS),\
4715 $(eval $(if $(findstring /,$p),\
4716 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4717 $(call core_dep_plugin,$p/plugins.mk,$p))))
4718
4719 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4720 # This file is part of erlang.mk and subject to the terms of the ISC License.
4721
4722 # Configuration.
4723
4724 DTL_FULL_PATH ?=
4725 DTL_PATH ?= templates/
4726 DTL_SUFFIX ?= _dtl
4727 DTL_OPTS ?=
4728
4729 # Verbosity.
4730
4731 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
4732 dtl_verbose = $(dtl_verbose_$(V))
4733
4734 # Core targets.
4735
4736 DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl))
4737
4738 ifneq ($(DTL_FILES),)
4739
4740 ifdef DTL_FULL_PATH
4741 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
4742 else
4743 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
4744 endif
4745
4746 ifneq ($(words $(DTL_FILES)),0)
4747 # Rebuild templates when the Makefile changes.
4748 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
4749 @mkdir -p $(ERLANG_MK_TMP)
4750 @if test -f $@; then \
4751 touch $(DTL_FILES); \
4752 fi
4753 @touch $@
4754
4755 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
4756 endif
4757
4758 define erlydtl_compile.erl
4759 [begin
4760 Module0 = case "$(strip $(DTL_FULL_PATH))" of
4761 "" ->
4762 filename:basename(F, ".dtl");
4763 _ ->
4764 "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
4765 re:replace(F2, "/", "_", [{return, list}, global])
4766 end,
4767 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
4768 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
4769 ok -> ok;
4770 {ok, _} -> ok
4771 end
4772 end || F <- string:tokens("$(1)", " ")],
4773 halt().
4774 endef
4775
4776 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
4777 $(if $(strip $?),\
4778 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
4779
4780 endif
4781
4782 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
47834734 # This file is part of erlang.mk and subject to the terms of the ISC License.
47844735
47854736 # Verbosity.
47984749
47994750 define compile_proto.erl
48004751 [begin
4801 Dir = filename:dirname(filename:dirname(F)),
48024752 protobuffs_compile:generate_source(F,
4803 [{output_include_dir, Dir ++ "/include"},
4804 {output_src_dir, Dir ++ "/ebin"}])
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
48054755 end || F <- string:tokens("$(1)", " ")],
48064756 halt().
48074757 endef
48114761 $(if $(strip $?),$(call compile_proto,$?))
48124762 endif
48134763
4814 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
48154765 # This file is part of erlang.mk and subject to the terms of the ISC License.
48164766
48174767 .PHONY: clean-app
48254775 ERLC_EXCLUDE ?=
48264776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
48274777
4778 ERLC_ASN1_OPTS ?=
4779
48284780 ERLC_MIB_OPTS ?=
48294781 COMPILE_MIB_FIRST ?=
48304782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
48744826
48754827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
48764828 define app_file
4877 {application, $(PROJECT), [
4829 {application, '$(PROJECT)', [
48784830 {description, "$(PROJECT_DESCRIPTION)"},
48794831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48804832 {id$(comma)$(space)"$(1)"}$(comma))
48814833 {modules, [$(call comma_list,$(2))]},
48824834 {registered, []},
4883 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48844837 ]}.
48854838 endef
48864839 else
48874840 define app_file
4888 {application, $(PROJECT), [
4841 {application, '$(PROJECT)', [
48894842 {description, "$(PROJECT_DESCRIPTION)"},
48904843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48914844 {id$(comma)$(space)"$(1)"}$(comma))
48924845 {modules, [$(call comma_list,$(2))]},
48934846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
48944847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4895 {mod, {$(PROJECT_MOD), []}}
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48964850 ]}.
48974851 endef
48984852 endif
49024856
49034857 # Source files.
49044858
4905 ERL_FILES = $(sort $(call core_find,src/,*.erl))
4906 CORE_FILES = $(sort $(call core_find,src/,*.core))
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
49074863
49084864 # ASN.1 files.
49094865
49134869
49144870 define compile_asn1
49154871 $(verbose) mkdir -p include/
4916 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
49174873 $(verbose) mv asn1/*.erl src/
49184874 $(verbose) mv asn1/*.hrl include/
49194875 $(verbose) mv asn1/*.asn1db include/
49364892
49374893 # Leex and Yecc files.
49384894
4939 XRL_FILES = $(sort $(call core_find,src/,*.xrl))
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
49404896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
49414897 ERL_FILES += $(XRL_ERL_FILES)
49424898
4943 YRL_FILES = $(sort $(call core_find,src/,*.yrl))
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
49444900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
49454901 ERL_FILES += $(YRL_ERL_FILES)
49464902
49474903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4948 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
49494905
49504906 # Erlang and Core Erlang files.
49514907
49954951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49964952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49974953 (F, Mod, import, {Imp, _}) ->
4998 case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
49994960 false -> ok;
50004961 true -> Add(Mod, Imp)
50014962 end;
50194980 end || F <- ErlFiles],
50204981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
50214982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
50224991 ok = file:write_file("$(1)", [
50234992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
5024 "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n"
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
50254994 ]),
50264995 halt()
50274996 endef
50345003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
50355004 # Rebuild everything when the Makefile changes.
50365005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5037 @mkdir -p $(ERLANG_MK_TMP)
5038 @if test -f $@; then \
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
50395008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
50405009 touch -c $(PROJECT).d; \
50415010 fi
5042 @touch $@
5011 $(verbose) touch $@
50435012
50445013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
50455014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
50465015 endif
50475016
5048 -include $(PROJECT).d
5017 include $(wildcard $(PROJECT).d)
50495018
50505019 ebin/$(PROJECT).app:: ebin/
50515020
50645033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
50655034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
50665035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5067 $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
50685037 > ebin/$(PROJECT).app
50695038 else
50705039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
50885057
50895058 endif
50905059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
50915061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
50925062 # This file is part of erlang.mk and subject to the terms of the ISC License.
50935063
51055075 doc-deps:
51065076 else
51075077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5108 $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5109 endif
5110
5111 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51125082 # This file is part of erlang.mk and subject to the terms of the ISC License.
51135083
51145084 .PHONY: rel-deps
51255095 rel-deps:
51265096 else
51275097 rel-deps: $(ALL_REL_DEPS_DIRS)
5128 $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5129 endif
5130
5131 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51325102 # This file is part of erlang.mk and subject to the terms of the ISC License.
51335103
51345104 .PHONY: test-deps test-dir test-build clean-test-dir
51505120 test-deps:
51515121 else
51525122 test-deps: $(ALL_TEST_DEPS_DIRS)
5153 $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
51545124 endif
51555125
51565126 ifneq ($(wildcard $(TEST_DIR)),)
51835153 endif
51845154 endif
51855155
5186 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51875157 # This file is part of erlang.mk and subject to the terms of the ISC License.
51885158
51895159 .PHONY: rebar.config
52195189 rebar.config:
52205190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
52215191
5222 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
52235193 # This file is part of erlang.mk and subject to the terms of the ISC License.
52245194
5225 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
5226
5227 MAN_INSTALL_PATH ?= /usr/local/share/man
5228 MAN_SECTIONS ?= 3 7
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
52295200
52305201 docs:: asciidoc
52315202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
52325207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
52335210
52345211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
52355212 asciidoc-guide:
52365213 else
5237 asciidoc-guide: distclean-asciidoc doc-deps
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
52385215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
52395216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5240 endif
5241
5242 ifeq ($(wildcard doc/src/manual/*.asciidoc),)
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
52435227 asciidoc-manual:
52445228 else
5245 asciidoc-manual: distclean-asciidoc doc-deps
5246 for f in doc/src/manual/*.asciidoc ; do \
5247 a2x -v -f manpage $$f ; \
5248 done
5249 for s in $(MAN_SECTIONS); do \
5250 mkdir -p doc/man$$s/ ; \
5251 mv doc/src/manual/*.$$s doc/man$$s/ ; \
5252 gzip doc/man$$s/*.$$s ; \
5253 done
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
52545262
52555263 install-docs:: install-asciidoc
52565264
52575265 install-asciidoc: asciidoc-manual
5258 for s in $(MAN_SECTIONS); do \
5259 mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
5260 install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
5261 done
5262 endif
5263
5264 distclean:: distclean-asciidoc
5265
5266 distclean-asciidoc:
5267 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
5268
5269 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
52705276 # This file is part of erlang.mk and subject to the terms of the ISC License.
52715277
52725278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
53235329 define bs_Makefile
53245330 PROJECT = $p
53255331 PROJECT_DESCRIPTION = New project
5326 PROJECT_VERSION = 0.0.1
5332 PROJECT_VERSION = 0.1.0
53275333
53285334 # Whitespace to be used when creating files from templates.
53295335 SP = $(SP)
53335339 define bs_Makefile
53345340 PROJECT = $p
53355341 PROJECT_DESCRIPTION = New project
5336 PROJECT_VERSION = 0.0.1
5342 PROJECT_VERSION = 0.1.0
53375343
53385344 endef
53395345 endif
53415347 define bs_apps_Makefile
53425348 PROJECT = $p
53435349 PROJECT_DESCRIPTION = New project
5344 PROJECT_VERSION = 0.0.1
5350 PROJECT_VERSION = 0.1.0
53455351
53465352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
53475353 endef
53615367 endef
53625368
53635369 define bs_relx_config
5364 {release, {$p_release, "1"}, [$p]}.
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
53655371 {extended_start_script, true}.
53665372 {sys_config, "rel/sys.config"}.
53675373 {vm_args, "rel/vm.args"}.
57125718 ifndef t
57135719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57145720 endif
5715 ifndef tpl_$(t)
5716 $(error Unknown template)
5717 endif
57185721 ifndef n
57195722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57205723 endif
57275730 list-templates:
57285731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
57295732
5730 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
57315734 # This file is part of erlang.mk and subject to the terms of the ISC License.
57325735
57335736 .PHONY: clean-c_src distclean-c_src-env
59625965 $(call render_template,bs_erl_nif,src/$n.erl)
59635966 endif
59645967
5965 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
59665969 # This file is part of erlang.mk and subject to the terms of the ISC License.
59675970
5968 .PHONY: ci ci-setup distclean-kerl
5969
5970 KERL ?= $(CURDIR)/kerl
5971 export KERL
5972
5973 KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
5974
5975 OTP_GIT ?= https://github.com/erlang/otp
5976
5977 CI_INSTALL_DIR ?= $(HOME)/erlang
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
59785973 CI_OTP ?=
5979
5980 ifeq ($(strip $(CI_OTP)),)
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
59815986 ci::
59825987 else
5983 ci:: $(addprefix ci-,$(CI_OTP))
5984
5985 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
59866007
59876008 ci-setup::
6009
6010 ci-extra::
59886011
59896012 ci_verbose_0 = @echo " CI " $(1);
59906013 ci_verbose = $(ci_verbose_$(V))
59916014
59926015 define ci_target
5993 ci-$(1): $(CI_INSTALL_DIR)/$(1)
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
59946018 $(ci_verbose) \
5995 PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
5996 CI_OTP_RELEASE="$(1)" \
5997 CT_OPTS="-label $(1)" \
5998 $(MAKE) clean ci-setup tests
5999 endef
6000
6001 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
60026030
60036031 define ci_otp_target
60046032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
60056033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6006 $(KERL) build git $(OTP_GIT) $(1) $(1)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
60076035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
60086036 endif
60096037 endef
60106038
60116039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
60126040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
60136052 $(KERL):
6014 $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
60156056 $(verbose) chmod +x $(KERL)
60166057
60176058 help::
60286069 $(gen_verbose) rm -rf $(KERL)
60296070 endif
60306071
6031 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
60326073 # This file is part of erlang.mk and subject to the terms of the ISC License.
60336074
60346075 .PHONY: ct apps-ct distclean-ct
60366077 # Configuration.
60376078
60386079 CT_OPTS ?=
6080
60396081 ifneq ($(wildcard $(TEST_DIR)),)
6040 CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6041 else
6042 CT_SUITES ?=
6043 endif
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
60446088
60456089 # Core targets.
60466090
60636107 -noinput \
60646108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
60656109 -dir $(TEST_DIR) \
6066 -logdir $(CURDIR)/logs
6110 -logdir $(CT_LOGS_DIR)
60676111
60686112 ifeq ($(CT_SUITES),)
60696113 ct: $(if $(IS_APP),,apps-ct)
60706114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
60716117 ct: test-build $(if $(IS_APP),,apps-ct)
6072 $(verbose) mkdir -p $(CURDIR)/logs/
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
60736119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
60746121 endif
60756122
60766123 ifneq ($(ALL_APPS_DIRS),)
60976144
60986145 define ct_suite_target
60996146 ct-$(1): test-build
6100 $(verbose) mkdir -p $(CURDIR)/logs/
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
61016148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
61026149 endef
61036150
61046151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
61056152
61066153 distclean-ct:
6107 $(gen_verbose) rm -rf $(CURDIR)/logs/
6108
6109 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61106157 # This file is part of erlang.mk and subject to the terms of the ISC License.
61116158
61126159 .PHONY: plt distclean-plt dialyze
61506197 endef
61516198
61526199 $(DIALYZER_PLT): deps app
6153 $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
61546204
61556205 plt: $(DIALYZER_PLT)
61566206
61646214 endif
61656215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
61666216
6167 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61686218 # This file is part of erlang.mk and subject to the terms of the ISC License.
61696219
61706220 .PHONY: distclean-edoc edoc
61726222 # Configuration.
61736223
61746224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
61756235
61766236 # Core targets.
61776237
6178 ifneq ($(wildcard doc/overview.edoc),)
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
61796239 docs:: edoc
61806240 endif
61816241
61846244 # Plugin-specific targets.
61856245
61866246 edoc: distclean-edoc doc-deps
6187 $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
61886248
61896249 distclean-edoc:
61906250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
61916251
6192 # Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61936253 # This file is part of erlang.mk and subject to the terms of the ISC License.
61946254
6195 .PHONY: distclean-escript escript
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
61966320
61976321 # Configuration.
61986322
61996323 ESCRIPT_NAME ?= $(PROJECT)
62006324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
62016325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
62026327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6203
6204 ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
6205 ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
6206 ESCRIPT_EMU_ARGS ?= -pa . \
6207 -sasl errlog_type error \
6208 -escript main $(ESCRIPT_NAME)
6209 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6210 ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
62116332
62126333 # Core targets.
62136334
62206341
62216342 # Plugin-specific targets.
62226343
6223 # Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
6224 # Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
6225 # Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
6226 # Software may only be used for the great good and the true happiness of all
6227 # sentient beings.
6228
6229 define ESCRIPT_RAW
6230 'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
6231 'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
6232 ' [F || F <- A, not filelib:is_dir(F) ] end,'\
6233 'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
6234 'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
6235 'Ez = fun(Escript) ->'\
6236 ' Static = Files([$(ESCRIPT_STATIC)]),'\
6237 ' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
6238 ' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
6239 ' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
6240 ' {archive, Archive, [memory]},'\
6241 ' {shebang, "$(ESCRIPT_SHEBANG)"},'\
6242 ' {comment, "$(ESCRIPT_COMMENT)"},'\
6243 ' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
6244 ' ]),'\
6245 ' file:change_mode(Escript, 8#755)'\
6246 'end,'\
6247 'Ez("$(ESCRIPT_FILE)"),'\
6248 'halt().'
6249 endef
6250
6251 ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
6252
6253 escript:: distclean-escript deps app
6254 $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
62556360
62566361 distclean-escript:
6257 $(gen_verbose) rm -f $(ESCRIPT_NAME)
6258
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
62596365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6260 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
62616366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
62626367
62636368 .PHONY: eunit apps-eunit
63216426
63226427 ifneq ($(ALL_APPS_DIRS),)
63236428 apps-eunit:
6324 $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done
6325 endif
6326 endif
6327
6328 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
63296436 # This file is part of erlang.mk and subject to the terms of the ISC License.
63306437
6331 .PHONY: relx-rel distclean-relx-rel distclean-relx run
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
63326439
63336440 # Configuration.
63346441
6335 RELX ?= $(CURDIR)/relx
6442 RELX ?= $(ERLANG_MK_TMP)/relx
63366443 RELX_CONFIG ?= $(CURDIR)/relx.config
63376444
63386445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
63396446 RELX_OPTS ?=
63406447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
63416454
63426455 ifeq ($(firstword $(RELX_OPTS)),-o)
63436456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
63506463 ifeq ($(IS_DEP),)
63516464 ifneq ($(wildcard $(RELX_CONFIG)),)
63526465 rel:: relx-rel
6353 endif
6354 endif
6355
6356 distclean:: distclean-relx-rel distclean-relx
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
63576472
63586473 # Plugin-specific targets.
63596474
63626477 $(verbose) chmod +x $(RELX)
63636478
63646479 relx-rel: $(RELX) rel-deps app
6365 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
63666484
63676485 distclean-relx-rel:
63686486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6369
6370 distclean-relx:
6371 $(gen_verbose) rm -rf $(RELX)
63726487
63736488 # Run target.
63746489
63776492 else
63786493
63796494 define get_relx_release.erl
6380 {ok, Config} = file:consult("$(RELX_CONFIG)"),
6381 {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
6382 io:format("~s", [Name]),
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
63836504 halt(0).
63846505 endef
63856506
6386 RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
63876514
63886515 run: all
6389 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
63906517
63916518 help::
63926519 $(verbose) printf "%s\n" "" \
63956522
63966523 endif
63976524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
63986526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6399 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
64006527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
64016528
64026529 .PHONY: shell
64216548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
64226549
64236550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6424 $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
64256552
64266553 shell: build-shell-deps
64276554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
64286555
6429 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
64306571 # This file is part of erlang.mk and subject to the terms of the ISC License.
64316572
64326573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
64376578 tests:: triq
64386579
64396580 define triq_check.erl
6440 code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
64416582 try
64426583 case $(1) of
64436584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
64696610 endif
64706611 endif
64716612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
64726614 # Copyright (c) 2015, Erlang Solutions Ltd.
64736615 # This file is part of erlang.mk and subject to the terms of the ISC License.
64746616
64856627 XREFR ?= $(CURDIR)/xrefr
64866628 export XREFR
64876629
6488 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
64896631
64906632 # Core targets.
64916633
64926634 help::
6493 $(verbose) printf "%s\n" "" \
6494 "Xref targets:" \
6495 " xref Run Xrefr using $XREF_CONFIG as config file if defined"
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
64966638
64976639 distclean:: distclean-xref
64986640
65086650 distclean-xref:
65096651 $(gen_verbose) rm -rf $(XREFR)
65106652
6511 # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
65126655 # This file is part of erlang.mk and subject to the terms of the ISC License.
65136656
65146657 COVER_REPORT_DIR = cover
65176660
65186661 ifdef COVER
65196662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
65206664 # All modules in 'ebin'
65216665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
65226666
65316675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
65326676 endif
65336677 endif
6678 endif
65346679
65356680 # Core targets
65366681
65526697 "Cover targets:" \
65536698 " cover-report Generate a HTML coverage report from previously collected" \
65546699 " cover data." \
6555 " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
65566701 "" \
65576702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
65586703 "target tests additionally generates a HTML coverage report from the combined" \
65656710
65666711 .PHONY: coverdata-clean
65676712 coverdata-clean:
6568 $(gen_verbose) rm -f *.coverdata ct.cover.spec
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
65696714
65706715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
65716721 all.coverdata: $(COVERDATA)
6572 $(gen_verbose) $(ERL) -eval ' \
6573 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
6574 cover:export("$@"), halt(0).'
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
65756723
65766724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
65776725 # empty if you want the coverdata files but not the HTML report.
65896737 # Modules which include eunit.hrl always contain one line without coverage
65906738 # because eunit defines test/0 which is never called. We compensate for this.
65916739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6592 grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
65936741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
65946742
65956743 define cover_report.erl
66246772 endef
66256773
66266774 cover-report:
6627 $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
66286776 $(gen_verbose) $(call erlang,$(cover_report.erl))
66296777
66306778 endif
66316779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
66326839
66336840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
66346841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
66976904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
66986905 endif
66996906 ifndef IS_APP
6700 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
67016908 $(MAKE) -C $$dep $@ \
67026909 IS_APP=1 \
6703 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6704 || exit $$?; \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67056911 done
67066912 endif
6707 $(verbose) for dep in $^ ; do \
6913 $(verbose) set -e; for dep in $^ ; do \
67086914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
67096915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6710 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
67116917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
67126918 $(MAKE) -C $$dep fetch-deps \
67136919 IS_DEP=1 \
6714 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6715 || exit $$?; \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67166921 fi \
67176922 fi \
67186923 done
22 # before the inclusion of erlang.mk leading to the wrong target becoming
33 # the default.
44 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
526 endif
627
728 # --------------------------------------------------------------------
2041 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
2142 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
2243 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
2345 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
2446 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
2547 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
2648 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
2749 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
2851 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
2952 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
3054 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
3155 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
3256 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
3559 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
3660 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
3761 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
3863 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
3964 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
4065 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
6590 dep_rabbitmq_web_mqtt = git_rmq rabbitmq-web-mqtt $(current_rmq_ref) $(base_rmq_ref) master
6691 dep_rabbitmq_web_mqtt_examples = git_rmq rabbitmq-web-mqtt-examples $(current_rmq_ref) $(base_rmq_ref) master
6792 dep_rabbitmq_website = git_rmq rabbitmq-website $(current_rmq_ref) $(base_rmq_ref) live master
68 dep_sockjs = git_rmq sockjs-erlang $(current_rmq_ref) $(base_rmq_ref) master
6993 dep_toke = git_rmq toke $(current_rmq_ref) $(base_rmq_ref) master
7094
7195 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
7296
73 # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
74 # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
75 # needs to add "ranch" as a BUILD_DEPS. The list of projects needing
76 # this workaround are:
77 # o rabbitmq-web-stomp
78 dep_ranch = git https://github.com/ninenines/ranch 1.2.1
97 # Third-party dependencies version pinning.
98 #
99 # We do that in this file, which is copied in all projects, to ensure
100 # all projects use the same versions. It avoids conflicts and makes it
101 # possible to work with rabbitmq-public-umbrella.
102
103 dep_cowboy_commit = 1.0.4
104 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
105 # Last commit of PropEr supporting Erlang R16B03.
106 dep_proper_commit = 735d972758d8bd85b12483626fe1b66450d6a6fe
107 dep_ranch_commit = 1.3.1
108 # Last commit of sockjs support Erlang R16B03 and 17.x.
109 dep_sockjs = git https://github.com/rabbitmq/sockjs-erlang.git 5af2b588c812c318b19bc105b577a759c71c3e0a
110 dep_webmachine_commit = 1.10.8p2
79111
80112 RABBITMQ_COMPONENTS = amqp_client \
81113 rabbit \
82114 rabbit_common \
83115 rabbitmq_amqp1_0 \
84116 rabbitmq_auth_backend_amqp \
117 rabbitmq_auth_backend_cache \
85118 rabbitmq_auth_backend_http \
86119 rabbitmq_auth_backend_ldap \
87120 rabbitmq_auth_mechanism_ssl \
88121 rabbitmq_boot_steps_visualiser \
89122 rabbitmq_clusterer \
123 rabbitmq_cli \
90124 rabbitmq_codegen \
91125 rabbitmq_consistent_hash_exchange \
126 rabbitmq_ct_client_helpers \
92127 rabbitmq_ct_helpers \
93128 rabbitmq_delayed_message_exchange \
94129 rabbitmq_dotnet_client \
97132 rabbitmq_federation_management \
98133 rabbitmq_java_client \
99134 rabbitmq_jms_client \
135 rabbitmq_jms_cts \
100136 rabbitmq_jms_topic_exchange \
101137 rabbitmq_lvc \
102138 rabbitmq_management \
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_ws_app).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_ws_client).
6262 Headers = proplists:get_value(headers, Info),
6363
6464 UseHTTPAuth = application:get_env(rabbitmq_web_stomp, use_http_auth, false),
65
6566 StompConfig0 = #stomp_configuration{implicit_connect = false},
6667
6768 StompConfig = case UseHTTPAuth of
6970 case lists:keyfind(authorization, 1, Headers) of
7071 false ->
7172 %% We fall back to the default STOMP credentials.
72 StompConfig0;
73 UserConfig = application:get_env(rabbitmq_stomp, default_user, undefined),
74 StompConfig1 = rabbit_stomp:parse_default_user(UserConfig, StompConfig0),
75 StompConfig1#stomp_configuration{force_default_creds = true};
7376 {_, AuthHd} ->
7477 {<<"basic">>, {HTTPLogin, HTTPPassCode}}
7578 = cowboy_http:token_ci(list_to_binary(AuthHd),
150153 handle_info(#'basic.cancel'{consumer_tag = Ctag}, State) ->
151154 ProcState = processor_state(State),
152155 case rabbit_stomp_processor:cancel_consumer(Ctag, ProcState) of
153 {ok, NewProcState} ->
156 {ok, NewProcState, _Connection} ->
154157 {noreply, processor_state(NewProcState, State)};
155158 {stop, Reason, NewProcState} ->
156159 {stop, Reason, processor_state(NewProcState, State)}
240243 rabbit_event:if_enabled(State, #state.stats_timer,
241244 fun() -> emit_stats(State) end).
242245
246 emit_stats(State=#state{connection = C}) when C == none; C == undefined ->
247 %% Avoid emitting stats on terminate when the connection has not yet been
248 %% established, as this causes orphan entries on the stats database
249 State1 = rabbit_event:reset_stats_timer(State, #state.stats_timer),
250 State1;
243251 emit_stats(State=#state{conn=Conn, connection=ConnPid}) ->
244252 Info = Conn:info(),
245253 Sock = proplists:get_value(socket, Info),
249257 {error, _} -> []
250258 end,
251259 Infos = [{pid, ConnPid}|SockInfos],
260 rabbit_core_metrics:connection_stats(ConnPid, Infos),
252261 rabbit_event:notify(connection_stats, Infos),
253262 State1 = rabbit_event:reset_stats_timer(State, #state.stats_timer),
254263 State1.
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_ws_client_sup).
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_ws_sockjs).
1717
1818 -export([init/0]).
19
20 %% for testing purposes
21 -export([get_binding_address/1, get_tcp_port/1, get_tcp_conf/2]).
1922
2023 -include_lib("rabbitmq_stomp/include/rabbit_stomp.hrl").
2124
2427
2528 -spec init() -> ok.
2629 init() ->
27 %% The 'tcp_config' option may include the port, but we already have
28 %% a 'port' option, so we prioritize the 'port' option over the one
29 %% found in 'tcp_config', if any.
30 TCPConf0 = get_env(tcp_config, []),
31 {TCPConf, Port} = case application:get_env(rabbitmq_web_stomp, port) of
32 undefined ->
33 {TCPConf0, proplists:get_value(port, TCPConf0, 15674)};
34 {ok, Port0} ->
35 {[{port, Port0}|TCPConf0], Port0}
36 end,
30 Port = get_tcp_port(application:get_all_env(rabbitmq_web_stomp)),
31
32 TcpConf = get_tcp_conf(get_env(tcp_config, []), Port),
3733
3834 WsFrame = get_env(ws_frame, text),
3935 CowboyOpts = get_env(cowboy_opts, []),
5147 undefined -> get_env(num_acceptors, 10);
5248 {ok, NumTcp} -> NumTcp
5349 end,
54 cowboy:start_http(http, NumTcpAcceptors,
55 TCPConf,
56 [{env, [{dispatch, Routes}]}|CowboyOpts]),
50 case cowboy:start_http(http, NumTcpAcceptors,
51 TcpConf,
52 [{env, [{dispatch, Routes}]}|CowboyOpts]) of
53 {ok, _} -> ok;
54 {error, {already_started, _}} -> ok;
55 {error, Err} ->
56 rabbit_log:error("Failed to start an HTTP listener for SockJS. Error: ~p, listener settings: ~p~n", [Err, TcpConf]),
57 throw(Err)
58 end,
59 listener_started('http/web-stomp', TcpConf),
5760 rabbit_log:info("rabbit_web_stomp: listening for HTTP connections on ~s:~w~n",
58 ["0.0.0.0", Port]),
61 [get_binding_address(TcpConf), Port]),
5962 case get_env(ssl_config, []) of
6063 [] ->
6164 ok;
62 TLSConf ->
65 TLSConf0 ->
6366 rabbit_networking:ensure_ssl(),
64 TLSPort = proplists:get_value(port, TLSConf),
67 TLSPort = proplists:get_value(port, TLSConf0),
68 TLSConf = maybe_parse_ip(TLSConf0),
6569 NumSslAcceptors = case application:get_env(rabbitmq_web_stomp, num_ssl_acceptors) of
66 undefined -> get_env(num_acceptors, 1);
70 undefined -> get_env(num_acceptors, 1);
6771 {ok, NumSsl} -> NumSsl
6872 end,
69 cowboy:start_https(https, NumSslAcceptors,
70 TLSConf,
71 [{env, [{dispatch, Routes}]}|CowboyOpts]),
73 {ok, _} = cowboy:start_https(https, NumSslAcceptors,
74 TLSConf,
75 [{env, [{dispatch, Routes}]} | CowboyOpts]),
76 listener_started('https/web-stomp', TLSConf),
7277 rabbit_log:info("rabbit_web_stomp: listening for HTTPS connections on ~s:~w~n",
73 ["0.0.0.0", TLSPort])
78 [get_binding_address(TLSConf), TLSPort])
7479 end,
80 ok.
81
82 listener_started(Protocol, Listener) ->
83 Port = rabbit_misc:pget(port, Listener),
84 [rabbit_networking:tcp_listener_started(Protocol, Listener,
85 IPAddress, Port)
86 || {IPAddress, _Port, _Family}
87 <- rabbit_networking:tcp_listener_addresses(Port)],
7588 ok.
7689
7790 get_env(Key, Default) ->
8093 {ok, V} -> V
8194 end.
8295
96
97 get_tcp_port(Configuration) ->
98 %% The 'tcp_config' option may include the port, and we already have
99 %% a 'port' option. We prioritize the 'port' option in 'tcp_config' (if any)
100 %% over the one found at the root of the env proplist.
101 TcpConfiguration = proplists:get_value(tcp_config, Configuration, []),
102 case proplists:get_value(port, TcpConfiguration) of
103 undefined ->
104 proplists:get_value(port, Configuration, 15674);
105 Port ->
106 Port
107 end.
108
109 get_tcp_conf(TcpConfiguration, Port0) ->
110 Port = [{port, Port0} | proplists:delete(port, TcpConfiguration)],
111 maybe_parse_ip(Port).
112
113 maybe_parse_ip(Configuration) ->
114 case proplists:get_value(ip, Configuration) of
115 undefined ->
116 Configuration;
117 IP when is_tuple(IP) ->
118 Configuration;
119 IP when is_list(IP) ->
120 {ok, ParsedIP} = inet_parse:address(IP),
121 [{ip, ParsedIP} | proplists:delete(ip, Configuration)]
122 end.
123
124 get_binding_address(Configuration) ->
125 case proplists:get_value(ip, Configuration) of
126 undefined ->
127 "0.0.0.0";
128 IP when is_tuple(IP) ->
129 inet:ntoa(IP);
130 IP when is_list(IP) ->
131 IP
132 end.
83133
84134 %% Don't print sockjs logs
85135 logger(_Service, Req, _Type) ->
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_ws_sup).
+0
-18
deps/rabbitmq_web_stomp/src/rabbitmq_web_stomp.app.src less more
0 {application, rabbitmq_web_stomp,
1 [
2 {description, "Rabbit WEB-STOMP - WebSockets to Stomp adapter"},
3 {vsn, "3.6.6"},
4 {modules, []},
5 {registered, []},
6 {mod, {rabbit_ws_app, []}},
7 {env, [{port, 15674},
8 {tcp_config, []},
9 {num_tcp_acceptors, 10},
10 {ssl_config, []},
11 {num_ssl_acceptors, 1},
12 {cowboy_opts, []},
13 {sockjs_opts, []},
14 {ws_frame, text},
15 {use_http_auth, false}]},
16 {applications, [kernel, stdlib, rabbit_common, rabbit, rabbitmq_stomp, cowboy, sockjs]}
17 ]}.
00 PROJECT = rabbitmq_web_stomp_examples
1 PROJECT_DESCRIPTION = Rabbit WEB-STOMP - examples
2 PROJECT_MOD = rabbit_web_stomp_examples_app
3
4 define PROJECT_ENV
5 [
6 {listener, [{port, 15670}]}
7 ]
8 endef
19
210 DEPS = rabbit_common rabbit rabbitmq_web_dispatch rabbitmq_web_stomp
311
12 DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
413 DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk
514
615 # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
0 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
11 #
22 # Permission to use, copy, modify, and/or distribute this software for any
33 # purpose with or without fee is hereby granted, provided that the above
1111 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1212 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1313
14 .PHONY: all app apps deps search rel docs install-docs check tests clean distclean help erlang-mk
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
1515
1616 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17
18 ERLANG_MK_VERSION = 2.0.0-pre.2-144-g647ffd1
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
1931
2032 # Core configuration.
2133
2436
2537 PROJECT_VERSION ?= rolling
2638 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
2740
2841 # Verbosity.
2942
8497 rel::
8598 $(verbose) :
8699
100 relup:: deps app
101
87102 check:: tests
88103
89104 clean:: clean-crashdump
101116 help::
102117 $(verbose) printf "%s\n" \
103118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
104 "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
105120 "" \
106121 "Usage: [V=1] $(MAKE) [target]..." \
107122 "" \
149164 core_native_path = $1
150165 endif
151166
152 ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
153 define core_http_get
154 wget --no-check-certificate -O $(1) $(2)|| rm $(1)
155 endef
156 else
157 define core_http_get.erl
158 ssl:start(),
159 inets:start(),
160 case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
161 {ok, {{_, 200, _}, _, Body}} ->
162 case file:write_file("$(1)", Body) of
163 ok -> ok;
164 {error, R1} -> halt(R1)
165 end;
166 {error, R2} ->
167 halt(R2)
168 end,
169 halt(0).
170 endef
171
172 define core_http_get
173 $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
174 endef
175 endif
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
176168
177169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
178170
192184 ERLANG_MK_BUILD_CONFIG ?= build.config
193185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
194186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
195188 erlang-mk:
196189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
197190 ifdef ERLANG_MK_COMMIT
198191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
199192 endif
200193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
201 $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
202195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
203196 rm -rf $(ERLANG_MK_BUILD_DIR)
204197
285278 pkg_apns_repo = https://github.com/inaka/apns4erl
286279 pkg_apns_commit = master
287280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
288289 PACKAGES += azdht
289290 pkg_azdht_name = azdht
290291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
405406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
406407 pkg_bootstrap_commit = master
407408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
408417 PACKAGES += boss_db
409418 pkg_boss_db_name = boss_db
410419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
413422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
414423 pkg_boss_db_commit = master
415424
416 PACKAGES += boss
417 pkg_boss_name = boss
418 pkg_boss_description = Erlang web MVC, now featuring Comet
419 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
420 pkg_boss_fetch = git
421 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
422 pkg_boss_commit = master
423
424425 PACKAGES += brod
425426 pkg_brod_name = brod
426427 pkg_brod_description = Kafka client in Erlang
565566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
566567 pkg_cloudi_service_api_requests_commit = master
567568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
568585 PACKAGES += cloudi_service_db_cassandra_cql
569586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
570587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
573590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
574591 pkg_cloudi_service_db_cassandra_cql_commit = master
575592
576 PACKAGES += cloudi_service_db_cassandra
577 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
579 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
580 pkg_cloudi_service_db_cassandra_fetch = git
581 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
582 pkg_cloudi_service_db_cassandra_commit = master
583
584593 PACKAGES += cloudi_service_db_couchdb
585594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
586595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
637646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
638647 pkg_cloudi_service_db_tokyotyrant_commit = master
639648
640 PACKAGES += cloudi_service_db
641 pkg_cloudi_service_db_name = cloudi_service_db
642 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
643 pkg_cloudi_service_db_homepage = http://cloudi.org/
644 pkg_cloudi_service_db_fetch = git
645 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
646 pkg_cloudi_service_db_commit = master
647
648649 PACKAGES += cloudi_service_filesystem
649650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
650651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
10371038 pkg_edown_repo = https://github.com/uwiger/edown
10381039 pkg_edown_commit = master
10391040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
10401049 PACKAGES += eep_app
10411050 pkg_eep_app_name = eep_app
10421051 pkg_eep_app_description = Embedded Event Processing
10451054 pkg_eep_app_repo = https://github.com/darach/eep-erl
10461055 pkg_eep_app_commit = master
10471056
1048 PACKAGES += eep
1049 pkg_eep_name = eep
1050 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1051 pkg_eep_homepage = https://github.com/virtan/eep
1052 pkg_eep_fetch = git
1053 pkg_eep_repo = https://github.com/virtan/eep
1054 pkg_eep_commit = master
1055
10561057 PACKAGES += efene
10571058 pkg_efene_name = efene
10581059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
12371238 pkg_eqm_repo = https://github.com/loucash/eqm
12381239 pkg_eqm_commit = master
12391240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
12401249 PACKAGES += eredis_pool
12411250 pkg_eredis_pool_name = eredis_pool
12421251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
12451254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
12461255 pkg_eredis_pool_commit = master
12471256
1248 PACKAGES += eredis
1249 pkg_eredis_name = eredis
1250 pkg_eredis_description = Erlang Redis client
1251 pkg_eredis_homepage = https://github.com/wooga/eredis
1252 pkg_eredis_fetch = git
1253 pkg_eredis_repo = https://github.com/wooga/eredis
1254 pkg_eredis_commit = master
1255
12561257 PACKAGES += erl_streams
12571258 pkg_erl_streams_name = erl_streams
12581259 pkg_erl_streams_description = Streams in Erlang
15411542 pkg_etap_repo = https://github.com/ngerakines/etap
15421543 pkg_etap_commit = master
15431544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
15441553 PACKAGES += etest_http
15451554 pkg_etest_http_name = etest_http
15461555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
15491558 pkg_etest_http_repo = https://github.com/wooga/etest_http
15501559 pkg_etest_http_commit = master
15511560
1552 PACKAGES += etest
1553 pkg_etest_name = etest
1554 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1555 pkg_etest_homepage = https://github.com/wooga/etest
1556 pkg_etest_fetch = git
1557 pkg_etest_repo = https://github.com/wooga/etest
1558 pkg_etest_commit = master
1559
15601561 PACKAGES += etoml
15611562 pkg_etoml_name = etoml
15621563 pkg_etoml_description = TOML language erlang parser
15651566 pkg_etoml_repo = https://github.com/kalta/etoml
15661567 pkg_etoml_commit = master
15671568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
15681577 PACKAGES += eunit_formatters
15691578 pkg_eunit_formatters_name = eunit_formatters
15701579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
15731582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
15741583 pkg_eunit_formatters_commit = master
15751584
1576 PACKAGES += eunit
1577 pkg_eunit_name = eunit
1578 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1579 pkg_eunit_homepage = https://github.com/richcarl/eunit
1580 pkg_eunit_fetch = git
1581 pkg_eunit_repo = https://github.com/richcarl/eunit
1582 pkg_eunit_commit = master
1583
15841585 PACKAGES += euthanasia
15851586 pkg_euthanasia_name = euthanasia
15861587 pkg_euthanasia_description = Merciful killer for your Erlang processes
15981599 pkg_evum_commit = master
15991600
16001601 PACKAGES += exec
1601 pkg_exec_name = exec
1602 pkg_exec_name = erlexec
16021603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
16031604 pkg_exec_homepage = http://saleyn.github.com/erlexec
16041605 pkg_exec_fetch = git
17171718 pkg_fn_repo = https://github.com/reiddraper/fn
17181719 pkg_fn_commit = master
17191720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
17201729 PACKAGES += folsom_cowboy
17211730 pkg_folsom_cowboy_name = folsom_cowboy
17221731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
17251734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
17261735 pkg_folsom_cowboy_commit = master
17271736
1728 PACKAGES += folsom
1729 pkg_folsom_name = folsom
1730 pkg_folsom_description = Expose Erlang Events and Metrics
1731 pkg_folsom_homepage = https://github.com/boundary/folsom
1732 pkg_folsom_fetch = git
1733 pkg_folsom_repo = https://github.com/boundary/folsom
1734 pkg_folsom_commit = master
1735
17361737 PACKAGES += folsomite
17371738 pkg_folsomite_name = folsomite
17381739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
20932094 pkg_jesse_repo = https://github.com/for-GET/jesse
20942095 pkg_jesse_commit = master
20952096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
20962105 PACKAGES += jiffy_v
20972106 pkg_jiffy_v_name = jiffy_v
20982107 pkg_jiffy_v_description = JSON validation utility
21012110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
21022111 pkg_jiffy_v_commit = master
21032112
2104 PACKAGES += jiffy
2105 pkg_jiffy_name = jiffy
2106 pkg_jiffy_description = JSON NIFs for Erlang.
2107 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2108 pkg_jiffy_fetch = git
2109 pkg_jiffy_repo = https://github.com/davisp/jiffy
2110 pkg_jiffy_commit = master
2111
21122113 PACKAGES += jobs
21132114 pkg_jobs_name = jobs
21142115 pkg_jobs_description = a Job scheduler for load regulation
21252126 pkg_joxa_repo = https://github.com/joxa/joxa
21262127 pkg_joxa_commit = master
21272128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
21282137 PACKAGES += json_rec
21292138 pkg_json_rec_name = json_rec
21302139 pkg_json_rec_description = JSON to erlang record
21332142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
21342143 pkg_json_rec_commit = master
21352144
2136 PACKAGES += json
2137 pkg_json_name = json
2138 pkg_json_description = a high level json library for erlang (17.0+)
2139 pkg_json_homepage = https://github.com/talentdeficit/json
2140 pkg_json_fetch = git
2141 pkg_json_repo = https://github.com/talentdeficit/json
2142 pkg_json_commit = master
2143
21442145 PACKAGES += jsone
21452146 pkg_jsone_name = jsone
21462147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
21812182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
21822183 pkg_jsx_commit = master
21832184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
21842193 PACKAGES += kafka_protocol
21852194 pkg_kafka_protocol_name = kafka_protocol
21862195 pkg_kafka_protocol_description = Kafka protocol Erlang library
21892198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
21902199 pkg_kafka_protocol_commit = master
21912200
2192 PACKAGES += kafka
2193 pkg_kafka_name = kafka
2194 pkg_kafka_description = Kafka consumer and producer in Erlang
2195 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2196 pkg_kafka_fetch = git
2197 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2198 pkg_kafka_commit = master
2199
22002201 PACKAGES += kai
22012202 pkg_kai_name = kai
22022203 pkg_kai_description = DHT storage by Takeshi Inoue
22932294 pkg_kvs_repo = https://github.com/synrc/kvs
22942295 pkg_kvs_commit = master
22952296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
22962305 PACKAGES += lager_amqp_backend
22972306 pkg_lager_amqp_backend_name = lager_amqp_backend
22982307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
23092318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
23102319 pkg_lager_syslog_commit = master
23112320
2312 PACKAGES += lager
2313 pkg_lager_name = lager
2314 pkg_lager_description = A logging framework for Erlang/OTP.
2315 pkg_lager_homepage = https://github.com/basho/lager
2316 pkg_lager_fetch = git
2317 pkg_lager_repo = https://github.com/basho/lager
2318 pkg_lager_commit = master
2319
23202321 PACKAGES += lambdapad
23212322 pkg_lambdapad_name = lambdapad
23222323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
25732574 pkg_mixer_repo = https://github.com/chef/mixer
25742575 pkg_mixer_commit = master
25752576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
25762585 PACKAGES += mochiweb_xpath
25772586 pkg_mochiweb_xpath_name = mochiweb_xpath
25782587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
25812590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
25822591 pkg_mochiweb_xpath_commit = master
25832592
2584 PACKAGES += mochiweb
2585 pkg_mochiweb_name = mochiweb
2586 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2587 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2588 pkg_mochiweb_fetch = git
2589 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2590 pkg_mochiweb_commit = master
2591
25922593 PACKAGES += mockgyver
25932594 pkg_mockgyver_name = mockgyver
25942595 pkg_mockgyver_description = A mocking library for Erlang
30613062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
30623063 pkg_quickrand_commit = master
30633064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
30643073 PACKAGES += rabbit_exchange_type_riak
30653074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
30663075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
30693078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
30703079 pkg_rabbit_exchange_type_riak_commit = master
30713080
3072 PACKAGES += rabbit
3073 pkg_rabbit_name = rabbit
3074 pkg_rabbit_description = RabbitMQ Server
3075 pkg_rabbit_homepage = https://www.rabbitmq.com/
3076 pkg_rabbit_fetch = git
3077 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3078 pkg_rabbit_commit = master
3079
30803081 PACKAGES += rack
30813082 pkg_rack_name = rack
30823083 pkg_rack_description = Rack handler for erlang
34933494 pkg_smother_repo = https://github.com/ramsay-t/Smother
34943495 pkg_smother_commit = master
34953496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
34963505 PACKAGES += social
34973506 pkg_social_name = social
34983507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
35413550 pkg_stable_repo = https://github.com/dvv/stable
35423551 pkg_stable_commit = master
35433552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
35443561 PACKAGES += statebox_riak
35453562 pkg_statebox_riak_name = statebox_riak
35463563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
35493566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
35503567 pkg_statebox_riak_commit = master
35513568
3552 PACKAGES += statebox
3553 pkg_statebox_name = statebox
3554 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3555 pkg_statebox_homepage = https://github.com/mochi/statebox
3556 pkg_statebox_fetch = git
3557 pkg_statebox_repo = https://github.com/mochi/statebox
3558 pkg_statebox_commit = master
3559
35603569 PACKAGES += statman
35613570 pkg_statman_name = statman
35623571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
37253734 pkg_tirerl_repo = https://github.com/inaka/tirerl
37263735 pkg_tirerl_commit = master
37273736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
37283745 PACKAGES += traffic_tools
37293746 pkg_traffic_tools_name = traffic_tools
37303747 pkg_traffic_tools_description = Simple traffic limiting library
40614078 pkg_zucchini_repo = https://github.com/devinus/zucchini
40624079 pkg_zucchini_commit = master
40634080
4064 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
40654082 # This file is part of erlang.mk and subject to the terms of the ISC License.
40664083
40674084 .PHONY: search
40884105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
40894106 endif
40904107
4091 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
40924109 # This file is part of erlang.mk and subject to the terms of the ISC License.
40934110
4094 .PHONY: distclean-deps
4111 .PHONY: distclean-deps clean-tmp-deps.log
40954112
40964113 # Configuration.
40974114
41104127
41114128 REBAR_DEPS_DIR = $(DEPS_DIR)
41124129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
41134150
41144151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
41154152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
41164153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
41174154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
41184155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
41194157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
41204158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
41214159
41384176
41394177 # Core targets.
41404178
4141 ifdef IS_APP
4142 apps::
4143 else
4144 apps:: $(ALL_APPS_DIRS)
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
41454180 ifeq ($(IS_APP)$(IS_DEP),)
41464181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
41474182 endif
41494184 # Create ebin directory for all apps to make sure Erlang recognizes them
41504185 # as proper OTP applications when using -include_lib. This is a temporary
41514186 # fix, a proper fix would be to compile apps/* in the right order.
4152 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4153 mkdir -p $$dep/ebin || exit $$?; \
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
41544190 done
4155 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
41564196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
41574197 :; \
41584198 else \
41594199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4160 $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
4200 $(MAKE) -C $$dep IS_APP=1; \
41614201 fi \
41624202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
41634207 endif
41644208
41654209 ifneq ($(SKIP_DEPS),)
41664210 deps::
41674211 else
4168 deps:: $(ALL_DEPS_DIRS) apps
4169 ifeq ($(IS_APP)$(IS_DEP),)
4170 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4171 endif
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
41724213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4173 $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
41744215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
41754216 :; \
41764217 else \
41774218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
41784219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4179 $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
4220 $(MAKE) -C $$dep IS_DEP=1; \
41804221 else \
4181 echo "Error: No Makefile to build dependency $$dep."; \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
41824223 exit 2; \
41834224 fi \
41844225 fi \
41924233 # in practice only Makefile is needed so far.
41934234 define dep_autopatch
41944235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
41954237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
41964238 $(call dep_autopatch_erlang_mk,$(1)); \
41974239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4198 if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
41994243 $(call dep_autopatch2,$(1)); \
4200 elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
42014245 $(call dep_autopatch2,$(1)); \
4202 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
42034247 $(call dep_autopatch2,$(1)); \
4204 else \
4205 $(call erlang,$(call dep_autopatch_app.erl,$(1))); \
42064248 fi \
42074249 else \
42084250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
42144256 endef
42154257
42164258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
42174262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
42184263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
42194264 fi; \
42204265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4221 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
42224267 $(call dep_autopatch_fetch_rebar); \
42234268 $(call dep_autopatch_rebar,$(1)); \
42244269 else \
42304275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
42314276 endef
42324277
4233 # Overwrite erlang.mk with the current file by default.
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
42344280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
42354281 define dep_autopatch_erlang_mk
4236 echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \
4237 > $(DEPS_DIR)/$1/erlang.mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
42384287 endef
42394288 else
42404289 define dep_autopatch_erlang_mk
42534302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
42544303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
42554304 cd $(ERLANG_MK_TMP)/rebar; \
4256 git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
42574306 $(MAKE); \
42584307 cd -; \
42594308 fi
42704319 define dep_autopatch_rebar.erl
42714320 application:load(rebar),
42724321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
42734323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
42744324 {ok, Conf0} -> Conf0;
42754325 _ -> []
44234473 [] -> ok;
44244474 _ ->
44254475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4426 PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
44274477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4428 PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L \\"~s\\" -lerl_interface -lei\n",
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
44294479 [code:lib_dir(erl_interface, lib)])),
44304480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
44314481 FilterEnv = fun(Env) ->
44644514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44654515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44664516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4467 [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
44684518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
44694519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
44704520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
44764526 end,
44774527 [PortSpec(S) || S <- PortSpecs]
44784528 end,
4479 Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"),
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
44804530 RunPlugin = fun(Plugin, Step) ->
44814531 case erlang:function_exported(Plugin, Step, 2) of
44824532 false -> ok;
45244574 halt()
45254575 endef
45264576
4527 define dep_autopatch_app.erl
4528 UpdateModules = fun(App) ->
4529 case filelib:is_regular(App) of
4530 false -> ok;
4531 true ->
4532 {ok, [{application, '$(1)', L0}]} = file:consult(App),
4533 Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true,
4534 fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
4535 L = lists:keystore(modules, 1, L0, {modules, Mods}),
4536 ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}]))
4537 end
4538 end,
4539 UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"),
4540 halt()
4541 endef
4542
45434577 define dep_autopatch_appsrc_script.erl
45444578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
45454579 AppSrcScript = AppSrc ++ ".script",
45874621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
45884622 endef
45894623
4590 define dep_fetch_hex.erl
4591 ssl:start(),
4592 inets:start(),
4593 {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
4594 {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
4595 [], [{body_format, binary}]),
4596 {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
4597 {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
4598 ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
4599 halt()
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
46004626 endef
46014627
46024628 # Hex only has a package version. No need to look in the Erlang.mk packages.
46034629 define dep_fetch_hex
4604 $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
46054634 endef
46064635
46074636 define dep_fetch_fail
46314660 $(eval DEP_NAME := $(call dep_name,$1))
46324661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
46334662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4634 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
46354664 exit 17; \
46364665 fi
46374666 $(verbose) mkdir -p $(DEPS_DIR)
46734702 clean:: clean-apps
46744703
46754704 clean-apps:
4676 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4677 $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
46784707 done
46794708
46804709 distclean:: distclean-apps
46814710
46824711 distclean-apps:
4683 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4684 $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
46854714 done
46864715 endif
46874716
47014730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
47024731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
47034732
4704 # External plugins.
4705
4706 DEP_PLUGINS ?=
4707
4708 define core_dep_plugin
4709 -include $(DEPS_DIR)/$(1)
4710
4711 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4712 endef
4713
4714 $(foreach p,$(DEP_PLUGINS),\
4715 $(eval $(if $(findstring /,$p),\
4716 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4717 $(call core_dep_plugin,$p/plugins.mk,$p))))
4718
4719 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4720 # This file is part of erlang.mk and subject to the terms of the ISC License.
4721
4722 # Configuration.
4723
4724 DTL_FULL_PATH ?=
4725 DTL_PATH ?= templates/
4726 DTL_SUFFIX ?= _dtl
4727 DTL_OPTS ?=
4728
4729 # Verbosity.
4730
4731 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
4732 dtl_verbose = $(dtl_verbose_$(V))
4733
4734 # Core targets.
4735
4736 DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl))
4737
4738 ifneq ($(DTL_FILES),)
4739
4740 ifdef DTL_FULL_PATH
4741 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
4742 else
4743 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
4744 endif
4745
4746 ifneq ($(words $(DTL_FILES)),0)
4747 # Rebuild templates when the Makefile changes.
4748 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
4749 @mkdir -p $(ERLANG_MK_TMP)
4750 @if test -f $@; then \
4751 touch $(DTL_FILES); \
4752 fi
4753 @touch $@
4754
4755 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
4756 endif
4757
4758 define erlydtl_compile.erl
4759 [begin
4760 Module0 = case "$(strip $(DTL_FULL_PATH))" of
4761 "" ->
4762 filename:basename(F, ".dtl");
4763 _ ->
4764 "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
4765 re:replace(F2, "/", "_", [{return, list}, global])
4766 end,
4767 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
4768 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
4769 ok -> ok;
4770 {ok, _} -> ok
4771 end
4772 end || F <- string:tokens("$(1)", " ")],
4773 halt().
4774 endef
4775
4776 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
4777 $(if $(strip $?),\
4778 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
4779
4780 endif
4781
4782 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
47834734 # This file is part of erlang.mk and subject to the terms of the ISC License.
47844735
47854736 # Verbosity.
47984749
47994750 define compile_proto.erl
48004751 [begin
4801 Dir = filename:dirname(filename:dirname(F)),
48024752 protobuffs_compile:generate_source(F,
4803 [{output_include_dir, Dir ++ "/include"},
4804 {output_src_dir, Dir ++ "/ebin"}])
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
48054755 end || F <- string:tokens("$(1)", " ")],
48064756 halt().
48074757 endef
48114761 $(if $(strip $?),$(call compile_proto,$?))
48124762 endif
48134763
4814 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
48154765 # This file is part of erlang.mk and subject to the terms of the ISC License.
48164766
48174767 .PHONY: clean-app
48254775 ERLC_EXCLUDE ?=
48264776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
48274777
4778 ERLC_ASN1_OPTS ?=
4779
48284780 ERLC_MIB_OPTS ?=
48294781 COMPILE_MIB_FIRST ?=
48304782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
48744826
48754827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
48764828 define app_file
4877 {application, $(PROJECT), [
4829 {application, '$(PROJECT)', [
48784830 {description, "$(PROJECT_DESCRIPTION)"},
48794831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48804832 {id$(comma)$(space)"$(1)"}$(comma))
48814833 {modules, [$(call comma_list,$(2))]},
48824834 {registered, []},
4883 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48844837 ]}.
48854838 endef
48864839 else
48874840 define app_file
4888 {application, $(PROJECT), [
4841 {application, '$(PROJECT)', [
48894842 {description, "$(PROJECT_DESCRIPTION)"},
48904843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48914844 {id$(comma)$(space)"$(1)"}$(comma))
48924845 {modules, [$(call comma_list,$(2))]},
48934846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
48944847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4895 {mod, {$(PROJECT_MOD), []}}
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48964850 ]}.
48974851 endef
48984852 endif
49024856
49034857 # Source files.
49044858
4905 ERL_FILES = $(sort $(call core_find,src/,*.erl))
4906 CORE_FILES = $(sort $(call core_find,src/,*.core))
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
49074863
49084864 # ASN.1 files.
49094865
49134869
49144870 define compile_asn1
49154871 $(verbose) mkdir -p include/
4916 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
49174873 $(verbose) mv asn1/*.erl src/
49184874 $(verbose) mv asn1/*.hrl include/
49194875 $(verbose) mv asn1/*.asn1db include/
49364892
49374893 # Leex and Yecc files.
49384894
4939 XRL_FILES = $(sort $(call core_find,src/,*.xrl))
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
49404896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
49414897 ERL_FILES += $(XRL_ERL_FILES)
49424898
4943 YRL_FILES = $(sort $(call core_find,src/,*.yrl))
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
49444900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
49454901 ERL_FILES += $(YRL_ERL_FILES)
49464902
49474903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4948 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
49494905
49504906 # Erlang and Core Erlang files.
49514907
49954951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49964952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49974953 (F, Mod, import, {Imp, _}) ->
4998 case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
49994960 false -> ok;
50004961 true -> Add(Mod, Imp)
50014962 end;
50194980 end || F <- ErlFiles],
50204981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
50214982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
50224991 ok = file:write_file("$(1)", [
50234992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
5024 "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n"
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
50254994 ]),
50264995 halt()
50274996 endef
50345003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
50355004 # Rebuild everything when the Makefile changes.
50365005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5037 @mkdir -p $(ERLANG_MK_TMP)
5038 @if test -f $@; then \
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
50395008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
50405009 touch -c $(PROJECT).d; \
50415010 fi
5042 @touch $@
5011 $(verbose) touch $@
50435012
50445013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
50455014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
50465015 endif
50475016
5048 -include $(PROJECT).d
5017 include $(wildcard $(PROJECT).d)
50495018
50505019 ebin/$(PROJECT).app:: ebin/
50515020
50645033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
50655034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
50665035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5067 $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
50685037 > ebin/$(PROJECT).app
50695038 else
50705039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
50885057
50895058 endif
50905059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
50915061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
50925062 # This file is part of erlang.mk and subject to the terms of the ISC License.
50935063
51055075 doc-deps:
51065076 else
51075077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5108 $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5109 endif
5110
5111 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51125082 # This file is part of erlang.mk and subject to the terms of the ISC License.
51135083
51145084 .PHONY: rel-deps
51255095 rel-deps:
51265096 else
51275097 rel-deps: $(ALL_REL_DEPS_DIRS)
5128 $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5129 endif
5130
5131 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51325102 # This file is part of erlang.mk and subject to the terms of the ISC License.
51335103
51345104 .PHONY: test-deps test-dir test-build clean-test-dir
51505120 test-deps:
51515121 else
51525122 test-deps: $(ALL_TEST_DEPS_DIRS)
5153 $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
51545124 endif
51555125
51565126 ifneq ($(wildcard $(TEST_DIR)),)
51835153 endif
51845154 endif
51855155
5186 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51875157 # This file is part of erlang.mk and subject to the terms of the ISC License.
51885158
51895159 .PHONY: rebar.config
52195189 rebar.config:
52205190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
52215191
5222 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
52235193 # This file is part of erlang.mk and subject to the terms of the ISC License.
52245194
5225 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
5226
5227 MAN_INSTALL_PATH ?= /usr/local/share/man
5228 MAN_SECTIONS ?= 3 7
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
52295200
52305201 docs:: asciidoc
52315202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
52325207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
52335210
52345211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
52355212 asciidoc-guide:
52365213 else
5237 asciidoc-guide: distclean-asciidoc doc-deps
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
52385215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
52395216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5240 endif
5241
5242 ifeq ($(wildcard doc/src/manual/*.asciidoc),)
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
52435227 asciidoc-manual:
52445228 else
5245 asciidoc-manual: distclean-asciidoc doc-deps
5246 for f in doc/src/manual/*.asciidoc ; do \
5247 a2x -v -f manpage $$f ; \
5248 done
5249 for s in $(MAN_SECTIONS); do \
5250 mkdir -p doc/man$$s/ ; \
5251 mv doc/src/manual/*.$$s doc/man$$s/ ; \
5252 gzip doc/man$$s/*.$$s ; \
5253 done
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
52545262
52555263 install-docs:: install-asciidoc
52565264
52575265 install-asciidoc: asciidoc-manual
5258 for s in $(MAN_SECTIONS); do \
5259 mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
5260 install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
5261 done
5262 endif
5263
5264 distclean:: distclean-asciidoc
5265
5266 distclean-asciidoc:
5267 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
5268
5269 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
52705276 # This file is part of erlang.mk and subject to the terms of the ISC License.
52715277
52725278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
53235329 define bs_Makefile
53245330 PROJECT = $p
53255331 PROJECT_DESCRIPTION = New project
5326 PROJECT_VERSION = 0.0.1
5332 PROJECT_VERSION = 0.1.0
53275333
53285334 # Whitespace to be used when creating files from templates.
53295335 SP = $(SP)
53335339 define bs_Makefile
53345340 PROJECT = $p
53355341 PROJECT_DESCRIPTION = New project
5336 PROJECT_VERSION = 0.0.1
5342 PROJECT_VERSION = 0.1.0
53375343
53385344 endef
53395345 endif
53415347 define bs_apps_Makefile
53425348 PROJECT = $p
53435349 PROJECT_DESCRIPTION = New project
5344 PROJECT_VERSION = 0.0.1
5350 PROJECT_VERSION = 0.1.0
53455351
53465352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
53475353 endef
53615367 endef
53625368
53635369 define bs_relx_config
5364 {release, {$p_release, "1"}, [$p]}.
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
53655371 {extended_start_script, true}.
53665372 {sys_config, "rel/sys.config"}.
53675373 {vm_args, "rel/vm.args"}.
57125718 ifndef t
57135719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57145720 endif
5715 ifndef tpl_$(t)
5716 $(error Unknown template)
5717 endif
57185721 ifndef n
57195722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57205723 endif
57275730 list-templates:
57285731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
57295732
5730 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
57315734 # This file is part of erlang.mk and subject to the terms of the ISC License.
57325735
57335736 .PHONY: clean-c_src distclean-c_src-env
59625965 $(call render_template,bs_erl_nif,src/$n.erl)
59635966 endif
59645967
5965 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
59665969 # This file is part of erlang.mk and subject to the terms of the ISC License.
59675970
5968 .PHONY: ci ci-setup distclean-kerl
5969
5970 KERL ?= $(CURDIR)/kerl
5971 export KERL
5972
5973 KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
5974
5975 OTP_GIT ?= https://github.com/erlang/otp
5976
5977 CI_INSTALL_DIR ?= $(HOME)/erlang
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
59785973 CI_OTP ?=
5979
5980 ifeq ($(strip $(CI_OTP)),)
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
59815986 ci::
59825987 else
5983 ci:: $(addprefix ci-,$(CI_OTP))
5984
5985 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
59866007
59876008 ci-setup::
6009
6010 ci-extra::
59886011
59896012 ci_verbose_0 = @echo " CI " $(1);
59906013 ci_verbose = $(ci_verbose_$(V))
59916014
59926015 define ci_target
5993 ci-$(1): $(CI_INSTALL_DIR)/$(1)
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
59946018 $(ci_verbose) \
5995 PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
5996 CI_OTP_RELEASE="$(1)" \
5997 CT_OPTS="-label $(1)" \
5998 $(MAKE) clean ci-setup tests
5999 endef
6000
6001 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
60026030
60036031 define ci_otp_target
60046032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
60056033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6006 $(KERL) build git $(OTP_GIT) $(1) $(1)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
60076035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
60086036 endif
60096037 endef
60106038
60116039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
60126040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
60136052 $(KERL):
6014 $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
60156056 $(verbose) chmod +x $(KERL)
60166057
60176058 help::
60286069 $(gen_verbose) rm -rf $(KERL)
60296070 endif
60306071
6031 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
60326073 # This file is part of erlang.mk and subject to the terms of the ISC License.
60336074
60346075 .PHONY: ct apps-ct distclean-ct
60366077 # Configuration.
60376078
60386079 CT_OPTS ?=
6080
60396081 ifneq ($(wildcard $(TEST_DIR)),)
6040 CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6041 else
6042 CT_SUITES ?=
6043 endif
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
60446088
60456089 # Core targets.
60466090
60636107 -noinput \
60646108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
60656109 -dir $(TEST_DIR) \
6066 -logdir $(CURDIR)/logs
6110 -logdir $(CT_LOGS_DIR)
60676111
60686112 ifeq ($(CT_SUITES),)
60696113 ct: $(if $(IS_APP),,apps-ct)
60706114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
60716117 ct: test-build $(if $(IS_APP),,apps-ct)
6072 $(verbose) mkdir -p $(CURDIR)/logs/
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
60736119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
60746121 endif
60756122
60766123 ifneq ($(ALL_APPS_DIRS),)
60976144
60986145 define ct_suite_target
60996146 ct-$(1): test-build
6100 $(verbose) mkdir -p $(CURDIR)/logs/
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
61016148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
61026149 endef
61036150
61046151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
61056152
61066153 distclean-ct:
6107 $(gen_verbose) rm -rf $(CURDIR)/logs/
6108
6109 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61106157 # This file is part of erlang.mk and subject to the terms of the ISC License.
61116158
61126159 .PHONY: plt distclean-plt dialyze
61506197 endef
61516198
61526199 $(DIALYZER_PLT): deps app
6153 $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
61546204
61556205 plt: $(DIALYZER_PLT)
61566206
61646214 endif
61656215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
61666216
6167 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61686218 # This file is part of erlang.mk and subject to the terms of the ISC License.
61696219
61706220 .PHONY: distclean-edoc edoc
61726222 # Configuration.
61736223
61746224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
61756235
61766236 # Core targets.
61776237
6178 ifneq ($(wildcard doc/overview.edoc),)
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
61796239 docs:: edoc
61806240 endif
61816241
61846244 # Plugin-specific targets.
61856245
61866246 edoc: distclean-edoc doc-deps
6187 $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
61886248
61896249 distclean-edoc:
61906250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
61916251
6192 # Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61936253 # This file is part of erlang.mk and subject to the terms of the ISC License.
61946254
6195 .PHONY: distclean-escript escript
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
61966320
61976321 # Configuration.
61986322
61996323 ESCRIPT_NAME ?= $(PROJECT)
62006324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
62016325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
62026327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6203
6204 ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
6205 ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
6206 ESCRIPT_EMU_ARGS ?= -pa . \
6207 -sasl errlog_type error \
6208 -escript main $(ESCRIPT_NAME)
6209 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6210 ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
62116332
62126333 # Core targets.
62136334
62206341
62216342 # Plugin-specific targets.
62226343
6223 # Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
6224 # Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
6225 # Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
6226 # Software may only be used for the great good and the true happiness of all
6227 # sentient beings.
6228
6229 define ESCRIPT_RAW
6230 'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
6231 'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
6232 ' [F || F <- A, not filelib:is_dir(F) ] end,'\
6233 'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
6234 'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
6235 'Ez = fun(Escript) ->'\
6236 ' Static = Files([$(ESCRIPT_STATIC)]),'\
6237 ' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
6238 ' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
6239 ' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
6240 ' {archive, Archive, [memory]},'\
6241 ' {shebang, "$(ESCRIPT_SHEBANG)"},'\
6242 ' {comment, "$(ESCRIPT_COMMENT)"},'\
6243 ' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
6244 ' ]),'\
6245 ' file:change_mode(Escript, 8#755)'\
6246 'end,'\
6247 'Ez("$(ESCRIPT_FILE)"),'\
6248 'halt().'
6249 endef
6250
6251 ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
6252
6253 escript:: distclean-escript deps app
6254 $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
62556360
62566361 distclean-escript:
6257 $(gen_verbose) rm -f $(ESCRIPT_NAME)
6258
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
62596365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6260 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
62616366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
62626367
62636368 .PHONY: eunit apps-eunit
63216426
63226427 ifneq ($(ALL_APPS_DIRS),)
63236428 apps-eunit:
6324 $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done
6325 endif
6326 endif
6327
6328 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
63296436 # This file is part of erlang.mk and subject to the terms of the ISC License.
63306437
6331 .PHONY: relx-rel distclean-relx-rel distclean-relx run
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
63326439
63336440 # Configuration.
63346441
6335 RELX ?= $(CURDIR)/relx
6442 RELX ?= $(ERLANG_MK_TMP)/relx
63366443 RELX_CONFIG ?= $(CURDIR)/relx.config
63376444
63386445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
63396446 RELX_OPTS ?=
63406447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
63416454
63426455 ifeq ($(firstword $(RELX_OPTS)),-o)
63436456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
63506463 ifeq ($(IS_DEP),)
63516464 ifneq ($(wildcard $(RELX_CONFIG)),)
63526465 rel:: relx-rel
6353 endif
6354 endif
6355
6356 distclean:: distclean-relx-rel distclean-relx
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
63576472
63586473 # Plugin-specific targets.
63596474
63626477 $(verbose) chmod +x $(RELX)
63636478
63646479 relx-rel: $(RELX) rel-deps app
6365 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
63666484
63676485 distclean-relx-rel:
63686486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6369
6370 distclean-relx:
6371 $(gen_verbose) rm -rf $(RELX)
63726487
63736488 # Run target.
63746489
63776492 else
63786493
63796494 define get_relx_release.erl
6380 {ok, Config} = file:consult("$(RELX_CONFIG)"),
6381 {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
6382 io:format("~s", [Name]),
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
63836504 halt(0).
63846505 endef
63856506
6386 RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
63876514
63886515 run: all
6389 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
63906517
63916518 help::
63926519 $(verbose) printf "%s\n" "" \
63956522
63966523 endif
63976524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
63986526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6399 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
64006527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
64016528
64026529 .PHONY: shell
64216548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
64226549
64236550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6424 $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
64256552
64266553 shell: build-shell-deps
64276554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
64286555
6429 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
64306571 # This file is part of erlang.mk and subject to the terms of the ISC License.
64316572
64326573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
64376578 tests:: triq
64386579
64396580 define triq_check.erl
6440 code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
64416582 try
64426583 case $(1) of
64436584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
64696610 endif
64706611 endif
64716612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
64726614 # Copyright (c) 2015, Erlang Solutions Ltd.
64736615 # This file is part of erlang.mk and subject to the terms of the ISC License.
64746616
64856627 XREFR ?= $(CURDIR)/xrefr
64866628 export XREFR
64876629
6488 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
64896631
64906632 # Core targets.
64916633
64926634 help::
6493 $(verbose) printf "%s\n" "" \
6494 "Xref targets:" \
6495 " xref Run Xrefr using $XREF_CONFIG as config file if defined"
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
64966638
64976639 distclean:: distclean-xref
64986640
65086650 distclean-xref:
65096651 $(gen_verbose) rm -rf $(XREFR)
65106652
6511 # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
65126655 # This file is part of erlang.mk and subject to the terms of the ISC License.
65136656
65146657 COVER_REPORT_DIR = cover
65176660
65186661 ifdef COVER
65196662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
65206664 # All modules in 'ebin'
65216665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
65226666
65316675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
65326676 endif
65336677 endif
6678 endif
65346679
65356680 # Core targets
65366681
65526697 "Cover targets:" \
65536698 " cover-report Generate a HTML coverage report from previously collected" \
65546699 " cover data." \
6555 " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
65566701 "" \
65576702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
65586703 "target tests additionally generates a HTML coverage report from the combined" \
65656710
65666711 .PHONY: coverdata-clean
65676712 coverdata-clean:
6568 $(gen_verbose) rm -f *.coverdata ct.cover.spec
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
65696714
65706715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
65716721 all.coverdata: $(COVERDATA)
6572 $(gen_verbose) $(ERL) -eval ' \
6573 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
6574 cover:export("$@"), halt(0).'
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
65756723
65766724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
65776725 # empty if you want the coverdata files but not the HTML report.
65896737 # Modules which include eunit.hrl always contain one line without coverage
65906738 # because eunit defines test/0 which is never called. We compensate for this.
65916739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6592 grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
65936741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
65946742
65956743 define cover_report.erl
66246772 endef
66256773
66266774 cover-report:
6627 $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
66286776 $(gen_verbose) $(call erlang,$(cover_report.erl))
66296777
66306778 endif
66316779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
66326839
66336840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
66346841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
66976904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
66986905 endif
66996906 ifndef IS_APP
6700 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
67016908 $(MAKE) -C $$dep $@ \
67026909 IS_APP=1 \
6703 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6704 || exit $$?; \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67056911 done
67066912 endif
6707 $(verbose) for dep in $^ ; do \
6913 $(verbose) set -e; for dep in $^ ; do \
67086914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
67096915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6710 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
67116917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
67126918 $(MAKE) -C $$dep fetch-deps \
67136919 IS_DEP=1 \
6714 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6715 || exit $$?; \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67166921 fi \
67176922 fi \
67186923 done
22 # before the inclusion of erlang.mk leading to the wrong target becoming
33 # the default.
44 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
526 endif
627
728 # --------------------------------------------------------------------
2041 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
2142 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
2243 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
2345 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
2446 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
2547 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
2648 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
2749 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
2851 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
2952 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
3054 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
3155 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
3256 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
3559 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
3660 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
3761 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
3863 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
3964 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
4065 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
7095
7196 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
7297
73 # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
74 # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
75 # needs to add "ranch" as a BUILD_DEPS. The list of projects needing
76 # this workaround are:
77 # o rabbitmq-web-stomp
78 dep_ranch = git https://github.com/ninenines/ranch 1.2.1
98 # Third-party dependencies version pinning.
99 #
100 # We do that in this file, which is copied in all projects, to ensure
101 # all projects use the same versions. It avoids conflicts and makes it
102 # possible to work with rabbitmq-public-umbrella.
103
104 dep_cowboy_commit = 1.0.4
105 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
106 dep_ranch_commit = 1.3.1
107 dep_webmachine_commit = 1.10.8p2
79108
80109 RABBITMQ_COMPONENTS = amqp_client \
81110 rabbit \
82111 rabbit_common \
83112 rabbitmq_amqp1_0 \
84113 rabbitmq_auth_backend_amqp \
114 rabbitmq_auth_backend_cache \
85115 rabbitmq_auth_backend_http \
86116 rabbitmq_auth_backend_ldap \
87117 rabbitmq_auth_mechanism_ssl \
88118 rabbitmq_boot_steps_visualiser \
89119 rabbitmq_clusterer \
120 rabbitmq_cli \
90121 rabbitmq_codegen \
91122 rabbitmq_consistent_hash_exchange \
123 rabbitmq_ct_client_helpers \
92124 rabbitmq_ct_helpers \
93125 rabbitmq_delayed_message_exchange \
94126 rabbitmq_dotnet_client \
97129 rabbitmq_federation_management \
98130 rabbitmq_java_client \
99131 rabbitmq_jms_client \
132 rabbitmq_jms_cts \
100133 rabbitmq_jms_topic_exchange \
101134 rabbitmq_lvc \
102135 rabbitmq_management \
1010 %% The Original Code is RabbitMQ.
1111 %%
1212 %% The Initial Developer of the Original Code is GoPivotal, Inc.
13 %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
13 %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved.
1414 %%
1515
1616 -module(rabbit_web_stomp_examples_app).
+0
-8
deps/rabbitmq_web_stomp_examples/src/rabbitmq_web_stomp_examples.app.src less more
0 {application, rabbitmq_web_stomp_examples,
1 [{description, "Rabbit WEB-STOMP - examples"},
2 {vsn, "3.6.6"},
3 {modules, []},
4 {registered, []},
5 {mod, {rabbit_web_stomp_examples_app, []}},
6 {env, [{listener, [{port, 15670}]}]},
7 {applications, [kernel, stdlib, rabbit_common, rabbit, rabbitmq_web_dispatch, rabbitmq_web_stomp]}]}.
+0
-33
deps/ranch/AUTHORS less more
0 Ranch is available thanks to the work of:
1
2 Loïc Hoguin
3 James Fish
4 Andrew Majorov
5 Ransom Richardson
6 Fred Hebert
7 Geoff Cant
8 Klaus Trainer
9 josh rotenberg
10 0x00F6
11 Alexander Zhuravlev
12 Ali Sabil
13 Andre Graf
14 Andrew Thompson
15 Jihyun Yu
16 Slava Yurin
17 Stéphane Wirtel
18 Xiao Jia
19
20 The Ranch code was initially part of Cowboy. Before it was
21 split into a separate project, the following people worked
22 on the code that then became Ranch:
23
24 Loïc Hoguin
25 Ali Sabil
26 Andrew Thompson
27 DeadZen
28 Hunter Morris
29 Jesper Louis Andersen
30 Paul Oliver
31 Roberto Ostinelli
32 Steven Gravell
00 = CHANGELOG
1
2 == 1.3.0
3
4 * Tested with OTP R16B+ on Linux, FreeBSD, OSX and Windows
5 * Add ssl to the list of dependencies
6 * Add ranch:info/0 and ranch:procs/2 to retrieve Ranch state information
7 * Allow configuring a listener with only SNI, without a default certificate
8 * Blacklist transport options instead of whitelist
9 ** Unknown options are now allowed, but will result in a Dialyzer warning
10 * Add many transport options typespecs and documentation
11 * Don't silently drop the accept rate when running out of fds
12 * Prevent a race condition when stopping listeners
13 * Improve reporting for common errors, for example eaddrinuse
14 * Fix double removal of connections bug
15 ** The number of active connections should now be exact
16 * Fix stuck acceptor bug when controlling_socket returned errors
17 * Numerous documentation and examples improvements
18
19 == 1.2.1
20
21 * Fix bug preventing node shutdown when SSL is used with OTP 17.1+
22 * Tune restart intensity in all supervisors
123
224 == 1.2.0
325
0 Copyright (c) 2011-2015, Loïc Hoguin <essen@ninenines.eu>
0 Copyright (c) 2011-2016, Loïc Hoguin <essen@ninenines.eu>
11
22 Permission to use, copy, modify, and/or distribute this software for any
33 purpose with or without fee is hereby granted, provided that the above
00 # See LICENSE for licensing information.
11
22 PROJECT = ranch
3 PROJECT_DESCRIPTION = Socket acceptor pool for TCP protocols.
4 PROJECT_VERSION = 1.3.0
5 PROJECT_REGISTERED = ranch_server
36
47 # Options.
58
710 CT_OPTS += -pa test -ct_hooks ranch_ct_hook []
811 PLT_APPS = crypto public_key ssl
912
10 CI_OTP = \
11 OTP_R15B01 OTP_R15B02 OTP_R15B03-1 \
13 CI_OTP ?= \
1214 OTP_R16B OTP_R16B01 OTP_R16B02 OTP_R16B03-1 \
13 OTP-17.0.2 OTP-17.1.2 OTP-17.2.2 OTP-17.3.4 OTP-17.4.1 OTP-17.5.6.6 \
14 OTP-18.0.3 OTP-18.1.5 OTP-18.2.1
15
16 # Only test on the most recent version on public CI services.
17 ifdef CI
18 ifndef BUILDKITE
19 CI_OTP := $(lastword $(CI_OTP))
20 endif
21 endif
15 OTP-17.1.2 OTP-17.2.2 OTP-17.3.4 OTP-17.4.1 OTP-17.5.6.6 \
16 OTP-18.0.3 OTP-18.1.5 OTP-18.2.4.1 OTP-18.3.4.4 \
17 OTP-19.0.7 OTP-19.1.6
18 #CI_HIPE ?= $(lastword $(CI_OTP))
19 #CI_ERLLVM ?= $(CI_HIPE)
2220
2321 # Dependencies.
22
23 LOCAL_DEPS = ssl
24
25 DOC_DEPS = asciideck
2426
2527 TEST_DEPS = ct_helper
2628 dep_ct_helper = git https://github.com/ninenines/ct_helper master
1616 Ranch also allows you to **upgrade** the acceptor pool without having
1717 to close any of the currently opened sockets.
1818
19 == Getting started
19 == Online documentation
2020
21 * link:doc/src/guide/book.asciidoc[Read the guide]
22 * link:doc/src/manual/[Check the manual]
23 * Look at the examples in the `examples/` directory
21 * http://ninenines.eu/docs/en/ranch/1.3/guide[User guide]
22 * http://ninenines.eu/docs/en/ranch/1.3/manual[Function reference]
23
24 == Offline documentation
25
26 * While still online, run `make docs`
27 * User guide available in `doc/` in PDF and HTML formats
28 * Function reference man pages available in `doc/man3/` and `doc/man7/`
29 * Run `make install-docs` to install man pages on your system
30 * Full documentation in Asciidoc available in `doc/src/`
31 * Examples available in `examples/`
2432
2533 == Support
2634
2735 * Official IRC Channel: #ninenines on irc.freenode.net
28 * http://lists.ninenines.eu[Mailing Lists]
29 * http://ninenines.eu/support[Commercial Support]
36 * https://github.com/ninenines/ranch/issues[Issues tracker]
37 * http://ninenines.eu/services[Commercial Support]
+0
-7
deps/ranch/appveyor.yml less more
0 build_script:
1 - C:\msys64\usr\bin\bash -lc "pacman --needed --noconfirm -Sy pacman-mirrors"
2 - C:\msys64\usr\bin\bash -lc "pacman --noconfirm -Sy"
3 - C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S git make"
4 test_script:
5 - C:\msys64\usr\bin\bash -lc "cd $APPVEYOR_BUILD_FOLDER && make dialyze"
6 - C:\msys64\usr\bin\bash -lc "cd $APPVEYOR_BUILD_FOLDER && make tests"
+0
-22
deps/ranch/circle.yml less more
0 general:
1 artifacts:
2 - "logs"
3
4 dependencies:
5 cache_directories:
6 - "~/.kerl"
7 - "~/erlang"
8
9 pre:
10 - sudo apt-get update
11 - sudo apt-get install autoconf2.59
12 - cd $HOME/bin && ln -s /usr/bin/autoconf2.59 autoconf
13 - cd $HOME/bin && ln -s /usr/bin/autoheader2.59 autoheader
14 - make ci-prepare:
15 timeout: 7200
16
17 test:
18 override:
19 - source $HOME/erlang/OTP-*/activate && make dialyze
20 - make -k ci:
21 timeout: 3600
+0
-20
deps/ranch/doc/src/guide/book.asciidoc less more
0 // a2x: --dblatex-opts "-P latex.output.revhistory=0 -P doc.publisher.show=0 -P index.numbered=0"
1 // a2x: -d book --attribute tabsize=4
2
3 = Ranch User Guide
4
5 include::introduction.asciidoc[Introduction]
6
7 include::listeners.asciidoc[Listeners]
8
9 include::transports.asciidoc[Transports]
10
11 include::protocols.asciidoc[Protocols]
12
13 include::embedded.asciidoc[Embedded mode]
14
15 include::parsers.asciidoc[Writing parsers]
16
17 include::ssl_auth.asciidoc[SSL client authentication]
18
19 include::internals.asciidoc[Internals]
+0
-48
deps/ranch/doc/src/guide/embedded.asciidoc less more
0 == Embedded mode
1
2 Embedded mode allows you to insert Ranch listeners directly
3 in your supervision tree. This allows for greater fault tolerance
4 control by permitting the shutdown of a listener due to the
5 failure of another part of the application and vice versa.
6
7 === Embedding
8
9 To embed Ranch in your application you can simply add the child specs
10 to your supervision tree. This can all be done in the `init/1` function
11 of one of your application supervisors.
12
13 Ranch requires at the minimum two kinds of child specs for embedding.
14 First, you need to add `ranch_sup` to your supervision tree, only once,
15 regardless of the number of listeners you will use. Then you need to
16 add the child specs for each listener.
17
18 Ranch has a convenience function for getting the listeners child specs
19 called `ranch:child_spec/6`, that works like `ranch:start_listener/6`,
20 except that it doesn't start anything, it only returns child specs.
21
22 As for `ranch_sup`, the child spec is simple enough to not require a
23 convenience function.
24
25 The following example adds both `ranch_sup` and one listener to another
26 application's supervision tree.
27
28 .Embed Ranch directly in your supervision tree
29
30 [source,erlang]
31 ----
32 init([]) ->
33 RanchSupSpec = {ranch_sup, {ranch_sup, start_link, []},
34 permanent, 5000, supervisor, [ranch_sup]},
35 ListenerSpec = ranch:child_spec(echo, 100,
36 ranch_tcp, [{port, 5555}],
37 echo_protocol, []
38 ),
39 {ok, {{one_for_one, 10, 10}, [RanchSupSpec, ListenerSpec]}}.
40 ----
41
42 Remember, you can add as many listener child specs as needed, but only
43 one `ranch_sup` spec!
44
45 It is recommended that your architecture makes sure that all listeners
46 are restarted if `ranch_sup` fails. See the Ranch internals chapter for
47 more details on how Ranch does it.
+0
-94
deps/ranch/doc/src/guide/internals.asciidoc less more
0 == Internals
1
2 This chapter may not apply to embedded Ranch as embedding allows you
3 to use an architecture specific to your application, which may or may
4 not be compatible with the description of the Ranch application.
5
6 Note that for everything related to efficiency and performance,
7 you should perform the benchmarks yourself to get the numbers that
8 matter to you. Generic benchmarks found on the web may or may not
9 be of use to you, you can never know until you benchmark your own
10 system.
11
12 === Architecture
13
14 Ranch is an OTP application.
15
16 Like all OTP applications, Ranch has a top supervisor. It is responsible
17 for supervising the `ranch_server` process and all the listeners that
18 will be started.
19
20 The `ranch_server` gen_server is a central process keeping track of the
21 listeners and their acceptors. It does so through the use of a public ets
22 table called `ranch_server`. The table is owned by the top supervisor
23 to improve fault tolerance. This way if the `ranch_server` gen_server
24 fails, it doesn't lose any information and the restarted process can
25 continue as if nothing happened.
26
27 Ranch uses a custom supervisor for managing connections. This supervisor
28 keeps track of the number of connections and handles connection limits
29 directly. While it is heavily optimized to perform the task of creating
30 connection processes for accepted connections, it is still following the
31 OTP principles and the usual `sys` and `supervisor` calls will work on
32 it as expected.
33
34 Listeners are grouped into the `ranch_listener_sup` supervisor and
35 consist of three kinds of processes: the listener gen_server, the
36 acceptor processes and the connection processes, both grouped under
37 their own supervisor. All of these processes are registered to the
38 `ranch_server` gen_server with varying amount of information.
39
40 All socket operations, including listening for connections, go through
41 transport handlers. Accepted connections are given to the protocol handler.
42 Transport handlers are simple callback modules for performing operations on
43 sockets. Protocol handlers start a new process, which receives socket
44 ownership, with no requirements on how the code should be written inside
45 that new process.
46
47 === Number of acceptors
48
49 The second argument to `ranch:start_listener/6` is the number of
50 processes that will be accepting connections. Care should be taken
51 when choosing this number.
52
53 First of all, it should not be confused with the maximum number
54 of connections. Acceptor processes are only used for accepting and
55 have nothing else in common with connection processes. Therefore
56 there is nothing to be gained from setting this number too high,
57 in fact it can slow everything else down.
58
59 Second, this number should be high enough to allow Ranch to accept
60 connections concurrently. But the number of cores available doesn't
61 seem to be the only factor for choosing this number, as we can
62 observe faster accepts if we have more acceptors than cores. It
63 might be entirely dependent on the protocol, however.
64
65 Our observations suggest that using 100 acceptors on modern hardware
66 is a good solution, as it's big enough to always have acceptors ready
67 and it's low enough that it doesn't have a negative impact on the
68 system's performances.
69
70 === Platform-specific TCP features
71
72 Some socket options are platform-specific and not supported by `inet`.
73 They can be of interest because they generally are related to
74 optimizations provided by the underlying OS. They can still be enabled
75 thanks to the `raw` option, for which we will see an example.
76
77 One of these features is `TCP_DEFER_ACCEPT` on Linux. It is a simplified
78 accept mechanism which will wait for application data to come in before
79 handing out the connection to the Erlang process.
80
81 This is especially useful if you expect many connections to be mostly
82 idle, perhaps part of a connection pool. They can be handled by the
83 kernel directly until they send any real data, instead of allocating
84 resources to idle connections.
85
86 To enable this mechanism, the following option can be used.
87
88 .Using raw transport options
89
90 [source,erlang]
91 {raw, 6, 9, << 30:32/native >>}
92
93 It means go on layer 6, turn on option 9 with the given integer parameter.
+0
-25
deps/ranch/doc/src/guide/introduction.asciidoc less more
0 == Introduction
1
2 Ranch is a socket acceptor pool for TCP protocols.
3
4 Ranch aims to provide everything you need to accept TCP connections
5 with a small code base and low latency while being easy to use directly
6 as an application or to embed into your own.
7
8 === Prerequisites
9
10 It is assumed the developer already knows Erlang and has some experience
11 with socket programming and TCP protocols.
12
13 === Supported platforms
14
15 Ranch is tested and supported on Linux.
16
17 Ranch is developed for Erlang R15B01+.
18
19 Ranch may be compiled on earlier Erlang versions with small source code
20 modifications but there is no guarantee that it will work as expected.
21
22 === Versioning
23
24 Ranch uses http://semver.org/[Semantic Versioning 2.0.0]
+0
-251
deps/ranch/doc/src/guide/listeners.asciidoc less more
0 == Listeners
1
2 A listener is a set of processes whose role is to listen on a port
3 for new connections. It manages a pool of acceptor processes, each
4 of them indefinitely accepting connections. When it does, it starts
5 a new process executing the protocol handler code. All the socket
6 programming is abstracted through the user of transport handlers.
7
8 The listener takes care of supervising all the acceptor and connection
9 processes, allowing developers to focus on building their application.
10
11 === Starting a listener
12
13 Ranch does nothing by default. It is up to the application developer
14 to request that Ranch listens for connections.
15
16 A listener can be started and stopped at will.
17
18 When starting a listener, a number of different settings are required:
19
20 * A name to identify it locally and be able to interact with it.
21 * The number of acceptors in the pool.
22 * A transport handler and its associated options.
23 * A protocol handler and its associated options.
24
25 Ranch includes both TCP and SSL transport handlers, respectively
26 `ranch_tcp` and `ranch_ssl`.
27
28 A listener can be started by calling the `ranch:start_listener/6`
29 function. Before doing so however, you must ensure that the `ranch`
30 application is started.
31
32 .Starting the Ranch application
33
34 [source,erlang]
35 ok = application:start(ranch).
36
37 You are then ready to start a listener. Let's call it `tcp_echo`. It will
38 have a pool of 100 acceptors, use a TCP transport and forward connections
39 to the `echo_protocol` handler.
40
41 .Starting a listener for TCP connections on port 5555
42
43 [source,erlang]
44 {ok, _} = ranch:start_listener(tcp_echo, 100,
45 ranch_tcp, [{port, 5555}],
46 echo_protocol, []
47 ).
48
49 You can try this out by compiling and running the `tcp_echo` example in the
50 examples directory. To do so, open a shell in the 'examples/tcp_echo/'
51 directory and run the following command:
52
53 .Building and starting a Ranch example
54
55 [source,bash]
56 $ make run
57
58 You can then connect to it using telnet and see the echo server reply
59 everything you send to it. Then when you're done testing, you can use
60 the `Ctrl+]` key to escape to the telnet command line and type
61 `quit` to exit.
62
63 .Connecting to the example listener with telnet
64
65 [source,bash]
66 ----
67 $ telnet localhost 5555
68 Trying 127.0.0.1...
69 Connected to localhost.
70 Escape character is '^]'.
71 Hello!
72 Hello!
73 It works!
74 It works!
75 ^]
76
77 telnet> quit
78 Connection closed.
79 ----
80
81 === Stopping a listener
82
83 All you need to stop a Ranch listener is to call the
84 `ranch:stop_listener/1` function with the listener's name
85 as argument. In the previous section we started the listener
86 named `tcp_echo`. We can now stop it.
87
88 .Stopping a listener
89
90 [source,erlang]
91 ranch:stop_listener(tcp_echo).
92
93 === Default transport options
94
95 By default the socket will be set to return `binary` data, with the
96 options `{active, false}`, `{packet, raw}`, `{reuseaddr, true}` set.
97 These values can't be overriden when starting the listener, but
98 they can be overriden using `Transport:setopts/2` in the protocol.
99
100 It will also set `{backlog, 1024}` and `{nodelay, true}`, which
101 can be overriden at listener startup.
102
103 === Listening on a random port
104
105 You do not have to specify a specific port to listen on. If you give
106 the port number 0, or if you omit the port number entirely, Ranch will
107 start listening on a random port.
108
109 You can retrieve this port number by calling `ranch:get_port/1`. The
110 argument is the name of the listener you gave in `ranch:start_listener/6`.
111
112 .Starting a listener for TCP connections on a random port
113
114 [source,erlang]
115 {ok, _} = ranch:start_listener(tcp_echo, 100,
116 ranch_tcp, [{port, 0}],
117 echo_protocol, []
118 ).
119 Port = ranch:get_port(tcp_echo).
120
121 === Listening on privileged ports
122
123 Some systems limit access to ports below 1024 for security reasons.
124 This can easily be identified by an `{error, eacces}` error when trying
125 to open a listening socket on such a port.
126
127 The methods for listening on privileged ports vary between systems,
128 please refer to your system's documentation for more information.
129
130 We recommend the use of port rewriting for systems with a single server,
131 and load balancing for systems with multiple servers. Documenting these
132 solutions is however out of the scope of this guide.
133
134 === Accepting connections on an existing socket
135
136 If you want to accept connections on an existing socket, you can use the
137 `socket` transport option, which should just be the relevant data returned
138 from the connect function for the transport or the underlying socket library
139 (`gen_tcp:connect`, `ssl:connect`). The accept function will then be
140 called on the passed in socket. You should connect the socket in
141 `{active, false}` mode, as well.
142
143 Note, however, that because of a bug in SSL, you cannot change ownership of an
144 SSL listen socket prior to R16. Ranch will catch the error thrown, but the
145 owner of the SSL socket will remain as whatever process created the socket.
146 However, this will not affect accept behaviour unless the owner process dies,
147 in which case the socket is closed. Therefore, to use this feature with SSL
148 with an erlang release prior to R16, ensure that the SSL socket is opened in a
149 persistant process.
150
151 === Limiting the number of concurrent connections
152
153 The `max_connections` transport option allows you to limit the number
154 of concurrent connections. It defaults to 1024. Its purpose is to
155 prevent your system from being overloaded and ensuring all the
156 connections are handled optimally.
157
158 .Customizing the maximum number of concurrent connections
159
160 [source,erlang]
161 {ok, _} = ranch:start_listener(tcp_echo, 100,
162 ranch_tcp, [{port, 5555}, {max_connections, 100}],
163 echo_protocol, []
164 ).
165
166 You can disable this limit by setting its value to the atom `infinity`.
167
168 .Disabling the limit for the number of connections
169
170 [source,erlang]
171 {ok, _} = ranch:start_listener(tcp_echo, 100,
172 ranch_tcp, [{port, 5555}, {max_connections, infinity}],
173 echo_protocol, []
174 ).
175
176 You may not always want connections to be counted when checking for
177 `max_connections`. For example you might have a protocol where both
178 short-lived and long-lived connections are possible. If the long-lived
179 connections are mostly waiting for messages, then they don't consume
180 much resources and can safely be removed from the count.
181
182 To remove the connection from the count, you must call the
183 `ranch:remove_connection/1` from within the connection process,
184 with the name of the listener as the only argument.
185
186 .Removing a connection from the count of connections
187
188 [source,erlang]
189 ranch:remove_connection(Ref).
190
191 As seen in the chapter covering protocols, this pid is received as the
192 first argument of the protocol's `start_link/4` callback.
193
194 You can modify the `max_connections` value on a running listener by
195 using the `ranch:set_max_connections/2` function, with the name of the
196 listener as first argument and the new value as the second.
197
198 .Upgrading the maximum number of connections
199
200 [source,erlang]
201 ranch:set_max_connections(tcp_echo, MaxConns).
202
203 The change will occur immediately.
204
205 === Using a supervisor for connection processes
206
207 Ranch allows you to define the type of process that will be used
208 for the connection processes. By default it expects a `worker`.
209 When the `connection_type` configuration value is set to `supervisor`,
210 Ranch will consider that the connection process it manages is a
211 supervisor and will reflect that in its supervision tree.
212
213 Connection processes of type `supervisor` can either handle the
214 socket directly or through one of their children. In the latter
215 case the start function for the connection process must return
216 two pids: the pid of the supervisor you created (that will be
217 supervised) and the pid of the protocol handling process (that
218 will receive the socket).
219
220 Instead of returning `{ok, ConnPid}`, simply return
221 `{ok, SupPid, ConnPid}`.
222
223 It is very important that the connection process be created
224 under the supervisor process so that everything works as intended.
225 If not, you will most likely experience issues when the supervised
226 process is stopped.
227
228 === Upgrading
229
230 Ranch allows you to upgrade the protocol options. This takes effect
231 immediately and for all subsequent connections.
232
233 To upgrade the protocol options, call `ranch:set_protocol_options/2`
234 with the name of the listener as first argument and the new options
235 as the second.
236
237 .Upgrading the protocol options
238
239 [source,erlang]
240 ranch:set_protocol_options(tcp_echo, NewOpts).
241
242 All future connections will use the new options.
243
244 You can also retrieve the current options similarly by
245 calling `ranch:get_protocol_options/1`.
246
247 .Retrieving the current protocol options
248
249 [source,erlang]
250 Opts = ranch:get_protocol_options(tcp_echo).
+0
-92
deps/ranch/doc/src/guide/parsers.asciidoc less more
0 == Writing parsers
1
2 There are three kinds of protocols:
3
4 * Text protocols
5 * Schema-less binary protocols
6 * Schema-based binary protocols
7
8 This chapter introduces the first two kinds. It will not cover
9 more advanced topics such as continuations or parser generators.
10
11 This chapter isn't specifically about Ranch, we assume here that
12 you know how to read data from the socket. The data you read and
13 the data that hasn't been parsed is saved in a buffer. Every
14 time you read from the socket, the data read is appended to the
15 buffer. What happens next depends on the kind of protocol. We
16 will only cover the first two.
17
18 === Parsing text
19
20 Text protocols are generally line based. This means that we can't
21 do anything with them until we receive the full line.
22
23 A simple way to get a full line is to use `binary:split/{2,3}`.
24
25 .Using binary:split/2 to get a line of input
26
27 [source,erlang]
28 case binary:split(Buffer, <<"\n">>) of
29 [_] ->
30 get_more_data(Buffer);
31 [Line, Rest] ->
32 handle_line(Line, Rest)
33 end.
34
35 In the above example, we can have two results. Either there was
36 a line break in the buffer and we get it split into two parts,
37 the line and the rest of the buffer; or there was no line break
38 in the buffer and we need to get more data from the socket.
39
40 Next, we need to parse the line. The simplest way is to again
41 split, here on space. The difference is that we want to split
42 on all spaces character, as we want to tokenize the whole string.
43
44 .Using binary:split/3 to split text
45
46 [source,erlang]
47 case binary:split(Line, <<" ">>, [global]) of
48 [<<"HELLO">>] ->
49 be_polite();
50 [<<"AUTH">>, User, Password] ->
51 authenticate_user(User, Password);
52 [<<"QUIT">>, Reason] ->
53 quit(Reason)
54 %% ...
55 end.
56
57 Pretty simple, right? Match on the command name, get the rest
58 of the tokens in variables and call the respective functions.
59
60 After doing this, you will want to check if there is another
61 line in the buffer, and handle it immediately if any.
62 Otherwise wait for more data.
63
64 === Parsing binary
65
66 Binary protocols can be more varied, although most of them are
67 pretty similar. The first four bytes of a frame tend to be
68 the size of the frame, which is followed by a certain number
69 of bytes for the type of frame and then various parameters.
70
71 Sometimes the size of the frame includes the first four bytes,
72 sometimes not. Other times this size is encoded over two bytes.
73 And even other times little-endian is used instead of big-endian.
74
75 The general idea stays the same though.
76
77 .Using binary pattern matching to split frames
78
79 [source,erlang]
80 << Size:32, _/bits >> = Buffer,
81 case Buffer of
82 << Frame:Size/binary, Rest/bits >> ->
83 handle_frame(Frame, Rest);
84 _ ->
85 get_more_data(Buffer)
86 end.
87
88 You will then need to parse this frame using binary pattern
89 matching, and handle it. Then you will want to check if there
90 is another frame fully received in the buffer, and handle it
91 immediately if any. Otherwise wait for more data.
+0
-125
deps/ranch/doc/src/guide/protocols.asciidoc less more
0 == Protocols
1
2 A protocol handler starts a connection process and defines the
3 protocol logic executed in this process.
4
5 === Writing a protocol handler
6
7 All protocol handlers must implement the `ranch_protocol` behavior
8 which defines a single callback, `start_link/4`. This callback is
9 responsible for spawning a new process for handling the connection.
10 It receives four arguments: the name of the listener, the socket, the
11 transport handler being used and the protocol options defined in
12 the call to `ranch:start_listener/6`. This callback must
13 return `{ok, Pid}`, with `Pid` the pid of the new process.
14
15 The newly started process can then freely initialize itself. However,
16 it must call `ranch:accept_ack/1` before doing any socket operation.
17 This will ensure the connection process is the owner of the socket.
18 It expects the listener's name as argument.
19
20 .Acknowledge accepting the socket
21
22 [source,erlang]
23 ok = ranch:accept_ack(Ref).
24
25 If your protocol code requires specific socket options, you should
26 set them while initializing your connection process, after
27 calling `ranch:accept_ack/1`. You can use `Transport:setopts/2`
28 for that purpose.
29
30 Following is the complete protocol code for the example found
31 in `examples/tcp_echo/`.
32
33 .Protocol module that echoes everything it receives
34
35 [source,erlang]
36 ----
37 -module(echo_protocol).
38 -behaviour(ranch_protocol).
39
40 -export([start_link/4]).
41 -export([init/4]).
42
43 start_link(Ref, Socket, Transport, Opts) ->
44 Pid = spawn_link(?MODULE, init, [Ref, Socket, Transport, Opts]),
45 {ok, Pid}.
46
47 init(Ref, Socket, Transport, _Opts = []) ->
48 ok = ranch:accept_ack(Ref),
49 loop(Socket, Transport).
50
51 loop(Socket, Transport) ->
52 case Transport:recv(Socket, 0, 5000) of
53 {ok, Data} ->
54 Transport:send(Socket, Data),
55 loop(Socket, Transport);
56 _ ->
57 ok = Transport:close(Socket)
58 end.
59 ----
60
61 === Using gen_server
62
63 Special processes like the ones that use the `gen_server` or `gen_fsm`
64 behaviours have the particularity of having their `start_link` call not
65 return until the `init` function returns. This is problematic, because
66 you won't be able to call `ranch:accept_ack/1` from the `init` callback
67 as this would cause a deadlock to happen.
68
69 There are two ways of solving this problem.
70
71 The first, and probably the most elegant one, is to make use of the
72 `gen_server:enter_loop/3` function. It allows you to start your process
73 normally (although it must be started with `proc_lib` like all special
74 processes), then perform any needed operations before falling back into
75 the normal `gen_server` execution loop.
76
77 .Use a gen_server for protocol handling
78
79 [source,erlang]
80 ----
81 -module(my_protocol).
82 -behaviour(gen_server).
83 -behaviour(ranch_protocol).
84
85 -export([start_link/4]).
86 -export([init/4]).
87 %% Exports of other gen_server callbacks here.
88
89 start_link(Ref, Socket, Transport, Opts) ->
90 proc_lib:start_link(?MODULE, init, [Ref, Socket, Transport, Opts]).
91
92 init(Ref, Socket, Transport, _Opts = []) ->
93 ok = proc_lib:init_ack({ok, self()}),
94 %% Perform any required state initialization here.
95 ok = ranch:accept_ack(Ref),
96 ok = Transport:setopts(Socket, [{active, once}]),
97 gen_server:enter_loop(?MODULE, [], {state, Socket, Transport}).
98
99 %% Other gen_server callbacks here.
100 ----
101
102 The second method involves triggering a timeout just after `gen_server:init`
103 ends. If you return a timeout value of `0` then the `gen_server` will call
104 `handle_info(timeout, _, _)` right away.
105
106 .Use a gen_server for protocol handling, method 2
107
108 [source,erlang]
109 ----
110 -module(my_protocol).
111 -behaviour(gen_server).
112 -behaviour(ranch_protocol).
113
114 %% Exports go here.
115
116 init([Ref, Socket, Transport]) ->
117 {ok, {state, Ref, Socket, Transport}, 0}.
118
119 handle_info(timeout, State={state, Ref, Socket, Transport}) ->
120 ok = ranch:accept_ack(Ref),
121 ok = Transport:setopts(Socket, [{active, once}]),
122 {noreply, State};
123 %% ...
124 ----
+0
-120
deps/ranch/doc/src/guide/ssl_auth.asciidoc less more
0 == SSL client authentication
1
2 === Purpose
3
4 SSL client authentication is a mechanism allowing applications to
5 identify certificates. This allows your application to make sure that
6 the client is an authorized certificate, but makes no claim about
7 whether the user can be trusted. This can be combined with a password
8 based authentication to attain greater security.
9
10 The server only needs to retain the certificate serial number and
11 the certificate issuer to authenticate the certificate. Together,
12 they can be used to uniquely identify a certicate.
13
14 As Ranch allows the same protocol code to be used for both SSL and
15 non-SSL transports, you need to make sure you are in an SSL context
16 before attempting to perform an SSL client authentication. This
17 can be done by checking the return value of `Transport:name/0`.
18
19 === Obtaining client certificates
20
21 You can obtain client certificates from various sources. You can
22 generate them yourself, or you can use a service like CAcert.org
23 which allows you to generate client and server certificates for
24 free.
25
26 Following are the steps you need to take to create a CAcert.org
27 account, generate a certificate and install it in your favorite
28 browser.
29
30 * Open [CAcert.org](http://cacert.org) in your favorite browser
31 * Root Certificate link: install both certificates
32 * Join (Register an account)
33 * Verify your account (check your email inbox!)
34 * Log in
35 * Client Certificates: New
36 * Follow instructions to create the certificate
37 * Install the certificate in your browser
38
39 You can optionally save the certificate for later use, for example
40 to extract the `IssuerID` information as will be detailed later on.
41
42 === Transport configuration
43
44 The SSL transport does not request a client certificate by default.
45 You need to specify the `{verify, verify_peer}` option when starting
46 the listener to enable this behavior.
47
48 .Configure a listener for SSL authentication
49
50 [source,erlang]
51 {ok, _} = ranch:start_listener(my_ssl, 100,
52 ranch_ssl, [
53 {port, SSLPort},
54 {certfile, PathToCertfile},
55 {cacertfile, PathToCACertfile},
56 {verify, verify_peer}
57 ],
58 my_protocol, []
59 ).
60
61 In this example we set the required `port` and `certfile`, but also
62 the `cacertfile` containing the CACert.org root certificate, and
63 the option to request the client certificate.
64
65 If you enable the `{verify, verify_peer}` option and the client does
66 not have a client certificate configured for your domain, then no
67 certificate will be sent. This allows you to use SSL for more than
68 just authenticated clients.
69
70 === Authentication
71
72 To authenticate users, you must first save the certificate information
73 required. If you have your users' certificate files, you can simply
74 load the certificate and retrieve the information directly.
75
76 .Retrieve the issuer ID from a certificate
77
78 [source,erlang]
79 ----
80 certfile_to_issuer_id(Filename) ->
81 {ok, Data} = file:read_file(Filename),
82 [{'Certificate', Cert, not_encrypted}] = public_key:pem_decode(Data),
83 {ok, IssuerID} = public_key:pkix_issuer_id(Cert, self),
84 IssuerID.
85 ----
86
87 The `IssuerID` variable contains both the certificate serial number
88 and the certificate issuer stored in a tuple, so this value alone can
89 be used to uniquely identify the user certificate. You can save this
90 value in a database, a configuration file or any other place where an
91 Erlang term can be stored and retrieved.
92
93 To retrieve the `IssuerID` from a running connection, you need to first
94 retrieve the client certificate and then extract this information from
95 it. Ranch does not provide a function to retrieve the client certificate.
96 Instead you can use the `ssl:peercert/1` function. Once you have the
97 certificate, you can again use the `public_key:pkix_issuer_id/2` to
98 extract the `IssuerID` value.
99
100 The following function returns the `IssuerID` or `false` if no client
101 certificate was found. This snippet is intended to be used from your
102 protocol code.
103
104 .Retrieve the issuer ID from the certificate for the current connection
105
106 [source,erlang]
107 ----
108 socket_to_issuer_id(Socket) ->
109 case ssl:peercert(Socket) of
110 {error, no_peercert} ->
111 false;
112 {ok, Cert} ->
113 {ok, IssuerID} = public_key:pkix_issuer_id(Cert, self),
114 IssuerID
115 end.
116 ----
117
118 You then only need to match the `IssuerID` value to authenticate the
119 user.
+0
-169
deps/ranch/doc/src/guide/transports.asciidoc less more
0 == Transports
1
2 A transport defines the interface to interact with a socket.
3
4 Transports can be used for connecting, listening and accepting
5 connections, but also for receiving and sending data. Both
6 passive and active mode are supported, although all sockets
7 are initialized as passive.
8
9 === TCP transport
10
11 The TCP transport is a thin wrapper around `gen_tcp`.
12
13 === SSL transport
14
15 The SSL transport is a thin wrapper around `ssl`. It requires
16 the `crypto`, `asn1`, `public_key` and `ssl` applications
17 to be started. When starting an SSL listener, Ranch will attempt
18 to automatically start them. It will not try to stop them when
19 the listener is removed, however.
20
21 .Starting the SSL application
22
23 [source,erlang]
24 ssl:start().
25
26 In a proper OTP setting, you will need to make your application
27 depend on the `crypto`, `public_key` and `ssl` applications.
28 They will be started automatically when starting your release.
29
30 The SSL transport `accept/2` function performs both transport
31 and SSL accepts. Errors occurring during the SSL accept phase
32 are returned as `{error, {ssl_accept, atom()}}` to differentiate
33 on which socket the problem occurred.
34
35 === Sending and receiving data
36
37 This section assumes that `Transport` is a valid transport handler
38 (like `ranch_tcp` or `ranch_ssl`) and `Socket` is a connected
39 socket obtained through the listener.
40
41 You can send data to a socket by calling the `Transport:send/2`
42 function. The data can be given as `iodata()`, which is defined as
43 `binary() | iolist()`. All the following calls will work:
44
45 .Sending data to the socket
46
47 [source,erlang]
48 ----
49 Transport:send(Socket, <<"Ranch is cool!">>).
50 Transport:send(Socket, "Ranch is cool!").
51 Transport:send(Socket, ["Ranch", ["is", "cool!"]]).
52 Transport:send(Socket, ["Ranch", [<<"is">>, "cool!"]]).
53 ----
54
55 You can receive data either in passive or in active mode. Passive mode
56 means that you will perform a blocking `Transport:recv/3` call, while
57 active mode means that you will receive the data as a message.
58
59 By default, all data will be received as binary. It is possible to
60 receive data as strings, although this is not recommended as binaries
61 are a more efficient construct, especially for binary protocols.
62
63 Receiving data using passive mode requires a single function call. The
64 first argument is the socket, and the third argument is a timeout duration
65 before the call returns with `{error, timeout}`.
66
67 The second argument is the amount of data in bytes that we want to receive.
68 The function will wait for data until it has received exactly this amount.
69 If you are not expecting a precise size, you can specify 0 which will make
70 this call return as soon as data was read, regardless of its size.
71
72 .Receiving data from the socket in passive mode
73
74 [source,erlang]
75 {ok, Data} = Transport:recv(Socket, 0, 5000).
76
77 Active mode requires you to inform the socket that you want to receive
78 data as a message and to write the code to actually receive it.
79
80 There are two kinds of active modes: `{active, once}` and
81 `{active, true}`. The first will send a single message before going
82 back to passive mode; the second will send messages indefinitely.
83 We recommend not using the `{active, true}` mode as it could quickly
84 flood your process mailbox. It's better to keep the data in the socket
85 and read it only when required.
86
87 Three different messages can be received:
88
89 * `{OK, Socket, Data}`
90 * `{Closed, Socket}`
91 * `{Error, Socket, Reason}`
92
93 The value of `OK`, `Closed` and `Error` can be different
94 depending on the transport being used. To be able to properly match
95 on them you must first call the `Transport:messages/0` function.
96
97 .Retrieving the transport's active message identifiers
98
99 [source,erlang]
100 {OK, Closed, Error} = Transport:messages().
101
102 To start receiving messages you will need to call the `Transport:setopts/2`
103 function, and do so every time you want to receive data.
104
105 .Receiving messages from the socket in active mode
106
107 [source,erlang]
108 ----
109 {OK, Closed, Error} = Transport:messages(),
110 Transport:setopts(Socket, [{active, once}]),
111 receive
112 {OK, Socket, Data} ->
113 io:format("data received: ~p~n", [Data]);
114 {Closed, Socket} ->
115 io:format("socket got closed!~n");
116 {Error, Socket, Reason} ->
117 io:format("error happened: ~p~n", [Reason])
118 end.
119 ----
120
121 You can easily integrate active sockets with existing Erlang code as all
122 you really need is just a few more clauses when receiving messages.
123
124 === Sending files
125
126 As in the previous section it is assumed `Transport` is a valid transport
127 handler and `Socket` is a connected socket obtained through the listener.
128
129 To send a whole file, with name `Filename`, over a socket:
130
131 .Sending a file by filename
132
133 [source,erlang]
134 {ok, SentBytes} = Transport:sendfile(Socket, Filename).
135
136 Or part of a file, with `Offset` greater than or equal to 0, `Bytes` number of
137 bytes and chunks of size `ChunkSize`:
138
139 .Sending part of a file by filename in chunks
140
141 [source,erlang]
142 Opts = [{chunk_size, ChunkSize}],
143 {ok, SentBytes} = Transport:sendfile(Socket, Filename, Offset, Bytes, Opts).
144
145 To improve efficiency when sending multiple parts of the same file it is also
146 possible to use a file descriptor opened in raw mode:
147
148 .Sending a file opened in raw mode
149
150 [source,erlang]
151 {ok, RawFile} = file:open(Filename, [raw, read, binary]),
152 {ok, SentBytes} = Transport:sendfile(Socket, RawFile, Offset, Bytes, Opts).
153
154 === Writing a transport handler
155
156 A transport handler is a module implementing the `ranch_transport` behavior.
157 It defines a certain number of callbacks that must be written in order to
158 allow transparent usage of the transport handler.
159
160 The behavior doesn't define the socket options available when opening a
161 socket. These do not need to be common to all transports as it's easy enough
162 to write different initialization functions for the different transports that
163 will be used. With one exception though. The `setopts/2` function *must*
164 implement the `{active, once}` and the `{active, true}` options.
165
166 If the transport handler doesn't have a native implementation of `sendfile/5` a
167 fallback is available, `ranch_transport:sendfile/6`. The extra first argument
168 is the transport's module. See `ranch_ssl` for an example.
+0
-178
deps/ranch/doc/src/manual/ranch.asciidoc less more
0 = ranch(3)
1
2 == Name
3
4 ranch - socket acceptor pool
5
6 == Description
7
8 The `ranch` module provides functions for starting and
9 manipulating Ranch listeners.
10
11 == Types
12
13 === max_conns() = non_neg_integer() | infinity
14
15 Maximum number of connections allowed on this listener.
16
17 This is a soft limit. The actual number of connections
18 might be slightly above the limit due to concurrency
19 when accepting new connections. Some connections may
20 also be removed from this count explicitly by the user
21 code.
22
23 === opt()
24
25 [source,erlang]
26 ----
27 opt() = {ack_timeout, timeout()}
28 | {connection_type, worker | supervisor}
29 | {max_connections, max_conns()}
30 | {shutdown, timeout() | brutal_kill}
31 | {socket, any()}
32 ----
33
34 Ranch-specific transport options.
35
36 These options are not passed on to the transports.
37 They are used by Ranch while setting up the listeners.
38
39 === ref() = any()
40
41 Unique name used to refer to a listener.
42
43 == Option descriptions
44
45 None of the options are required.
46
47 ack_timeout (5000)::
48 Maximum allowed time for the `ranch:accept_ack/1` call to finish.
49 connection_type (worker)::
50 Type of process that will handle the connection.
51 max_connections (1024)::
52 Maximum number of active connections. Soft limit. Using `infinity` will disable the limit entirely.
53 shutdown (5000)::
54 Maximum allowed time for children to stop on listener shutdown.
55 socket::
56 Listening socket opened externally to be used instead of calling `Transport:listen/1`.
57
58 == Exports
59
60 === accept_ack(Ref) -> ok
61
62 Ref = ref():: Listener name.
63
64 Acknowledge that the connection is accepted.
65
66 This function MUST be used by a connection process to inform
67 Ranch that it initialized properly and let it perform any
68 additional operations before the socket can be safely used.
69
70 === child_spec(Ref, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts) -> supervisor:child_spec()
71
72 Ref = ref():: Listener name.
73 NbAcceptors = non_neg_integer():: Number of acceptor processes.
74 Transport = module():: Transport module.
75 TransOpts = any():: Transport options.
76 Protocol = module():: Protocol module.
77 ProtoOpts = any():: Protocol options.
78
79 Return child specifications for a new listener.
80
81 This function can be used to embed a listener directly
82 in an application instead of letting Ranch handle it.
83
84 === get_addr(Ref) -> {IP, Port}
85
86 Ref = ref():: Listener name.
87 IP = inet:ip_address():: IP of the interface used by this listener.
88 Port = inet:port_number():: Port number used by this listener.
89
90 Return the IP address and port for the given listener.
91
92 === get_max_connections(Ref) -> MaxConns
93
94 Ref = ref():: Listener name.
95 MaxConns = max_conns():: Current maximum number of connections.
96
97 Return the max number of connections allowed for the given listener.
98
99 === get_port(Ref) -> Port
100
101 Ref = ref():: Listener name.
102 Port = inet:port_number():: Port number used by this listener.
103
104 Return the port for the given listener.
105
106 === get_protocol_options(Ref) -> ProtoOpts
107
108 Ref = ref():: Listener name.
109 ProtoOpts = any():: Current protocol options.
110
111 Return the protocol options set for the given listener.
112
113 === remove_connection(Ref) -> ok
114
115 Ref = ref():: Listener name.
116
117 Do not count this connection when limiting the number of connections.
118
119 You can use this function for long-running connection processes
120 which spend most of their time idling rather than consuming
121 resources. This allows Ranch to accept a lot more connections
122 without sacrificing the latency of the system.
123
124 This function may only be called from a connection process.
125
126 === set_max_connections(Ref, MaxConns) -> ok
127
128 Ref = ref():: Listener name.
129 MaxConns = max_conns():: New maximum number of connections.
130
131 Set the max number of connections for the given listener.
132
133 The change will be applied immediately. If the new value is
134 smaller than the previous one, Ranch will not kill the extra
135 connections, but will wait for them to terminate properly.
136
137 === set_protocol_options(Ref, ProtoOpts) -> ok
138
139 Ref = ref():: Listener name.
140 ProtoOpts = any():: New protocol options.
141
142 Set the protocol options for the given listener.
143
144 The change will be applied immediately for all new connections.
145 Old connections will not receive the new options.
146
147 === start_listener(Ref, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts) -> {ok, pid()} | {error, badarg}
148
149 Ref = ref():: Listener name.
150 NbAcceptors = non_neg_integer():: Number of acceptor processes.
151 Transport = module():: Transport module.
152 TransOpts = any():: Transport options.
153 Protocol = module():: Protocol module.
154 ProtoOpts = any():: Protocol options.
155
156 Start listening for connections using the given transport
157 and protocol. Returns the pid for this listener's supervisor.
158
159 There are additional transport options that apply
160 regardless of transport. They allow configuring how the
161 connections are supervised, rate limited and more. Please
162 consult the previous section for more details.
163
164 === stop_listener(Ref) -> ok | {error, not_found}
165
166 Ref = ref():: Listener name.
167
168 Stop the given listener.
169
170 The listener is stopped gracefully, first by closing the
171 listening port, then by stopping the connection processes.
172 These processes are stopped according to the `shutdown`
173 transport option, which may be set to brutally kill all
174 connection processes or give them some time to stop properly.
175
176 This function does not return until the listener is
177 completely stopped.
+0
-27
deps/ranch/doc/src/manual/ranch_app.asciidoc less more
0 = ranch(7)
1
2 == Name
3
4 ranch - Socket acceptor pool for TCP protocols.
5
6 == Dependencies
7
8 The `ranch` application has no particular dependency required
9 to start.
10
11 It has optional dependencies that are only required when
12 listening for SSL connections. The dependencies are `crypto`,
13 `asn1`, `public_key` and `ssl`. They are started automatically
14 if they weren't before.
15
16 == Environment
17
18 The `ranch` application defines one application environment
19 configuration parameter.
20
21 profile (false)::
22 When enabled, Ranch will start `eprof` profiling automatically.
23
24 You can use the `ranch_app:profile_output/0` function to stop
25 profiling and output the results to the files 'procs.profile'
26 and 'total.profile'. Do not use in production.
+0
-44
deps/ranch/doc/src/manual/ranch_protocol.asciidoc less more
0 = ranch_protocol(3)
1
2 == Name
3
4 ranch_protocol - behaviour for protocol modules
5
6 == Description
7
8 The `ranch_protocol` behaviour defines the interface used
9 by Ranch protocols.
10
11 == Types
12
13 None.
14
15 == Callbacks
16
17 === start_link(Ref, Socket, Transport, ProtoOpts) -> {ok, pid()} | {ok, pid(), pid()}
18
19 Ref = ranch:ref():: Listener name.
20 Socket = any():: Socket for this connection.
21 Transport = module():: Transport module for this socket.
22 ProtoOpts = any():: Protocol options.
23
24 Start a new connection process for the given socket.
25
26 The only purpose of this callback is to start a process that
27 will handle the socket. It must spawn the process, link and
28 then return the new pid. This function will always be called
29 from inside a supervisor.
30
31 This callback can also return two pids. The first pid is the
32 pid of the process that will be supervised. The second pid is
33 the pid of the process that will receive ownership of the
34 socket. This second process must be a child of the first. This
35 form is only available when `connection_type` is set to
36 `supervisor`.
37
38 If any other value is returned, the supervisor will close the
39 socket and assume no process has been started.
40
41 Do not perform any operations in this callback, as this would
42 block the supervisor responsible for starting connection
43 processes and degrade performance severely.
+0
-142
deps/ranch/doc/src/manual/ranch_ssl.asciidoc less more
0 = ranch_ssl(3)
1
2 == Name
3
4 ranch_ssl - SSL transport module
5
6 == Description
7
8 The `ranch_ssl` module implements an SSL Ranch transport.
9
10 == Types
11
12 === ssl_opt()
13
14 [source,erlang]
15 ----
16 ssl_opt() = {alpn_preferred_protocols, [binary()]}
17 | {cacertfile, string()}
18 | {cacerts, [public_key:der_encoded()]}
19 | {cert, public_key:der_encoded()}
20 | {certfile, string()}
21 | {ciphers, [ssl:erl_cipher_suite()] | string()}
22 | {client_renegotiation, boolean()}
23 | {crl_cache, {module(), {internal | any(), list()}}}
24 | {crl_check, boolean() | peer | best_effort}
25 | {depth, 0..255}
26 | {dh, public_key:der_encoded()}
27 | {dhfile, string()}
28 | {fail_if_no_peer_cert, boolean()}
29 | {hibernate_after, integer() | undefined}
30 | {honor_cipher_order, boolean()}
31 | {key, {'RSAPrivateKey' | 'DSAPrivateKey' | 'PrivateKeyInfo', public_key:der_encoded()}}
32 | {keyfile, string()}
33 | {log_alert, boolean()}
34 | {next_protocols_advertised, [binary()]}
35 | {partial_chain, fun(([public_key:der_encoded()]) -> {trusted_ca, public_key:der_encoded()} | unknown_ca)}
36 | {password, string()}
37 | {psk_identity, string()}
38 | {reuse_session, fun()}
39 | {reuse_sessions, boolean()}
40 | {secure_renegotiate, boolean()}
41 | {sni_fun, fun()}
42 | {sni_hosts, [{string(), ssl_opt()}]}
43 | {user_lookup_fun, {fun(), any()}}
44 | {verify, ssl:verify_type()}
45 | {verify_fun, {fun(), any()}}
46 | {versions, [atom()]}.
47 ----
48
49 SSL-specific listen options.
50
51 === opt() = ranch_tcp:opt() | ssl_opt()
52
53 Listen options.
54
55 === opts() = [opt()]
56
57 List of listen options.
58
59 == Option descriptions
60
61 Specifying a certificate is mandatory, either through the `cert`
62 or the `certfile` option. None of the other options are required.
63
64 The default value is given next to the option name.
65
66 alpn_preferred_protocols::
67 Perform Application-Layer Protocol Negotiation with the given list of preferred protocols.
68 cacertfile::
69 Path to PEM encoded trusted certificates file used to verify peer certificates.
70 cacerts::
71 List of DER encoded trusted certificates.
72 cert::
73 DER encoded user certificate.
74 certfile::
75 Path to the PEM encoded user certificate file. May also contain the private key.
76 ciphers::
77 List of ciphers that clients are allowed to use.
78 client_renegotiation (true)::
79 Whether to allow client-initiated renegotiation.
80 crl_cache ({ssl_crl_cache, {internal, []}})::
81 Customize the module used to cache Certificate Revocation Lists.
82 crl_check (false)::
83 Whether to perform CRL check on all certificates in the chain during validation.
84 depth (1)::
85 Maximum of intermediate certificates allowed in the certification path.
86 dh::
87 DER encoded Diffie-Hellman parameters.
88 dhfile::
89 Path to the PEM encoded Diffie-Hellman parameters file.
90 fail_if_no_peer_cert (false)::
91 Whether to refuse the connection if the client sends an empty certificate.
92 hibernate_after (undefined)::
93 Time in ms after which SSL socket processes go into hibernation to reduce memory usage.
94 honor_cipher_order (false)::
95 If true, use the server's preference for cipher selection. If false, use the client's preference.
96 key::
97 DER encoded user private key.
98 keyfile::
99 Path to the PEM encoded private key file, if different than the certfile.
100 log_alert (true)::
101 If false, error reports will not be displayed.
102 next_protocols_advertised::
103 List of protocols to send to the client if it supports the Next Protocol extension.
104 nodelay (true)::
105 Whether to enable TCP_NODELAY.
106 partial_chain::
107 Claim an intermediate CA in the chain as trusted.
108 password::
109 Password to the private key file, if password protected.
110 psk_identity::
111 Provide the given PSK identity hint to the client during the handshake.
112 reuse_session::
113 Custom policy to decide whether a session should be reused.
114 reuse_sessions (false)::
115 Whether to allow session reuse.
116 secure_renegotiate (false)::
117 Whether to reject renegotiation attempts that do not conform to RFC5746.
118 sni_fun::
119 Function called when the client requests a host using Server Name Indication. Returns options to apply.
120 sni_hosts::
121 Options to apply for the host that matches what the client requested with Server Name Indication.
122 user_lookup_fun::
123 Function called to determine the shared secret when using PSK, or provide parameters when using SRP.
124 verify (verify_none)::
125 Use `verify_peer` to request a certificate from the client.
126 verify_fun::
127 Custom policy to decide whether a client certificate is valid.
128 versions::
129 TLS protocol versions that will be supported.
130
131 Note that the client will not send a certificate unless the
132 value for the `verify` option is set to `verify_peer`. This
133 means that the `fail_if_no_peer_cert` only apply when combined
134 with the `verify` option. The `verify_fun` option allows
135 greater control over the client certificate validation.
136
137 The options `sni_fun` and `sni_hosts` are mutually exclusive.
138
139 == Exports
140
141 None.
+0
-123
deps/ranch/doc/src/manual/ranch_tcp.asciidoc less more
0 = ranch_tcp(3)
1
2 == Name
3
4 ranch_tcp - TCP transport module
5
6 == Description
7
8 The `ranch_tcp` module implements a TCP Ranch transport.
9
10 Note that due to bugs in OTP up to at least R16B02, it is
11 recommended to disable async threads when using the
12 `sendfile` function of this transport, as it can make
13 the threads stuck indefinitely.
14
15 == Types
16
17 === opt()
18
19 [source,erlang]
20 ----
21 opt() = {backlog, non_neg_integer()}
22 | {buffer, non_neg_integer()}
23 | {delay_send, boolean()}
24 | {dontroute, boolean()}
25 | {exit_on_close, boolean()}
26 | {fd, non_neg_integer()}
27 | {high_msgq_watermark, non_neg_integer()}
28 | {high_watermark, non_neg_integer()}
29 | inet
30 | inet6
31 | {ip, inet:ip_address()}
32 | {keepalive, boolean()}
33 | {linger, {boolean(), non_neg_integer()}}
34 | {low_msgq_watermark, non_neg_integer()}
35 | {low_watermark, non_neg_integer()}
36 | {nodelay, boolean()}
37 | {port, inet:port_number()}
38 | {priority, integer()}
39 | {raw, non_neg_integer(), non_neg_integer(), binary()}
40 | {recbuf, non_neg_integer()}
41 | {send_timeout, timeout()}
42 | {send_timeout_close, boolean()}
43 | {sndbuf, non_neg_integer()}
44 | {tos, integer()}
45 ----
46
47 Listen options.
48
49 This does not represent the entirety of the options that can
50 be set on the socket, but only the options that may be
51 set independently of protocol implementation.
52
53 === opts() = [opt()]
54
55 List of listen options.
56
57 Option descriptions
58 -------------------
59
60 None of the options are required.
61
62 Please consult the `gen_tcp` and `inet` manuals for a more
63 thorough description of these options. This manual only aims
64 to provide a short description along with what the defaults
65 are. Defaults may be different in Ranch compared to `gen_tcp`.
66 Defaults are given next to the option name.
67
68 backlog (1024)::
69 Max length of the queue of pending connections.
70 buffer::
71 Size of the buffer used by the Erlang driver. Default is system-dependent.
72 delay_send (false)::
73 Always queue packets before sending, to send fewer, larger packets over the network.
74 dontroute (false)::
75 Don't send via a gateway, only send to directly connected hosts.
76 exit_on_close (true)::
77 Disable to allow sending data after a close has been detected.
78 fd::
79 File descriptor of the socket, if it was opened externally.
80 high_msgq_watermark (8192)::
81 Limit in the amount of data in the socket message queue before the socket queue becomes busy.
82 high_watermark (8192)::
83 Limit in the amount of data in the ERTS socket implementation's queue before the socket becomes busy.
84 inet::
85 Set up the socket for IPv4.
86 inet6::
87 Set up the socket for IPv6.
88 ip::
89 Interface to listen on. Listen on all interfaces by default.
90 keepalive (false)::
91 Enable sending of keep-alive messages.
92 linger ({false, 0})::
93 Whether to wait and how long to flush data sent before closing the socket.
94 low_msgq_watermark (4096)::
95 Amount of data in the socket message queue before the socket queue leaves busy state.
96 low_watermark (4096)::
97 Amount of data in the ERTS socket implementation's queue before the socket leaves busy state.
98 nodelay (true)::
99 Whether to enable TCP_NODELAY.
100 port (0)::
101 TCP port number to listen on. 0 means a random port will be used.
102 priority (0)::
103 Priority value for all packets to be sent by this socket.
104 recbuf::
105 Minimum size of the socket's receive buffer. Default is system-dependent.
106 send_timeout (30000)::
107 How long the send call may wait for confirmation before returning.
108 send_timeout_close (true)::
109 Whether to close the socket when the confirmation wasn't received.
110 sndbuf::
111 Minimum size of the socket's send buffer. Default is system-dependent.
112 tos::
113 Value for the IP_TOS IP level option. Use with caution.
114
115 In addition, the `raw` option can be used to set system-specific
116 options by specifying the protocol level, the option number and
117 the actual option value specified as a binary. This option is not
118 portable. Use with caution.
119
120 == Exports
121
122 None.
+0
-194
deps/ranch/doc/src/manual/ranch_transport.asciidoc less more
0 = ranch_transport(3)
1
2 == Name
3
4 ranch_transport - behaviour for transport modules
5
6 == Description
7
8 The `ranch_transport` behaviour defines the interface used
9 by Ranch transports.
10
11 == Types
12
13 === sendfile_opts() = [{chunk_size, non_neg_integer()}]
14
15 Options used by the sendfile function and callbacks.
16
17 Allows configuring the chunk size, in bytes. Defaults to 8191 bytes.
18
19 == Callbacks
20
21 === accept(LSocket, Timeout) -> {ok, CSocket} | {error, closed | timeout | atom()}
22
23 LSocket = CSocket = any():: Listening socket.
24 Timeout = timeout():: Accept timeout.
25
26 Accept a connection on the given listening socket.
27
28 The `accept_ack` callback will be used to initialize the socket
29 after accepting the connection. This is most useful when the
30 transport is not raw TCP, like with SSL for example.
31
32 === accept_ack(CSocket, Timeout) -> ok
33
34 CSocket = any():: Socket for this connection.
35 Timeout = timeout():: Ack timeout.
36
37 Perform post-accept initialization of the connection.
38
39 This function will be called by connection processes
40 before performing any socket operation. It allows
41 transports that require extra initialization to perform
42 their task and make the socket ready to use.
43
44 === close(Socket) -> ok
45
46 Socket = any():: Socket opened with listen/1 or accept/2.
47
48 Close the given socket.
49
50 === controlling_process(Socket, Pid) -> ok | {error, closed | not_owner | atom()}
51
52 Socket = any():: Socket opened with listen/1 or accept/2.
53 Pid = pid():: Pid of the new owner of the socket.
54
55 Change the controlling process for the given socket.
56
57 The controlling process is the process that is allowed to
58 perform operations on the socket, and that will receive
59 messages from the socket when active mode is used. When
60 the controlling process dies, the socket is closed.
61
62 === listen(TransOpts) -> {ok, LSocket} | {error, atom()}
63
64 TransOpts = any():: Transport options.
65 LSocket = any():: Listening socket.
66
67 Listen for connections on the given port.
68
69 The port is given as part of the transport options under
70 the key `port`. Any other option is transport dependent.
71
72 The socket returned by this call can then be used to
73 accept connections. It is not possible to send or receive
74 data from the listening socket.
75
76 === messages() -> {OK, Closed, Error}
77
78 OK = Closed = Error = atom():: Tuple names.
79
80 Return the atoms used to identify messages sent in active mode.
81
82 === name() -> Name
83
84 Name = atom():: Transport module name.
85
86 Return the name of the transport.
87
88 === peername(CSocket) -> {ok, {IP, Port}} | {error, atom()}
89
90 CSocket = any():: Socket for this connection.
91 IP = inet:ip_address():: IP of the remote endpoint.
92 Port = inet:port_number():: Port of the remote endpoint.
93
94 Return the IP and port of the remote endpoint.
95
96 === recv(CSocket, Length, Timeout) -> {ok, Packet} | {error, closed | timeout | atom()}
97
98 CSocket = any():: Socket for this connection.
99 Length = non_neg_integer():: Requested length.
100 Timeout = timeout():: Receive timeout.
101 Packet = iodata() | any():: Data received.
102
103 Receive data from the given socket when in passive mode.
104
105 Trying to receive data from a socket that is in active mode
106 will return an error.
107
108 A length of 0 will return any data available on the socket.
109
110 While it is possible to use the timeout value `infinity`,
111 this is highly discouraged as this could cause your process
112 to get stuck waiting for data that will never come. This may
113 happen when a socket becomes half-open due to a crash of the
114 remote endpoint. Wi-Fi going down is another common culprit
115 of this issue.
116
117 === send(CSocket, Packet) -> ok | {error, atom()}
118
119 CSocket = any():: Socket for this connection.
120 Packet = iodata():: Data to be sent.
121
122 Send data to the given socket.
123
124 === sendfile(CSocket, File) -> sendfile(CSocket, File, 0, 0, [])
125
126 Alias of `ranch_transport:sendfile/5`.
127
128 === sendfile(CSocket, File, Offset, Bytes) -> sendfile(CSocket, File, Offset, Bytes, [])
129
130 Alias of `ranch_transport:sendfile/5`.
131
132 === sendfile(CSocket, File, Offset, Bytes, SfOpts) -> {ok, SentBytes} | {error, atom()}
133
134 CSocket = any():: Socket for this connection.
135 File = file:filename_all() | file:fd():: Filename or file descriptor for the file to be sent.
136 Offset = non_neg_integer():: Begin sending at this position in the file.
137 Bytes = non_neg_integer():: Send this many bytes.
138 SentBytes = non_neg_integer():: This many bytes were sent.
139 SfOpts = sendfile_opts():: Sendfile options.
140
141 Send data from a file to the given socket.
142
143 The file may be sent full or in parts, and may be specified
144 by its filename or by an already open file descriptor.
145
146 Transports that manipulate TCP directly may use the
147 `file:sendfile/{2,4,5}` function, which calls the sendfile
148 syscall where applicable (on Linux, for example). Other
149 transports can use the `sendfile/6` function exported from
150 this module.
151
152 === setopts(CSocket, SockOpts) -> ok | {error, atom()}
153
154 CSocket = any():: Socket for this connection.
155 SockOpts = any():: Socket options.
156
157 Change options for the given socket.
158
159 This is mainly useful for switching to active or passive mode
160 or to set protocol-specific options.
161
162 === shutdown(CSocket, How) -> ok | {error, atom()}
163
164 CSocket = any():: Socket for this connection.
165 How = read | write | read_write:: Which side(s) of the socket to close.
166
167 Immediately close the socket in one or two directions.
168
169 === sockname(Socket) -> {ok, {IP, Port}} | {error, atom()}
170
171 Socket = any():: Socket opened with listen/1 or accept/2.
172 IP = inet:ip_address():: IP of the local endpoint.
173 Port = inet:port_number():: Port of the local endpoint.
174
175 Return the IP and port of the local endpoint.
176
177 == Exports
178
179 === sendfile(Transport, CSocket, File, Offset, Bytes, SfOpts) -> {ok, SentBytes} | {error, atom()}
180
181 Transport = module():: Transport module for this socket.
182 CSocket = any():: Socket for this connection.
183 File = file:filename_all() | file:fd():: Filename or file descriptor for the file to be sent.
184 Offset = non_neg_integer():: Begin sending at this position in the file.
185 Bytes = non_neg_integer():: Send this many bytes.
186 SentBytes = non_neg_integer():: This many bytes were sent.
187 SfOpts = sendfile_opts():: Sendfile options.
188
189 Send data from a file to the given socket.
190
191 This function emulates the function `file:sendfile/{2,4,5}`
192 and may be used when transports are not manipulating TCP
193 directly.
+0
-3
deps/ranch/examples/tcp_echo/Makefile less more
0 PROJECT = tcp_echo
1 DEPS = ranch
2 include ../../erlang.mk
+0
-27
deps/ranch/examples/tcp_echo/README.md less more
0 Ranch TCP echo example
1 ======================
2
3 To try this example, you need GNU `make` and `git` in your PATH.
4
5 To build the example, run the following command:
6
7 ``` bash
8 $ make
9 ```
10
11 To start the release in the foreground:
12
13 ``` bash
14 $ ./_rel/tcp_echo_example/bin/tcp_echo_example console
15 ```
16
17 Then start a telnet session to port 5555:
18
19 ``` bash
20 $ telnet localhost 5555
21 ```
22
23 Type in a few words and see them echoed back.
24
25 Be aware that there is a timeout of 5 seconds without receiving
26 data before the example server disconnects your session.
+0
-2
deps/ranch/examples/tcp_echo/relx.config less more
0 {release, {tcp_echo_example, "1"}, [tcp_echo]}.
1 {extended_start_script, true}.
+0
-24
deps/ranch/examples/tcp_echo/src/echo_protocol.erl less more
0 %% Feel free to use, reuse and abuse the code in this file.
1
2 -module(echo_protocol).
3 -behaviour(ranch_protocol).
4
5 -export([start_link/4]).
6 -export([init/4]).
7
8 start_link(Ref, Socket, Transport, Opts) ->
9 Pid = spawn_link(?MODULE, init, [Ref, Socket, Transport, Opts]),
10 {ok, Pid}.
11
12 init(Ref, Socket, Transport, _Opts = []) ->
13 ok = ranch:accept_ack(Ref),
14 loop(Socket, Transport).
15
16 loop(Socket, Transport) ->
17 case Transport:recv(Socket, 0, 5000) of
18 {ok, Data} ->
19 Transport:send(Socket, Data),
20 loop(Socket, Transport);
21 _ ->
22 ok = Transport:close(Socket)
23 end.
+0
-15
deps/ranch/examples/tcp_echo/src/tcp_echo.app.src less more
0 %% Feel free to use, reuse and abuse the code in this file.
1
2 {application, tcp_echo, [
3 {description, "Ranch TCP echo example."},
4 {vsn, "1"},
5 {modules, []},
6 {registered, [tcp_echo_sup]},
7 {applications, [
8 kernel,
9 stdlib,
10 ranch
11 ]},
12 {mod, {tcp_echo_app, []}},
13 {env, []}
14 ]}.
+0
-19
deps/ranch/examples/tcp_echo/src/tcp_echo_app.erl less more
0 %% Feel free to use, reuse and abuse the code in this file.
1
2 %% @private
3 -module(tcp_echo_app).
4 -behaviour(application).
5
6 %% API.
7 -export([start/2]).
8 -export([stop/1]).
9
10 %% API.
11
12 start(_Type, _Args) ->
13 {ok, _} = ranch:start_listener(tcp_echo, 1,
14 ranch_tcp, [{port, 5555}], echo_protocol, []),
15 tcp_echo_sup:start_link().
16
17 stop(_State) ->
18 ok.
+0
-22
deps/ranch/examples/tcp_echo/src/tcp_echo_sup.erl less more
0 %% Feel free to use, reuse and abuse the code in this file.
1
2 %% @private
3 -module(tcp_echo_sup).
4 -behaviour(supervisor).
5
6 %% API.
7 -export([start_link/0]).
8
9 %% supervisor.
10 -export([init/1]).
11
12 %% API.
13
14 -spec start_link() -> {ok, pid()}.
15 start_link() ->
16 supervisor:start_link({local, ?MODULE}, ?MODULE, []).
17
18 %% supervisor.
19
20 init([]) ->
21 {ok, {{one_for_one, 10, 10}, []}}.
+0
-3
deps/ranch/examples/tcp_reverse/Makefile less more
0 PROJECT = tcp_reverse
1 DEPS = ranch
2 include ../../erlang.mk
+0
-33
deps/ranch/examples/tcp_reverse/README.md less more
0 Ranch TCP reverse example
1 =========================
2
3 This example uses a `gen_server` to handle a protocol to revese input.
4 See `reverse_protocol.erl` for the implementation. Documentation about
5 this topic can be found in the guide:
6
7 http://ninenines.eu/docs/en/ranch/HEAD/guide/protocols/#using_gen_server
8
9 To try this example, you need GNU `make` and `git` in your PATH.
10
11 To build the example, run the following command:
12
13 ``` bash
14 $ make
15 ```
16
17 To start the release in the foreground:
18
19 ``` bash
20 $ ./_rel/tcp_reverse_example/bin/tcp_reverse_example console
21 ```
22
23 Then start a telnet session to port 5555:
24
25 ``` bash
26 $ telnet localhost 5555
27 ```
28
29 Type in a few words and see them reversed! Amazing!
30
31 Be aware that there is a timeout of 5 seconds without receiving
32 data before the example server disconnects your session.
+0
-2
deps/ranch/examples/tcp_reverse/relx.config less more
0 {release, {tcp_reverse_example, "1"}, [tcp_reverse]}.
1 {extended_start_script, true}.
+0
-73
deps/ranch/examples/tcp_reverse/src/reverse_protocol.erl less more
0 %% Feel free to use, reuse and abuse the code in this file.
1
2 -module(reverse_protocol).
3 -behaviour(gen_server).
4 -behaviour(ranch_protocol).
5
6 %% API.
7 -export([start_link/4]).
8
9 %% gen_server.
10 -export([init/1]).
11 -export([init/4]).
12 -export([handle_call/3]).
13 -export([handle_cast/2]).
14 -export([handle_info/2]).
15 -export([terminate/2]).
16 -export([code_change/3]).
17
18 -define(TIMEOUT, 5000).
19
20 -record(state, {socket, transport}).
21
22 %% API.
23
24 start_link(Ref, Socket, Transport, Opts) ->
25 proc_lib:start_link(?MODULE, init, [Ref, Socket, Transport, Opts]).
26
27 %% gen_server.
28
29 %% This function is never called. We only define it so that
30 %% we can use the -behaviour(gen_server) attribute.
31 init([]) -> {ok, undefined}.
32
33 init(Ref, Socket, Transport, _Opts = []) ->
34 ok = proc_lib:init_ack({ok, self()}),
35 ok = ranch:accept_ack(Ref),
36 ok = Transport:setopts(Socket, [{active, once}]),
37 gen_server:enter_loop(?MODULE, [],
38 #state{socket=Socket, transport=Transport},
39 ?TIMEOUT).
40
41 handle_info({tcp, Socket, Data}, State=#state{
42 socket=Socket, transport=Transport}) ->
43 Transport:setopts(Socket, [{active, once}]),
44 Transport:send(Socket, reverse_binary(Data)),
45 {noreply, State, ?TIMEOUT};
46 handle_info({tcp_closed, _Socket}, State) ->
47 {stop, normal, State};
48 handle_info({tcp_error, _, Reason}, State) ->
49 {stop, Reason, State};
50 handle_info(timeout, State) ->
51 {stop, normal, State};
52 handle_info(_Info, State) ->
53 {stop, normal, State}.
54
55 handle_call(_Request, _From, State) ->
56 {reply, ok, State}.
57
58 handle_cast(_Msg, State) ->
59 {noreply, State}.
60
61 terminate(_Reason, _State) ->
62 ok.
63
64 code_change(_OldVsn, State, _Extra) ->
65 {ok, State}.
66
67 %% Internal.
68
69 reverse_binary(B) when is_binary(B) ->
70 [list_to_binary(lists:reverse(binary_to_list(
71 binary:part(B, {0, byte_size(B)-2})
72 ))), "\r\n"].
+0
-15
deps/ranch/examples/tcp_reverse/src/tcp_reverse.app.src less more
0 %% Feel free to use, reuse and abuse the code in this file.
1
2 {application, tcp_reverse, [
3 {description, "Ranch TCP reverse example."},
4 {vsn, "1"},
5 {modules, []},
6 {registered, [tcp_reverse_sup]},
7 {applications, [
8 kernel,
9 stdlib,
10 ranch
11 ]},
12 {mod, {tcp_reverse_app, []}},
13 {env, []}
14 ]}.
+0
-19
deps/ranch/examples/tcp_reverse/src/tcp_reverse_app.erl less more
0 %% Feel free to use, reuse and abuse the code in this file.
1
2 %% @private
3 -module(tcp_reverse_app).
4 -behaviour(application).
5
6 %% API.
7 -export([start/2]).
8 -export([stop/1]).
9
10 %% API.
11
12 start(_Type, _Args) ->
13 {ok, _} = ranch:start_listener(tcp_reverse, 10,
14 ranch_tcp, [{port, 5555}], reverse_protocol, []),
15 tcp_reverse_sup:start_link().
16
17 stop(_State) ->
18 ok.
+0
-22
deps/ranch/examples/tcp_reverse/src/tcp_reverse_sup.erl less more
0 %% Feel free to use, reuse and abuse the code in this file.
1
2 %% @private
3 -module(tcp_reverse_sup).
4 -behaviour(supervisor).
5
6 %% API.
7 -export([start_link/0]).
8
9 %% supervisor.
10 -export([init/1]).
11
12 %% API.
13
14 -spec start_link() -> {ok, pid()}.
15 start_link() ->
16 supervisor:start_link({local, ?MODULE}, ?MODULE, []).
17
18 %% supervisor.
19
20 init([]) ->
21 {ok, {{one_for_one, 10, 10}, []}}.
00 {application,ranch,
11 [{description,"Socket acceptor pool for TCP protocols."},
2 {vsn,"1.2.1"},
3 {id,"git"},
2 {vsn,"1.3.0"},
43 {modules,[]},
54 {registered,[ranch_sup,ranch_server]},
6 {applications,[kernel,stdlib]},
5 {applications,[kernel,stdlib,ssl]},
76 {mod,{ranch_app,[]}},
87 {env,[]}]}.
0 %% Copyright (c) 2011-2015, Loïc Hoguin <essen@ninenines.eu>
0 %% Copyright (c) 2011-2016, Loïc Hoguin <essen@ninenines.eu>
11 %%
22 %% Permission to use, copy, modify, and/or distribute this software for any
33 %% purpose with or without fee is hereby granted, provided that the above
2424 -export([set_max_connections/2]).
2525 -export([get_protocol_options/1]).
2626 -export([set_protocol_options/2]).
27 -export([info/0]).
28 -export([procs/2]).
2729 -export([filter_options/3]).
2830 -export([set_option_default/3]).
2931 -export([require/1]).
4345
4446 -spec start_listener(ref(), non_neg_integer(), module(), any(), module(), any())
4547 -> supervisor:startchild_ret().
46 start_listener(Ref, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts)
47 when is_integer(NbAcceptors) andalso is_atom(Transport)
48 start_listener(Ref, NumAcceptors, Transport, TransOpts, Protocol, ProtoOpts)
49 when is_integer(NumAcceptors) andalso is_atom(Transport)
4850 andalso is_atom(Protocol) ->
4951 _ = code:ensure_loaded(Transport),
50 %% @todo Remove in Ranch 2.0 and simply require ssl.
51 _ = ensure_ssl(Transport),
5252 case erlang:function_exported(Transport, name, 0) of
5353 false ->
5454 {error, badarg};
5555 true ->
56 Res = supervisor:start_child(ranch_sup, child_spec(Ref, NbAcceptors,
56 Res = supervisor:start_child(ranch_sup, child_spec(Ref, NumAcceptors,
5757 Transport, TransOpts, Protocol, ProtoOpts)),
5858 Socket = proplists:get_value(socket, TransOpts),
5959 case Res of
7373 _ ->
7474 ok
7575 end,
76 Res
77 end.
76 maybe_started(Res)
77 end.
78
79 maybe_started({error, {{shutdown,
80 {failed_to_start_child, ranch_acceptors_sup,
81 {listen_error, _, Reason}}}, _}} = Error) ->
82 start_error(Reason, Error);
83 maybe_started(Res) ->
84 Res.
85
86 start_error(E=eaddrinuse, _) -> {error, E};
87 start_error(E=eacces, _) -> {error, E};
88 start_error(E=no_cert, _) -> {error, E};
89 start_error(_, Error) -> Error.
7890
7991 -spec stop_listener(ref()) -> ok | {error, not_found}.
8092 stop_listener(Ref) ->
88100
89101 -spec child_spec(ref(), non_neg_integer(), module(), any(), module(), any())
90102 -> supervisor:child_spec().
91 child_spec(Ref, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts)
92 when is_integer(NbAcceptors) andalso is_atom(Transport)
103 child_spec(Ref, NumAcceptors, Transport, TransOpts, Protocol, ProtoOpts)
104 when is_integer(NumAcceptors) andalso is_atom(Transport)
93105 andalso is_atom(Protocol) ->
94 %% @todo Remove in Ranch 2.0 and simply require ssl.
95 _ = ensure_ssl(Transport),
96106 {{ranch_listener_sup, Ref}, {ranch_listener_sup, start_link, [
97 Ref, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts
107 Ref, NumAcceptors, Transport, TransOpts, Protocol, ProtoOpts
98108 ]}, permanent, infinity, supervisor, [ranch_listener_sup]}.
99
100 %% @todo Remove in Ranch 2.0 and simply require ssl.
101 ensure_ssl(ranch_ssl) ->
102 require([crypto, asn1, public_key, ssl]);
103 ensure_ssl(_) ->
104 ok.
105109
106110 -spec accept_ack(ref()) -> ok.
107111 accept_ack(Ref) ->
112116 -spec remove_connection(ref()) -> ok.
113117 remove_connection(Ref) ->
114118 ConnsSup = ranch_server:get_connections_sup(Ref),
115 ConnsSup ! {remove_connection, Ref},
119 ConnsSup ! {remove_connection, Ref, self()},
116120 ok.
117121
118122 -spec get_addr(ref()) -> {inet:ip_address(), inet:port_number()}.
140144 set_protocol_options(Ref, Opts) ->
141145 ranch_server:set_protocol_options(Ref, Opts).
142146
147 -spec info() -> [{any(), [{atom(), any()}]}].
148 info() ->
149 Children = supervisor:which_children(ranch_sup),
150 [{Ref, listener_info(Ref, Pid)}
151 || {{ranch_listener_sup, Ref}, Pid, _, [_]} <- Children].
152
153 listener_info(Ref, Pid) ->
154 [_, NumAcceptors, Transport, TransOpts, Protocol, _] = listener_start_args(Ref),
155 ConnsSup = ranch_server:get_connections_sup(Ref),
156 {IP, Port} = get_addr(Ref),
157 MaxConns = get_max_connections(Ref),
158 ProtoOpts = get_protocol_options(Ref),
159 [
160 {pid, Pid},
161 {ip, IP},
162 {port, Port},
163 {num_acceptors, NumAcceptors},
164 {max_connections, MaxConns},
165 {active_connections, ranch_conns_sup:active_connections(ConnsSup)},
166 {all_connections, proplists:get_value(active, supervisor:count_children(ConnsSup))},
167 {transport, Transport},
168 {transport_options, TransOpts},
169 {protocol, Protocol},
170 {protocol_options, ProtoOpts}
171 ].
172
173 listener_start_args(Ref) ->
174 case erlang:function_exported(supervisor, get_childspec, 2) of
175 true ->
176 %% Can't use map syntax before R18.
177 {ok, Map} = supervisor:get_childspec(ranch_sup, {ranch_listener_sup, Ref}),
178 {ranch_listener_sup, start_link, StartArgs} = maps:get(start, Map),
179 StartArgs;
180 false ->
181 %% Awful solution for compatibility with R16 and R17.
182 {status, _, _, [_, _, _, _, [_, _,
183 {data, [{_, {state, _, _, Children, _, _, _, _, _, _}}]}]]}
184 = sys:get_status(ranch_sup),
185 [StartArgs] = [StartArgs || {child, _, {ranch_listener_sup, ChildRef},
186 {ranch_listener_sup, start_link, StartArgs}, _, _, _, _}
187 <- Children, ChildRef =:= Ref],
188 StartArgs
189 end.
190
191 -spec procs(ref(), acceptors | connections) -> [pid()].
192 procs(Ref, acceptors) ->
193 procs1(Ref, ranch_acceptors_sup);
194 procs(Ref, connections) ->
195 procs1(Ref, ranch_conns_sup).
196
197 procs1(Ref, Sup) ->
198 {_, ListenerSup, _, _} = lists:keyfind({ranch_listener_sup, Ref}, 1,
199 supervisor:which_children(ranch_sup)),
200 {_, SupPid, _, _} = lists:keyfind(Sup, 1,
201 supervisor:which_children(ListenerSup)),
202 [Pid || {_, Pid, _, _} <- supervisor:which_children(SupPid)].
203
143204 -spec filter_options([inet | inet6 | {atom(), any()} | {raw, any(), any(), any()}],
144205 [atom()], Acc) -> Acc when Acc :: [any()].
145 filter_options(UserOptions, AllowedKeys, DefaultOptions) ->
146 AllowedOptions = filter_user_options(UserOptions, AllowedKeys),
206 filter_options(UserOptions, DisallowedKeys, DefaultOptions) ->
207 AllowedOptions = filter_user_options(UserOptions, DisallowedKeys),
147208 lists:foldl(fun merge_options/2, DefaultOptions, AllowedOptions).
148209
149210 %% 2-tuple options.
150 filter_user_options([Opt = {Key, _}|Tail], AllowedKeys) ->
151 case lists:member(Key, AllowedKeys) of
211 filter_user_options([Opt = {Key, _}|Tail], DisallowedKeys) ->
212 case lists:member(Key, DisallowedKeys) of
213 false ->
214 [Opt|filter_user_options(Tail, DisallowedKeys)];
152215 true ->
153 [Opt|filter_user_options(Tail, AllowedKeys)];
154 false ->
155216 filter_options_warning(Opt),
156 filter_user_options(Tail, AllowedKeys)
217 filter_user_options(Tail, DisallowedKeys)
157218 end;
158219 %% Special option forms.
159220 filter_user_options([inet|Tail], AllowedKeys) ->
162223 [inet6|filter_user_options(Tail, AllowedKeys)];
163224 filter_user_options([Opt = {raw, _, _, _}|Tail], AllowedKeys) ->
164225 [Opt|filter_user_options(Tail, AllowedKeys)];
165 filter_user_options([Opt|Tail], AllowedKeys) ->
226 filter_user_options([Opt|Tail], DisallowedKeys) ->
166227 filter_options_warning(Opt),
167 filter_user_options(Tail, AllowedKeys);
228 filter_user_options(Tail, DisallowedKeys);
168229 filter_user_options([], _) ->
169230 [].
170231
0 %% Copyright (c) 2011-2015, Loïc Hoguin <essen@ninenines.eu>
0 %% Copyright (c) 2011-2016, Loïc Hoguin <essen@ninenines.eu>
11 %%
22 %% Permission to use, copy, modify, and/or distribute this software for any
33 %% purpose with or without fee is hereby granted, provided that the above
3838 %% We can't accept anymore anyway, so we might as well wait
3939 %% a little for the situation to resolve itself.
4040 {error, emfile} ->
41 error_logger:warning_msg("Ranch acceptor reducing accept rate: out of file descriptors~n"),
4142 receive after 100 -> ok end;
4243 %% We want to crash if the listening socket got closed.
4344 {error, Reason} when Reason =/= closed ->
0 %% Copyright (c) 2011-2015, Loïc Hoguin <essen@ninenines.eu>
0 %% Copyright (c) 2011-2016, Loïc Hoguin <essen@ninenines.eu>
11 %%
22 %% Permission to use, copy, modify, and/or distribute this software for any
33 %% purpose with or without fee is hereby granted, provided that the above
1919
2020 -spec start_link(ranch:ref(), non_neg_integer(), module(), any())
2121 -> {ok, pid()}.
22 start_link(Ref, NbAcceptors, Transport, TransOpts) ->
23 supervisor:start_link(?MODULE, [Ref, NbAcceptors, Transport, TransOpts]).
22 start_link(Ref, NumAcceptors, Transport, TransOpts) ->
23 supervisor:start_link(?MODULE, [Ref, NumAcceptors, Transport, TransOpts]).
2424
25 init([Ref, NbAcceptors, Transport, TransOpts]) ->
25 init([Ref, NumAcceptors, Transport, TransOpts]) ->
2626 ConnsSup = ranch_server:get_connections_sup(Ref),
2727 LSocket = case proplists:get_value(socket, TransOpts) of
2828 undefined ->
4444 {{acceptor, self(), N}, {ranch_acceptor, start_link, [
4545 LSocket, Transport, ConnsSup
4646 ]}, permanent, brutal_kill, worker, []}
47 || N <- lists:seq(1, NbAcceptors)],
47 || N <- lists:seq(1, NumAcceptors)],
4848 {ok, {{one_for_one, 1, 5}, Procs}}.
4949
5050 -spec listen_error(any(), module(), any(), atom()) -> no_return().
51 listen_error(Ref, Transport, TransOpts2, Reason) ->
51 listen_error(Ref, Transport, TransOpts0, Reason) ->
52 TransOpts1 = lists:keyreplace(cert, 1, TransOpts0, {cert, '...'}),
53 TransOpts = lists:keyreplace(key, 1, TransOpts1, {key, '...'}),
5254 error_logger:error_msg(
53 "Failed to start Ranch listener ~p in ~p:listen(~p) for reason ~p (~s)~n",
54 [Ref, Transport, TransOpts2, Reason, inet:format_error(Reason)]),
55 "Failed to start Ranch listener ~p in ~p:listen(~999999p) for reason ~p (~s)~n",
56 [Ref, Transport, TransOpts, Reason, format_error(Reason)]),
5557 exit({listen_error, Ref, Reason}).
58
59 format_error(no_cert) ->
60 "no certificate provided; see cert, certfile, sni_fun or sni_hosts options";
61 format_error(Reason) ->
62 inet:format_error(Reason).
0 %% Copyright (c) 2011-2015, Loïc Hoguin <essen@ninenines.eu>
0 %% Copyright (c) 2011-2016, Loïc Hoguin <essen@ninenines.eu>
11 %%
22 %% Permission to use, copy, modify, and/or distribute this software for any
33 %% purpose with or without fee is hereby granted, provided that the above
0 %% Copyright (c) 2011-2015, Loïc Hoguin <essen@ninenines.eu>
0 %% Copyright (c) 2011-2016, Loïc Hoguin <essen@ninenines.eu>
11 %%
22 %% Permission to use, copy, modify, and/or distribute this software for any
33 %% purpose with or without fee is hereby granted, provided that the above
135135 To ! {Tag, CurConns},
136136 loop(State, CurConns, NbChildren, Sleepers);
137137 %% Remove a connection from the count of connections.
138 {remove_connection, Ref} ->
139 loop(State, CurConns - 1, NbChildren, Sleepers);
138 {remove_connection, Ref, Pid} ->
139 case put(Pid, removed) of
140 active ->
141 loop(State, CurConns - 1, NbChildren, Sleepers);
142 remove ->
143 loop(State, CurConns, NbChildren, Sleepers);
144 undefined ->
145 _ = erase(Pid),
146 loop(State, CurConns, NbChildren, Sleepers)
147 end;
140148 %% Upgrade the max number of connections allowed concurrently.
141149 %% We resume all sleeping acceptors if this number increases.
142150 {set_max_conns, MaxConns2} when MaxConns2 > MaxConns ->
153161 {'EXIT', Parent, Reason} ->
154162 terminate(State, Reason, NbChildren);
155163 {'EXIT', Pid, Reason} when Sleepers =:= [] ->
156 report_error(Ref, Protocol, Pid, Reason),
157 erase(Pid),
158 loop(State, CurConns - 1, NbChildren - 1, Sleepers);
164 case erase(Pid) of
165 active ->
166 report_error(Ref, Protocol, Pid, Reason),
167 loop(State, CurConns - 1, NbChildren - 1, Sleepers);
168 removed ->
169 report_error(Ref, Protocol, Pid, Reason),
170 loop(State, CurConns, NbChildren - 1, Sleepers);
171 undefined ->
172 loop(State, CurConns, NbChildren, Sleepers)
173 end;
159174 %% Resume a sleeping acceptor if needed.
160175 {'EXIT', Pid, Reason} ->
161 report_error(Ref, Protocol, Pid, Reason),
162 erase(Pid),
163 [To|Sleepers2] = Sleepers,
164 To ! self(),
165 loop(State, CurConns - 1, NbChildren - 1, Sleepers2);
176 case erase(Pid) of
177 active when CurConns > MaxConns ->
178 report_error(Ref, Protocol, Pid, Reason),
179 loop(State, CurConns - 1, NbChildren - 1, Sleepers);
180 active ->
181 report_error(Ref, Protocol, Pid, Reason),
182 [To|Sleepers2] = Sleepers,
183 To ! self(),
184 loop(State, CurConns - 1, NbChildren - 1, Sleepers2);
185 removed ->
186 report_error(Ref, Protocol, Pid, Reason),
187 loop(State, CurConns, NbChildren - 1, Sleepers);
188 undefined ->
189 loop(State, CurConns, NbChildren, Sleepers)
190 end;
166191 {system, From, Request} ->
167192 sys:handle_system_msg(Request, From, Parent, ?MODULE, [],
168193 {State, CurConns, NbChildren, Sleepers});
169194 %% Calls from the supervisor module.
170195 {'$gen_call', {To, Tag}, which_children} ->
171 Pids = get_keys(true),
172196 Children = [{Protocol, Pid, ConnType, [Protocol]}
173 || Pid <- Pids, is_pid(Pid)],
197 || {Pid, Type} <- get(),
198 Type =:= active orelse Type =:= removed],
174199 To ! {Tag, Children},
175200 loop(State, CurConns, NbChildren, Sleepers);
176201 {'$gen_call', {To, Tag}, count_children} ->
195220 case Transport:controlling_process(Socket, ProtocolPid) of
196221 ok ->
197222 ProtocolPid ! {shoot, Ref, Transport, Socket, AckTimeout},
198 put(SupPid, true),
223 put(SupPid, active),
199224 CurConns2 = CurConns + 1,
200225 if CurConns2 < MaxConns ->
201226 To ! self(),
208233 %% Only kill the supervised pid, because the connection's pid,
209234 %% when different, is supposed to be sitting under it and linked.
210235 exit(SupPid, kill),
236 To ! self(),
211237 loop(State, CurConns, NbChildren, Sleepers)
212238 end.
213239
214240 -spec terminate(#state{}, any(), non_neg_integer()) -> no_return().
215 %% Kill all children and then exit. We unlink first to avoid
216 %% getting a message for each child getting killed.
217241 terminate(#state{shutdown=brutal_kill}, Reason, _) ->
218 Pids = get_keys(true),
219 _ = [begin
220 unlink(P),
221 exit(P, kill)
222 end || P <- Pids],
242 kill_children(get_keys(active)),
243 kill_children(get_keys(removed)),
223244 exit(Reason);
224245 %% Attempt to gracefully shutdown all children.
225246 terminate(#state{shutdown=Shutdown}, Reason, NbChildren) ->
226 shutdown_children(),
247 shutdown_children(get_keys(active)),
248 shutdown_children(get_keys(removed)),
227249 _ = if
228250 Shutdown =:= infinity ->
229251 ok;
233255 wait_children(NbChildren),
234256 exit(Reason).
235257
258 %% Kill all children and then exit. We unlink first to avoid
259 %% getting a message for each child getting killed.
260 kill_children(Pids) ->
261 _ = [begin
262 unlink(P),
263 exit(P, kill)
264 end || P <- Pids],
265 ok.
266
236267 %% Monitor processes so we can know which ones have shutdown
237268 %% before the timeout. Unlink so we avoid receiving an extra
238269 %% message. Then send a shutdown exit signal.
239 shutdown_children() ->
240 Pids = get_keys(true),
270 shutdown_children(Pids) ->
241271 _ = [begin
242272 monitor(process, P),
243273 unlink(P),
250280 wait_children(NbChildren) ->
251281 receive
252282 {'DOWN', _, process, Pid, _} ->
253 _ = erase(Pid),
254 wait_children(NbChildren - 1);
283 case erase(Pid) of
284 active -> wait_children(NbChildren - 1);
285 removed -> wait_children(NbChildren - 1);
286 _ -> wait_children(NbChildren)
287 end;
255288 kill ->
256 Pids = get_keys(true),
257 _ = [exit(P, kill) || P <- Pids],
289 Active = get_keys(active),
290 _ = [exit(P, kill) || P <- Active],
291 Removed = get_keys(removed),
292 _ = [exit(P, kill) || P <- Removed],
258293 ok
259294 end.
260295
0 %% Copyright (c) 2011-2015, Loïc Hoguin <essen@ninenines.eu>
0 %% Copyright (c) 2011-2016, Loïc Hoguin <essen@ninenines.eu>
11 %%
22 %% Permission to use, copy, modify, and/or distribute this software for any
33 %% purpose with or without fee is hereby granted, provided that the above
1919
2020 -spec start_link(ranch:ref(), non_neg_integer(), module(), any(), module(), any())
2121 -> {ok, pid()}.
22 start_link(Ref, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts) ->
22 start_link(Ref, NumAcceptors, Transport, TransOpts, Protocol, ProtoOpts) ->
2323 MaxConns = proplists:get_value(max_connections, TransOpts, 1024),
2424 ranch_server:set_new_listener_opts(Ref, MaxConns, ProtoOpts),
2525 supervisor:start_link(?MODULE, {
26 Ref, NbAcceptors, Transport, TransOpts, Protocol
26 Ref, NumAcceptors, Transport, TransOpts, Protocol
2727 }).
2828
29 init({Ref, NbAcceptors, Transport, TransOpts, Protocol}) ->
29 init({Ref, NumAcceptors, Transport, TransOpts, Protocol}) ->
3030 AckTimeout = proplists:get_value(ack_timeout, TransOpts, 5000),
3131 ConnType = proplists:get_value(connection_type, TransOpts, worker),
3232 Shutdown = proplists:get_value(shutdown, TransOpts, 5000),
3535 [Ref, ConnType, Shutdown, Transport, AckTimeout, Protocol]},
3636 permanent, infinity, supervisor, [ranch_conns_sup]},
3737 {ranch_acceptors_sup, {ranch_acceptors_sup, start_link,
38 [Ref, NbAcceptors, Transport, TransOpts]},
38 [Ref, NumAcceptors, Transport, TransOpts]},
3939 permanent, infinity, supervisor, [ranch_acceptors_sup]}
4040 ],
4141 {ok, {{rest_for_one, 1, 5}, ChildSpecs}}.
0 %% Copyright (c) 2012-2015, Loïc Hoguin <essen@ninenines.eu>
0 %% Copyright (c) 2012-2016, Loïc Hoguin <essen@ninenines.eu>
11 %%
22 %% Permission to use, copy, modify, and/or distribute this software for any
33 %% purpose with or without fee is hereby granted, provided that the above
0 %% Copyright (c) 2012-2015, Loïc Hoguin <essen@ninenines.eu>
0 %% Copyright (c) 2012-2016, Loïc Hoguin <essen@ninenines.eu>
11 %%
22 %% Permission to use, copy, modify, and/or distribute this software for any
33 %% purpose with or without fee is hereby granted, provided that the above
5858 _ = ets:delete(?TAB, {addr, Ref}),
5959 _ = ets:delete(?TAB, {max_conns, Ref}),
6060 _ = ets:delete(?TAB, {opts, Ref}),
61 %% We also remove the pid of the connections supervisor.
62 %% Depending on the timing, it might already have been deleted
63 %% when we handled the monitor DOWN message. However, in some
64 %% cases when calling stop_listener followed by get_connections_sup,
65 %% we could end up with the pid still being returned, when we
66 %% expected a crash (because the listener was stopped).
67 %% Deleting it explictly here removes any possible confusion.
68 _ = ets:delete(?TAB, {conns_sup, Ref}),
6169 ok.
6270
6371 -spec set_connections_sup(ranch:ref(), pid()) -> ok.
140148 handle_info({'DOWN', MonitorRef, process, Pid, _},
141149 State=#state{monitors=Monitors}) ->
142150 {_, Ref} = lists:keyfind({MonitorRef, Pid}, 1, Monitors),
143 true = ets:delete(?TAB, {conns_sup, Ref}),
151 _ = ets:delete(?TAB, {conns_sup, Ref}),
144152 Monitors2 = lists:keydelete({MonitorRef, Pid}, 1, Monitors),
145153 {noreply, State#state{monitors=Monitors2}};
146154 handle_info(_Info, State) ->
0 %% Copyright (c) 2011-2015, Loïc Hoguin <essen@ninenines.eu>
0 %% Copyright (c) 2011-2016, Loïc Hoguin <essen@ninenines.eu>
11 %%
22 %% Permission to use, copy, modify, and/or distribute this software for any
33 %% purpose with or without fee is hereby granted, provided that the above
1818 -export([secure/0]).
1919 -export([messages/0]).
2020 -export([listen/1]).
21 -export([listen_options/0]).
21 -export([disallowed_listen_options/0]).
2222 -export([accept/2]).
2323 -export([accept_ack/2]).
2424 -export([connect/3]).
3636 -export([close/1]).
3737
3838 -type ssl_opt() :: {alpn_preferred_protocols, [binary()]}
39 | {beast_mitigation, one_n_minus_one | zero_n | disabled}
3940 | {cacertfile, string()}
4041 | {cacerts, [public_key:der_encoded()]}
4142 | {cert, public_key:der_encoded()}
5455 | {keyfile, string()}
5556 | {log_alert, boolean()}
5657 | {next_protocols_advertised, [binary()]}
58 | {padding_check, boolean()}
5759 | {partial_chain, fun(([public_key:der_encoded()]) -> {trusted_ca, public_key:der_encoded()} | unknown_ca)}
5860 | {password, string()}
5961 | {psk_identity, string()}
6062 | {reuse_session, fun()}
6163 | {reuse_sessions, boolean()}
6264 | {secure_renegotiate, boolean()}
65 | {signature_algs, [{atom(), atom()}]}
6366 | {sni_fun, fun()}
6467 | {sni_hosts, [{string(), ssl_opt()}]}
6568 | {user_lookup_fun, {fun(), any()}}
69 | {v2_hello_compatible, boolean()}
6670 | {verify, ssl:verify_type()}
6771 | {verify_fun, {fun(), any()}}
6872 | {versions, [atom()]}.
8488
8589 -spec listen(opts()) -> {ok, ssl:sslsocket()} | {error, atom()}.
8690 listen(Opts) ->
87 true = lists:keymember(cert, 1, Opts)
88 orelse lists:keymember(certfile, 1, Opts),
91 case lists:keymember(cert, 1, Opts)
92 orelse lists:keymember(certfile, 1, Opts)
93 orelse lists:keymember(sni_fun, 1, Opts)
94 orelse lists:keymember(sni_hosts, 1, Opts) of
95 true ->
96 do_listen(Opts);
97 false ->
98 {error, no_cert}
99 end.
100
101 do_listen(Opts) ->
89102 Opts2 = ranch:set_option_default(Opts, backlog, 1024),
90103 Opts3 = ranch:set_option_default(Opts2, ciphers, unbroken_cipher_suites()),
91104 Opts4 = ranch:set_option_default(Opts3, nodelay, true),
94107 %% We set the port to 0 because it is given in the Opts directly.
95108 %% The port in the options takes precedence over the one in the
96109 %% first argument.
97 ssl:listen(0, ranch:filter_options(Opts6, listen_options(),
98 [binary, {active, false}, {packet, raw},
99 {reuseaddr, true}, {nodelay, true}])).
100
101 listen_options() ->
102 [alpn_preferred_protocols, cacertfile, cacerts, cert, certfile,
103 ciphers, client_renegotiation, crl_cache, crl_check, depth,
104 dh, dhfile, fail_if_no_peer_cert, hibernate_after, honor_cipher_order,
105 key, keyfile, log_alert, next_protocols_advertised, partial_chain,
106 password, psk_identity, reuse_session, reuse_sessions, secure_renegotiate,
107 sni_fun, sni_hosts, user_lookup_fun, verify, verify_fun, versions
108 |ranch_tcp:listen_options()].
110 ssl:listen(0, ranch:filter_options(Opts6, disallowed_listen_options(),
111 [binary, {active, false}, {packet, raw}, {reuseaddr, true}])).
112
113 %% 'binary' and 'list' are disallowed but they are handled
114 %% specifically as they do not have 2-tuple equivalents.
115 disallowed_listen_options() ->
116 [alpn_advertised_protocols, client_preferred_next_protocols,
117 fallback, server_name_indication, srp_identity
118 |ranch_tcp:disallowed_listen_options()].
109119
110120 -spec accept(ssl:sslsocket(), timeout())
111121 -> {ok, ssl:sslsocket()} | {error, closed | timeout | atom()}.
0 %% Copyright (c) 2011-2015, Loïc Hoguin <essen@ninenines.eu>
0 %% Copyright (c) 2011-2016, Loïc Hoguin <essen@ninenines.eu>
11 %%
22 %% Permission to use, copy, modify, and/or distribute this software for any
33 %% purpose with or without fee is hereby granted, provided that the above
0 %% Copyright (c) 2011-2015, Loïc Hoguin <essen@ninenines.eu>
0 %% Copyright (c) 2011-2016, Loïc Hoguin <essen@ninenines.eu>
11 %%
22 %% Permission to use, copy, modify, and/or distribute this software for any
33 %% purpose with or without fee is hereby granted, provided that the above
1818 -export([secure/0]).
1919 -export([messages/0]).
2020 -export([listen/1]).
21 -export([listen_options/0]).
21 -export([disallowed_listen_options/0]).
2222 -export([accept/2]).
2323 -export([accept_ack/2]).
2424 -export([connect/3]).
4646 | inet
4747 | inet6
4848 | {ip, inet:ip_address()}
49 | {ipv6_v6only, boolean()}
4950 | {keepalive, boolean()}
5051 | {linger, {boolean(), non_neg_integer()}}
5152 | {low_msgq_watermark, non_neg_integer()}
8182 %% We set the port to 0 because it is given in the Opts directly.
8283 %% The port in the options takes precedence over the one in the
8384 %% first argument.
84 gen_tcp:listen(0, ranch:filter_options(Opts5, listen_options(),
85 gen_tcp:listen(0, ranch:filter_options(Opts5, disallowed_listen_options(),
8586 [binary, {active, false}, {packet, raw}, {reuseaddr, true}])).
8687
87 %% 'inet' and 'inet6' are also allowed but they are handled
88 %% 'binary' and 'list' are disallowed but they are handled
8889 %% specifically as they do not have 2-tuple equivalents.
89 %%
90 %% The 4-tuple 'raw' option is also handled specifically.
91 listen_options() ->
92 [backlog, buffer, delay_send, dontroute, exit_on_close, fd,
93 high_msgq_watermark, high_watermark, ip,
94 keepalive, linger, low_msgq_watermark,
95 low_watermark, nodelay, port, priority, recbuf,
96 send_timeout, send_timeout_close, sndbuf, tos].
90 disallowed_listen_options() ->
91 [active, header, mode, packet, packet_size, line_delimiter, reuseaddr].
9792
9893 -spec accept(inet:socket(), timeout())
9994 -> {ok, inet:socket()} | {error, closed | timeout | atom()}.
0 %% Copyright (c) 2012-2015, Loïc Hoguin <essen@ninenines.eu>
0 %% Copyright (c) 2012-2016, Loïc Hoguin <essen@ninenines.eu>
11 %%
22 %% Permission to use, copy, modify, and/or distribute this software for any
33 %% purpose with or without fee is hereby granted, provided that the above
99
1010 DEPS += cowboy
1111 dep_cowboy = git https://github.com/ninenines/cowboy.git 1.0.3
12 COMPILE_FIRST +=
1312
1413
1514 rebar_dep: preprocess pre-deps deps pre-app app
2019
2120 pre-app::
2221
23 include ../../erlang.mk
24
25 ERLC_OPTS += $(SOCKJS_ERLC_OPTS)
22 include ../../erlang.mk
+0
-56
deps/sockjs/examples/cowboy_echo.erl less more
0 #!/usr/bin/env escript
1 %%! -smp disable +A1 +K true -pa ebin -env ERL_LIBS deps -input
2 -module(cowboy_echo).
3 -mode(compile).
4
5 -export([main/1]).
6
7 %% Cowboy callbacks
8 -export([init/3, handle/2, terminate/3]).
9
10
11 main(_) ->
12 Port = 8081,
13 ok = application:start(xmerl),
14 ok = application:start(sockjs),
15 ok = application:start(ranch),
16 ok = application:start(crypto),
17 ok = application:start(cowlib),
18 ok = application:start(cowboy),
19
20 SockjsState = sockjs_handler:init_state(
21 <<"/echo">>, fun service_echo/3, state, []),
22
23 VhostRoutes = [{<<"/echo/[...]">>, sockjs_cowboy_handler, SockjsState},
24 {'_', ?MODULE, []}],
25 Routes = [{'_', VhostRoutes}], % any vhost
26 Dispatch = cowboy_router:compile(Routes),
27
28 io:format(" [*] Running at http://localhost:~p~n", [Port]),
29 cowboy:start_http(cowboy_echo_http_listener, 100,
30 [{port, Port}],
31 [{env, [{dispatch, Dispatch}]}]),
32 receive
33 _ -> ok
34 end.
35
36 %% --------------------------------------------------------------------------
37
38 init({_Any, http}, Req, []) ->
39 {ok, Req, []}.
40
41 handle(Req, State) ->
42 {ok, Data} = file:read_file("./examples/echo.html"),
43 {ok, Req1} = cowboy_req:reply(200, [{<<"Content-Type">>, "text/html"}],
44 Data, Req),
45 {ok, Req1, State}.
46
47 terminate(_Reason, _Req, _State) ->
48 ok.
49
50 %% --------------------------------------------------------------------------
51
52 service_echo(_Conn, init, state) -> {ok, state};
53 service_echo(Conn, {recv, Data}, state) -> sockjs:send(Data, Conn);
54 service_echo(_Conn, {info, _Info}, state) -> {ok, state};
55 service_echo(_Conn, closed, state) -> {ok, state}.
+0
-86
deps/sockjs/examples/cowboy_echo_authen_callback.erl less more
0 #!/usr/bin/env escript
1 %%! -smp disable +A1 +K true -pa ebin -env ERL_LIBS deps -input
2 -module(cowboy_echo).
3 -mode(compile).
4
5 -export([main/1]).
6
7 %% Cowboy callbacks
8 -export([init/3, handle/2, terminate/3]).
9
10
11 main(_) ->
12 Port = 8081,
13 ok = application:start(xmerl),
14 ok = application:start(sockjs),
15 ok = application:start(ranch),
16 ok = application:start(crypto),
17 ok = application:start(cowlib),
18 ok = application:start(cowboy),
19
20 SockjsState = sockjs_handler:init_state(
21 <<"/echo">>, fun service_echo/3, [], []),
22
23 VhostRoutes = [{<<"/echo/[...]">>, sockjs_cowboy_handler, SockjsState},
24 {'_', ?MODULE, []}],
25 Routes = [{'_', VhostRoutes}], % any vhost
26 Dispatch = cowboy_router:compile(Routes),
27
28 io:format(" [*] Running at http://localhost:~p~n", [Port]),
29 cowboy:start_http(cowboy_echo_http_listener, 100,
30 [{port, Port}],
31 [{env, [{dispatch, Dispatch}]}]),
32 receive
33 _ -> ok
34 end.
35
36 %% --------------------------------------------------------------------------
37
38 init({_Any, http}, Req, []) ->
39 {ok, Req, []}.
40
41 handle(Req, State) ->
42 {ok, Data} = file:read_file("./examples/echo_authen_callback.html"),
43 {ok, Req1} = cowboy_req:reply(200, [{<<"Content-Type">>, "text/html"}],
44 Data, Req),
45 {ok, Req1, State}.
46
47 terminate(_Reason, _Req, _State) ->
48 ok.
49
50 %% --------------------------------------------------------------------------
51
52 authen(Conn, init, State) ->
53 {ok, TRef} = timer:apply_after(5000, sockjs, close, [Conn]),
54 {ok, [TRef | State]};
55 authen(Conn, {recv, Data}, [TRef | State] = State1) ->
56 case Data of
57 <<"auth">> ->
58 sockjs:send(<<"Authenticate successfully!">>, Conn),
59 timer:cancel(TRef),
60 {success, [{user_id, element(3, erlang:now())} | State]};
61 _Else ->
62 {ok, State1}
63 end;
64 authen(_Conn, closed, [TRef | State]) ->
65 timer:cancel(TRef),
66 {ok, State}.
67
68 service_echo(Conn, init, State) ->
69 authen(Conn, init, State);
70 service_echo(Conn, {recv, Data}, State) ->
71 case lists:keyfind(user_id, 1, State) of
72 {user_id, UserId} ->
73 sockjs:send([Data, " from ", erlang:integer_to_binary(UserId)], Conn);
74 false ->
75 case authen(Conn, {recv, Data}, State) of
76 {success, State1} ->
77 {ok, State1};
78 Else ->
79 Else
80 end
81 end;
82 service_echo(_Conn, {info, _Info}, State) ->
83 {ok, State};
84 service_echo(Conn, closed, State) ->
85 authen(Conn, closed, State).
+0
-105
deps/sockjs/examples/cowboy_test_server.erl less more
0 #!/usr/bin/env escript
1 %%! -smp disable +A1 +K true -pa ebin -env ERL_LIBS deps -input
2 -module(cowboy_test_server).
3 -mode(compile).
4
5 -export([main/1]).
6
7 %% Cowboy callbacks
8 -export([init/3, handle/2, terminate/2]).
9
10
11 main(_) ->
12 Port = 8081,
13 ok = application:start(xmerl),
14 ok = application:start(sockjs),
15 ok = application:start(ranch),
16 ok = application:start(crypto),
17 ok = application:start(cowlib),
18 ok = application:start(cowboy),
19
20 StateEcho = sockjs_handler:init_state(
21 <<"/echo">>, fun service_echo/3, state,
22 [{response_limit, 4096}]),
23 StateClose = sockjs_handler:init_state(
24 <<"/close">>, fun service_close/3, state, []),
25 StateAmplify = sockjs_handler:init_state(
26 <<"/amplify">>, fun service_amplify/3, state, []),
27 StateBroadcast = sockjs_handler:init_state(
28 <<"/broadcast">>, fun service_broadcast/3, state, []),
29 StateDWSEcho = sockjs_handler:init_state(
30 <<"/disabled_websocket_echo">>, fun service_echo/3, state,
31 [{websocket, false}]),
32 StateCNEcho = sockjs_handler:init_state(
33 <<"/cookie_needed_echo">>, fun service_echo/3, state,
34 [{cookie_needed, true}]),
35
36 VRoutes = [{<<"/echo/[...]">>, sockjs_cowboy_handler, StateEcho},
37 {<<"/close/[...]">>, sockjs_cowboy_handler, StateClose},
38 {<<"/amplify/[...]">>, sockjs_cowboy_handler, StateAmplify},
39 {<<"/broadcast/[...]">>, sockjs_cowboy_handler, StateBroadcast},
40 {<<"/disabled_websocket_echo/[...]">>, sockjs_cowboy_handler,
41 StateDWSEcho},
42 {<<"/cookie_needed_echo/[...]">>, sockjs_cowboy_handler,
43 StateCNEcho},
44 {'_', ?MODULE, []}],
45 Routes = [{'_', VRoutes}], % any vhost
46 Dispatch = cowboy_router:compile(Routes),
47
48 io:format(" [*] Running at http://localhost:~p~n", [Port]),
49
50 cowboy:start_http(cowboy_test_server_http_listener, 100,
51 [{port, Port}],
52 [{env, [{dispatch, Dispatch}]}]),
53 receive
54 _ -> ok
55 end.
56
57 %% --------------------------------------------------------------------------
58
59 init({_Any, http}, Req, []) ->
60 {ok, Req, []}.
61
62 handle(Req, State) ->
63 {ok, Req2} = cowboy_req:reply(404, [],
64 <<"404 - Nothing here (via sockjs-erlang fallback)\n">>, Req),
65 {ok, Req2, State}.
66
67 terminate(_Req, _State) ->
68 ok.
69
70 %% --------------------------------------------------------------------------
71
72 service_echo(_Conn, init, state) -> {ok, state};
73 service_echo(Conn, {recv, Data}, state) -> Conn:send(Data);
74 service_echo(_Conn, closed, state) -> {ok, state}.
75
76 service_close(Conn, _, _State) ->
77 Conn:close(3000, "Go away!").
78
79 service_amplify(Conn, {recv, Data}, _State) ->
80 N0 = list_to_integer(binary_to_list(Data)),
81 N = if N0 > 0 andalso N0 < 19 -> N0;
82 true -> 1
83 end,
84 Conn:send(list_to_binary(
85 string:copies("x", round(math:pow(2, N)))));
86 service_amplify(_Conn, _, _State) ->
87 ok.
88
89 service_broadcast(Conn, init, _State) ->
90 case ets:info(broadcast_table, memory) of
91 undefined ->
92 ets:new(broadcast_table, [public, named_table]);
93 _Any ->
94 ok
95 end,
96 true = ets:insert(broadcast_table, {Conn}),
97 ok;
98 service_broadcast(Conn, closed, _State) ->
99 true = ets:delete_object(broadcast_table, {Conn}),
100 ok;
101 service_broadcast(_Conn, {recv, Data}, _State) ->
102 ets:foldl(fun({Conn1}, _Acc) -> Conn1:send(Data) end,
103 [], broadcast_table),
104 ok.
+0
-72
deps/sockjs/examples/echo.html less more
0 <!DOCTYPE html>
1 <html><head>
2 <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js">
3 </script>
4 <script src="http://cdn.sockjs.org/sockjs-0.3.4.min.js">
5 </script>
6 <style>
7 .box {
8 border: 1px dashed black;
9 border-radius: 4px;
10 -moz-border-radius: 4px;
11 width: 400px;
12 display: block;
13 height: 300px;
14 float: left;
15 }
16 #output {
17 border-color: grey;
18 overflow:auto;
19 }
20 #input {
21 vertical-align: text-top;
22 -moz-outline-style: none;
23 outline-style: none;
24 outline-width: 0px;
25 outline-color: -moz-use-text-color;
26 }
27 body {
28 background-color: #F0F0F0;
29 }
30 </style>
31 </head><body lang="en">
32 <h2>SockJS-erlang Echo example</h2>
33 <form id="form">
34 <input id="input" autocomplete="off" class="box"
35 placeholder="type something here" />
36 </form>
37 <div id="output" class="box"></div>
38 <script>
39 function log(m) {
40 $('#output').append($("<code>").text(m));
41 $('#output').append($("<br>"));
42 $('#output').scrollTop($('#output').scrollTop()+10000);
43 }
44
45 var sockjs_url = '/echo';
46 var sockjs = new SockJS(sockjs_url);
47 sockjs.onopen = function() {
48 log(' [*] Connected (using: '+sockjs.protocol+')');
49 };
50 sockjs.onclose = function(e) {
51 log(' [*] Disconnected ('+e.status + ' ' + e.reason+ ')');
52 };
53 sockjs.onmessage = function(e) {
54 log(' [ ] received: ' + JSON.stringify(e.data));
55 };
56
57 $('#input').focus();
58 $('#form').submit(function() {
59 var val = $('#input').val();
60 $('#input').val('');
61 var l = ' [ ] sending: ' + JSON.stringify(val);
62 if (sockjs.readyState !== SockJS.OPEN) {
63 l += ' (error, connection not established)';
64 } else {
65 sockjs.send(val);
66 }
67 log(l);
68 return false;
69 });
70 </script>
71 </body></html>
+0
-72
deps/sockjs/examples/echo_authen_callback.html less more
0 <!DOCTYPE html>
1 <html><head>
2 <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js">
3 </script>
4 <script src="http://cdn.sockjs.org/sockjs-0.3.4.min.js">
5 </script>
6 <style>
7 .box {
8 border: 1px dashed black;
9 border-radius: 4px;
10 -moz-border-radius: 4px;
11 width: 400px;
12 display: block;
13 height: 300px;
14 float: left;
15 }
16 #output {
17 border-color: grey;
18 overflow:auto;
19 }
20 #input {
21 vertical-align: text-top;
22 -moz-outline-style: none;
23 outline-style: none;
24 outline-width: 0px;
25 outline-color: -moz-use-text-color;
26 }
27 body {
28 background-color: #F0F0F0;
29 }
30 </style>
31 </head><body lang="en">
32 <h2>SockJS-erlang Echo example</h2>
33 <form id="form">
34 <input id="input" autocomplete="off" class="box"
35 placeholder="type something here" />
36 </form>
37 <div id="output" class="box"></div>
38 <script>
39 function log(m) {
40 $('#output').append($("<code>").text(m));
41 $('#output').append($("<br>"));
42 $('#output').scrollTop($('#output').scrollTop()+10000);
43 }
44
45 var sockjs_url = '/echo';
46 var sockjs = new SockJS(sockjs_url);
47 sockjs.onopen = function() {
48 log(' [*] Connected (using: '+sockjs.protocol+')');
49 };
50 sockjs.onclose = function(e) {
51 log(' [*] Disconnected ('+e.status + ' ' + e.reason+ ')');
52 };
53 sockjs.onmessage = function(e) {
54 log(' [ ] received: ' + JSON.stringify(e.data));
55 };
56
57 $('#input').focus();
58 $('#form').submit(function() {
59 var val = $('#input').val();
60 $('#input').val('');
61 var l = ' [ ] sending: ' + JSON.stringify(val);
62 if (sockjs.readyState !== SockJS.OPEN) {
63 l += ' (error, connection not established)';
64 } else {
65 sockjs.send(val);
66 }
67 log(l);
68 return false;
69 });
70 </script>
71 </body></html>
+0
-87
deps/sockjs/examples/multiplex/cowboy_multiplex.erl less more
0 #!/usr/bin/env escript
1 %%! -smp disable +A1 +K true -pa ebin -env ERL_LIBS deps -input
2 -module(cowboy_multiplex).
3 -mode(compile).
4
5 -export([main/1]).
6
7 %% Cowboy callbacks
8 -export([init/3, handle/2, terminate/3]).
9
10 main(_) ->
11 Port = 8081,
12 ok = application:start(xmerl),
13 ok = application:start(sockjs),
14 ok = application:start(ranch),
15 ok = application:start(crypto),
16 ok = application:start(cowlib),
17 ok = application:start(cowboy),
18
19 MultiplexState = sockjs_multiplex:init_state(
20 [{"ann", fun service_ann/3, []},
21 {"bob", fun service_bob/3, []},
22 {"carl", fun service_carl/3, []}]),
23
24 SockjsState = sockjs_handler:init_state(
25 <<"/multiplex">>, sockjs_multiplex, MultiplexState, []),
26
27 VhostRoutes = [{<<"/multiplex/[...]">>, sockjs_cowboy_handler, SockjsState},
28 {'_', ?MODULE, []}],
29 Routes = [{'_', VhostRoutes}], % any vhost
30 Dispatch = cowboy_router:compile(Routes),
31
32 io:format(" [*] Running at http://localhost:~p~n", [Port]),
33 cowboy:start_http(http, 100,
34 [{port, Port}],
35 [{env, [{dispatch, Dispatch}]}]),
36 receive
37 _ -> ok
38 end.
39
40 %% --------------------------------------------------------------------------
41
42 init({_Any, http}, Req, []) ->
43 {ok, Req, []}.
44
45 handle(Req, State) ->
46 {Path, Req1} = cowboy_req:path(Req),
47 {ok, Req2} = case Path of
48 <<"/">> ->
49 {ok, Data} = file:read_file("./examples/multiplex/index.html"),
50 cowboy_req:reply(200, [{<<"Content-Type">>, "text/html"}],
51 Data, Req1);
52 _ ->
53 cowboy_req:reply(404, [],
54 <<"404 - Nothing here\n">>, Req1)
55 end,
56 {ok, Req2, State}.
57
58 terminate(_Reason, _Req, _State) ->
59 ok.
60
61 %% --------------------------------------------------------------------------
62
63 service_ann(Conn, init, State) ->
64 sockjs:send("Ann says hi!", Conn),
65 {ok, State};
66 service_ann(Conn, {recv, Data}, State) ->
67 sockjs:send(["Ann nods: ", Data], Conn),
68 {ok, State};
69 service_ann(_Conn, closed, State) ->
70 {ok, State}.
71
72 service_bob(Conn, init, State) ->
73 sockjs:send("Bob doesn't agree.", Conn),
74 {ok, State};
75 service_bob(Conn, {recv, Data}, State) ->
76 sockjs:send(["Bob says no to: ", Data], Conn),
77 {ok, State};
78 service_bob(_Conn, closed, State) ->
79 {ok, State}.
80
81 service_carl(Conn, init, State) ->
82 sockjs:send("Carl says goodbye!", Conn),
83 sockjs:close(Conn),
84 {ok, State};
85 service_carl(_Conn, _, State) ->
86 {ok, State}.
+0
-107
deps/sockjs/examples/multiplex/cowboy_multiplex_authen_callback.erl less more
0 #!/usr/bin/env escript
1 %%! -smp disable +A1 +K true -pa ebin -env ERL_LIBS deps -input
2 -module(cowboy_multiplex).
3 -mode(compile).
4
5 -export([main/1]).
6
7 %% Cowboy callbacks
8 -export([init/3, handle/2, terminate/3]).
9
10 main(_) ->
11 Port = 8081,
12 ok = application:start(xmerl),
13 ok = application:start(sockjs),
14 ok = application:start(ranch),
15 ok = application:start(crypto),
16 ok = application:start(cowlib),
17 ok = application:start(cowboy),
18
19 MultiplexState = sockjs_multiplex:init_state(
20 [{"ann", fun service_ann/3, []},
21 {"bob", fun service_bob/3, []},
22 {"carl", fun service_carl/3, []}],
23 {fun authen/3, [{state, []}]}),
24
25 SockjsState = sockjs_handler:init_state(
26 <<"/multiplex">>, sockjs_multiplex, MultiplexState, []),
27
28 VhostRoutes = [{<<"/multiplex/[...]">>, sockjs_cowboy_handler, SockjsState},
29 {'_', ?MODULE, []}],
30 Routes = [{'_', VhostRoutes}], % any vhost
31 Dispatch = cowboy_router:compile(Routes),
32
33 io:format(" [*] Running at http://localhost:~p~n", [Port]),
34 cowboy:start_http(http, 100,
35 [{port, Port}],
36 [{env, [{dispatch, Dispatch}]}]),
37 receive
38 _ -> ok
39 end.
40
41 %% --------------------------------------------------------------------------
42
43 init({_Any, http}, Req, []) ->
44 {ok, Req, []}.
45
46 handle(Req, State) ->
47 {Path, Req1} = cowboy_req:path(Req),
48 {ok, Req2} = case Path of
49 <<"/">> ->
50 {ok, Data} = file:read_file("./examples/multiplex/index_authen_callback.html"),
51 cowboy_req:reply(200, [{<<"Content-Type">>, "text/html"}],
52 Data, Req1);
53 _ ->
54 cowboy_req:reply(404, [],
55 <<"404 - Nothing here\n">>, Req1)
56 end,
57 {ok, Req2, State}.
58
59 terminate(_Reason, _Req, _State) ->
60 ok.
61
62 %% --------------------------------------------------------------------------
63
64 authen(Conn, init, Extra) ->
65 {ok, TRef} = timer:apply_after(5000, sockjs, close, [Conn]),
66 {ok, [TRef | Extra]};
67 authen(Conn, {recv, Data}, [TRef | Extra] = State) ->
68 case Data of
69 <<"auth">> ->
70 sockjs:send(<<"Authenticate successfully!">>, Conn),
71 timer:cancel(TRef),
72 {success, [{user_id, element(3, erlang:now())} | Extra]};
73 _Else ->
74 {ok, State}
75 end;
76 authen(_Conn, closed, [TRef | Extra]) ->
77 timer:cancel(TRef),
78 {ok, Extra}.
79
80 service_ann(Conn, init, State) ->
81 sockjs:send("Ann says hi!", Conn),
82 {ok, State};
83 service_ann(Conn, {recv, Data}, State) ->
84 {user_id, UserId} = lists:keyfind(user_id, 1, State),
85 sockjs:send(["Ann nods: ", Data, " from ", erlang:integer_to_binary(UserId)], Conn),
86 {ok, State};
87 service_ann(_Conn, closed, State) ->
88 {ok, State}.
89
90 service_bob(Conn, init, State) ->
91 sockjs:send("Bob doesn't agree.", Conn),
92 {ok, State};
93 service_bob(Conn, {recv, Data}, State) ->
94 {user_id, UserId} = lists:keyfind(user_id, 1, State),
95 sockjs:send(["Bob says no to: ", Data, " from ", erlang:integer_to_binary(UserId)],
96 Conn),
97 {ok, State};
98 service_bob(_Conn, closed, State) ->
99 {ok, State}.
100
101 service_carl(Conn, init, State) ->
102 sockjs:send("Carl says goodbye!", Conn),
103 sockjs:close(Conn),
104 {ok, State};
105 service_carl(_Conn, _, State) ->
106 {ok, State}.
+0
-96
deps/sockjs/examples/multiplex/index.html less more
0 <!doctype html>
1 <html><head>
2 <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
3 <script src="http://cdn.sockjs.org/sockjs-0.3.min.js"></script>
4 <script src="http://d1fxtkz8shb9d2.cloudfront.net/websocket-multiplex-0.1.js"></script>
5 <style>
6 .box {
7 width: 300px;
8 float: left;
9 margin: 0 20px 0 20px;
10 }
11 .box div, .box input {
12 border: 1px solid;
13 -moz-border-radius: 4px;
14 border-radius: 4px;
15 width: 100%;
16 padding: 0px;
17 margin: 5px;
18 }
19 .box div {
20 border-color: grey;
21 height: 300px;
22 overflow: auto;
23 }
24 .box input {
25 height: 30px;
26 }
27 h1 {
28 margin-left: 75px;
29 }
30 body {
31 background-color: #F0F0F0;
32 font-family: "Arial";
33 }
34 </style>
35 <head><body lang="en">
36 <h1>SockJS Multiplex example</h1>
37
38 <div id="first" class="box">
39 <div></div>
40 <form><input autocomplete="off" value="Type here..."></input></form>
41 </div>
42
43 <div id="second" class="box">
44 <div></div>
45 <form><input autocomplete="off"></input></form>
46 </div>
47
48 <div id="third" class="box">
49 <div></div>
50 <form><input autocomplete="off"></input></form>
51 </div>
52
53 <script>
54 // Pipe - convenience wrapper to present data received from an
55 // object supporting WebSocket API in an html element. And the other
56 // direction: data typed into an input box shall be sent back.
57 var pipe = function(ws, el_name) {
58 var div = $(el_name + ' div');
59 var inp = $(el_name + ' input');
60 var form = $(el_name + ' form');
61
62 var print = function(m, p) {
63 p = (p === undefined) ? '' : JSON.stringify(p);
64 div.append($("<code>").text(m + ' ' + p));
65 div.append($("<br>"));
66 div.scrollTop(div.scrollTop() + 10000);
67 };
68
69 ws.onopen = function() {print('[*] open', ws.protocol);};
70 ws.onmessage = function(e) {print('[.] message', e.data);};
71 ws.onclose = function() {print('[*] close');};
72
73 form.submit(function() {
74 print('[ ] sending', inp.val());
75 ws.send(inp.val());
76 inp.val('');
77 return false;
78 });
79 };
80
81 var sockjs_url = '/multiplex';
82 var sockjs = new SockJS(sockjs_url);
83
84 var multiplexer = new WebSocketMultiplex(sockjs);
85 var ann = multiplexer.channel('ann');
86 var bob = multiplexer.channel('bob');
87 var carl = multiplexer.channel('carl');
88
89 pipe(ann, '#first');
90 pipe(bob, '#second');
91 pipe(carl, '#third');
92
93 $('#first input').focus();
94 </script>
95 </body></html>
+0
-109
deps/sockjs/examples/multiplex/index_authen_callback.html less more
0 <!doctype html>
1 <html><head>
2 <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
3 <script src="http://cdn.sockjs.org/sockjs-0.3.min.js"></script>
4 <script src="http://d1fxtkz8shb9d2.cloudfront.net/websocket-multiplex-0.1.js"></script>
5 <style>
6 .box {
7 width: 300px;
8 float: left;
9 margin: 0 20px 0 20px;
10 }
11 .box div, .box input {
12 border: 1px solid;
13 -moz-border-radius: 4px;
14 border-radius: 4px;
15 width: 100%;
16 padding: 0px;
17 margin: 5px;
18 }
19 .box div {
20 border-color: grey;
21 height: 300px;
22 overflow: auto;
23 }
24 .box input {
25 height: 30px;
26 }
27 h1 {
28 margin-left: 75px;
29 }
30 body {
31 background-color: #F0F0F0;
32 font-family: "Arial";
33 }
34 </style>
35 <head><body lang="en">
36 <h1>SockJS Multiplex example</h1>
37
38 <div id="main" class="box">
39 <div></div>
40 <form><input autocomplete="off"></input></form>
41 </div>
42
43 <div id="first" class="box">
44 <div></div>
45 <form><input autocomplete="off" value="Type here..."></input></form>
46 </div>
47
48 <div id="second" class="box">
49 <div></div>
50 <form><input autocomplete="off"></input></form>
51 </div>
52
53 <div id="third" class="box">
54 <div></div>
55 <form><input autocomplete="off"></input></form>
56 </div>
57
58 <script>
59 // Pipe - convenience wrapper to present data received from an
60 // object supporting WebSocket API in an html element. And the other
61 // direction: data typed into an input box shall be sent back.
62 var pipe = function(ws, el_name) {
63 var div = $(el_name + ' div');
64 var inp = $(el_name + ' input');
65 var form = $(el_name + ' form');
66
67 var print = function(m, p) {
68 p = (p === undefined) ? '' : JSON.stringify(p);
69 div.append($("<code>").text(m + ' ' + p));
70 div.append($("<br>"));
71 div.scrollTop(div.scrollTop() + 10000);
72 };
73
74 ws.onopen = function() {print('[*] open', ws.protocol);};
75 ws.onmessage = function(e) {
76 if (e.data === 'Authenticate successfully!') {
77 var multiplexer = new WebSocketMultiplex(sockjs);
78 var ann = multiplexer.channel('ann');
79 var bob = multiplexer.channel('bob');
80 var carl = multiplexer.channel('carl');
81
82 pipe(ann, '#first');
83 pipe(bob, '#second');
84 pipe(carl, '#third');
85 }
86 else {
87 print('[.] message', e.data);
88 console.log(e.data);
89 }
90 };
91 ws.onclose = function() {print('[*] close');};
92
93 form.submit(function() {
94 print('[ ] sending', inp.val());
95 ws.send(inp.val());
96 inp.val('');
97 return false;
98 });
99 };
100
101 var sockjs_url = '/multiplex';
102 var sockjs = new SockJS(sockjs_url);
103
104 pipe(sockjs, '#main');
105
106 $('#first input').focus();
107 </script>
108 </body></html>
215215 lists:reverse([$\} | Acc1]).
216216
217217 json_encode_string(A, State) when is_atom(A) ->
218 L = atom_to_list(A),
219 case json_string_is_safe(L) of
220 true ->
221 [?Q, L, ?Q];
222 false ->
223 json_encode_string_unicode(xmerl_ucs:from_utf8(L), State, [?Q])
224 end;
218 B = iolist_to_binary(atom_to_list(A)),
219 json_encode_string(B, State);
225220 json_encode_string(B, State) when is_binary(B) ->
226221 case json_bin_is_safe(B) of
227222 true ->
228 [?Q, B, ?Q];
223 <<?Q, B/binary, ?Q>>;
229224 false ->
230 json_encode_string_unicode(xmerl_ucs:from_utf8(B), State, [?Q])
225 json_encode_string_unicode_bin(B, State)
231226 end;
232227 json_encode_string(I, _State) when is_integer(I) ->
233 [?Q, integer_to_list(I), ?Q];
228 B = integer_to_binary(I),
229 <<?Q, B/binary, ?Q>>;
234230 json_encode_string(L, State) when is_list(L) ->
235231 case json_string_is_safe(L) of
236232 true ->
289285 json_bin_is_safe(Rest)
290286 end.
291287
288 %% encode utf8 string with binraies
289 json_encode_string_unicode_bin(Bin, State) when is_binary(Bin) ->
290 Body = json_encode_string_unicode_bin(Bin, State, <<"">>, <<"">>),
291 <<?Q, Body/binary, ?Q>>.
292
293 json_encode_string_unicode_bin(<<"">>, State, Acc, Buff) ->
294 Buff0 = json_encode_string_unicode_bin_check(Buff, State),
295 <<Acc/binary, Buff0/binary>>;
296
297 json_encode_string_unicode_bin(<<C:8, Rest/binary>>, State, Acc, Buff) ->
298 {Acc1, Buff1} = case C of
299 $\" -> json_encode_string_unicode_bin_simple(State, Acc, Buff, $");
300 $\\ -> json_encode_string_unicode_bin_simple(State, Acc, Buff, $\\);
301 $\b -> json_encode_string_unicode_bin_simple(State, Acc, Buff, $b);
302 $\f -> json_encode_string_unicode_bin_simple(State, Acc, Buff, $f);
303 $\n -> json_encode_string_unicode_bin_simple(State, Acc, Buff, $n);
304 $\r -> json_encode_string_unicode_bin_simple(State, Acc, Buff, $r);
305 $\t -> json_encode_string_unicode_bin_simple(State, Acc, Buff, $t);
306
307 C when C >= 0, C < $\s ->
308 Buff0 = json_encode_string_unicode_bin_check(Buff, State),
309 CEsc = unihex_bin(C),
310 {<<Acc/binary, Buff0/binary, CEsc/binary>>,
311 <<"">>};
312 C when C < 2#01111111 ->
313 Buff0 = json_encode_string_unicode_bin_check(Buff, State),
314 {<<Acc/binary, Buff0/binary, C>>,
315 <<"">>};
316 C when C < 2#11000000, byte_size(Buff) >= 1 ->
317 {Acc,
318 <<Buff/binary, C>>};
319 C when C >= 2#11000000 ->
320 Buff0 = json_encode_string_unicode_bin_check(Buff, State),
321 {<<Acc/binary, Buff0/binary>>,
322 <<C>>};
323 _ -> exit({json_encode, {bad_char, [Buff]}})
324 end,
325 json_encode_string_unicode_bin(Rest, State, Acc1, Buff1).
326
327 json_encode_string_unicode_bin_simple(State, Acc, Buff, Repl) ->
328 Buff0 = json_encode_string_unicode_bin_check(Buff, State),
329 {<<Acc/binary, Buff0/binary, $\\, Repl>>,
330 <<"">>}.
331
332 json_encode_string_unicode_bin_check(Buff, _State) when byte_size(Buff) < 1 ->
333 Buff;
334 json_encode_string_unicode_bin_check(Buff, State) ->
335 UniCode = case Buff of
336 <<P0:3/integer, D0:5/integer,
337 P1:2/integer, D1:6/integer>> when P0==2#110, P1==2#10 ->
338 D0*16#40 + D1;
339 <<P0:4/integer, D0:4/integer,
340 P1:2/integer, D1:6/integer,
341 P1:2/integer, D2:6/integer>> when P0==2#1110, P1==2#10 ->
342 D0*16#1000 + D1*16#40 + D2;
343 <<P0:5/integer, D0:3/integer,
344 P1:2/integer, D1:6/integer,
345 P1:2/integer, D2:6/integer,
346 P1:2/integer, D3:6/integer>> when P0==2#11110, P1==2#10 ->
347 D0*16#040000 + D1*16#1000 + D2*16#40 + D3;
348 _ -> exit({json_encode, {bad_unicode_char, [Buff]}})
349 end,
350 case State#encoder.utf8 of
351 false -> unihex_bin(UniCode);
352 true -> Buff
353 end.
354
355
356 unihex_bin(C) when C < 16#10000 ->
357 Digits = << <<begin hexdigit(D) end>> || <<D:4>> <= <<C:16>> >>,
358 <<$\\, $u, Digits/binary>>;
359 unihex_bin(C) when C =< 16#10FFFF ->
360 N = C - 16#10000,
361 S1 = unihex_bin(16#d800 bor ((N bsr 10) band 16#3ff)),
362 S2 = unihex_bin(16#dc00 bor (N band 16#3ff)),
363 <<S1/binary, S2/binary>>.
364
365 %
292366 json_encode_string_unicode([], _State, Acc) ->
293367 lists:reverse([$\" | Acc]);
294368 json_encode_string_unicode([C | Cs], State, Acc) ->
728802 %% test utf8 encoding
729803 encoder_utf8_test() ->
730804 %% safe conversion case (default)
731 [34,"\\u0001","\\u0442","\\u0435","\\u0441","\\u0442",34] =
732 encode(<<1,"\321\202\320\265\321\201\321\202">>),
805 Ans1 = iolist_to_binary([34,"\\u0001","\\u0442","\\u0435","\\u0441","\\u0442",34]),
806 Ans1 = encode(<<1,"\321\202\320\265\321\201\321\202">>),
733807
734808 %% raw utf8 output (optional)
735 Enc = mochijson2:encoder([{utf8, true}]),
736 [34,"\\u0001",[209,130],[208,181],[209,129],[209,130],34] =
737 Enc(<<1,"\321\202\320\265\321\201\321\202">>).
809 Enc = mochijson2_fork:encoder([{utf8, true}]),
810 Ans2 = iolist_to_binary([34,"\\u0001",[209,130],[208,181],[209,129],[209,130],34]),
811 Ans2 = Enc(<<1,"\321\202\320\265\321\201\321\202">>).
738812
739813 input_validation_test() ->
740814 Good = [
3232 websocket_init(_TransportName, Req,
3333 Service = #service{logger = Logger,
3434 subproto_pref = SubProtocolPref}) ->
35 Req3 = case cowboy_req:header(<<"Sec-Websocket-Protocol">>, Req) of
35 Req3 = case cowboy_req:header(<<"sec-websocket-protocol">>, Req) of
3636 {undefined, Req1} ->
3737 Req1;
3838 {SubProtocols, Req1} ->
3939 SelectedSubProtocol =
4040 choose_subprotocol_bin(SubProtocols, SubProtocolPref),
41 {ok, Req2} = cowboy_req:set_resp_header(
42 <<"Sec-Websocket-Protocol">>,
43 SelectedSubProtocol, Req1),
44 Req2
41 cowboy_req:set_resp_header(
42 <<"sec-websocket-protocol">>,
43 SelectedSubProtocol, Req1)
4544 end,
4645
4746 Req4 = Logger(Service, {cowboy, Req3}, websocket),
1212 cache_for(Req, Headers) ->
1313 Expires = calendar:gregorian_seconds_to_datetime(
1414 calendar:datetime_to_gregorian_seconds(
15 calendar:now_to_datetime(now())) + ?YEAR),
15 calendar:now_to_datetime(os:timestamp())) + ?YEAR),
1616 H = [{"Cache-Control", "public, max-age=" ++ integer_to_list(?YEAR)},
1717 {"Expires", httpd_util:rfc1123_date(Expires)}],
1818 {H ++ Headers, Req}.
2222 method_atom(<<"DELETE">>) -> 'DELETE';
2323 method_atom(<<"OPTIONS">>) -> 'OPTIONS';
2424 method_atom(<<"PATCH">>) -> 'PATCH';
25 method_atom(<<"HEAD">>) -> 'HEAD';
26 method_atom('GET') -> 'GET';
27 method_atom('PUT') -> 'PUT';
28 method_atom('POST') -> 'POST';
29 method_atom('DELETE') -> 'DELETE';
30 method_atom('OPTIONS') -> 'OPTIONS';
31 method_atom('PATCH') -> 'PATCH';
32 method_atom('HEAD') -> 'HEAD'.
25 method_atom(<<"HEAD">>) -> 'HEAD'.
3326
3427 -spec body(req()) -> {binary(), req()}.
3528 body({cowboy, Req}) -> {ok, Body, Req1} = cowboy_req:body(Req),
5548 end.
5649
5750 -spec header(atom(), req()) -> {nonempty_string() | undefined, req()}.
58 header(K, {cowboy, Req})->
59 {H, Req2} = cowboy_req:header(K, Req),
60 {V, Req3} = case H of
61 undefined ->
62 cowboy_req:header(atom_to_binary(K, utf8), Req2);
63 _ -> {H, Req2}
64 end,
51 header(K, {cowboy, Req0})->
52 {V, Req} = cowboy_req:header(atom_to_binary(K, latin1), Req0),
6553 case V of
66 undefined -> {undefined, {cowboy, Req3}};
67 _ -> {binary_to_list(V), {cowboy, Req3}}
54 undefined -> {undefined, {cowboy, Req}};
55 _ -> {binary_to_list(V), {cowboy, Req}}
6856 end.
6957
7058 -spec jsessionid(req()) -> {nonempty_string() | undefined, req()}.
104104 orddict:store(Topic, emit(init, Channel), Channels);
105105 {"uns", true} ->
106106 Channel = orddict:fetch(Topic, Channels),
107 emit(closed, Channel),
107 _ = emit(closed, Channel),
108108 orddict:erase(Topic, Channels);
109109 {"msg", true} ->
110110 Channel = orddict:fetch(Topic, Channels),
128128
129129 split(Char, Str, Limit) when Limit > 0 ->
130130 Acc = split(Char, Str, Limit, []),
131 lists:reverse(Acc);
132 split(_Char, Str, 0) ->
133 [Str].
131 lists:reverse(Acc).
134132
135133 split(_Char, Str, 1, Acc) ->
136134 [Str | Acc];
1616
1717 -export_type([conn/0]).
1818
19 -ifdef(pre17_type_specs).
19 -ifdef(use_old_builtin_types).
2020 -define(QUEUE_TYPE, queue()).
2121 -else.
2222 -define(QUEUE_TYPE, queue:queue()).
2424
2525 -record(session, {id :: session(),
2626 outbound_queue = queue:new() :: ?QUEUE_TYPE,
27 response_pid :: pid(),
28 disconnect_tref :: reference(),
27 response_pid :: pid() | undefined,
28 disconnect_tref :: reference() | undefined,
2929 disconnect_delay = 5000 :: non_neg_integer(),
30 heartbeat_tref :: reference() | triggered,
30 heartbeat_tref :: reference() | triggered | undefined,
3131 heartbeat_delay = 25000 :: non_neg_integer(),
3232 ready_state = connecting :: connecting | open | closed,
33 close_msg :: {non_neg_integer(), string()},
33 close_msg :: {non_neg_integer(), string()} | undefined,
3434 callback,
3535 state,
3636 handle :: handle()
1111 rand32() ->
1212 case get(random_seeded) of
1313 undefined ->
14 {MegaSecs, Secs, MicroSecs} = now(),
14 {MegaSecs, Secs, MicroSecs} = os:timestamp(),
1515 _ = random:seed(MegaSecs, Secs, MicroSecs),
1616 put(random_seeded, true);
1717 _Else ->
+0
-6
deps/webmachine/Emakefile less more
0 % -*- mode: erlang -*-
1 {["src/*"],
2 [{i, "include"},
3 {outdir, "ebin"},
4 debug_info]
5 }.
+0
-178
deps/webmachine/LICENSE less more
0
1 Apache License
2 Version 2.0, January 2004
3 http://www.apache.org/licenses/
4
5 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
7 1. Definitions.
8
9 "License" shall mean the terms and conditions for use, reproduction,
10 and distribution as defined by Sections 1 through 9 of this document.
11
12 "Licensor" shall mean the copyright owner or entity authorized by
13 the copyright owner that is granting the License.
14
15 "Legal Entity" shall mean the union of the acting entity and all
16 other entities that control, are controlled by, or are under common
17 control with that entity. For the purposes of this definition,
18 "control" means (i) the power, direct or indirect, to cause the
19 direction or management of such entity, whether by contract or
20 otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 outstanding shares, or (iii) beneficial ownership of such entity.
22
23 "You" (or "Your") shall mean an individual or Legal Entity
24 exercising permissions granted by this License.
25
26 "Source" form shall mean the preferred form for making modifications,
27 including but not limited to software source code, documentation
28 source, and configuration files.
29
30 "Object" form shall mean any form resulting from mechanical
31 transformation or translation of a Source form, including but
32 not limited to compiled object code, generated documentation,
33 and conversions to other media types.
34
35 "Work" shall mean the work of authorship, whether in Source or
36 Object form, made available under the License, as indicated by a
37 copyright notice that is included in or attached to the work
38 (an example is provided in the Appendix below).
39
40 "Derivative Works" shall mean any work, whether in Source or Object
41 form, that is based on (or derived from) the Work and for which the
42 editorial revisions, annotations, elaborations, or other modifications
43 represent, as a whole, an original work of authorship. For the purposes
44 of this License, Derivative Works shall not include works that remain
45 separable from, or merely link (or bind by name) to the interfaces of,
46 the Work and Derivative Works thereof.
47
48 "Contribution" shall mean any work of authorship, including
49 the original version of the Work and any modifications or additions
50 to that Work or Derivative Works thereof, that is intentionally
51 submitted to Licensor for inclusion in the Work by the copyright owner
52 or by an individual or Legal Entity authorized to submit on behalf of
53 the copyright owner. For the purposes of this definition, "submitted"
54 means any form of electronic, verbal, or written communication sent
55 to the Licensor or its representatives, including but not limited to
56 communication on electronic mailing lists, source code control systems,
57 and issue tracking systems that are managed by, or on behalf of, the
58 Licensor for the purpose of discussing and improving the Work, but
59 excluding communication that is conspicuously marked or otherwise
60 designated in writing by the copyright owner as "Not a Contribution."
61
62 "Contributor" shall mean Licensor and any individual or Legal Entity
63 on behalf of whom a Contribution has been received by Licensor and
64 subsequently incorporated within the Work.
65
66 2. Grant of Copyright License. Subject to the terms and conditions of
67 this License, each Contributor hereby grants to You a perpetual,
68 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 copyright license to reproduce, prepare Derivative Works of,
70 publicly display, publicly perform, sublicense, and distribute the
71 Work and such Derivative Works in Source or Object form.
72
73 3. Grant of Patent License. Subject to the terms and conditions of
74 this License, each Contributor hereby grants to You a perpetual,
75 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 (except as stated in this section) patent license to make, have made,
77 use, offer to sell, sell, import, and otherwise transfer the Work,
78 where such license applies only to those patent claims licensable
79 by such Contributor that are necessarily infringed by their
80 Contribution(s) alone or by combination of their Contribution(s)
81 with the Work to which such Contribution(s) was submitted. If You
82 institute patent litigation against any entity (including a
83 cross-claim or counterclaim in a lawsuit) alleging that the Work
84 or a Contribution incorporated within the Work constitutes direct
85 or contributory patent infringement, then any patent licenses
86 granted to You under this License for that Work shall terminate
87 as of the date such litigation is filed.
88
89 4. Redistribution. You may reproduce and distribute copies of the
90 Work or Derivative Works thereof in any medium, with or without
91 modifications, and in Source or Object form, provided that You
92 meet the following conditions:
93
94 (a) You must give any other recipients of the Work or
95 Derivative Works a copy of this License; and
96
97 (b) You must cause any modified files to carry prominent notices
98 stating that You changed the files; and
99
100 (c) You must retain, in the Source form of any Derivative Works
101 that You distribute, all copyright, patent, trademark, and
102 attribution notices from the Source form of the Work,
103 excluding those notices that do not pertain to any part of
104 the Derivative Works; and
105
106 (d) If the Work includes a "NOTICE" text file as part of its
107 distribution, then any Derivative Works that You distribute must
108 include a readable copy of the attribution notices contained
109 within such NOTICE file, excluding those notices that do not
110 pertain to any part of the Derivative Works, in at least one
111 of the following places: within a NOTICE text file distributed
112 as part of the Derivative Works; within the Source form or
113 documentation, if provided along with the Derivative Works; or,
114 within a display generated by the Derivative Works, if and
115 wherever such third-party notices normally appear. The contents
116 of the NOTICE file are for informational purposes only and
117 do not modify the License. You may add Your own attribution
118 notices within Derivative Works that You distribute, alongside
119 or as an addendum to the NOTICE text from the Work, provided
120 that such additional attribution notices cannot be construed
121 as modifying the License.
122
123 You may add Your own copyright statement to Your modifications and
124 may provide additional or different license terms and conditions
125 for use, reproduction, or distribution of Your modifications, or
126 for any such Derivative Works as a whole, provided Your use,
127 reproduction, and distribution of the Work otherwise complies with
128 the conditions stated in this License.
129
130 5. Submission of Contributions. Unless You explicitly state otherwise,
131 any Contribution intentionally submitted for inclusion in the Work
132 by You to the Licensor shall be under the terms and conditions of
133 this License, without any additional terms or conditions.
134 Notwithstanding the above, nothing herein shall supersede or modify
135 the terms of any separate license agreement you may have executed
136 with Licensor regarding such Contributions.
137
138 6. Trademarks. This License does not grant permission to use the trade
139 names, trademarks, service marks, or product names of the Licensor,
140 except as required for reasonable and customary use in describing the
141 origin of the Work and reproducing the content of the NOTICE file.
142
143 7. Disclaimer of Warranty. Unless required by applicable law or
144 agreed to in writing, Licensor provides the Work (and each
145 Contributor provides its Contributions) on an "AS IS" BASIS,
146 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 implied, including, without limitation, any warranties or conditions
148 of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 PARTICULAR PURPOSE. You are solely responsible for determining the
150 appropriateness of using or redistributing the Work and assume any
151 risks associated with Your exercise of permissions under this License.
152
153 8. Limitation of Liability. In no event and under no legal theory,
154 whether in tort (including negligence), contract, or otherwise,
155 unless required by applicable law (such as deliberate and grossly
156 negligent acts) or agreed to in writing, shall any Contributor be
157 liable to You for damages, including any direct, indirect, special,
158 incidental, or consequential damages of any character arising as a
159 result of this License or out of the use or inability to use the
160 Work (including but not limited to damages for loss of goodwill,
161 work stoppage, computer failure or malfunction, or any and all
162 other commercial damages or losses), even if such Contributor
163 has been advised of the possibility of such damages.
164
165 9. Accepting Warranty or Additional Liability. While redistributing
166 the Work or Derivative Works thereof, You may choose to offer,
167 and charge a fee for, acceptance of support, warranty, indemnity,
168 or other liability obligations and/or rights consistent with this
169 License. However, in accepting such obligations, You may act only
170 on Your own behalf and on Your sole responsibility, not on behalf
171 of any other Contributor, and only if You agree to indemnify,
172 defend, and hold each Contributor harmless for any liability
173 incurred by, or claims asserted against, such Contributor by reason
174 of your accepting any such warranty or additional liability.
175
176 END OF TERMS AND CONDITIONS
177
+0
-25
deps/webmachine/Makefile less more
0 IGNORE_DEPS += edown eper eunit_formatters meck node_package rebar_lock_deps_plugin rebar_vsn_plugin reltool_util
1 C_SRC_DIR = /path/do/not/exist
2 C_SRC_TYPE = rebar
3 DRV_CFLAGS = -fPIC
4 export DRV_CFLAGS
5 ERLANG_ARCH = 64
6 export ERLANG_ARCH
7 ERLC_OPTS = +debug_info
8 export ERLC_OPTS
9 ERLC_OPTS += -Dold_hash=1
10
11 DEPS += mochiweb
12 dep_mochiweb = git git://github.com/rabbitmq/mochiweb 845428379ed8b58eadc49aba26838d86ea809663
13 COMPILE_FIRST +=
14
15
16 rebar_dep: preprocess pre-deps deps pre-app app
17
18 preprocess::
19
20 pre-deps::
21
22 pre-app::
23
24 include ../../erlang.mk
+0
-24
deps/webmachine/Makefile.orig.mk less more
0 ERL ?= erl
1 APP := webmachine
2
3 .PHONY: deps
4
5 all: deps
6 @(./rebar compile)
7
8 deps:
9 @(./rebar get-deps)
10
11 clean:
12 @(./rebar clean)
13
14 distclean: clean
15 @(./rebar delete-deps)
16
17 edoc:
18 @$(ERL) -noshell -run edoc_run application '$(APP)' '"."' '[{preprocess, true},{includes, ["."]}]'
19
20 test: all
21 @(./rebar skip_deps=true eunit)
22
23
+0
-66
deps/webmachine/README.org less more
0 * webmachine
1 ** Overview
2
3 [[http://travis-ci.org/basho/webmachine][Travis-CI]] :: [[https://secure.travis-ci.org/basho/webmachine.png]]
4
5 Webmachine is an application layer that adds HTTP semantic awareness
6 on top of the excellent bit-pushing and HTTP syntax-management
7 provided by mochiweb, and provides a simple and clean way to connect
8 that to your application's behavior.
9
10 More information is available [[http://webmachine.basho.com/][here]].
11
12 ** Quick Start
13 A shell script is provided in the =webmachine= repository to help
14 users quickly and easily create a new =webmachine= application.
15
16 #+BEGIN_SRC shell
17 git clone git://github.com/basho/webmachine.git
18 cd webmachine
19 ./scripts/new_webmachine.sh mydemo
20 #+END_SRC
21
22 A destination path can also be passed to the =new_webmachine.sh=
23 script.
24
25 #+BEGIN_SRC shell
26 ./scripts/new_webmachine.sh mydemo ~/webmachine_applications
27 #+END_SRC
28
29 Once a new application has been created it can be built and started.
30
31 #+BEGIN_SRC shell
32 cd mydemo
33 make
34 ./start.sh
35 #+END_SRC
36
37 The application will be available at [[http://localhost:8000]].
38
39 To learn more continue reading [[http://webmachine.basho.com/][here]].
40
41 ** Contributing
42 We encourage contributions to =webmachine= from the community.
43
44 1) Fork the =webmachine= repository on [[https://github.com/basho/webmachine][Github]].
45 2) Clone your fork or add the remote if you already have a clone of
46 the repository.
47 #+BEGIN_SRC shell
48 git clone git@github.com:yourusername/webmachine.git
49 # or
50 git remote add mine git@github.com:yourusername/webmachine.git
51 #+END_SRC
52 3) Create a topic branch for your change.
53 #+BEGIN_SRC shell
54 git checkout -b some-topic-branch
55 #+END_SRC
56 4) Make your change and commit. Use a clear and descriptive commit
57 message, spanning multiple lines if detailed explanation is
58 needed.
59 5) Push to your fork of the repository and then send a pull-request
60 through Github.
61 #+BEGIN_SRC shell
62 git push mine some-topic-branch
63 #+END_SRC
64 6) A Basho engineer or community maintainer will review your patch
65 and merge it into the main repository or send you feedback.
+0
-21
deps/webmachine/THANKS less more
0 The following people have contributed to Webmachine:
1
2 Andy Gross
3 Justin Sheehy
4 John Muellerleile
5 Robert Ahrens
6 Jeremy Latt
7 Bryan Fink
8 Ryan Tilder
9 Taavi Talvik
10 Marc Worrell
11 Seth Falcon
12 Tuncer Ayaz
13 Martin Scholl
14 Paul Mineiro
15 Dave Smith
16 Arjan Scherpenisse
17 Benjamin Black
18 Anthony Molinaro
19 Phil Pirozhkov
20 Rusty Klophaus
+0
-19
deps/webmachine/demo/Makefile less more
0 ERL ?= erl
1 APP := webmachine_demo
2
3 .PHONY: deps
4
5 all: deps
6 @../rebar compile
7
8 deps:
9 @../rebar get-deps
10
11 clean:
12 @../rebar clean
13
14 distclean: clean
15 @../rebar delete-deps
16
17 docs:
18 @erl -noshell -run edoc_run application '$(APP)' '"."' '[]'
+0
-43
deps/webmachine/demo/README less more
0 Project Skeleton for the webmachine_demo app.
1
2 You should find in this directory:
3
4 README : this file
5 Makefile : simple make commands
6 rebar : the Rebar build tool for Erlang applications
7 rebar.config : configuration for Rebar
8 start.sh : simple startup script for running webmachine_demo
9 /ebin
10 /webmachine_demo.app : the Erlang app specification
11 /src
12 /webmachine_demo_app.erl : base module for the Erlang application
13 /webmachine_demo_sup.erl : OTP supervisor for the application
14 /webmachine_demo_resource.erl : a simple example Webmachine resource
15 /priv
16 /dispatch.conf : the Webmachine URL-dispatching table
17 /www : a convenient place to put your static web content
18
19 You probably want to do one of a couple of things at this point:
20
21 0. Build the skeleton application:
22 $ make
23 - or -
24 $ ./rebar compile
25
26 1. Start up the skeleton application:
27 $ ./start.sh
28
29 2. Test the basic application:
30 Visit http://localhost:8000/demo
31
32 3. Change the basic application:
33 edit src/webmachine_demo_resource.erl
34
35 4. Test the filesystem resource:
36 $ mkdir /tmp/fs
37 $ echo "Hello World." > /tmp/fs/demo.txt
38 Visit http://localhost:8000/fs/demo.txt
39
40 5. Add some new resources:
41 edit src/YOUR_NEW_RESOURCE.erl
42 edit priv/dispatch.conf
+0
-3
deps/webmachine/demo/priv/dispatch.conf less more
0 %%-*- mode: erlang -*-
1 {["demo", '*'], webmachine_demo_resource, []}.
2 {["fs", '*'], webmachine_demo_fs_resource, [{root, "/tmp/fs"}]}.
+0
-3
deps/webmachine/demo/rebar.config less more
0 %%-*- mode: erlang -*-
1
2 {deps, [{webmachine, "1.10.*", {git, "git://github.com/basho/webmachine", "HEAD"}}]}.
+0
-17
deps/webmachine/demo/src/webmachine_demo.app.src less more
0 %%-*- mode: erlang -*-
1 {application, webmachine_demo,
2 [
3 {description, "demo"},
4 {vsn, "1"},
5 {modules, []},
6 {registered, []},
7 {applications, [
8 kernel,
9 stdlib,
10 crypto,
11 mochiweb,
12 webmachine
13 ]},
14 {mod, { webmachine_demo_app, []}},
15 {env, []}
16 ]}.
+0
-42
deps/webmachine/demo/src/webmachine_demo.erl less more
0 -module(webmachine_demo).
1 -author('Andy Gross <andy@basho.com>').
2 -author('Justin Sheehy <justin@@basho.com>').
3 -export([start/0, start_link/0, stop/0]).
4
5 ensure_started(App) ->
6 case application:start(App) of
7 ok ->
8 ok;
9 {error, {already_started, App}} ->
10 ok
11 end.
12
13 %% @spec start_link() -> {ok,Pid::pid()}
14 %% @doc Starts the app for inclusion in a supervisor tree
15 start_link() ->
16 ensure_started(crypto),
17 ensure_started(mochiweb),
18 application:set_env(webmachine, webmachine_logger_module,
19 webmachine_logger),
20 ensure_started(webmachine),
21 webmachine_demo_sup:start_link().
22
23 %% @spec start() -> ok
24 %% @doc Start the webmachine_demo server.
25 start() ->
26 ensure_started(inets),
27 ensure_started(crypto),
28 ensure_started(mochiweb),
29 application:set_env(webmachine, webmachine_logger_module,
30 webmachine_logger),
31 ensure_started(webmachine),
32 application:start(webmachine_demo).
33
34 %% @spec stop() -> ok
35 %% @doc Stop the webmachine_demo server.
36 stop() ->
37 Res = application:stop(webmachine_demo),
38 application:stop(webmachine),
39 application:stop(mochiweb),
40 application:stop(crypto),
41 Res.
+0
-20
deps/webmachine/demo/src/webmachine_demo_app.erl less more
0 %% @author Andy Gross <andy@basho.com>
1 %% @author Justin Sheehy <justin@basho.com>
2
3 %% @doc Callbacks for the webmachine_demo application.
4
5 -module(webmachine_demo_app).
6
7 -behaviour(application).
8 -export([start/2,stop/1]).
9
10
11 %% @spec start(_Type, _StartArgs) -> ServerRet
12 %% @doc application start callback for webmachine_demo.
13 start(_Type, _StartArgs) ->
14 webmachine_demo_sup:start_link().
15
16 %% @spec stop(_State) -> ServerRet
17 %% @doc application stop callback for webmachine_demo.
18 stop(_State) ->
19 ok.
+0
-159
deps/webmachine/demo/src/webmachine_demo_fs_resource.erl less more
0 %% @author Bryan Fink <bryan@basho.com>
1 %% @author Andy Gross <andy@basho.com>
2 %% @author Justin Sheehy <justin@basho.com>
3 %% @copyright 2008-2009 Basho Technologies, Inc.
4
5 -module(webmachine_demo_fs_resource).
6 -export([init/1]).
7 -export([allowed_methods/2,
8 resource_exists/2,
9 last_modified/2,
10 content_types_provided/2,
11 content_types_accepted/2,
12 delete_resource/2,
13 post_is_create/2,
14 create_path/2,
15 provide_content/2,
16 accept_content/2,
17 generate_etag/2]).
18
19 -record(context, {root,response_body=undefined,metadata=[]}).
20
21 -include_lib("kernel/include/file.hrl").
22 -include_lib("webmachine/include/webmachine.hrl").
23
24 init(ConfigProps) ->
25 {root, Root} = proplists:lookup(root, ConfigProps),
26 {ok, #context{root=Root}}.
27
28 allowed_methods(ReqData, Context) ->
29 {['HEAD', 'GET', 'PUT', 'DELETE', 'POST'], ReqData, Context}.
30
31 file_path(_Context, []) ->
32 false;
33 file_path(Context, Name) ->
34 RelName = case hd(Name) of
35 "/" -> tl(Name);
36 _ -> Name
37 end,
38 filename:join([Context#context.root, RelName]).
39
40 file_exists(Context, Name) ->
41 NamePath = file_path(Context, Name),
42 case filelib:is_regular(NamePath) of
43 true ->
44 {true, NamePath};
45 false ->
46 false
47 end.
48
49 resource_exists(ReqData, Context) ->
50 Path = wrq:disp_path(ReqData),
51 case file_exists(Context, Path) of
52 {true, _} ->
53 {true, ReqData, Context};
54 _ ->
55 case Path of
56 "p" -> {true, ReqData, Context};
57 _ -> {false, ReqData, Context}
58 end
59 end.
60
61 maybe_fetch_object(Context, Path) ->
62 % if returns {true, NewContext} then NewContext has response_body
63 case Context#context.response_body of
64 undefined ->
65 case file_exists(Context, Path) of
66 {true, FullPath} ->
67 {ok, Value} = file:read_file(FullPath),
68 {true, Context#context{response_body=Value}};
69 false ->
70 {false, Context}
71 end;
72 _Body ->
73 {true, Context}
74 end.
75
76 content_types_provided(ReqData, Context) ->
77 CT = webmachine_util:guess_mime(wrq:disp_path(ReqData)),
78 {[{CT, provide_content}], ReqData,
79 Context#context{metadata=[{'content-type', CT}|Context#context.metadata]}}.
80
81 content_types_accepted(ReqData, Context) ->
82 CT = case wrq:get_req_header("content-type", ReqData) of
83 undefined -> "application/octet-stream";
84 X -> X
85 end,
86 {MT, _Params} = webmachine_util:media_type_to_detail(CT),
87 {[{MT, accept_content}], ReqData,
88 Context#context{metadata=[{'content-type', MT}|Context#context.metadata]}}.
89
90 accept_content(ReqData, Context) ->
91 Path = wrq:disp_path(ReqData),
92 FP = file_path(Context, Path),
93 ok = filelib:ensure_dir(FP),
94 ReqData1 = case file_exists(Context, Path) of
95 {true, _} ->
96 ReqData;
97 _ ->
98 LOC = "http://" ++
99 wrq:get_req_header("host", ReqData) ++
100 "/fs/" ++ Path,
101 wrq:set_resp_header("Location", LOC, ReqData)
102 end,
103 Value = wrq:req_body(ReqData1),
104 case file:write_file(FP, Value) of
105 ok ->
106 {true, wrq:set_resp_body(Value, ReqData1), Context};
107 Err ->
108 {{error, Err}, ReqData1, Context}
109 end.
110
111 post_is_create(ReqData, Context) ->
112 {true, ReqData, Context}.
113
114 create_path(ReqData, Context) ->
115 case wrq:get_req_header("slug", ReqData) of
116 undefined -> {undefined, ReqData, Context};
117 Slug ->
118 case file_exists(Context, Slug) of
119 {true, _} -> {undefined, ReqData, Context};
120 _ -> {Slug, ReqData, Context}
121 end
122 end.
123
124 delete_resource(ReqData, Context) ->
125 case file:delete(file_path(
126 Context, wrq:disp_path(ReqData))) of
127 ok -> {true, ReqData, Context};
128 _ -> {false, ReqData, Context}
129 end.
130
131 provide_content(ReqData, Context) ->
132 case maybe_fetch_object(Context, wrq:disp_path(ReqData)) of
133 {true, NewContext} ->
134 Body = NewContext#context.response_body,
135 {Body, ReqData, Context};
136 {false, NewContext} ->
137 {error, ReqData, NewContext}
138 end.
139
140 last_modified(ReqData, Context) ->
141 {true, FullPath} = file_exists(Context,
142 wrq:disp_path(ReqData)),
143 LMod = filelib:last_modified(FullPath),
144 {LMod, ReqData, Context#context{metadata=[{'last-modified',
145 httpd_util:rfc1123_date(LMod)}|Context#context.metadata]}}.
146
147 hash_body(Body) -> mochihex:to_hex(binary_to_list(crypto:sha(Body))).
148
149 generate_etag(ReqData, Context) ->
150 case maybe_fetch_object(Context, wrq:disp_path(ReqData)) of
151 {true, BodyContext} ->
152 ETag = hash_body(BodyContext#context.response_body),
153 {ETag, ReqData,
154 BodyContext#context{metadata=[{etag,ETag}|
155 BodyContext#context.metadata]}};
156 _ ->
157 {undefined, ReqData, Context}
158 end.
+0
-51
deps/webmachine/demo/src/webmachine_demo_resource.erl less more
0 %% @author Justin Sheehy <justin@basho.com>
1 %% @copyright 2007-2009 Basho Technologies, Inc. All Rights Reserved.
2 %% @doc Example webmachine_resource.
3
4 -module(webmachine_demo_resource).
5 -author('Justin Sheehy <justin@basho.com>').
6 -export([init/1, to_html/2, to_text/2, content_types_provided/2,
7 is_authorized/2, generate_etag/2, expires/2, last_modified/2]).
8
9 -include_lib("webmachine/include/webmachine.hrl").
10
11 init([]) -> {ok, undefined}.
12
13 content_types_provided(ReqData, Context) ->
14 {[{"text/html", to_html},{"text/plain",to_text}], ReqData, Context}.
15
16 to_text(ReqData, Context) ->
17 Path = wrq:disp_path(ReqData),
18 Body = io_lib:format("Hello ~s from webmachine.~n", [Path]),
19 {Body, ReqData, Context}.
20
21 to_html(ReqData, Context) ->
22 {Body, _RD, Ctx2} = to_text(ReqData, Context),
23 HBody = io_lib:format("<html><body>~s</body></html>~n",
24 [erlang:iolist_to_binary(Body)]),
25 {HBody, ReqData, Ctx2}.
26
27 is_authorized(ReqData, Context) ->
28 case wrq:disp_path(ReqData) of
29 "authdemo" ->
30 case wrq:get_req_header("authorization", ReqData) of
31 "Basic "++Base64 ->
32 Str = base64:mime_decode_to_string(Base64),
33 case string:tokens(Str, ":") of
34 ["authdemo", "demo1"] ->
35 {true, ReqData, Context};
36 _ ->
37 {"Basic realm=webmachine", ReqData, Context}
38 end;
39 _ ->
40 {"Basic realm=webmachine", ReqData, Context}
41 end;
42 _ -> {true, ReqData, Context}
43 end.
44
45 expires(ReqData, Context) -> {{{2021,1,1},{0,0,0}}, ReqData, Context}.
46
47 last_modified(ReqData, Context) ->
48 {calendar:now_to_universal_time(os:timestamp()), ReqData, Context}.
49
50 generate_etag(ReqData, Context) -> {wrq:raw_path(ReqData), ReqData, Context}.
+0
-56
deps/webmachine/demo/src/webmachine_demo_sup.erl less more
0 %% @author author <author@example.com>
1 %% @copyright YYYY author.
2
3 %% @doc Supervisor for the webmachine_demo application.
4
5 -module(webmachine_demo_sup).
6
7 -behaviour(supervisor).
8
9 %% External exports
10 -export([start_link/0, upgrade/0]).
11
12 %% supervisor callbacks
13 -export([init/1]).
14
15 %% @spec start_link() -> ServerRet
16 %% @doc API for starting the supervisor.
17 start_link() ->
18 supervisor:start_link({local, ?MODULE}, ?MODULE, []).
19
20 %% @spec upgrade() -> ok
21 %% @doc Add processes if necessary.
22 upgrade() ->
23 {ok, {_, Specs}} = init([]),
24
25 Old = sets:from_list(
26 [Name || {Name, _, _, _} <- supervisor:which_children(?MODULE)]),
27 New = sets:from_list([Name || {Name, _, _, _, _, _} <- Specs]),
28 Kill = sets:subtract(Old, New),
29
30 sets:fold(fun (Id, ok) ->
31 supervisor:terminate_child(?MODULE, Id),
32 supervisor:delete_child(?MODULE, Id),
33 ok
34 end, ok, Kill),
35
36 [supervisor:start_child(?MODULE, Spec) || Spec <- Specs],
37 ok.
38
39 %% @spec init([]) -> SupervisorTree
40 %% @doc supervisor callback.
41 init([]) ->
42 Ip = case os:getenv("WEBMACHINE_IP") of false -> "0.0.0.0"; Any -> Any end,
43 {ok, Dispatch} = file:consult(filename:join(
44 [filename:dirname(code:which(?MODULE)),
45 "..", "priv", "dispatch.conf"])),
46 WebConfig = [
47 {ip, Ip},
48 {port, 8000},
49 {log_dir, "priv/log"},
50 {dispatch, Dispatch}],
51 Web = {webmachine_mochiweb,
52 {webmachine_mochiweb, start, [WebConfig]},
53 permanent, 5000, worker, dynamic},
54 Processes = [Web],
55 {ok, { {one_for_one, 10, 10}, Processes} }.
+0
-3
deps/webmachine/demo/start.sh less more
0 #!/bin/sh
1 cd `dirname $0`
2 exec erl -pa $PWD/ebin $PWD/deps/*/ebin -boot start_sasl -s reloader -s webmachine_demo
deps/webmachine/docs/http-headers-status-v3.png less more
Binary diff not shown
+0
-8
deps/webmachine/include/webmachine.hrl less more
0 -export([ping/2]).
1
2 -include_lib("webmachine/include/wm_reqdata.hrl").
3
4 ping(ReqData, State) ->
5 {pong, ReqData, State}.
6
7
+0
-16
deps/webmachine/include/webmachine_logger.hrl less more
0 -record(wm_log_data,
1 {resource_module :: atom(),
2 start_time :: tuple(),
3 method :: atom(),
4 headers,
5 peer,
6 path :: string(),
7 version,
8 response_code,
9 response_length,
10 end_time :: tuple(),
11 finish_time :: tuple(),
12 notes}).
13 -type wm_log_data() :: #wm_log_data{}.
14
15 -define(EVENT_LOGGER, webmachine_log_event).
+0
-8
deps/webmachine/include/wm_reqdata.hrl less more
0 -record(wm_reqdata, {method, scheme, version, peer, wm_state,
1 disp_path, path, raw_path, path_info, path_tokens,
2 app_root,response_code,max_recv_body, max_recv_hunk,
3 req_cookie, req_qs, req_headers, req_body,
4 resp_redirect, resp_headers, resp_body, resp_range,
5 host_tokens, port, notes
6 }).
7
+0
-10
deps/webmachine/include/wm_reqstate.hrl less more
0 -record(wm_reqstate, {socket=undefined,
1 metadata=orddict:new(),
2 range=undefined,
3 peer=undefined,
4 reqdata=undefined,
5 bodyfetch=undefined,
6 reqbody=undefined,
7 log_data=undefined
8 }).
9
+0
-1
deps/webmachine/include/wm_resource.hrl less more
0 -record(wm_resource, {module, modstate, modexports, trace}).
+0
-19
deps/webmachine/priv/templates/Makefile less more
0 ERL ?= erl
1 APP := {{appid}}
2
3 .PHONY: deps
4
5 all: deps
6 @./rebar compile
7
8 deps:
9 @./rebar get-deps
10
11 clean:
12 @./rebar clean
13
14 distclean: clean
15 @./rebar delete-deps
16
17 docs:
18 @erl -noshell -run edoc_run application '$(APP)' '"."' '[]'
+0
-35
deps/webmachine/priv/templates/README less more
0 Project Skeleton for the {{appid}} app.
1
2 You should find in this directory:
3
4 README : this file
5 Makefile : simple make commands
6 rebar : the Rebar build tool for Erlang applications
7 rebar.config : configuration for Rebar
8 start.sh : simple startup script for running {{appid}}
9 /ebin
10 /{{appid}}.app : the Erlang app specification
11 /src
12 /{{appid}}_app.erl : base module for the Erlang application
13 /{{appid}}_sup.erl : OTP supervisor for the application
14 /{{appid}}_resource.erl : a simple example Webmachine resource
15 /priv
16 /dispatch.conf : the Webmachine URL-dispatching table
17 /www : a convenient place to put your static web content
18
19 You probably want to do one of a couple of things at this point:
20
21 0. Build the skeleton application:
22 $ make
23 - or -
24 $ ./rebar compile
25
26 1. Start up the skeleton application:
27 $ ./start.sh
28
29 2. Change the basic application:
30 edit src/{{appid}}_resource.erl
31
32 3. Add some new resources:
33 edit src/YOUR_NEW_RESOURCE.erl
34 edit priv/dispatch.conf
+0
-2
deps/webmachine/priv/templates/priv/dispatch.conf less more
0 %%-*- mode: erlang -*-
1 {[], {{appid}}_resource, []}.
+0
-3
deps/webmachine/priv/templates/rebar.config less more
0 %%-*- mode: erlang -*-
1
2 {deps, [{webmachine, "1.10.*", {git, "git://github.com/basho/webmachine", "HEAD"}}]}.
+0
-18
deps/webmachine/priv/templates/src/wmskel.app.src less more
0 %%-*- mode: erlang -*-
1 {application, {{appid}},
2 [
3 {description, "{{appid}}"},
4 {vsn, "1"},
5 {modules, []},
6 {registered, []},
7 {applications, [
8 kernel,
9 stdlib,
10 inets,
11 crypto,
12 mochiweb,
13 webmachine
14 ]},
15 {mod, { {{appid}}_app, []}},
16 {env, []}
17 ]}.
+0
-48
deps/webmachine/priv/templates/src/wmskel.erl less more
0 %% @author author <author@example.com>
1 %% @copyright YYYY author.
2
3 %% @doc {{appid}} startup code
4
5 -module({{appid}}).
6 -author('author <author@example.com>').
7 -export([start/0, start_link/0, stop/0]).
8
9 ensure_started(App) ->
10 case application:start(App) of
11 ok ->
12 ok;
13 {error, {already_started, App}} ->
14 ok
15 end.
16
17 %% @spec start_link() -> {ok,Pid::pid()}
18 %% @doc Starts the app for inclusion in a supervisor tree
19 start_link() ->
20 ensure_started(inets),
21 ensure_started(crypto),
22 ensure_started(mochiweb),
23 application:set_env(webmachine, webmachine_logger_module,
24 webmachine_logger),
25 ensure_started(webmachine),
26 {{appid}}_sup:start_link().
27
28 %% @spec start() -> ok
29 %% @doc Start the {{appid}} server.
30 start() ->
31 ensure_started(inets),
32 ensure_started(crypto),
33 ensure_started(mochiweb),
34 application:set_env(webmachine, webmachine_logger_module,
35 webmachine_logger),
36 ensure_started(webmachine),
37 application:start({{appid}}).
38
39 %% @spec stop() -> ok
40 %% @doc Stop the {{appid}} server.
41 stop() ->
42 Res = application:stop({{appid}}),
43 application:stop(webmachine),
44 application:stop(mochiweb),
45 application:stop(crypto),
46 application:stop(inets),
47 Res.
+0
-21
deps/webmachine/priv/templates/src/wmskel_app.erl less more
0 %% @author author <author@example.com>
1 %% @copyright YYYY author.
2
3 %% @doc Callbacks for the {{appid}} application.
4
5 -module({{appid}}_app).
6 -author('author <author@example.com>').
7
8 -behaviour(application).
9 -export([start/2,stop/1]).
10
11
12 %% @spec start(_Type, _StartArgs) -> ServerRet
13 %% @doc application start callback for {{appid}}.
14 start(_Type, _StartArgs) ->
15 {{appid}}_sup:start_link().
16
17 %% @spec stop(_State) -> ServerRet
18 %% @doc application stop callback for {{appid}}.
19 stop(_State) ->
20 ok.
+0
-13
deps/webmachine/priv/templates/src/wmskel_resource.erl less more
0 %% @author author <author@example.com>
1 %% @copyright YYYY author.
2 %% @doc Example webmachine_resource.
3
4 -module({{appid}}_resource).
5 -export([init/1, to_html/2]).
6
7 -include_lib("webmachine/include/webmachine.hrl").
8
9 init([]) -> {ok, undefined}.
10
11 to_html(ReqData, State) ->
12 {"<html><body>Hello, new world</body></html>", ReqData, State}.
+0
-72
deps/webmachine/priv/templates/src/wmskel_sup.erl less more
0 %% @author author <author@example.com>
1 %% @copyright YYYY author.
2
3 %% @doc Supervisor for the {{appid}} application.
4
5 -module({{appid}}_sup).
6 -author('author <author@example.com>').
7
8 -behaviour(supervisor).
9
10 %% External exports
11 -export([start_link/0, upgrade/0]).
12
13 %% supervisor callbacks
14 -export([init/1]).
15
16 %% @spec start_link() -> ServerRet
17 %% @doc API for starting the supervisor.
18 start_link() ->
19 supervisor:start_link({local, ?MODULE}, ?MODULE, []).
20
21 %% @spec upgrade() -> ok
22 %% @doc Add processes if necessary.
23 upgrade() ->
24 {ok, {_, Specs}} = init([]),
25
26 Old = sets:from_list(
27 [Name || {Name, _, _, _} <- supervisor:which_children(?MODULE)]),
28 New = sets:from_list([Name || {Name, _, _, _, _, _} <- Specs]),
29 Kill = sets:subtract(Old, New),
30
31 sets:fold(fun (Id, ok) ->
32 supervisor:terminate_child(?MODULE, Id),
33 supervisor:delete_child(?MODULE, Id),
34 ok
35 end, ok, Kill),
36
37 [supervisor:start_child(?MODULE, Spec) || Spec <- Specs],
38 ok.
39
40 %% @spec init([]) -> SupervisorTree
41 %% @doc supervisor callback.
42 init([]) ->
43 Ip = case os:getenv("WEBMACHINE_IP") of false -> "0.0.0.0"; Any -> Any end,
44 {ok, App} = application:get_application(?MODULE),
45 {ok, Dispatch} = file:consult(filename:join([priv_dir(App),
46 "dispatch.conf"])),
47 Port = case os:getenv("WEBMACHINE_PORT") of
48 false -> 8000;
49 AnyPort -> AnyPort
50 end,
51 WebConfig = [
52 {ip, Ip},
53 {port, Port},
54 {log_dir, "priv/log"},
55 {dispatch, Dispatch}],
56 Web = {webmachine_mochiweb,
57 {webmachine_mochiweb, start, [WebConfig]},
58 permanent, 5000, worker, [mochiweb_socket_server]},
59 Processes = [Web],
60 {ok, { {one_for_one, 10, 10}, Processes} }.
61
62 %%
63 %% @doc return the priv dir
64 priv_dir(Mod) ->
65 case code:priv_dir(Mod) of
66 {error, bad_name} ->
67 Ebin = filename:dirname(code:which(Mod)),
68 filename:join(filename:dirname(Ebin), "priv");
69 PrivDir ->
70 PrivDir
71 end.
+0
-3
deps/webmachine/priv/templates/start.sh less more
0 #!/bin/sh
1 cd `dirname $0`
2 exec erl -pa $PWD/ebin $PWD/deps/*/ebin -boot start_sasl -s reloader -s {{appid}}
+0
-35
deps/webmachine/priv/templates/wmskel.template less more
0 %%-*- mode: erlang -*-
1 %% Basic Webmachine application skeleton
2
3 %% Variables:
4 %% appid: name of the application to build
5 %% default = "wmskel"
6 %% webmachine: path to webmachine from this template
7 %% default = "../.."
8 %% prefix: path where the application should be created
9 %% default = "."
10 {variables, [{appid, "wmskel"},
11 {webmachine, "../.."},
12 {prefix, "."}]}.
13
14 %% main project files
15 {template, "README", "{{prefix}}/README"}.
16 {template, "Makefile", "{{prefix}}/Makefile"}.
17 {template, "rebar.config", "{{prefix}}/rebar.config"}.
18 {file, "{{webmachine}}/rebar", "{{prefix}}/rebar"}.
19 {chmod, 8#744, "{{prefix}}/rebar"}.
20 {template, "start.sh", "{{prefix}}/start.sh"}.
21 {chmod, 8#744, "{{prefix}}/start.sh"}.
22
23 {template, "src/wmskel.app.src", "{{prefix}}/src/{{appid}}.app.src"}.
24
25 {template, "src/wmskel.erl", "{{prefix}}/src/{{appid}}.erl"}.
26 {template, "src/wmskel_app.erl", "{{prefix}}/src/{{appid}}_app.erl"}.
27 {template, "src/wmskel_sup.erl", "{{prefix}}/src/{{appid}}_sup.erl"}.
28 {template, "src/wmskel_resource.erl", "{{prefix}}/src/{{appid}}_resource.erl"}.
29
30 {template, "priv/dispatch.conf", "{{prefix}}/priv/dispatch.conf"}.
31 {dir, "{{prefix}}/priv/www"}.
32
33 %% dependencies
34 {dir, "{{prefix}}/deps"}.
deps/webmachine/priv/trace/http-headers-status-v3.png less more
Binary diff not shown
+0
-107
deps/webmachine/priv/trace/wmtrace.css less more
0 body {
1 margin:0px;
2 padding:0px;
3 }
4
5 canvas#v3map {
6 margin-top:2em;
7 z-index: 1;
8 }
9
10 div#sizetest {
11 width:100%;
12 }
13
14 div#zoompanel {
15 height:2em;
16 position:fixed;
17 z-index:10;
18 }
19
20 div#preview {
21 position:absolute;
22 display:none;
23 background:#dddddd;
24 border:1px solid #999999;
25 }
26
27 div#preview ul {
28 padding: 0px 0px 0px 0.5em;
29 margin: 0px;
30 list-style: none;
31 }
32
33 div#infopanel {
34 z-index:20;
35 background:#dddddd;
36 position:fixed;
37 top:0px;
38 right:0px;
39 bottom:0px;
40 left:75%;
41 min-width:30em;
42 padding:5px;
43 }
44
45 div#infocontrols {
46 position:absolute;
47 top:0px;
48 bottom:0px;
49 left:-5px;
50 width:5px;
51 background:#999999;
52 cursor:ew-resize;
53 }
54
55 div#infocontrols div {
56 position:absolute;
57 left:-15px;
58 width:20px;
59 height:49px;
60 background:#999999;
61 cursor:pointer;
62 }
63
64 div#infocontrols div.selectedtab {
65 background:#dddddd;
66 border-top: 1px solid #999999;
67 border-left: 1px solid #999999;
68 border-bottom: 1px solid #999999;
69 }
70
71 div#requesttab {
72 top:2px;
73 }
74
75 div#responsetab {
76 top:54px;
77 }
78
79 div#decisiontab {
80 top:106px;
81 }
82
83 div#requestdetail, div#responsedetail, div#decisiondetail {
84 height:100%;
85 }
86
87 div#responsedetail, div#decisiondetail {
88 display:none;
89 }
90
91 div#infopanel ul {
92 list-style:none;
93 padding-left:0px;
94 height:5em;
95 overflow-y:scroll;
96 }
97
98 pre {
99 height:40%;
100 overflow:scroll;
101 }
102
103 div#responsebody, div#requestbody {
104 height:70%;
105 overflow-y:scroll;
106 }
+0
-713
deps/webmachine/priv/trace/wmtrace.js less more
0 var HIGHLIGHT = '#cc00cc';
1 var REGULAR = '#666666';
2
3 var cols = {
4 'a':173,
5 'b':325,
6 'c':589,
7 'd':797,
8 'e':1005,
9 'f':1195,
10 'g':1402,
11 'gg':1515,
12 'h':1572,
13 'i':1799,
14 'j':1893,
15 'k':1988,
16 'l':2157,
17 'll':2346,
18 'm':2403,
19 'mm':2535,
20 'n':2554,
21 'o':2649,
22 'oo':2781,
23 'ooo':2801,
24 'p':2894,
25 'q':3007
26 };
27
28 var rows = {
29 '1':221,
30 '2':298,
31 '3':373,
32 '4':448,
33 '5':524,
34 '6':599,
35 '7':675,
36 '8':751,
37 '9':826,
38 '10':902,
39 '11':977,
40 '12':1053,
41 '13':1129,
42 '14':1204,
43 '15':1280,
44 '16':1355,
45 '17':1431,
46 '18':1506,
47 '19':1583,
48 '20':1658,
49 '21':1734,
50 '22':1809,
51 '23':1885,
52 '24':1961,
53 '25':2036,
54 '26':2112
55 };
56
57 var edges = {
58 'b14b13':['b14','b13'],
59
60 'b13b12':['b13','b12'],
61 'b13503':['b13','503'],
62
63 'b12b11':['b12','b11'],
64 'b12501':['b12','501'],
65
66 'b11b10':['b11','b10'],
67 'b11414':['b11','414'],
68
69 'b10b9':['b10','b9'],
70 'b10405':['b10','405'],
71
72 'b9b8':['b9','b8'],
73 'b9400':['b9','400'],
74
75 'b8b7':['b8','b7'],
76 'b8401':['b8','401'],
77
78 'b7b6':['b7','b6'],
79 'b7403':['b7','403'],
80
81 'b6b5':['b6','b5'],
82 'b6501':['b6','501a'],
83
84 'b5b4':['b5','b4'],
85 'b5415':['b5','415'],
86
87 'b4b3':['b4','b3'],
88 'b4413':['b4','b4'],
89
90 'b3c3':['b3','c3'],
91 'b3200':['b3','200'],
92
93 'c3c4':['c3','c4'],
94 'c3d4':['c3','d3','d4'],
95
96 'c4d4':['c4','d4'],
97 'c4406':['c4','406'],
98
99 'd4d5':['d4','d5'],
100 'd4e5':['d4','e4','e5'],
101
102 'd5e5':['d5','e5'],
103 'd5406':['d5','d7','406'],
104
105 'e5e6':['e5','e6'],
106 'e5f6':['e5','f5','f6'],
107
108 'e6f6':['e6','f6'],
109 'e6406':['e6','e7','406'],
110
111 'f6f7':['f6','f7'],
112 'f6g7':['f6','g6','g7'],
113
114 'f7g7':['f7','g7'],
115 'f7406':['f7','406'],
116
117 'g7g8':['g7','g8'],
118 'g7h7':['g7','h7'],
119
120 'g8g9':['g8','g9'],
121 'g8h10':['g8','h8','h10'],
122
123 'g9g11':['g9','g11'],
124 'g9h10':['g9','gg9','gg10','h10'],
125
126 'g11h10':['g11','gg11','gg10','h10'],
127 'g11412':['g11','g18','412a'],
128
129 'h7i7':['h7','i7'],
130 'h7412':['h7','412'],
131
132 'h10h11':['h10','h11'],
133 'h10i12':['h10','i10','i12'],
134
135 'h11h12':['h11','h12'],
136 'h11i12':['h11','i11','i12'],
137
138 'h12i12':['h12','i12'],
139 'h12412':['h12','412a'],
140
141 'i4p3':['i4','i3','p3'],
142 'i4301':['i4','301'],
143
144 'i7i4':['i7','i4'],
145 'i7k7':['i7','k7'],
146
147 'i12l13':['i12','l12','l13'],
148 'i12i13':['i12','i13'],
149
150 'i13k13':['i13','k13'],
151 'i13j18':['i13','i17','j17','j18'],
152
153 'j18412':['j18','412a'],
154 'j18304':['j18','304'],
155
156 'k5l5':['k5','l5'],
157 'k5301':['k5','301'],
158
159 'k7k5':['k7','k5'],
160 'k7l7':['k7','l7'],
161
162 'k13j18':['k13','k17','j17','j18'],
163 'k13l13':['k13','l13'],
164
165 'l5m5':['l5','m5'],
166 'l5307':['l5','307'],
167
168 'l7m7':['l7','m7'],
169 'l7404':['l7','l8','404'],
170
171 'l13l14':['l13','l14'],
172 'l13m16':['l13','m13','m16'],
173
174 'l14l15':['l14','l15'],
175 'l14m16':['l14','m14','m16'],
176
177 'l15l17':['l15','l17'],
178 'l15m16':['l15','ll15','ll16','m16'],
179
180 'l17m16':['l17','ll17','ll16','m16'],
181 'l17304':['l17','304'],
182
183 'm5n5':['m5','n5'],
184 'm5410':['m5','m4','410'],
185
186 'm7n11':['m7','n7','n11'],
187 'm7404':['m7','404'],
188
189 'm16m20':['m16','m20'],
190 'm16n16':['m16','n16'],
191
192 'm20o20':['m20','o20'],
193 'm20202':['m20','202'],
194
195 'n5n11':['n5','n11'],
196 'n5410':['n5','410'],
197
198 'n11p11':['n11','p11'],
199 'n11303':['n11','303'],
200
201 'n16n11':['n16','n11'],
202 'n16o16':['n16','o16'],
203
204 'o14p11':['o14','o11','p11'],
205 'o14409':['o14','409a'],
206
207 'o16o14':['o16','o14'],
208 'o16o18':['o16','o18'],
209
210 'o18200':['o18','200a'],
211 'o18300':['o18','oo18','300'],
212
213 'o20o18':['o20','o18'],
214 'o20204':['o20','204'],
215
216 'p3p11':['p3','p11'],
217 'p3409':['p3','409'],
218
219 'p11o20':['p11','p20','o20'],
220 'p11201':['p11','q11','201']
221 };
222
223 var ends = {
224 '200': {col:'a', row:'3', width:190},
225 '200a': {col:'mm', row:'18', width:116},
226 '201': {col:'q', row:'12', width:154},
227 '202': {col:'m', row:'21', width:116},
228 '204': {col:'o', row:'21', width:152},
229
230 '300': {col:'oo', row:'19', width:152},
231 '301': {col:'k', row:'4', width:154},
232 '303': {col:'m', row:'11', width:116},
233 '304': {col:'l', row:'18', width:116},
234 '307': {col:'l', row:'4', width:154},
235
236 '400': {col:'a', row:'9', width:190},
237 '401': {col:'a', row:'8', width:190},
238 '403': {col:'a', row:'7', width:190},
239 '404': {col:'m', row:'8', width:116},
240 '405': {col:'a', row:'10', width:190},
241 '406': {col:'c', row:'7', width:152},
242 '409': {col:'p', row:'2', width:116},
243 '409a': {col:'oo', row:'14', width:116},
244 '410': {col:'n', row:'4', width:116},
245 '412': {col:'h', row:'6', width:152},
246 '412a': {col:'h', row:'18', width:152},
247 '413': {col:'a', row:'4', width:190},
248 '414': {col:'a', row:'11', width:190},
249 '415': {col:'a', row:'5', width:190},
250
251 '501a': {col:'a', row:'6', width:190},
252 '501': {col:'a', row:'12', width:190},
253 '503': {col:'a', row:'13', width:190}
254 };
255
256 var canvas;
257
258 function decorateTrace() {
259 trace[0].x = cols[trace[0].d[0]];
260 trace[0].y = rows[trace[0].d.slice(1)];
261 trace[0].previewCalls = previewCalls(trace[0]);
262
263 for (var i = 1; i < trace.length; i++) {
264 trace[i].x = cols[trace[i].d[0]];
265 trace[i].y = rows[trace[i].d.slice(1)];
266 trace[i].previewCalls = previewCalls(trace[i]);
267
268 var path = edges[trace[i-1].d+trace[i].d];
269 if (path) {
270 trace[i].path = [path.length-1];
271 for (var p = 1; p < path.length; p++) {
272 trace[i].path[p-1] = getSeg(path[p-1], path[p], p == path.length-1);
273 }
274 } else {
275 trace[i].path = [];
276 }
277 }
278
279 var path = edges[trace[i-1].d+response.code];
280 if (path) {
281 var end = ends[path[path.length-1]];
282 response.x = cols[end.col];
283 response.y = rows[end.row];
284 response.width = end.width;
285 response.type = 'normal';
286
287 response.path = [path.length-1];
288 for (var p = 1; p < path.length; p++) {
289 response.path[p-1] = getSeg(path[p-1], path[p], p == path.length-1);
290 }
291 } else {
292 var ld = trace[trace.length-1];
293 response.x = ld.x+50;
294 response.y = ld.y-50;
295 response.width = 38;
296 response.type = 'other';
297
298 response.path = [
299 {x1: ld.x+10, y1: ld.y-10,
300 x2: ld.x+36, y2: ld.y-36}
301 ];
302 }
303 };
304
305 function previewCalls(dec) {
306 var prev = '';
307 for (var i = 0; i < dec.calls.length; i++) {
308 if (dec.calls[i].output != "wmtrace_not_exported")
309 prev += '<li>'+dec.calls[i].module+':'+dec.calls[i]['function']+'</li>';
310 }
311 return prev;
312 };
313
314 function drawTrace() {
315 drawDecision(trace[0]);
316 for (var i = 1; i < trace.length; i++) {
317 drawPath(trace[i].path);
318 drawDecision(trace[i]);
319 }
320
321 drawPath(response.path);
322 drawResponse();
323 };
324
325 function drawResponse() {
326 if (response.type == 'normal') {
327 var context = canvas.getContext('2d');
328 context.strokeStyle=HIGHLIGHT;
329 context.lineWidth=4;
330
331 context.beginPath();
332 context.rect(response.x-(response.width/2),
333 response.y-19,
334 response.width,
335 38);
336 context.stroke();
337 } else {
338 var context = canvas.getContext('2d');
339 context.strokeStyle='#ff0000';
340 context.lineWidth=4;
341
342 context.beginPath();
343 context.arc(response.x, response.y, 19,
344 0, 2*3.14159, false);
345 context.stroke();
346
347 }
348 };
349
350 function drawDecision(dec) {
351 var context = canvas.getContext('2d');
352
353 if (dec.previewCalls == '')
354 context.strokeStyle=REGULAR;
355 else
356 context.strokeStyle=HIGHLIGHT;
357 context.lineWidth=4;
358
359 context.beginPath();
360 context.moveTo(dec.x, dec.y-19);
361 context.lineTo(dec.x+19, dec.y);
362 context.lineTo(dec.x, dec.y+19);
363 context.lineTo(dec.x-19, dec.y);
364 context.closePath();
365 context.stroke();
366 };
367
368 function drawPath(path) {
369 var context = canvas.getContext('2d');
370 context.strokeStyle=REGULAR;
371 context.lineWidth=4;
372
373 context.beginPath();
374 context.moveTo(path[0].x1, path[0].y1);
375 for (var p = 0; p < path.length; p++) {
376 context.lineTo(path[p].x2, path[p].y2);
377 }
378 context.stroke();
379 };
380
381 function getSeg(p1, p2, last) {
382 var seg = {
383 x1:cols[p1[0]],
384 y1:rows[p1.slice(1)]
385 };
386 if (ends[p2]) {
387 seg.x2 = cols[ends[p2].col];
388 seg.y2 = rows[ends[p2].row];
389 } else {
390 seg.x2 = cols[p2[0]];
391 seg.y2 = rows[p2.slice(1)];
392 }
393
394 if (seg.x1 == seg.x2) {
395 if (seg.y1 < seg.y2) {
396 seg.y1 = seg.y1+19;
397 if (last) seg.y2 = seg.y2-19;
398 } else {
399 seg.y1 = seg.y1-19;
400 if (last) seg.y2 = seg.y2+19;
401 }
402 } else {
403 //assume seg.y1 == seg.y2
404 if (seg.x1 < seg.x2) {
405 seg.x1 = seg.x1+19;
406 if (last) seg.x2 = seg.x2-(ends[p2] ? (ends[p2].width/2) : 19);
407 } else {
408 seg.x1 = seg.x1-19;
409 if (last) seg.x2 = seg.x2+(ends[p2] ? (ends[p2].width/2) : 19);
410 }
411 }
412 return seg;
413 };
414
415 function traceDecision(name) {
416 for (var i = trace.length-1; i >= 0; i--)
417 if (trace[i].d == name) return trace[i];
418 };
419
420 var detailPanels = {};
421 function initDetailPanels() {
422 var windowWidth = document.getElementById('sizetest').clientWidth;
423 var infoPanel = document.getElementById('infopanel');
424 var panelWidth = windowWidth-infoPanel.offsetLeft;
425
426 var panels = {
427 'request': document.getElementById('requestdetail'),
428 'response': document.getElementById('responsedetail'),
429 'decision': document.getElementById('decisiondetail')
430 };
431
432 var tabs = {
433 'request': document.getElementById('requesttab'),
434 'response': document.getElementById('responsetab'),
435 'decision': document.getElementById('decisiontab')
436 };
437
438 var decisionId = document.getElementById('decisionid');
439 var decisionCalls = document.getElementById('decisioncalls');
440 var callInput = document.getElementById('callinput');
441 var callOutput = document.getElementById('calloutput');
442
443 var lastUsedPanelWidth = windowWidth-infoPanel.offsetLeft;
444
445 var setPanelWidth = function(width) {
446 infoPanel.style.left = (windowWidth-width)+'px';
447 canvas.style.marginRight = (width+20)+'px';
448 panelWidth = width;
449 };
450 setPanelWidth(panelWidth);
451
452 var ensureVisible = function() {
453 if (windowWidth-infoPanel.offsetLeft < 10)
454 setPanelWidth(lastUsedPanelWidth);
455 };
456
457 var decChoices = '';
458 for (var i = 0; i < trace.length; i++) {
459 decChoices += '<option value="'+trace[i].d+'">'+trace[i].d+'</option>';
460 }
461 decisionId.innerHTML = decChoices;
462 decisionId.selectedIndex = -1;
463
464 decisionId.onchange = function() {
465 detailPanels.setDecision(traceDecision(decisionId.value));
466 }
467
468 detailPanels.setDecision = function(dec) {
469 decisionId.value = dec.d;
470
471 var calls = [];
472 for (var i = 0; i < dec.calls.length; i++) {
473 calls.push('<option value="'+dec.d+'-'+i+'">');
474 calls.push(dec.calls[i].module+':'+dec.calls[i]['function']);
475 calls.push('</option>');
476 }
477 decisionCalls.innerHTML = calls.join('');
478 decisionCalls.selectedIndex = 0;
479
480 decisionCalls.onchange();
481 };
482
483 detailPanels.show = function(name) {
484 for (p in panels) {
485 if (p == name) {
486 panels[p].style.display = 'block';
487 tabs[p].className = 'selectedtab';
488 }
489 else {
490 panels[p].style.display = 'none';
491 tabs[p].className = '';
492 }
493 }
494 ensureVisible();
495 };
496
497 detailPanels.hide = function() {
498 setPanelWidth(0);
499 }
500
501 decisionCalls.onchange = function() {
502 var val = decisionCalls.value;
503 if (val) {
504 var dec = traceDecision(val.substring(0, val.indexOf('-')));
505 var call = dec.calls[parseInt(val.substring(val.indexOf('-')+1, val.length))];
506
507 if (call.output != "wmtrace_not_exported") {
508 callInput.style.color='#000000';
509 callInput.innerHTML = call.input;
510 if (call.output != null) {
511 callOutput.style.color = '#000000';
512 callOutput.innerHTML = call.output;
513 } else {
514 callOutput.style.color = '#ff0000';
515 callOutput.textContent = 'Error: '+call.module+':'+call['function']+' never returned';
516 }
517 } else {
518 callInput.style.color='#999999';
519 callInput.textContent = call.module+':'+call['function']+' was not exported';
520 callOutput.textContent = '';
521 }
522 } else {
523 callInput.textContent = '';
524 callOutput.textContent = '';
525 }
526 };
527
528 var headersList = function(headers) {
529 var h = '';
530 for (n in headers) h += '<li>'+n+': '+headers[n];
531 return h;
532 };
533
534 document.getElementById('requestmethod').innerHTML = request.method;
535 document.getElementById('requestpath').innerHTML = request.path;
536 document.getElementById('requestheaders').innerHTML = headersList(request.headers);
537 document.getElementById('requestbody').innerHTML = request.body;
538
539 document.getElementById('responsecode').innerHTML = response.code;
540 document.getElementById('responseheaders').innerHTML = headersList(response.headers);
541 document.getElementById('responsebody').innerHTML = response.body;
542
543
544 var infoControls = document.getElementById('infocontrols');
545 var md = false;
546 var dragged = false;
547 var msoff = 0;
548 infoControls.onmousedown = function(ev) {
549 md = true;
550 dragged = false;
551 msoff = ev.clientX-infoPanel.offsetLeft;
552 };
553
554 infoControls.onclick = function(ev) {
555 if (dragged) {
556 lastUsedPanelWidth = panelWidth;
557 }
558 else if (panelWidth < 10) {
559 switch(ev.target.id) {
560 case 'requesttab': detailPanels.show('request'); break;
561 case 'responsetab': detailPanels.show('response'); break;
562 case 'decisiontab': detailPanels.show('decision'); break;
563 default: ensureVisible();
564 }
565 } else {
566 var name = 'none';
567 switch(ev.target.id) {
568 case 'requesttab': name = 'request'; break;
569 case 'responsetab': name = 'response'; break;
570 case 'decisiontab': name = 'decision'; break;
571 }
572
573 if (panels[name] && panels[name].style.display != 'block')
574 detailPanels.show(name);
575 else
576 detailPanels.hide();
577 }
578
579 return false;
580 };
581
582 document.onmousemove = function(ev) {
583 if (md) {
584 dragged = true;
585 panelWidth = windowWidth-(ev.clientX-msoff);
586 if (panelWidth < 0) {
587 panelWidth = 0;
588 infoPanel.style.left = windowWidth+"px";
589 }
590 else if (panelWidth > windowWidth-21) {
591 panelWidth = windowWidth-21;
592 infoPanel.style.left = '21px';
593 }
594 else
595 infoPanel.style.left = (ev.clientX-msoff)+"px";
596
597 canvas.style.marginRight = panelWidth+20+"px";
598 return false;
599 }
600 };
601
602 document.onmouseup = function() { md = false; };
603
604 window.onresize = function() {
605 windowWidth = document.getElementById('sizetest').clientWidth;
606 infoPanel.style.left = windowWidth-panelWidth+'px';
607 };
608 };
609
610 window.onload = function() {
611 canvas = document.getElementById('v3map');
612
613 initDetailPanels();
614
615 var scale = 0.25;
616 var coy = canvas.offsetTop;
617 function findDecision(ev) {
618 var x = (ev.clientX+window.pageXOffset)/scale;
619 var y = (ev.clientY+window.pageYOffset-coy)/scale;
620
621 for (var i = trace.length-1; i >= 0; i--) {
622 if (x >= trace[i].x-19 && x <= trace[i].x+19 &&
623 y >= trace[i].y-19 && y <= trace[i].y+19)
624 return trace[i];
625 }
626 };
627
628 var preview = document.getElementById('preview');
629 var previewId = document.getElementById('previewid');
630 var previewCalls = document.getElementById('previewcalls');
631 function previewDecision(dec) {
632 preview.style.left = (dec.x*scale)+'px';
633 preview.style.top = (dec.y*scale+coy+15)+'px';
634 preview.style.display = 'block';
635 previewId.textContent = dec.d;
636
637 previewCalls.innerHTML = dec.previewCalls;
638 };
639
640 function overResponse(ev) {
641 var x = (ev.clientX+window.pageXOffset)/scale;
642 var y = (ev.clientY+window.pageYOffset-coy)/scale;
643
644 return (x >= response.x-(response.width/2)
645 && x <= response.x+(response.width/2)
646 && y >= response.y-19 && y <= response.y+19);
647 };
648
649 decorateTrace();
650
651 var bg = new Image(3138, 2184);
652
653 function drawMap() {
654 var ctx = canvas.getContext("2d");
655
656 ctx.save();
657 ctx.scale(1/scale, 1/scale);
658 ctx.fillStyle = '#ffffff';
659 ctx.fillRect(0, 0, 3138, 2184);
660 ctx.restore();
661
662 ctx.drawImage(bg, 0, 0);
663 drawTrace();
664 };
665
666 bg.onload = function() {
667 canvas.getContext("2d").scale(scale, scale);
668 drawMap(scale);
669
670 canvas.onmousemove = function(ev) {
671 if (findDecision(ev)) {
672 canvas.style.cursor = 'pointer';
673 previewDecision(findDecision(ev));
674 }
675 else {
676 preview.style.display = 'none';
677 if (overResponse(ev))
678 canvas.style.cursor = 'pointer';
679 else
680 canvas.style.cursor = 'default';
681 }
682 };
683
684 canvas.onclick = function(ev) {
685 var dec = findDecision(ev);
686 if (dec) {
687 detailPanels.setDecision(dec);
688 detailPanels.show('decision');
689 } else if (overResponse(ev)) {
690 detailPanels.show('response');
691 }
692 };
693
694 document.getElementById('zoomin').onclick = function() {
695 scale = scale*2;
696 canvas.getContext("2d").scale(2, 2);
697 drawMap();
698 };
699
700 document.getElementById('zoomout').onclick = function() {
701 scale = scale/2;
702 canvas.getContext("2d").scale(0.5, 0.5);
703 drawMap();
704 };
705 };
706
707 bg.onerror = function() {
708 alert('Failed to load background image.');
709 };
710
711 bg.src = 'static/map.png';
712 };
+0
-8
deps/webmachine/priv/www/index.html less more
0 <html>
1 <head>
2 <title>It Worked</title>
3 </head>
4 <body>
5 Running.
6 </body>
7 </html>
deps/webmachine/rebar less more
Binary diff not shown
+0
-9
deps/webmachine/rebar.config less more
0 %%-*- mode: erlang -*-
1 {erl_opts, [warnings_as_errors]}.
2 {cover_enabled, true}.
3 {edoc_opts, [{preprocess, true}]}.
4
5 {deps, [
6 {mochiweb, ".*", {git, "git://github.com/rabbitmq/mochiweb",
7 "845428379ed8b58eadc49aba26838d86ea809663"}}
8 ]}.
+0
-11
deps/webmachine/rebar.config.script less more
0 case erlang:system_info(otp_release) =< "R15B01" of
1 true ->
2 HashDefine = [{d,old_hash}],
3 case lists:keysearch(erl_opts, 1, CONFIG) of
4 {value, {erl_opts, Opts}} ->
5 lists:keyreplace(erl_opts,1,CONFIG,{erl_opts,Opts++HashDefine});
6 false ->
7 CONFIG ++ [{erl_opts, HashDefine}]
8 end;
9 false -> CONFIG
10 end.
+0
-39
deps/webmachine/scripts/new_webmachine.sh less more
0 #!/usr/bin/env bash
1
2 SCRIPT=${0##*/}
3 NAME=$1
4 DESTDIR=$2
5
6 usage() {
7 echo "usage: new_webmachine.sh name [destdir]"
8 }
9
10 if [ -z $NAME ] || [[ $NAME =~ ^[\.\~\/] ]]; then
11 usage
12 exit 1
13 fi
14
15 erl -noshell -eval 'halt(if is_atom('"$NAME"') -> 0; true -> 1 end).'
16 if [[ $? -ne 0 ]]; then
17 echo $SCRIPT: \""$NAME"\" is not allowed as a project name
18 echo ' The project name must begin with a lowercase letter and'
19 echo ' contain only alphanumeric characters and underscores.'
20 usage
21 exit 1
22 fi
23
24 if [ -z $DESTDIR ]; then
25 DESTDIR="."
26 elif [[ $DESTDIR =~ /${NAME}$ ]]; then
27 DESTDIR=${DESTDIR%/*}
28 fi
29
30 if [ ! -e $DESTDIR ]; then
31 $(mkdir -p $DESTDIR)
32 fi
33
34 ABSDEST=$(cd $DESTDIR && pwd)
35
36 cd ${0%/*}/../priv
37
38 ../rebar create template=wmskel appid=$NAME prefix=$ABSDEST/$NAME
+0
-8
deps/webmachine/src/webmachine.app.src less more
0 {application,webmachine,
1 [{description,"webmachine"},
2 {vsn,"1.10.3"},
3 {modules,[]},
4 {registered,[]},
5 {applications,[kernel,stdlib,mochiweb]},
6 {mod,{webmachine_app,[]}},
7 {env,[]}]}.
+0
-78
deps/webmachine/src/webmachine.erl less more
0 %% @author Justin Sheehy <justin@basho.com>
1 %% @author Andy Gross <andy@basho.com>
2 %% @copyright 2007-2009 Basho Technologies
3 %%
4 %% Licensed under the Apache License, Version 2.0 (the "License");
5 %% you may not use this file except in compliance with the License.
6 %% You may obtain a copy of the License at
7 %%
8 %% http://www.apache.org/licenses/LICENSE-2.0
9 %%
10 %% Unless required by applicable law or agreed to in writing, software
11 %% distributed under the License is distributed on an "AS IS" BASIS,
12 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 %% See the License for the specific language governing permissions and
14 %% limitations under the License.
15
16 -module(webmachine).
17 -author('Justin Sheehy <justin@basho.com>').
18 -author('Andy Gross <andy@basho.com>').
19 -export([start/0, stop/0]).
20 -export([new_request/2]).
21
22 -include("webmachine_logger.hrl").
23 -include("wm_reqstate.hrl").
24 -include("wm_reqdata.hrl").
25
26 %% @spec start() -> ok
27 %% @doc Start the webmachine server.
28 start() ->
29 webmachine_deps:ensure(),
30 application:start(webmachine).
31
32 %% @spec stop() -> ok
33 %% @doc Stop the webmachine server.
34 stop() ->
35 application:stop(webmachine).
36
37 new_request(mochiweb, Request) ->
38 Method = Request:get(method),
39 Scheme = Request:get(scheme),
40 Version = Request:get(version),
41 {Headers, RawPath} = case application:get_env(webmachine, rewrite_module) of
42 {ok, RewriteMod} ->
43 do_rewrite(RewriteMod,
44 Method,
45 Scheme,
46 Version,
47 Request:get(headers),
48 Request:get(raw_path));
49 undefined ->
50 {Request:get(headers), Request:get(raw_path)}
51 end,
52 Socket = Request:get(socket),
53 InitState = #wm_reqstate{socket=Socket,
54 reqdata=wrq:create(Method,Scheme,Version,RawPath,Headers)},
55
56 InitReq = {webmachine_request,InitState},
57 {Peer, ReqState} = InitReq:get_peer(),
58 PeerState = ReqState#wm_reqstate{reqdata=wrq:set_peer(Peer,
59 ReqState#wm_reqstate.reqdata)},
60 LogData = #wm_log_data{start_time=now(),
61 method=Method,
62 headers=Headers,
63 peer=PeerState#wm_reqstate.peer,
64 path=RawPath,
65 version=Version,
66 response_code=404,
67 response_length=0},
68 webmachine_request:new(PeerState#wm_reqstate{log_data=LogData}).
69
70 do_rewrite(RewriteMod, Method, Scheme, Version, Headers, RawPath) ->
71 case RewriteMod:rewrite(Method, Scheme, Version, Headers, RawPath) of
72 %% only raw path has been rewritten (older style rewriting)
73 NewPath when is_list(NewPath) -> {Headers, NewPath};
74
75 %% headers and raw path rewritten (new style rewriting)
76 {NewHeaders, NewPath} -> {NewHeaders,NewPath}
77 end.
+0
-50
deps/webmachine/src/webmachine_app.erl less more
0 %% @author Justin Sheehy <justin@basho.com>
1 %% @author Andy Gross <andy@basho.com>
2 %% @copyright 2007-2008 Basho Technologies
3 %%
4 %% Licensed under the Apache License, Version 2.0 (the "License");
5 %% you may not use this file except in compliance with the License.
6 %% You may obtain a copy of the License at
7 %%
8 %% http://www.apache.org/licenses/LICENSE-2.0
9 %%
10 %% Unless required by applicable law or agreed to in writing, software
11 %% distributed under the License is distributed on an "AS IS" BASIS,
12 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 %% See the License for the specific language governing permissions and
14 %% limitations under the License.
15
16 %% @doc Callbacks for the webmachine application.
17
18 -module(webmachine_app).
19 -author('Justin Sheehy <justin@basho.com>').
20 -author('Andy Gross <andy@basho.com>').
21
22 -behaviour(application).
23
24 -export([start/2,
25 stop/1]).
26
27 -include("webmachine_logger.hrl").
28
29 %% @spec start(_Type, _StartArgs) -> ServerRet
30 %% @doc application start callback for webmachine.
31 start(_Type, _StartArgs) ->
32 webmachine_deps:ensure(),
33 {ok, _Pid} = SupLinkRes = webmachine_sup:start_link(),
34 Handlers = case application:get_env(webmachine, log_handlers) of
35 undefined ->
36 [];
37 {ok, Val} ->
38 Val
39 end,
40 %% handlers failing to start are handled in the handler_watcher
41 _ = [supervisor:start_child(webmachine_logger_watcher_sup,
42 [?EVENT_LOGGER, Module, Config]) ||
43 {Module, Config} <- Handlers],
44 SupLinkRes.
45
46 %% @spec stop(_State) -> ServerRet
47 %% @doc application stop callback for webmachine.
48 stop(_State) ->
49 ok.
+0
-764
deps/webmachine/src/webmachine_decision_core.erl less more
0 %% @author Justin Sheehy <justin@basho.com>
1 %% @author Andy Gross <andy@basho.com>
2 %% @author Bryan Fink <bryan@basho.com>
3 %% @copyright 2007-2009 Basho Technologies
4 %%
5 %% Licensed under the Apache License, Version 2.0 (the "License");
6 %% you may not use this file except in compliance with the License.
7 %% You may obtain a copy of the License at
8 %%
9 %% http://www.apache.org/licenses/LICENSE-2.0
10 %%
11 %% Unless required by applicable law or agreed to in writing, software
12 %% distributed under the License is distributed on an "AS IS" BASIS,
13 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 %% See the License for the specific language governing permissions and
15 %% limitations under the License.
16
17 %% @doc Decision core for webmachine
18
19 -module(webmachine_decision_core).
20 -author('Justin Sheehy <justin@basho.com>').
21 -author('Andy Gross <andy@basho.com>').
22 -author('Bryan Fink <bryan@basho.com>').
23 -export([handle_request/2]).
24 -export([do_log/1]).
25 -include("webmachine_logger.hrl").
26
27 handle_request(Resource, ReqState) ->
28 [erase(X) || X <- [decision, code, req_body, bytes_written, tmp_reqstate]],
29 put(resource, Resource),
30 put(reqstate, ReqState),
31 try
32 d(v3b13)
33 catch
34 error:_ ->
35 error_response(erlang:get_stacktrace())
36 end.
37
38 wrcall(X) ->
39 RS0 = get(reqstate),
40 Req = webmachine_request:new(RS0),
41 {Response, RS1} = Req:call(X),
42 put(reqstate, RS1),
43 Response.
44
45 resource_call(Fun) ->
46 Resource = get(resource),
47 {Reply, NewResource, NewRS} = Resource:do(Fun,get()),
48 put(resource, NewResource),
49 put(reqstate, NewRS),
50 Reply.
51
52 get_header_val(H) -> wrcall({get_req_header, H}).
53
54 method() -> wrcall(method).
55
56 d(DecisionID) ->
57 put(decision, DecisionID),
58 log_decision(DecisionID),
59 decision(DecisionID).
60
61 respond(Code) when is_integer(Code) ->
62 respond({Code, undefined});
63 respond({_, _}=CodeAndPhrase) ->
64 Resource = get(resource),
65 EndTime = now(),
66 respond(CodeAndPhrase, Resource, EndTime).
67
68 respond({Code, _ReasonPhrase}=CodeAndPhrase, Resource, EndTime)
69 when Code >= 400, Code < 600 ->
70 error_response(CodeAndPhrase, Resource, EndTime);
71 respond({304, _ReasonPhrase}=CodeAndPhrase, Resource, EndTime) ->
72 wrcall({remove_resp_header, "Content-Type"}),
73 case resource_call(generate_etag) of
74 undefined -> nop;
75 ETag -> wrcall({set_resp_header, "ETag", webmachine_util:quoted_string(ETag)})
76 end,
77 case resource_call(expires) of
78 undefined -> nop;
79 Exp ->
80 wrcall({set_resp_header, "Expires",
81 webmachine_util:rfc1123_date(Exp)})
82 end,
83 finish_response(CodeAndPhrase, Resource, EndTime);
84 respond(CodeAndPhrase, Resource, EndTime) ->
85 finish_response(CodeAndPhrase, Resource, EndTime).
86
87 finish_response({Code, _}=CodeAndPhrase, Resource, EndTime) ->
88 put(code, Code),
89 wrcall({set_response_code, CodeAndPhrase}),
90 resource_call(finish_request),
91 wrcall({send_response, CodeAndPhrase}),
92 RMod = wrcall({get_metadata, 'resource_module'}),
93 Notes = wrcall(notes),
94 LogData0 = wrcall(log_data),
95 LogData = LogData0#wm_log_data{resource_module=RMod,
96 end_time=EndTime,
97 notes=Notes},
98 spawn(fun() -> do_log(LogData) end),
99 Resource:stop().
100
101 error_response(Reason) ->
102 error_response(500, Reason).
103
104 error_response(Code, Reason) ->
105 Resource = get(resource),
106 EndTime = now(),
107 error_response({Code, undefined}, Reason, Resource, EndTime).
108
109 error_response({Code, _}=CodeAndPhrase, Resource, EndTime) ->
110 error_response({Code, _}=CodeAndPhrase,
111 webmachine_error:reason(Code),
112 Resource,
113 EndTime).
114
115 error_response({Code, _}=CodeAndPhrase, Reason, Resource, EndTime) ->
116 {ok, ErrorHandler} = application:get_env(webmachine, error_handler),
117 {ErrorHTML, ReqState} = ErrorHandler:render_error(
118 Code, {webmachine_request,get(reqstate)}, Reason),
119 put(reqstate, ReqState),
120 wrcall({set_resp_body, ErrorHTML}),
121 finish_response(CodeAndPhrase, Resource, EndTime).
122
123 decision_test(Test,TestVal,TrueFlow,FalseFlow) ->
124 case Test of
125 {error, Reason} -> error_response(Reason);
126 {error, Reason0, Reason1} -> error_response({Reason0, Reason1});
127 {halt, Code} -> respond(Code);
128 TestVal -> decision_flow(TrueFlow, Test);
129 _ -> decision_flow(FalseFlow, Test)
130 end.
131
132 decision_test_fn({error, Reason}, _TestFn, _TrueFlow, _FalseFlow) ->
133 error_response(Reason);
134 decision_test_fn({error, R0, R1}, _TestFn, _TrueFlow, _FalseFlow) ->
135 error_response({R0, R1});
136 decision_test_fn({halt, Code}, _TestFn, _TrueFlow, _FalseFlow) ->
137 respond(Code);
138 decision_test_fn(Test,TestFn,TrueFlow,FalseFlow) ->
139 case TestFn(Test) of
140 true -> decision_flow(TrueFlow, Test);
141 false -> decision_flow(FalseFlow, Test)
142 end.
143
144 decision_flow(X, TestResult) when is_integer(X) ->
145 if X >= 500 -> error_response(X, TestResult);
146 true -> respond(X)
147 end;
148 decision_flow(X, _TestResult) when is_atom(X) -> d(X).
149
150 do_log(LogData) ->
151 webmachine_log:log_access(LogData).
152
153 log_decision(DecisionID) ->
154 Resource = get(resource),
155 Resource:log_d(DecisionID).
156
157 %% "Service Available"
158 decision(v3b13) ->
159 decision_test(resource_call(ping), pong, v3b13b, 503);
160 decision(v3b13b) ->
161 decision_test(resource_call(service_available), true, v3b12, 503);
162 %% "Known method?"
163 decision(v3b12) ->
164 decision_test(lists:member(method(), resource_call(known_methods)),
165 true, v3b11, 501);
166 %% "URI too long?"
167 decision(v3b11) ->
168 decision_test(resource_call(uri_too_long), true, 414, v3b10);
169 %% "Method allowed?"
170 decision(v3b10) ->
171 Methods = resource_call(allowed_methods),
172 case lists:member(method(), Methods) of
173 true ->
174 d(v3b9);
175 false ->
176 wrcall({set_resp_headers, [{"Allow",
177 string:join([atom_to_list(M) || M <- Methods], ", ")}]}),
178 respond(405)
179 end;
180
181 %% "Content-MD5 present?"
182 decision(v3b9) ->
183 decision_test(get_header_val("content-md5"), undefined, v3b9b, v3b9a);
184 %% "Content-MD5 valid?"
185 decision(v3b9a) ->
186 case resource_call(validate_content_checksum) of
187 {error, Reason} ->
188 error_response(Reason);
189 {halt, Code} ->
190 respond(Code);
191 not_validated ->
192 Checksum = base64:decode(get_header_val("content-md5")),
193 BodyHash = compute_body_md5(),
194 case BodyHash =:= Checksum of
195 true -> d(v3b9b);
196 _ ->
197 respond(400)
198 end;
199 false ->
200 respond(400);
201 _ -> d(v3b9b)
202 end;
203 %% "Malformed?"
204 decision(v3b9b) ->
205 decision_test(resource_call(malformed_request), true, 400, v3b8);
206 %% "Authorized?"
207 decision(v3b8) ->
208 case resource_call(is_authorized) of
209 true -> d(v3b7);
210 {error, Reason} ->
211 error_response(Reason);
212 {halt, Code} ->
213 respond(Code);
214 AuthHead ->
215 wrcall({set_resp_header, "WWW-Authenticate", AuthHead}),
216 respond(401)
217 end;
218 %% "Forbidden?"
219 decision(v3b7) ->
220 decision_test(resource_call(forbidden), true, 403, v3b6);
221 %% "Okay Content-* Headers?"
222 decision(v3b6) ->
223 decision_test(resource_call(valid_content_headers), true, v3b5, 501);
224 %% "Known Content-Type?"
225 decision(v3b5) ->
226 decision_test(resource_call(known_content_type), true, v3b4, 415);
227 %% "Req Entity Too Large?"
228 decision(v3b4) ->
229 decision_test(resource_call(valid_entity_length), true, v3b3, 413);
230 %% "OPTIONS?"
231 decision(v3b3) ->
232 case method() of
233 'OPTIONS' ->
234 Hdrs = resource_call(options),
235 wrcall({set_resp_headers, Hdrs}),
236 respond(200);
237 _ ->
238 d(v3c3)
239 end;
240 %% Accept exists?
241 decision(v3c3) ->
242 PTypes = [Type || {Type,_Fun} <- resource_call(content_types_provided)],
243 case get_header_val("accept") of
244 undefined ->
245 wrcall({set_metadata, 'content-type', hd(PTypes)}),
246 d(v3d4);
247 _ ->
248 d(v3c4)
249 end;
250 %% Acceptable media type available?
251 decision(v3c4) ->
252 PTypes = [Type || {Type,_Fun} <- resource_call(content_types_provided)],
253 AcceptHdr = get_header_val("accept"),
254 case webmachine_util:choose_media_type(PTypes, AcceptHdr) of
255 none ->
256 respond(406);
257 MType ->
258 wrcall({set_metadata, 'content-type', MType}),
259 d(v3d4)
260 end;
261 %% Accept-Language exists?
262 decision(v3d4) ->
263 decision_test(get_header_val("accept-language"),
264 undefined, v3e5, v3d5);
265 %% Acceptable Language available? %% WMACH-46 (do this as proper conneg)
266 decision(v3d5) ->
267 decision_test(resource_call(language_available), true, v3e5, 406);
268 %% Accept-Charset exists?
269 decision(v3e5) ->
270 case get_header_val("accept-charset") of
271 undefined -> decision_test(choose_charset("*"),
272 none, 406, v3f6);
273 _ -> d(v3e6)
274 end;
275 %% Acceptable Charset available?
276 decision(v3e6) ->
277 decision_test(choose_charset(get_header_val("accept-charset")),
278 none, 406, v3f6);
279 %% Accept-Encoding exists?
280 % (also, set content-type header here, now that charset is chosen)
281 decision(v3f6) ->
282 CType = wrcall({get_metadata, 'content-type'}),
283 CSet = case wrcall({get_metadata, 'chosen-charset'}) of
284 undefined -> "";
285 CS -> "; charset=" ++ CS
286 end,
287 wrcall({set_resp_header, "Content-Type", CType ++ CSet}),
288 case get_header_val("accept-encoding") of
289 undefined ->
290 decision_test(choose_encoding("identity;q=1.0,*;q=0.5"),
291 none, 406, v3g7);
292 _ -> d(v3f7)
293 end;
294 %% Acceptable encoding available?
295 decision(v3f7) ->
296 decision_test(choose_encoding(get_header_val("accept-encoding")),
297 none, 406, v3g7);
298 %% "Resource exists?"
299 decision(v3g7) ->
300 % this is the first place after all conneg, so set Vary here
301 case variances() of
302 [] -> nop;
303 Variances ->
304 wrcall({set_resp_header, "Vary", string:join(Variances, ", ")})
305 end,
306 decision_test(resource_call(resource_exists), true, v3g8, v3h7);
307 %% "If-Match exists?"
308 decision(v3g8) ->
309 decision_test(get_header_val("if-match"), undefined, v3h10, v3g9);
310 %% "If-Match: * exists"
311 decision(v3g9) ->
312 decision_test(get_header_val("if-match"), "*", v3h10, v3g11);
313 %% "ETag in If-Match"
314 decision(v3g11) ->
315 ETags = webmachine_util:split_quoted_strings(get_header_val("if-match")),
316 decision_test_fn(resource_call(generate_etag),
317 fun(ETag) -> lists:member(ETag, ETags) end,
318 v3h10, 412);
319 %% "If-Match exists"
320 %% (note: need to reflect this change at in next version of diagram)
321 decision(v3h7) ->
322 decision_test(get_header_val("if-match"), undefined, v3i7, 412);
323 %% "If-unmodified-since exists?"
324 decision(v3h10) ->
325 decision_test(get_header_val("if-unmodified-since"),undefined,v3i12,v3h11);
326 %% "I-UM-S is valid date?"
327 decision(v3h11) ->
328 IUMSDate = get_header_val("if-unmodified-since"),
329 decision_test(webmachine_util:convert_request_date(IUMSDate),
330 bad_date, v3i12, v3h12);
331 %% "Last-Modified > I-UM-S?"
332 decision(v3h12) ->
333 ReqDate = get_header_val("if-unmodified-since"),
334 ReqErlDate = webmachine_util:convert_request_date(ReqDate),
335 ResErlDate = resource_call(last_modified),
336 decision_test(ResErlDate > ReqErlDate,
337 true, 412, v3i12);
338 %% "Moved permanently? (apply PUT to different URI)"
339 decision(v3i4) ->
340 case resource_call(moved_permanently) of
341 {true, MovedURI} ->
342 wrcall({set_resp_header, "Location", MovedURI}),
343 respond(301);
344 false ->
345 d(v3p3);
346 {error, Reason} ->
347 error_response(Reason);
348 {halt, Code} ->
349 respond(Code)
350 end;
351 %% PUT?
352 decision(v3i7) ->
353 decision_test(method(), 'PUT', v3i4, v3k7);
354 %% "If-none-match exists?"
355 decision(v3i12) ->
356 decision_test(get_header_val("if-none-match"), undefined, v3l13, v3i13);
357 %% "If-None-Match: * exists?"
358 decision(v3i13) ->
359 decision_test(get_header_val("if-none-match"), "*", v3j18, v3k13);
360 %% GET or HEAD?
361 decision(v3j18) ->
362 decision_test(lists:member(method(),['GET','HEAD']),
363 true, 304, 412);
364 %% "Moved permanently?"
365 decision(v3k5) ->
366 case resource_call(moved_permanently) of
367 {true, MovedURI} ->
368 wrcall({set_resp_header, "Location", MovedURI}),
369 respond(301);
370 false ->
371 d(v3l5);
372 {error, Reason} ->
373 error_response(Reason);
374 {halt, Code} ->
375 respond(Code)
376 end;
377 %% "Previously existed?"
378 decision(v3k7) ->
379 decision_test(resource_call(previously_existed), true, v3k5, v3l7);
380 %% "Etag in if-none-match?"
381 decision(v3k13) ->
382 ETags = webmachine_util:split_quoted_strings(get_header_val("if-none-match")),
383 decision_test_fn(resource_call(generate_etag),
384 %% Membership test is a little counter-intuitive here; if the
385 %% provided ETag is a member, we follow the error case out
386 %% via v3j18.
387 fun(ETag) -> lists:member(ETag, ETags) end,
388 v3j18, v3l13);
389 %% "Moved temporarily?"
390 decision(v3l5) ->
391 case resource_call(moved_temporarily) of
392 {true, MovedURI} ->
393 wrcall({set_resp_header, "Location", MovedURI}),
394 respond(307);
395 false ->
396 d(v3m5);
397 {error, Reason} ->
398 error_response(Reason);
399 {halt, Code} ->
400 respond(Code)
401 end;
402 %% "POST?"
403 decision(v3l7) ->
404 decision_test(method(), 'POST', v3m7, 404);
405 %% "IMS exists?"
406 decision(v3l13) ->
407 decision_test(get_header_val("if-modified-since"), undefined, v3m16, v3l14);
408 %% "IMS is valid date?"
409 decision(v3l14) ->
410 IMSDate = get_header_val("if-modified-since"),
411 decision_test(webmachine_util:convert_request_date(IMSDate),
412 bad_date, v3m16, v3l15);
413 %% "IMS > Now?"
414 decision(v3l15) ->
415 NowDateTime = calendar:universal_time(),
416 ReqDate = get_header_val("if-modified-since"),
417 ReqErlDate = webmachine_util:convert_request_date(ReqDate),
418 decision_test(ReqErlDate > NowDateTime,
419 true, v3m16, v3l17);
420 %% "Last-Modified > IMS?"
421 decision(v3l17) ->
422 ReqDate = get_header_val("if-modified-since"),
423 ReqErlDate = webmachine_util:convert_request_date(ReqDate),
424 ResErlDate = resource_call(last_modified),
425 decision_test(ResErlDate =:= undefined orelse ResErlDate > ReqErlDate,
426 true, v3m16, 304);
427 %% "POST?"
428 decision(v3m5) ->
429 decision_test(method(), 'POST', v3n5, 410);
430 %% "Server allows POST to missing resource?"
431 decision(v3m7) ->
432 decision_test(resource_call(allow_missing_post), true, v3n11, 404);
433 %% "DELETE?"
434 decision(v3m16) ->
435 decision_test(method(), 'DELETE', v3m20, v3n16);
436 %% DELETE enacted immediately?
437 %% Also where DELETE is forced.
438 decision(v3m20) ->
439 Result = resource_call(delete_resource),
440 %% DELETE may have body and TCP connection will be closed unless body is read.
441 %% See mochiweb_request:should_close.
442 maybe_flush_body_stream(),
443 decision_test(Result, true, v3m20b, 500);
444 decision(v3m20b) ->
445 decision_test(resource_call(delete_completed), true, v3o20, 202);
446 %% "Server allows POST to missing resource?"
447 decision(v3n5) ->
448 decision_test(resource_call(allow_missing_post), true, v3n11, 410);
449 %% "Redirect?"
450 decision(v3n11) ->
451 Stage1 = case resource_call(post_is_create) of
452 true ->
453 case resource_call(create_path) of
454 undefined -> error_response("post_is_create w/o create_path");
455 NewPath ->
456 case is_list(NewPath) of
457 false -> error_response({"create_path not a string", NewPath});
458 true ->
459 BaseUri = case resource_call(base_uri) of
460 undefined -> wrcall(base_uri);
461 Any ->
462 case [lists:last(Any)] of
463 "/" -> lists:sublist(Any, erlang:length(Any) - 1);
464 _ -> Any
465 end
466 end,
467 FullPath = filename:join(["/", wrcall(path), NewPath]),
468 wrcall({set_disp_path, NewPath}),
469 case wrcall({get_resp_header, "Location"}) of
470 undefined -> wrcall({set_resp_header, "Location", BaseUri ++ FullPath});
471 _ -> ok
472 end,
473
474 Res = accept_helper(),
475 case Res of
476 {respond, Code} -> respond(Code);
477 {halt, Code} -> respond(Code);
478 {error, _,_} -> error_response(Res);
479 {error, _} -> error_response(Res);
480 _ -> stage1_ok
481 end
482 end
483 end;
484 _ ->
485 case resource_call(process_post) of
486 true ->
487 encode_body_if_set(),
488 stage1_ok;
489 {halt, Code} -> respond(Code);
490 Err -> error_response(Err)
491 end
492 end,
493 case Stage1 of
494 stage1_ok ->
495 case wrcall(resp_redirect) of
496 true ->
497 case wrcall({get_resp_header, "Location"}) of
498 undefined ->
499 Reason = "Response had do_redirect but no Location",
500 error_response(500, Reason);
501 _ ->
502 respond(303)
503 end;
504 _ ->
505 d(v3p11)
506 end;
507 _ -> nop
508 end;
509 %% "POST?"
510 decision(v3n16) ->
511 decision_test(method(), 'POST', v3n11, v3o16);
512 %% Conflict?
513 decision(v3o14) ->
514 case resource_call(is_conflict) of
515 true -> respond(409);
516 _ -> Res = accept_helper(),
517 case Res of
518 {respond, Code} -> respond(Code);
519 {halt, Code} -> respond(Code);
520 {error, _,_} -> error_response(Res);
521 {error, _} -> error_response(Res);
522 _ -> d(v3p11)
523 end
524 end;
525 %% "PUT?"
526 decision(v3o16) ->
527 decision_test(method(), 'PUT', v3o14, v3o18);
528 %% Multiple representations?
529 % (also where body generation for GET and HEAD is done)
530 decision(v3o18) ->
531 BuildBody = case method() of
532 'GET' -> true;
533 'HEAD' -> true;
534 _ -> false
535 end,
536 FinalBody = case BuildBody of
537 true ->
538 case resource_call(generate_etag) of
539 undefined -> nop;
540 ETag -> wrcall({set_resp_header, "ETag", webmachine_util:quoted_string(ETag)})
541 end,
542 CT = wrcall({get_metadata, 'content-type'}),
543 case resource_call(last_modified) of
544 undefined -> nop;
545 LM ->
546 wrcall({set_resp_header, "Last-Modified",
547 webmachine_util:rfc1123_date(LM)})
548 end,
549 case resource_call(expires) of
550 undefined -> nop;
551 Exp ->
552 wrcall({set_resp_header, "Expires",
553 webmachine_util:rfc1123_date(Exp)})
554 end,
555 F = hd([Fun || {Type,Fun} <- resource_call(content_types_provided),
556 CT =:= webmachine_util:format_content_type(Type)]),
557 resource_call(F);
558 false -> nop
559 end,
560 case FinalBody of
561 {error, _} -> error_response(FinalBody);
562 {error, _,_} -> error_response(FinalBody);
563 {halt, Code} -> respond(Code);
564 nop -> d(v3o18b);
565 _ -> wrcall({set_resp_body,
566 encode_body(FinalBody)}),
567 d(v3o18b)
568 end;
569
570 decision(v3o18b) ->
571 decision_test(resource_call(multiple_choices), true, 300, 200);
572 %% Response includes an entity?
573 decision(v3o20) ->
574 decision_test(wrcall(has_resp_body), true, v3o18, 204);
575 %% Conflict?
576 decision(v3p3) ->
577 case resource_call(is_conflict) of
578 true -> respond(409);
579 _ -> Res = accept_helper(),
580 case Res of
581 {respond, Code} -> respond(Code);
582 {halt, Code} -> respond(Code);
583 {error, _,_} -> error_response(Res);
584 {error, _} -> error_response(Res);
585 _ -> d(v3p11)
586 end
587 end;
588
589 %% New resource? (at this point boils down to "has location header")
590 decision(v3p11) ->
591 case wrcall({get_resp_header, "Location"}) of
592 undefined -> d(v3o20);
593 _ -> respond(201)
594 end.
595
596 accept_helper() ->
597 accept_helper(get_header_val("Content-Type")).
598
599 accept_helper(undefined) ->
600 accept_helper("application/octet-stream");
601 accept_helper([]) ->
602 accept_helper("application/octet-stream");
603 accept_helper(CT) ->
604 {MT, MParams} = webmachine_util:media_type_to_detail(CT),
605 wrcall({set_metadata, 'mediaparams', MParams}),
606 case [Fun || {Type,Fun} <-
607 resource_call(content_types_accepted), MT =:= Type] of
608 [] -> {respond,415};
609 AcceptedContentList ->
610 F = hd(AcceptedContentList),
611 case resource_call(F) of
612 true ->
613 encode_body_if_set(),
614 true;
615 Result -> Result
616 end
617 end.
618
619 encode_body_if_set() ->
620 case wrcall(has_resp_body) of
621 true ->
622 Body = wrcall(resp_body),
623 wrcall({set_resp_body, encode_body(Body)}),
624 true;
625 _ -> false
626 end.
627
628 encode_body(Body) ->
629 ChosenCSet = wrcall({get_metadata, 'chosen-charset'}),
630 Charsetter =
631 case resource_call(charsets_provided) of
632 no_charset -> fun(X) -> X end;
633 CP -> hd([Fun || {CSet,Fun} <- CP, ChosenCSet =:= CSet])
634 end,
635 ChosenEnc = wrcall({get_metadata, 'content-encoding'}),
636 Encoder = hd([Fun || {Enc,Fun} <- resource_call(encodings_provided),
637 ChosenEnc =:= Enc]),
638 case Body of
639 {stream, StreamBody} ->
640 {stream, make_encoder_stream(Encoder, Charsetter, StreamBody)};
641 {known_length_stream, 0, _StreamBody} ->
642 {known_length_stream, 0, empty_stream()};
643 {known_length_stream, Size, StreamBody} ->
644 case method() of
645 'HEAD' ->
646 {known_length_stream, Size, empty_stream()};
647 _ ->
648 {known_length_stream, Size, make_encoder_stream(Encoder, Charsetter, StreamBody)}
649 end;
650 {stream, Size, Fun} ->
651 {stream, Size, make_size_encoder_stream(Encoder, Charsetter, Fun)};
652 {writer, BodyFun} ->
653 {writer, {Encoder, Charsetter, BodyFun}};
654 _ ->
655 Encoder(Charsetter(iolist_to_binary(Body)))
656 end.
657
658 %% @private
659 empty_stream() ->
660 {<<>>, fun() -> {<<>>, done} end}.
661
662 make_encoder_stream(Encoder, Charsetter, {Body, done}) ->
663 {Encoder(Charsetter(Body)), done};
664 make_encoder_stream(Encoder, Charsetter, {Body, Next}) ->
665 {Encoder(Charsetter(Body)),
666 fun() -> make_encoder_stream(Encoder, Charsetter, Next()) end}.
667
668 make_size_encoder_stream(Encoder, Charsetter, Fun) ->
669 fun(Start, End) ->
670 make_encoder_stream(Encoder, Charsetter, Fun(Start, End))
671 end.
672
673 choose_encoding(AccEncHdr) ->
674 Encs = [Enc || {Enc,_Fun} <- resource_call(encodings_provided)],
675 case webmachine_util:choose_encoding(Encs, AccEncHdr) of
676 none -> none;
677 ChosenEnc ->
678 case ChosenEnc of
679 "identity" ->
680 nop;
681 _ ->
682 wrcall({set_resp_header, "Content-Encoding",ChosenEnc})
683 end,
684 wrcall({set_metadata, 'content-encoding',ChosenEnc}),
685 ChosenEnc
686 end.
687
688 choose_charset(AccCharHdr) ->
689 case resource_call(charsets_provided) of
690 no_charset ->
691 no_charset;
692 CL ->
693 CSets = [CSet || {CSet,_Fun} <- CL],
694 case webmachine_util:choose_charset(CSets, AccCharHdr) of
695 none -> none;
696 Charset ->
697 wrcall({set_metadata, 'chosen-charset',Charset}),
698 Charset
699 end
700 end.
701
702 variances() ->
703 Accept = case length(resource_call(content_types_provided)) of
704 1 -> [];
705 0 -> [];
706 _ -> ["Accept"]
707 end,
708 AcceptEncoding = case length(resource_call(encodings_provided)) of
709 1 -> [];
710 0 -> [];
711 _ -> ["Accept-Encoding"]
712 end,
713 AcceptCharset = case resource_call(charsets_provided) of
714 no_charset -> [];
715 CP ->
716 case length(CP) of
717 1 -> [];
718 0 -> [];
719 _ -> ["Accept-Charset"]
720 end
721 end,
722 Accept ++ AcceptEncoding ++ AcceptCharset ++ resource_call(variances).
723
724 md5(Bin) ->
725 erlang:md5(Bin).
726
727 md5_init() ->
728 erlang:md5_init().
729
730 md5_update(Ctx, Bin) ->
731 erlang:md5_update(Ctx, Bin).
732
733 md5_final(Ctx) ->
734 erlang:md5_final(Ctx).
735
736 compute_body_md5() ->
737 case wrcall({req_body, 52428800}) of
738 stream_conflict ->
739 compute_body_md5_stream();
740 Body ->
741 md5(Body)
742 end.
743
744 compute_body_md5_stream() ->
745 MD5Ctx = md5_init(),
746 compute_body_md5_stream(MD5Ctx, wrcall({stream_req_body, 8192}), <<>>).
747
748 compute_body_md5_stream(MD5, {Hunk, done}, Body) ->
749 %% Save the body so it can be retrieved later
750 put(reqstate, wrq:set_resp_body(Body, get(reqstate))),
751 md5_final(md5_update(MD5, Hunk));
752 compute_body_md5_stream(MD5, {Hunk, Next}, Body) ->
753 compute_body_md5_stream(md5_update(MD5, Hunk), Next(), <<Body/binary, Hunk/binary>>).
754
755 maybe_flush_body_stream() ->
756 maybe_flush_body_stream(wrcall({stream_req_body, 8192})).
757
758 maybe_flush_body_stream(stream_conflict) ->
759 ok;
760 maybe_flush_body_stream({_Hunk, done}) ->
761 ok;
762 maybe_flush_body_stream({_Hunk, Next}) ->
763 maybe_flush_body_stream(Next()).
+0
-91
deps/webmachine/src/webmachine_deps.erl less more
0 %% @author Justin Sheehy <justin@basho.com>
1 %% @author Andy Gross <andy@basho.com>
2 %% @copyright 2007-2008 Basho Technologies
3 %%
4 %% Licensed under the Apache License, Version 2.0 (the "License");
5 %% you may not use this file except in compliance with the License.
6 %% You may obtain a copy of the License at
7 %%
8 %% http://www.apache.org/licenses/LICENSE-2.0
9 %%
10 %% Unless required by applicable law or agreed to in writing, software
11 %% distributed under the License is distributed on an "AS IS" BASIS,
12 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 %% See the License for the specific language governing permissions and
14 %% limitations under the License.
15
16 %% @doc Ensure that the relatively-installed dependencies are on the code
17 %% loading path, and locate resources relative
18 %% to this application's path.
19
20 -module(webmachine_deps).
21 -author('Justin Sheehy <justin@basho.com>').
22 -author('Andy Gross <andy@basho.com>').
23
24 -export([ensure/0, ensure/1]).
25 -export([get_base_dir/0, get_base_dir/1]).
26 -export([local_path/1, local_path/2]).
27 -export([deps_on_path/0, new_siblings/1]).
28
29 %% @spec deps_on_path() -> [ProjNameAndVers]
30 %% @doc List of project dependencies on the path.
31 deps_on_path() ->
32 ordsets:from_list([filename:basename(filename:dirname(X)) || X <- code:get_path()]).
33
34 %% @spec new_siblings(Module) -> [Dir]
35 %% @doc Find new siblings paths relative to Module that aren't already on the
36 %% code path.
37 new_siblings(Module) ->
38 Existing = deps_on_path(),
39 SiblingEbin = [ X || X <- filelib:wildcard(local_path(["deps", "*", "ebin"], Module)),
40 filename:basename(filename:dirname(X)) /= %% don't include self
41 filename:basename(filename:dirname(
42 filename:dirname(
43 filename:dirname(X)))) ],
44 Siblings = [filename:dirname(X) || X <- SiblingEbin,
45 ordsets:is_element(
46 filename:basename(filename:dirname(X)),
47 Existing) =:= false],
48 lists:filter(fun filelib:is_dir/1,
49 lists:append([[filename:join([X, "ebin"]),
50 filename:join([X, "include"])] ||
51 X <- Siblings])).
52
53
54 %% @spec ensure(Module) -> ok
55 %% @doc Ensure that all ebin and include paths for dependencies
56 %% of the application for Module are on the code path.
57 ensure(Module) ->
58 code:add_paths(new_siblings(Module)),
59 ok.
60
61 %% @spec ensure() -> ok
62 %% @doc Ensure that the ebin and include paths for dependencies of
63 %% this application are on the code path. Equivalent to
64 %% ensure(?Module).
65 ensure() ->
66 ensure(?MODULE).
67
68 %% @spec get_base_dir(Module) -> string()
69 %% @doc Return the application directory for Module. It assumes Module is in
70 %% a standard OTP layout application in the ebin or src directory.
71 get_base_dir(Module) ->
72 {file, Here} = code:is_loaded(Module),
73 filename:dirname(filename:dirname(Here)).
74
75 %% @spec get_base_dir() -> string()
76 %% @doc Return the application directory for this application. Equivalent to
77 %% get_base_dir(?MODULE).
78 get_base_dir() ->
79 get_base_dir(?MODULE).
80
81 %% @spec local_path([string()], Module) -> string()
82 %% @doc Return an application-relative directory from Module's application.
83 local_path(Components, Module) ->
84 filename:join([get_base_dir(Module) | Components]).
85
86 %% @spec local_path(Components) -> string()
87 %% @doc Return an application-relative directory for this application.
88 %% Equivalent to local_path(Components, ?MODULE).
89 local_path(Components) ->
90 local_path(Components, ?MODULE).
+0
-564
deps/webmachine/src/webmachine_dispatcher.erl less more
0 %% @author Robert Ahrens <rahrens@basho.com>
1 %% @author Justin Sheehy <justin@basho.com>
2 %% @copyright 2007-2009 Basho Technologies
3 %%
4 %% Licensed under the Apache License, Version 2.0 (the "License");
5 %% you may not use this file except in compliance with the License.
6 %% You may obtain a copy of the License at
7 %%
8 %% http://www.apache.org/licenses/LICENSE-2.0
9 %%
10 %% Unless required by applicable law or agreed to in writing, software
11 %% distributed under the License is distributed on an "AS IS" BASIS,
12 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 %% See the License for the specific language governing permissions and
14 %% limitations under the License.
15
16 %% @doc Module for URL-dispatch by pattern matching.
17
18 -module(webmachine_dispatcher).
19 -author('Robert Ahrens <rahrens@basho.com>').
20 -author('Justin Sheehy <justin@basho.com>').
21 -author('Bryan Fink <bryan@basho.com>').
22
23 -export([dispatch/3, dispatch/4]).
24
25 -define(SEPARATOR, $\/).
26 -define(MATCH_ALL, '*').
27
28 %% @spec dispatch(Path::string(), DispatchList::[matchterm()],
29 %% wrq:reqdata()) ->
30 %% dispterm() | dispfail()
31 %% @doc Interface for URL dispatching.
32 %% See also http://bitbucket.org/justin/webmachine/wiki/DispatchConfiguration
33 dispatch(PathAsString, DispatchList, RD) ->
34 dispatch([], PathAsString, DispatchList, RD).
35
36 %% @spec dispatch(Host::string(), Path::string(),
37 %% DispatchList::[matchterm()], wrq:reqdata()) ->
38 %% dispterm() | dispfail()
39 %% @doc Interface for URL dispatching.
40 %% See also http://bitbucket.org/justin/webmachine/wiki/DispatchConfiguration
41 dispatch(HostAsString, PathAsString, DispatchList, RD) ->
42 Path = string:tokens(PathAsString, [?SEPARATOR]),
43 % URIs that end with a trailing slash are implicitly one token
44 % "deeper" than we otherwise might think as we are "inside"
45 % a directory named by the last token.
46 ExtraDepth = case lists:last(PathAsString) == ?SEPARATOR of
47 true -> 1;
48 _ -> 0
49 end,
50 {Host, Port} = split_host_port(HostAsString, wrq:scheme(RD)),
51 try_host_binding(DispatchList, Host, Port, Path, ExtraDepth, RD).
52
53 split_host_port(HostAsString, Scheme) ->
54 case string:tokens(HostAsString, ":") of
55 [HostPart, PortPart] ->
56 {split_host(HostPart), list_to_integer(PortPart)};
57 [HostPart] ->
58 {split_host(HostPart), default_port(Scheme)};
59 [] ->
60 %% no host header
61 {[], default_port(Scheme)};
62 _ ->
63 %% Invalid host header
64 {invalid_host, default_port(Scheme)}
65 end.
66
67 split_host(HostAsString) ->
68 string:tokens(HostAsString, ".").
69
70 default_port(http) -> 80;
71 default_port(https) -> 443.
72
73 %% @type matchterm() = hostmatchterm() | pathmatchterm().
74 % The dispatch configuration is a list of these terms, and the
75 % first one whose host and path terms match the input is used.
76 % Using a pathmatchterm() here is equivalent to using a hostmatchterm()
77 % of the form {{['*'],'*'}, [pathmatchterm()]}.
78
79 %% @type hostmatchterm() = {hostmatch(), [pathmatchterm()]}.
80 % The dispatch configuration contains a list of these terms, and the
81 % first one whose host and one pathmatchterm match is used.
82
83 %% @type hostmatch() = [hostterm()] | {[hostterm()], portterm()}.
84 % A host header (Host, X-Forwarded-For, etc.) will be matched against
85 % this term. Using a raws [hostterm()] list is equivalent to using
86 % {[hostterm()], '*'}.
87
88 %% @type hostterm() = '*' | string() | atom().
89 % A list of hostterms is matched against a '.'-separated hostname.
90 % The '*' hosterm matches all remaining tokens, and is only allowed at
91 % the head of the list.
92 % A string hostterm will match a token of exactly the same string.
93 % Any atom hostterm other than '*' will match any token and will
94 % create a binding in the result if a complete match occurs.
95
96 %% @type portterm() = '*' | integer() | atom().
97 % A portterm is matched against the integer port after any ':' in
98 % the hostname, or 80 if no port is found.
99 % The '*' portterm patches any port
100 % An integer portterm will match a port of exactly the same integer.
101 % Any atom portterm other than '*' will match any port and will
102 % create a binding in the result if a complete match occurs.
103
104 %% @type pathmatchterm() = {[pathterm()], matchmod(), matchopts()} |
105 %% {[pathterm()], guardfun(), matchmod(), matchopts()}.
106 % The dispatch configuration contains a list of these terms, and the
107 % first one whose list of pathterms matches the input path is used.
108
109 %% @type pathterm() = '*' | string() | atom().
110 % A list of pathterms is matched against a '/'-separated input path.
111 % The '*' pathterm matches all remaining tokens.
112 % A string pathterm will match a token of exactly the same string.
113 % Any atom pathterm other than '*' will match any token and will
114 % create a binding in the result if a complete match occurs.
115
116 %% @type guardfun() = (wrq:reqdata()) -> boolean()
117 %% | {Mod::atom(), Fun::atom()}.
118 % This function or tuple representing a function, if present, is
119 % called after a successful match of the host, port, and path for a
120 % dispatch entry. The function should take a single argument, the
121 % request data object, and return a boolean. If the return value is
122 % 'true', then this dispatch entry is used to service the
123 % request. Otherwise, webmachine will continue with the next dispatch
124 % entry.
125
126 %% @type matchmod() = atom().
127 % This atom, if present in a successful matchterm, will appear in
128 % the resulting dispterm. In Webmachine this is used to name the
129 % resource module that will handle the matching request.
130
131 %% @type matchopts() = [term()].
132 % This term, if present in a successful matchterm, will appear in
133 % the resulting dispterm. In Webmachine this is used to provide
134 % arguments to the resource module handling the matching request.
135
136 %% @type dispterm() = {matchmod(), matchopts(), pathtokens(),
137 %% bindings(), approot(), stringpath()}.
138
139 %% @type pathtokens() = [pathtoken()].
140 % This is the list of tokens matched by a trailing '*' pathterm.
141
142 %% @type pathtoken() = string().
143
144 %% @type bindings() = [{bindingterm(),pathtoken()}].
145 % This is a proplist of bindings indicated by atom terms in the
146 % matching spec, bound to the matching tokens in the request path.
147
148 %% @type approot() = string().
149
150 %% @type stringpath() = string().
151 % This is the path portion matched by a trailing '*' pathterm.
152
153 %% @type dispfail() = {no_dispatch_match, pathtokens()}.
154
155 try_host_binding(_Dispatch, invalid_host, _Port, _Path, _Depth, _RD) ->
156 {error, invalid_host};
157 try_host_binding(Dispatch, Host, Port, Path, Depth, RD) ->
158 %% save work during each dispatch attempt by reversing Host up front
159 try_host_binding1(Dispatch, lists:reverse(Host), Port, Path, Depth, RD).
160
161 try_host_binding1([], Host, Port, Path, _Depth, _RD) ->
162 %% Host was reversed inbound, correct it for result
163 {no_dispatch_match, {lists:reverse(Host), Port}, Path};
164 try_host_binding1([Dispatch|Rest], Host, Port, Path, Depth, RD) ->
165 {{HostSpec,PortSpec},PathSpec} =
166 case Dispatch of
167 {{H,P},S} -> {{H,P},S};
168 {H,S} -> {{H,?MATCH_ALL},S};
169 S -> {{[?MATCH_ALL],?MATCH_ALL},[S]}
170 end,
171 case bind_port(PortSpec, Port, []) of
172 {ok, PortBindings} ->
173 case bind(lists:reverse(HostSpec), Host, PortBindings, 0) of
174 {ok, RevHostRemainder, HostBindings, _} ->
175 %% Host was reversed inbound, correct it for remainder
176 HostRemainder = lists:reverse(RevHostRemainder),
177 case try_path_binding(PathSpec, Path, HostRemainder, Port, HostBindings, Depth, RD) of
178 {Mod, Props, PathRemainder, PathBindings,
179 AppRoot, StringPath} ->
180 {Mod, Props, HostRemainder, Port, PathRemainder,
181 PathBindings, AppRoot, StringPath};
182 {no_dispatch_match, _} ->
183 try_host_binding1(Rest, Host, Port, Path, Depth, RD)
184 end;
185 fail ->
186 try_host_binding1(Rest, Host, Port, Path, Depth, RD)
187 end;
188 fail ->
189 try_host_binding1(Rest, Host, Port, Path, Depth, RD)
190 end.
191
192 bind_port(Port, Port, Bindings) -> {ok, Bindings};
193 bind_port(?MATCH_ALL, _Port, Bindings) -> {ok, Bindings};
194 bind_port(PortAtom, Port, Bindings) when is_atom(PortAtom) ->
195 {ok, [{PortAtom, Port}|Bindings]};
196 bind_port(_, _, _) -> fail.
197
198 try_path_binding([], PathTokens, _, _, _, _, _) ->
199 {no_dispatch_match, PathTokens};
200 try_path_binding([PathSpec|Rest], PathTokens, HostRemainder, Port, HostBindings, ExtraDepth, RD) ->
201 {PathSchema, Guard, Mod, Props} =
202 case PathSpec of
203 {P, M, Pr} -> {P, undefined, M, Pr};
204 {P, G, M, Pr} -> {P, G, M, Pr}
205 end,
206
207 case bind(PathSchema, PathTokens, HostBindings, 0) of
208 {ok, Remainder, NewBindings, Depth} ->
209 AppRoot = calculate_app_root(Depth + ExtraDepth),
210 StringPath = reconstitute(Remainder),
211 PathInfo = orddict:from_list(NewBindings),
212 RD1 =
213 case RD of
214 testing_ignore_dialyzer_warning_here ->
215 testing_ignore_dialyzer_warning_here;
216 _ ->
217 wrq:load_dispatch_data(PathInfo, HostRemainder, Port, Remainder,
218 AppRoot, StringPath, RD)
219 end,
220 case run_guard(Guard, RD1) of
221 true ->
222 {Mod, Props, Remainder, NewBindings, AppRoot, StringPath};
223 false ->
224 try_path_binding(Rest, PathTokens, HostRemainder, Port, HostBindings, ExtraDepth, RD)
225 end;
226 fail ->
227 try_path_binding(Rest, PathTokens, HostRemainder, Port, HostBindings, ExtraDepth, RD)
228 end.
229
230 run_guard(undefined, _RD) ->
231 true;
232 run_guard(Fun, RD) when is_function(Fun) ->
233 try
234 Fun(RD) == true
235 catch _Type : Msg ->
236 error_logger:error_msg("Error running guard ~p: ~p~n", [Fun, Msg]),
237 throw({error_running_guard, Fun, Msg})
238 end;
239 run_guard({Mod, Fun}, RD) ->
240 try
241 Mod:Fun(RD) == true
242 catch _Type : Msg ->
243 error_logger:error_msg("Error running guard ~p:~p/1: ~p~n", [Mod, Fun, Msg]),
244 throw({error_running_guard, {Mod, Fun}, Msg})
245 end;
246 run_guard(Other, _) ->
247 error_logger:error_msg("Unknown guard type in webmachine_dispatcher: ~p~n", [Other]),
248 throw({unknown_guard_type, Other}).
249
250 bind([], [], Bindings, Depth) ->
251 {ok, [], Bindings, Depth};
252 bind([?MATCH_ALL], Rest, Bindings, Depth) when is_list(Rest) ->
253 {ok, Rest, Bindings, Depth + length(Rest)};
254 bind(_, [], _, _) ->
255 fail;
256 bind([Token|RestToken],[Match|RestMatch],Bindings,Depth) when is_atom(Token) ->
257 bind(RestToken, RestMatch, [{Token, Match}|Bindings], Depth + 1);
258 bind([Token|RestToken], [Token|RestMatch], Bindings, Depth) ->
259 bind(RestToken, RestMatch, Bindings, Depth + 1);
260 bind(_, _, _, _) ->
261 fail.
262
263 reconstitute([]) -> "";
264 reconstitute(UnmatchedTokens) -> string:join(UnmatchedTokens, [?SEPARATOR]).
265
266 calculate_app_root(1) -> ".";
267 calculate_app_root(N) when N > 1 ->
268 string:join(lists:duplicate(N, ".."), [?SEPARATOR]).
269
270 %%
271 %% TEST
272 %%
273 -ifdef(TEST).
274
275 -include_lib("eunit/include/eunit.hrl").
276 -include("wm_reqstate.hrl").
277 -include("wm_reqdata.hrl").
278
279 app_root_test() ->
280 ?assertEqual(".", calculate_app_root(1)),
281 ?assertEqual("../..", calculate_app_root(2)),
282 ?assertEqual("../../..", calculate_app_root(3)),
283 ?assertEqual("../../../..", calculate_app_root(4)).
284
285 reconstitute_test() ->
286 ?assertEqual("", reconstitute([])),
287 ?assertEqual("foo", reconstitute(["foo"])),
288 ?assertEqual("foo/bar", reconstitute(["foo","bar"])),
289 ?assertEqual("foo/bar/baz", reconstitute(["foo","bar","baz"])).
290
291 split_host_test() ->
292 ?assertEqual(["foo","bar","baz"], split_host("foo.bar.baz")).
293
294 split_host_port_test() ->
295 ?assertEqual({[], 80}, split_host_port("", http)),
296 ?assertEqual({["foo","bar","baz"], 80},
297 split_host_port("foo.bar.baz:80", http)),
298 ?assertEqual({["foo","bar","baz"], 1234},
299 split_host_port("foo.bar.baz:1234", http)),
300
301 ?assertEqual({[], 443}, split_host_port("", https)),
302 ?assertEqual({["foo","bar","baz"], 443},
303 split_host_port("foo.bar.baz", https)),
304 ?assertEqual({["foo","bar","baz"], 1234},
305 split_host_port("foo.bar.baz:1234", https)).
306
307 %% port binding
308 bind_port_simple_match_test() ->
309 ?assertEqual({ok, []}, bind_port(80, 80, [])),
310 ?assertEqual({ok, [{foo, bar}]},
311 bind_port(1234, 1234, [{foo, bar}])).
312
313 bind_port_matchall_test() ->
314 ?assertEqual({ok, []}, bind_port('*', 80, [])),
315 ?assertEqual({ok, [{foo, bar}]},
316 bind_port('*', 1234, [{foo, bar}])).
317
318 bind_port_match_test() ->
319 ?assertEqual({ok, [{foo, 80}]}, bind_port(foo, 80, [])),
320 {ok, WholeBinding} = bind_port(foo, 1234, [{bar, baz}]),
321 ?assertEqual(2, length(WholeBinding)),
322 ?assertEqual(1234, proplists:get_value(foo, WholeBinding)),
323 ?assertEqual(baz, proplists:get_value(bar, WholeBinding)).
324
325 ind_port_fail_test() ->
326 ?assertEqual(fail, bind_port(80, 1234, [])).
327
328 %% path binding
329
330 bind_path_empty_test() ->
331 ?assertEqual({ok, [], [], 0}, bind([], [], [], 0)),
332 ?assertEqual({ok, [], [{x,"a"}], 1},
333 bind([], [], [{x,"a"}], 1)).
334
335 bind_path_matchall_test() ->
336 ?assertEqual({ok, [], [], 1},
337 bind(['*'], [], [], 1)),
338 ?assertEqual({ok, ["a","b"], [], 2},
339 bind(['*'], ["a","b"], [], 0)).
340
341 bind_path_fail_longer_match_test() ->
342 ?assertEqual(fail, bind(["x"], [], [], 0)),
343 ?assertEqual(fail, bind([foo], [], [], 0)).
344
345 bind_path_with_binding_test() ->
346 ?assertEqual({ok, [], [{foo, "a"}], 1},
347 bind([foo], ["a"], [], 0)),
348 {ok, Rest, Bind, Depth} = bind([foo,'*'], ["a","b"], [{bar, baz}], 1),
349 ?assertEqual(["b"], Rest),
350 ?assertEqual(3, Depth),
351 ?assertEqual(2, length(Bind)),
352 ?assertEqual("a", proplists:get_value(foo, Bind)),
353 ?assertEqual(baz, proplists:get_value(bar, Bind)).
354
355 bind_path_string_match_test() ->
356 ?assertEqual({ok, [], [], 1},
357 bind(["a"], ["a"], [], 0)),
358 ?assertEqual({ok, [], [{foo, bar}], 4},
359 bind(["a","b","c"], ["a","b","c"], [{foo, bar}], 1)).
360
361 bind_path_string_fail_test() ->
362 ?assertEqual(fail, bind(["a"], ["b"], [], 0)),
363 ?assertEqual(fail, bind(["a","b"], ["a","c"], [], 0)).
364
365 try_path_matching_test() ->
366 RD = testing_ignore_dialyzer_warning_here,
367 ?assertEqual({bar, baz, [], [], ".", ""},
368 try_path_binding([{["foo"], bar, baz}], ["foo"], [], 80, [], 0, RD)),
369 Dispatch = [{["a", x], foo, bar},
370 {["b", y], baz, quux},
371 {["b", y, '*'], baz2, quux2}],
372 ?assertEqual({foo, bar, [], [{x, "c"}], "../..", []},
373 try_path_binding(Dispatch, ["a","c"], [], 80, [], 0, RD)),
374 ?assertEqual({baz, quux, [], [{y, "c"}], "../..", []},
375 try_path_binding(Dispatch, ["b","c"], [], 80, [], 0, RD)),
376 ?assertEqual({baz2, quux2, ["z"], [{y, "c"}], "../../..", "z"},
377 try_path_binding(Dispatch, ["b","c","z"], [], 80, [], 0, RD)),
378 ?assertEqual({baz2, quux2, ["z","v"], [{y, "c"}], "../../../..", "z/v"},
379 try_path_binding(Dispatch, ["b","c","z","v"], [], 80, [], 0, RD)).
380
381 try_path_failing_test() ->
382 RD = testing_ignore_dialyzer_warning_here,
383 ?assertEqual({no_dispatch_match, ["a"]},
384 try_path_binding([{["b"], x, y}], ["a"], [], 80, [], 0, RD)).
385
386 %% host binding
387
388 try_host_binding_nohosts_test() ->
389 RD = testing_ignore_dialyzer_warning_here,
390 PathDispatches = [{["a"], foo, bar},
391 {["b"], baz, quux}],
392 ?assertEqual(try_host_binding([{{['*'],'*'},PathDispatches}],
393 ["quux","baz"], 80, ["a"], 0, RD),
394 try_host_binding(PathDispatches,
395 ["quux","baz"], 80, ["a"], 0, RD)),
396 ?assertEqual(try_host_binding([{{['*'],'*'},PathDispatches}],
397 ["quux","baz"], 80, ["b"], 0, RD),
398 try_host_binding(PathDispatches,
399 ["quux","baz"], 80, ["b"], 0, RD)),
400 ?assertEqual(try_host_binding([ {{['*'],'*'},[D]} || D <- PathDispatches],
401 ["quux","baz"], 1234, ["a"], 0, RD),
402 try_host_binding(PathDispatches,
403 ["quux","baz"], 1234, ["a"], 0, RD)),
404 ?assertEqual(try_host_binding([ {{['*'],'*'},[D]} || D <- PathDispatches],
405 ["quux","baz"], 1234, ["b"], 0, RD),
406 try_host_binding(PathDispatches,
407 ["quux","baz"], 1234, ["b"], 0, RD)).
408
409 try_host_binding_noport_test() ->
410 RD = testing_ignore_dialyzer_warning_here,
411 Dispatch = [{["foo","bar"], [{["a"],x,y}]},
412 {["baz","quux"],[{["b"],z,q}]},
413 {[m,"quux"], [{["c"],r,s}]},
414 {['*',"quux"], [{["d"],t,u}]}],
415 ExplicitWildPort = [ {{H, '*'},P} || {H, P} <- Dispatch ],
416 ?assertEqual(try_host_binding(ExplicitWildPort,
417 ["bar","foo"], 80, ["a"], 0, RD),
418 try_host_binding(Dispatch,
419 ["bar","foo"], 80, ["a"], 0, RD)),
420 ?assertEqual(try_host_binding(ExplicitWildPort,
421 ["quux","baz"], 1234, ["b"], 0, RD),
422 try_host_binding(Dispatch,
423 ["quux","baz"], 1234, ["b"], 0, RD)),
424 ?assertEqual(try_host_binding(ExplicitWildPort,
425 ["quux","yes"], 81, ["c"], 0, RD),
426 try_host_binding(Dispatch,
427 ["quux","yes"], 81, ["c"], 0, RD)),
428 ?assertEqual(try_host_binding(ExplicitWildPort,
429 ["quux","no"], 82, ["d"], 0, RD),
430 try_host_binding(Dispatch,
431 ["quux","no"], 82, ["d"], 0, RD)).
432
433 try_host_binding_fullmatch_test() ->
434 RD = testing_ignore_dialyzer_warning_here,
435 Dispatch = [{{["foo","bar"],80},[{["a"],x,y}]},
436 {{[foo,"bar"],80}, [{["b"],z,q}]},
437 {{[foo,"bar"],baz}, [{["c"],r,s}]},
438 {{['*',"bar"],'*'}, [{["d"],t,u}]}],
439 ?assertEqual({x, y, [], 80, [], [], ".", ""},
440 try_host_binding(Dispatch,
441 ["foo","bar"], 80, ["a"], 0, RD)),
442 ?assertEqual({z, q, [], 80, [], [{foo,"baz"}], ".", ""},
443 try_host_binding(Dispatch,
444 ["baz","bar"], 80, ["b"], 0, RD)),
445 {Mod, Props, HostRemainder, Port, PathRemainder,
446 PathBindings, AppRoot, StringPath}=
447 try_host_binding(Dispatch, ["quux","bar"], 1234, ["c"], 0, RD),
448 ?assertEqual(r, Mod),
449 ?assertEqual(s, Props),
450 ?assertEqual("", HostRemainder),
451 ?assertEqual(1234, Port),
452 ?assertEqual([], PathRemainder),
453 ?assertEqual(2, length(PathBindings)),
454 ?assertEqual("quux", proplists:get_value(foo, PathBindings)),
455 ?assertEqual(1234, proplists:get_value(baz, PathBindings)),
456 ?assertEqual(".", AppRoot),
457 ?assertEqual("", StringPath),
458 ?assertEqual({t, u, ["foo","quux"], 80, [], [], ".", ""},
459 try_host_binding(Dispatch, ["foo","quux","bar"],80,["d"],0, RD)).
460
461 try_host_binding_wildcard_token_order_test() ->
462 RD = wrq:create('GET', http, {1,1}, "testing", mochiweb_headers:from_list([])),
463 Dispatch = [{{['*',"quux","com"],80},[{['*'],x,y}]}],
464 ?assertEqual({x,y,["foo","bar","baz"],80,[],[],".",""},
465 dispatch("foo.bar.baz.quux.com","/",Dispatch,RD)).
466
467 try_host_binding_fail_test() ->
468 RD = testing_ignore_dialyzer_warning_here,
469 ?assertEqual({no_dispatch_match, {["bar","foo"], 1234}, ["x","y","z"]},
470 try_host_binding([], ["bar","foo"], 1234, ["x","y","z"], 0, RD)).
471
472 dispatch_test() ->
473 RD = wrq:create('GET', http, {1,1}, "testing", mochiweb_headers:from_list([])),
474 TrueFun = fun(_) -> true end,
475 FalseFun = fun(_) -> false end,
476
477 ?assertEqual({x, y, [], 80, [], [], "../../..", ""},
478 dispatch("a/b/c",[{["a","b","c"],x,y}], RD)),
479 ?assertEqual({x, y, [], 80, [], [], "../../..", ""},
480 dispatch("a/b/c",[{["a","b","c"],TrueFun,x,y}], RD)),
481 ?assertEqual({no_dispatch_match, {[],80},["a","b","c"]},
482 dispatch("a/b/c",[{["a","b","c"],FalseFun,x,y}], RD)),
483 ?assertEqual({x, y, [], 80, [], [], "../../..", ""},
484 dispatch("foo.bar", "a/b/c",
485 [{{["foo","bar"],80},[{["a","b","c"],x,y}]}], RD)),
486 ?assertEqual({x, y, [], 1234, [], [], "../../..", ""},
487 dispatch("foo.bar:1234", "a/b/",
488 [{{["foo","bar"],1234},[{["a","b"],x,y}]}], RD)),
489 ?assertEqual({no_dispatch_match, {["baz","bar"],8000}, ["q","r"]},
490 dispatch("baz.bar:8000", "q/r",
491 [{{["foo","bar"],80},[{["a","b","c"],x,y}]}], RD)).
492
493 guard1_test() ->
494 %% Basic guard test. Match everything.
495 Guard = fun(_) -> true end,
496 DispatchList = [{['*'], Guard, foo, bar}],
497 ?assertEqual(
498 {foo, bar, [], 80, ["test"], [], ".", "test"},
499 dispatch("test", DispatchList, make_reqdata("/test"))),
500 ok.
501
502 guard2_test() ->
503 %% Basic guard test. Use guard to prevent all matches.
504 Guard = fun(_) -> false end,
505 DispatchList = [{['*'], Guard, foo, bar}],
506 ?assertEqual(
507 {no_dispatch_match, {[], 80}, ["test"]},
508 dispatch("test", DispatchList, make_reqdata("/test"))),
509 ok.
510
511 guard3_test() ->
512 %% Check that path_info and path_tokens are passed to the guard...
513 Guard =
514 fun(RD) ->
515 ?assertEqual("a", wrq:path_info(a, RD)),
516 ?assertEqual("b", wrq:path_info(b, RD)),
517 ?assertEqual("c", wrq:path_info(c, RD)),
518 ?assertEqual(["d", "e"], wrq:path_tokens(RD)),
519 true
520 end,
521 DispatchList = [{[a,b,c,'*'], Guard, foo, bar}],
522 ?assertEqual(
523 {foo,bar,[],80, ["d","e"],
524 [{c,"c"},{b,"b"},{a,"a"}],
525 "../../../../..","d/e"},
526 dispatch("a/b/c/d/e", DispatchList, make_reqdata("/a/b/c/d/e"))),
527 ok.
528
529 guard4_test() ->
530 %% Check that host and port are possed to the guard...
531 Guard =
532 fun(RD) ->
533 ?assertEqual("0", wrq:path_info(x, RD)),
534 ?assertEqual("0", wrq:path_info(y, RD)),
535 ?assertEqual("1", wrq:path_info(z, RD)),
536 ?assertEqual(80, wrq:port(RD)),
537 true
538 end,
539 DispatchList=
540 [{
541 {["127",x,y,z], 80},
542 [
543 {['*'], Guard, foo, bar}
544 ]
545 }],
546 ?assertEqual(
547 {foo,bar,[],80,
548 ["a","b","c","d","e"],
549 [{x,"0"},{y,"0"},{z,"1"}],
550 "../../../../..","a/b/c/d/e"},
551 dispatch("127.0.0.1", "a/b/c/d/e", DispatchList, make_reqdata("http://127.0.0.1:80/a/b/c/d/e"))),
552 ok.
553
554 make_reqdata(Path) ->
555 %% Helper function to construct a request and return the ReqData
556 %% object.
557 MochiReq = mochiweb_request:new(testing, 'GET', Path, {1, 1},
558 mochiweb_headers:make([])),
559 Req = webmachine:new_request(mochiweb, MochiReq),
560 {RD, _} = Req:get_reqdata(),
561 RD.
562
563 -endif.
+0
-75
deps/webmachine/src/webmachine_error.erl less more
0 %% Copyright (c) 2011-2013 Basho Technologies, Inc. All Rights Reserved.
1 %%
2 %% This file is provided to you under the Apache License,
3 %% Version 2.0 (the "License"); you may not use this file
4 %% except in compliance with the License. You may obtain
5 %% a copy of the License at
6 %%
7 %% http://www.apache.org/licenses/LICENSE-2.0
8 %%
9 %% Unless required by applicable law or agreed to in writing,
10 %% software distributed under the License is distributed on an
11 %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
12 %% KIND, either express or implied. See the License for the
13 %% specific language governing permissions and limitations
14 %% under the License.
15
16 %% @doc Default HTTP error reasons for webmachine error responsesf
17
18 -module(webmachine_error).
19
20 -export([reason/1]).
21
22 -spec reason(pos_integer()) -> string().
23 reason(400) ->
24 "Bad Request";
25 reason(401) ->
26 "Unauthorized";
27 reason(402) ->
28 "Payment Requested";
29 reason(403) ->
30 "Forbidden";
31 reason(404) ->
32 "Not Found";
33 reason(405) ->
34 "Method Not Allowed";
35 reason(406) ->
36 "Not Acceptable";
37 reason(407) ->
38 "Proxy Authentication Required";
39 reason(408) ->
40 "Request Timeout";
41 reason(409) ->
42 "Conflict";
43 reason(410) ->
44 "Gone";
45 reason(411) ->
46 "Length Required";
47 reason(412) ->
48 "Precondition Failed";
49 reason(413) ->
50 "Request Entity Too Large";
51 reason(414) ->
52 "Request-URI Too Long";
53 reason(415) ->
54 "Unsupported Media Type";
55 reason(416) ->
56 "Request Range Not Satisfiable";
57 reason(417) ->
58 "Expectation Failed";
59 reason(500) ->
60 "Internal Server Error";
61 reason(501) ->
62 "Not Implemented";
63 reason(502) ->
64 "Bad Gateway";
65 reason(503) ->
66 "Service Unavailable";
67 reason(504) ->
68 "Gateway Timeout";
69 reason(505) ->
70 "HTTP Version Not Supported";
71 reason(Code) when Code >= 400, Code < 500 ->
72 "Client Error";
73 reason(Code) when Code >= 500 ->
74 "Server Error".
+0
-96
deps/webmachine/src/webmachine_error_handler.erl less more
0 %% @author Justin Sheehy <justin@basho.com>
1 %% @author Andy Gross <andy@basho.com>
2 %% @author Jeremy Latt <jeremy@basho.com>
3 %% @copyright 2007-2008 Basho Technologies
4 %%
5 %% Licensed under the Apache License, Version 2.0 (the "License");
6 %% you may not use this file except in compliance with the License.
7 %% You may obtain a copy of the License at
8 %%
9 %% http://www.apache.org/licenses/LICENSE-2.0
10 %%
11 %% Unless required by applicable law or agreed to in writing, software
12 %% distributed under the License is distributed on an "AS IS" BASIS,
13 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 %% See the License for the specific language governing permissions and
15 %% limitations under the License.
16
17
18 %% @doc Some fairly minimal error message formatters.
19
20 -module(webmachine_error_handler).
21 -author('Justin Sheehy <justin@basho.com>').
22 -author('Andy Gross <andy@basho.com>').
23 -author('Jeremy Latt <jeremy@basho.com>').
24
25 -export([render_error/3]).
26
27 render_error(Code, Req, Reason) ->
28 case Req:has_response_body() of
29 {true,_} ->
30 maybe_log(Req, Reason),
31 Req:response_body();
32 {false,_} -> render_error_body(Code, Req:trim_state(), Reason)
33 end.
34
35 render_error_body(404, Req, _Reason) ->
36 {ok, ReqState} = Req:add_response_header("Content-Type", "text/html"),
37 {<<"<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD><BODY><H1>Not Found</H1>The requested document was not found on this server.<P><HR><ADDRESS>mochiweb+webmachine web server</ADDRESS></BODY></HTML>">>, ReqState};
38
39 render_error_body(500, Req, Reason) ->
40 {ok, ReqState} = Req:add_response_header("Content-Type", "text/html"),
41 maybe_log(Req, Reason),
42 STString = io_lib:format("~p", [Reason]),
43 ErrorStart = "<html><head><title>500 Internal Server Error</title></head><body><h1>Internal Server Error</h1>The server encountered an error while processing this request:<br><pre>",
44 ErrorEnd = "</pre><P><HR><ADDRESS>mochiweb+webmachine web server</ADDRESS></body></html>",
45 ErrorIOList = [ErrorStart,STString,ErrorEnd],
46 {erlang:iolist_to_binary(ErrorIOList), ReqState};
47
48 render_error_body(501, Req, _Reason) ->
49 {ok, ReqState} = Req:add_response_header("Content-Type", "text/html"),
50 {Method,_} = Req:method(),
51 error_logger:error_msg("Webmachine does not support method ~p~n",
52 [Method]),
53 ErrorStr = io_lib:format("<html><head><title>501 Not Implemented</title>"
54 "</head><body><h1>Not Implemented</h1>"
55 "The server does not support the ~p method.<br>"
56 "<P><HR><ADDRESS>mochiweb+webmachine web server"
57 "</ADDRESS></body></html>",
58 [Method]),
59 {erlang:iolist_to_binary(ErrorStr), ReqState};
60
61 render_error_body(503, Req, _Reason) ->
62 {ok, ReqState} = Req:add_response_header("Content-Type", "text/html"),
63 error_logger:error_msg("Webmachine cannot fulfill"
64 " the request at this time"),
65 ErrorStr = "<html><head><title>503 Service Unavailable</title>"
66 "</head><body><h1>Service Unavailable</h1>"
67 "The server is currently unable to handle "
68 "the request due to a temporary overloading "
69 "or maintenance of the server.<br>"
70 "<P><HR><ADDRESS>mochiweb+webmachine web server"
71 "</ADDRESS></body></html>",
72 {list_to_binary(ErrorStr), ReqState};
73
74 render_error_body(Code, Req, Reason) ->
75 {ok, ReqState} = Req:add_response_header("Content-Type", "text/html"),
76 ReasonPhrase = httpd_util:reason_phrase(Code),
77 Body = ["<html><head><title>",
78 integer_to_list(Code),
79 " ",
80 ReasonPhrase,
81 "</title></head><body><h1>",
82 ReasonPhrase,
83 "</h1>",
84 Reason,
85 "<p><hr><address>mochiweb+webmachine web server</address></body></html>"],
86 {iolist_to_binary(Body), ReqState}.
87
88 maybe_log(_Req, {error, {exit, normal, _Stack}}) ->
89 %% webmachine_request did an exit(normal), so suppress this
90 %% message. This usually happens when a chunked upload is
91 %% interrupted by network failure.
92 ok;
93 maybe_log(Req, Reason) ->
94 {Path,_} = Req:path(),
95 error_logger:error_msg("webmachine error: path=~p~n~p~n", [Path, Reason]).
+0
-239
deps/webmachine/src/webmachine_log.erl less more
0 %% Copyright (c) 2011-2012 Basho Technologies, Inc. All Rights Reserved.
1 %%
2 %% This file is provided to you under the Apache License,
3 %% Version 2.0 (the "License"); you may not use this file
4 %% except in compliance with the License. You may obtain
5 %% a copy of the License at
6 %%
7 %% http://www.apache.org/licenses/LICENSE-2.0
8 %%
9 %% Unless required by applicable law or agreed to in writing,
10 %% software distributed under the License is distributed on an
11 %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
12 %% KIND, either express or implied. See the License for the
13 %% specific language governing permissions and limitations
14 %% under the License.
15
16 %% @doc Helper functions for webmachine's default log handlers
17
18 -module(webmachine_log).
19
20 -include("webmachine_logger.hrl").
21
22 -export([add_handler/2,
23 call/2,
24 call/3,
25 datehour/0,
26 datehour/1,
27 defer_refresh/1,
28 delete_handler/1,
29 fix_log/2,
30 fmt_ip/1,
31 fmtnow/0,
32 log_access/1,
33 log_close/3,
34 log_open/1,
35 log_open/2,
36 log_write/2,
37 maybe_rotate/3,
38 month/1,
39 refresh/2,
40 suffix/1,
41 zeropad/2,
42 zone/0]).
43
44 -record(state, {hourstamp :: non_neg_integer(),
45 filename :: string(),
46 handle :: file:io_device()}).
47
48 %% @doc Add a handler to receive log events
49 -type add_handler_result() :: ok | {'EXIT', term()} | term().
50 -spec add_handler(atom() | {atom(), term()}, term()) -> add_handler_result().
51 add_handler(Mod, Args) ->
52 gen_event:add_handler(?EVENT_LOGGER, Mod, Args).
53
54 %% @doc Make a synchronous call directly to a specific event handler
55 %% module
56 -type error() :: {error, bad_module} | {'EXIT', term()} | term().
57 -spec call(atom(), term()) -> term() | error().
58 call(Mod, Msg) ->
59 gen_event:call(?EVENT_LOGGER, Mod, Msg).
60
61 %% @doc Make a synchronous call directly to a specific event handler
62 %% module
63 -spec call(atom(), term(), timeout()) -> term() | error().
64 call(Mod, Msg, Timeout) ->
65 gen_event:call(?EVENT_LOGGER, Mod, Msg, Timeout).
66
67 %% @doc Return a four-tuple containing year, month, day, and hour
68 %% of the current time.
69 -type datehour() :: {calendar:year(), calendar:month(), calendar:day(), calendar:hour()}.
70 -spec datehour() -> datehour().
71 datehour() ->
72 datehour(os:timestamp()).
73
74 %% @doc Return a four-tuple containing year, month, day, and hour
75 %% of the specified time.
76 -spec datehour(erlang:timestamp()) -> datehour().
77 datehour(TS) ->
78 {{Y, M, D}, {H, _, _}} = calendar:now_to_universal_time(TS),
79 {Y, M, D, H}.
80
81 %% @doc Defer the refresh of a log file.
82 -spec defer_refresh(atom()) -> {ok, timer:tref()} | {error, term()}.
83 defer_refresh(Mod) ->
84 {_, {_, M, S}} = calendar:universal_time(),
85 Time = 1000 * (3600 - ((M * 60) + S)),
86 timer:apply_after(Time, ?MODULE, refresh, [Mod, os:timestamp()]).
87
88 %% @doc Remove a log handler
89 -type delete_handler_result() :: term() | {error, module_not_found} | {'EXIT', term()}.
90 -spec delete_handler(atom() | {atom(), term()}) -> delete_handler_result().
91 delete_handler(Mod) ->
92 gen_event:delete_handler(?EVENT_LOGGER, Mod, []).
93
94 %% Seek backwards to the last valid log entry
95 -spec fix_log(file:io_device(), non_neg_integer()) -> ok.
96 fix_log(_FD, 0) ->
97 ok;
98 fix_log(FD, 1) ->
99 {ok, 0} = file:position(FD, 0),
100 ok;
101 fix_log(FD, Location) ->
102 case file:pread(FD, Location - 1, 1) of
103 {ok, [$\n | _]} ->
104 ok;
105 {ok, _} ->
106 fix_log(FD, Location - 1)
107 end.
108
109 %% @doc Format an IP address or host name
110 -spec fmt_ip(undefined | string() | inet:ip4_address() | inet:ip6_address()) -> string().
111 fmt_ip(IP) when is_tuple(IP) ->
112 inet_parse:ntoa(IP);
113 fmt_ip(undefined) ->
114 "0.0.0.0";
115 fmt_ip(HostName) ->
116 HostName.
117
118 %% @doc Format the current time into a string
119 -spec fmtnow() -> string().
120 fmtnow() ->
121 {{Year, Month, Date}, {Hour, Min, Sec}} = calendar:local_time(),
122 io_lib:format("[~2..0w/~s/~4..0w:~2..0w:~2..0w:~2..0w ~s]",
123 [Date,month(Month),Year, Hour, Min, Sec, zone()]).
124
125 %% @doc Notify registered log event handler of an access event.
126 -spec log_access(wm_log_data()) -> ok.
127 log_access(#wm_log_data{}=LogData) ->
128 gen_event:sync_notify(?EVENT_LOGGER, {log_access, LogData}).
129
130 %% @doc Close a log file.
131 -spec log_close(atom(), string(), file:io_device()) -> ok | {error, term()}.
132 log_close(Mod, Name, FD) ->
133 error_logger:info_msg("~p: closing log file: ~p~n", [Mod, Name]),
134 file:close(FD).
135
136 %% @doc Open a new log file for writing
137 -spec log_open(string()) -> {file:io_device(), non_neg_integer()}.
138 log_open(FileName) ->
139 DateHour = datehour(),
140 {log_open(FileName, DateHour), DateHour}.
141
142 %% @doc Open a new log file for writing
143 -spec log_open(string(), non_neg_integer()) -> file:io_device().
144 log_open(FileName, DateHour) ->
145 LogName = FileName ++ suffix(DateHour),
146 error_logger:info_msg("opening log file: ~p~n", [LogName]),
147 filelib:ensure_dir(LogName),
148 {ok, FD} = file:open(LogName, [read, write, raw]),
149 {ok, Location} = file:position(FD, eof),
150 fix_log(FD, Location),
151 file:truncate(FD),
152 FD.
153
154 -spec log_write(file:io_device(), iolist()) -> ok | {error, term()}.
155 log_write(FD, IoData) ->
156 file:write(FD, lists:flatten(IoData)).
157
158 %% @doc Rotate a log file if the hour it represents
159 %% has passed.
160 -spec maybe_rotate(atom(), erlang:timestamp(), #state{}) -> #state{}.
161 maybe_rotate(Mod, Time, State) ->
162 ThisHour = datehour(Time),
163 if ThisHour == State#state.hourstamp ->
164 State;
165 true ->
166 defer_refresh(Mod),
167 log_close(Mod, State#state.filename, State#state.handle),
168 Handle = log_open(State#state.filename, ThisHour),
169 State#state{hourstamp=ThisHour, handle=Handle}
170 end.
171
172 %% @doc Convert numeric month value to the abbreviation
173 -spec month(1..12) -> string().
174 month(1) ->
175 "Jan";
176 month(2) ->
177 "Feb";
178 month(3) ->
179 "Mar";
180 month(4) ->
181 "Apr";
182 month(5) ->
183 "May";
184 month(6) ->
185 "Jun";
186 month(7) ->
187 "Jul";
188 month(8) ->
189 "Aug";
190 month(9) ->
191 "Sep";
192 month(10) ->
193 "Oct";
194 month(11) ->
195 "Nov";
196 month(12) ->
197 "Dec".
198
199 %% @doc Make a synchronous call to instruct a log handler to refresh
200 %% itself.
201 -spec refresh(atom(), erlang:timestamp()) -> ok | {error, term()}.
202 refresh(Mod, Time) ->
203 call(Mod, {refresh, Time}, infinity).
204
205 -spec suffix(datehour()) -> string().
206 suffix({Y, M, D, H}) ->
207 YS = zeropad(Y, 4),
208 MS = zeropad(M, 2),
209 DS = zeropad(D, 2),
210 HS = zeropad(H, 2),
211 lists:flatten([$., YS, $_, MS, $_, DS, $_, HS]).
212
213 -spec zeropad(integer(), integer()) -> string().
214 zeropad(Num, MinLength) ->
215 NumStr = integer_to_list(Num),
216 zeropad_str(NumStr, MinLength - length(NumStr)).
217
218 -spec zeropad_str(string(), integer()) -> string().
219 zeropad_str(NumStr, Zeros) when Zeros > 0 ->
220 zeropad_str([$0 | NumStr], Zeros - 1);
221 zeropad_str(NumStr, _) ->
222 NumStr.
223
224 -spec zone() -> string().
225 zone() ->
226 Time = erlang:universaltime(),
227 LocalTime = calendar:universal_time_to_local_time(Time),
228 DiffSecs = calendar:datetime_to_gregorian_seconds(LocalTime) -
229 calendar:datetime_to_gregorian_seconds(Time),
230 zone((DiffSecs/3600)*100).
231
232 %% Ugly reformatting code to get times like +0000 and -1300
233
234 -spec zone(integer()) -> string().
235 zone(Val) when Val < 0 ->
236 io_lib:format("-~4..0w", [trunc(abs(Val))]);
237 zone(Val) when Val >= 0 ->
238 io_lib:format("+~4..0w", [trunc(abs(Val))]).
+0
-121
deps/webmachine/src/webmachine_log_handler.erl less more
0 %% Copyright (c) 2011-2013 Basho Technologies, Inc. All Rights Reserved.
1 %%
2 %% This file is provided to you under the Apache License,
3 %% Version 2.0 (the "License"); you may not use this file
4 %% except in compliance with the License. You may obtain
5 %% a copy of the License at
6 %%
7 %% http://www.apache.org/licenses/LICENSE-2.0
8 %%
9 %% Unless required by applicable law or agreed to in writing,
10 %% software distributed under the License is distributed on an
11 %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
12 %% KIND, either express or implied. See the License for the
13 %% specific language governing permissions and limitations
14 %% under the License.
15
16 %% @doc Default log handler for webmachine
17
18 -module(webmachine_log_handler).
19
20 -behaviour(gen_event).
21
22 %% gen_event callbacks
23 -export([init/1,
24 handle_call/2,
25 handle_event/2,
26 handle_info/2,
27 terminate/2,
28 code_change/3]).
29
30 -include("webmachine_logger.hrl").
31
32 -ifdef(TEST).
33 -include_lib("eunit/include/eunit.hrl").
34 -endif.
35
36 -record(state, {hourstamp, filename, handle}).
37
38 -define(FILENAME, "access.log").
39
40 %% ===================================================================
41 %% gen_event callbacks
42 %% ===================================================================
43
44 %% @private
45 init([BaseDir]) ->
46 webmachine_log:defer_refresh(?MODULE),
47 FileName = filename:join(BaseDir, ?FILENAME),
48 {Handle, DateHour} = webmachine_log:log_open(FileName),
49 {ok, #state{filename=FileName, handle=Handle, hourstamp=DateHour}}.
50
51 %% @private
52 handle_call({_Label, MRef, get_modules}, State) ->
53 {ok, {MRef, [?MODULE]}, State};
54 handle_call({refresh, Time}, State) ->
55 {ok, ok, webmachine_log:maybe_rotate(?MODULE, Time, State)};
56 handle_call(_Request, State) ->
57 {ok, ok, State}.
58
59 %% @private
60 handle_event({log_access, LogData}, State) ->
61 NewState = webmachine_log:maybe_rotate(?MODULE, os:timestamp(), State),
62 Msg = format_req(LogData),
63 webmachine_log:log_write(NewState#state.handle, Msg),
64 {ok, NewState};
65 handle_event(_Event, State) ->
66 {ok, State}.
67
68 %% @private
69 handle_info(_Info, State) ->
70 {ok, State}.
71
72 %% @private
73 terminate(_Reason, _State) ->
74 ok.
75
76 %% @private
77 code_change(_OldVsn, State, _Extra) ->
78 {ok, State}.
79
80 %% ===================================================================
81 %% Internal functions
82 %% ===================================================================
83
84 format_req(#wm_log_data{method=Method,
85 headers=Headers,
86 peer=Peer,
87 path=Path,
88 version=Version,
89 response_code=ResponseCode,
90 response_length=ResponseLength}) ->
91 User = "-",
92 Time = webmachine_log:fmtnow(),
93 Status = case ResponseCode of
94 {Code, _ReasonPhrase} when is_integer(Code) ->
95 integer_to_list(Code);
96 _ when is_integer(ResponseCode) ->
97 integer_to_list(ResponseCode);
98 _ ->
99 ResponseCode
100 end,
101 Length = integer_to_list(ResponseLength),
102 Referer =
103 case mochiweb_headers:get_value("Referer", Headers) of
104 undefined -> "";
105 R -> R
106 end,
107 UserAgent =
108 case mochiweb_headers:get_value("User-Agent", Headers) of
109 undefined -> "";
110 U -> U
111 end,
112 fmt_alog(Time, Peer, User, atom_to_list(Method), Path, Version,
113 Status, Length, Referer, UserAgent).
114
115 fmt_alog(Time, Ip, User, Method, Path, {VM,Vm},
116 Status, Length, Referrer, UserAgent) ->
117 [webmachine_log:fmt_ip(Ip), " - ", User, [$\s], Time, [$\s, $"], Method, " ", Path,
118 " HTTP/", integer_to_list(VM), ".", integer_to_list(Vm), [$",$\s],
119 Status, [$\s], Length, [$\s,$"], Referrer,
120 [$",$\s,$"], UserAgent, [$",$\n]].
+0
-88
deps/webmachine/src/webmachine_logger_watcher.erl less more
0 %% Copyright (c) 2011-2012 Basho Technologies, Inc. All Rights Reserved.
1 %%
2 %% This file is provided to you under the Apache License,
3 %% Version 2.0 (the "License"); you may not use this file
4 %% except in compliance with the License. You may obtain
5 %% a copy of the License at
6 %%
7 %% http://www.apache.org/licenses/LICENSE-2.0
8 %%
9 %% Unless required by applicable law or agreed to in writing,
10 %% software distributed under the License is distributed on an
11 %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
12 %% KIND, either express or implied. See the License for the
13 %% specific language governing permissions and limitations
14 %% under the License.
15
16 %% @doc A process that does a gen_event:add_sup_handler and attempts to re-add
17 %% event handlers when they exit.
18
19 %% @private
20
21 -module(webmachine_logger_watcher).
22
23 -behaviour(gen_server).
24
25 -ifdef(TEST).
26 -include_lib("eunit/include/eunit.hrl").
27 -endif.
28
29 %% callbacks
30 -export([init/1,
31 handle_call/3,
32 handle_cast/2,
33 handle_info/2,
34 terminate/2,
35 code_change/3]).
36
37 -export([start_link/3,
38 start/3]).
39
40 -record(state, {module, config, event}).
41
42 start_link(Event, Module, Config) ->
43 gen_server:start_link(?MODULE, [Event, Module, Config], []).
44
45 start(Event, Module, Config) ->
46 gen_server:start(?MODULE, [Event, Module, Config], []).
47
48 init([Event, Module, Config]) ->
49 install_handler(Event, Module, Config),
50 {ok, #state{event=Event, module=Module, config=Config}}.
51
52 handle_call(_Call, _From, State) ->
53 {reply, ok, State}.
54
55 handle_cast(_Request, State) ->
56 {noreply, State}.
57
58 handle_info({gen_event_EXIT, Module, normal}, #state{module=Module} = State) ->
59 {stop, normal, State};
60 handle_info({gen_event_EXIT, Module, shutdown}, #state{module=Module} = State) ->
61 {stop, normal, State};
62 handle_info({gen_event_EXIT, Module, _Reason}, #state{module=Module,
63 config=Config, event=Event} = State) ->
64 install_handler(Event, Module, Config),
65 {noreply, State};
66 handle_info(reinstall_handler, #state{module=Module, config=Config, event=Event} = State) ->
67 install_handler(Event, Module, Config),
68 {noreply, State};
69 handle_info(_Info, State) ->
70 {noreply, State}.
71
72 terminate(_Reason, _State) ->
73 ok.
74
75 code_change(_OldVsn, State, _Extra) ->
76 {ok, State}.
77
78 %% internal
79
80 install_handler(Event, Module, Config) ->
81 case gen_event:add_sup_handler(Event, Module, Config) of
82 ok ->
83 ok;
84 _Error ->
85 erlang:send_after(5000, self(), reinstall_handler),
86 ok
87 end.
+0
-39
deps/webmachine/src/webmachine_logger_watcher_sup.erl less more
0 %% Copyright (c) 2011-2012 Basho Technologies, Inc. All Rights Reserved.
1 %%
2 %% This file is provided to you under the Apache License,
3 %% Version 2.0 (the "License"); you may not use this file
4 %% except in compliance with the License. You may obtain
5 %% a copy of the License at
6 %%
7 %% http://www.apache.org/licenses/LICENSE-2.0
8 %%
9 %% Unless required by applicable law or agreed to in writing,
10 %% software distributed under the License is distributed on an
11 %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
12 %% KIND, either express or implied. See the License for the
13 %% specific language governing permissions and limitations
14 %% under the License.
15
16 %% @doc A supervisor for monitoring webmachine_logger_handler_watcher processes.
17
18 %% @private
19
20 -module(webmachine_logger_watcher_sup).
21
22 -behaviour(supervisor).
23
24 %% API
25 -export([start_link/0]).
26
27 %% Callbacks
28 -export([init/1]).
29
30 start_link() ->
31 supervisor:start_link({local, ?MODULE}, ?MODULE, []).
32
33 init([]) ->
34 {ok, {{simple_one_for_one, 10, 60},
35 [
36 {webmachine_logger_watcher, {webmachine_logger_watcher, start_link, []},
37 transient, 5000, worker, [webmachine_logger_watcher]}
38 ]}}.
+0
-170
deps/webmachine/src/webmachine_mochiweb.erl less more
0 %% @author Justin Sheehy <justin@basho.com>
1 %% @author Andy Gross <andy@basho.com>
2 %% @copyright 2007-2008 Basho Technologies
3 %%
4 %% Licensed under the Apache License, Version 2.0 (the "License");
5 %% you may not use this file except in compliance with the License.
6 %% You may obtain a copy of the License at
7 %%
8 %% http://www.apache.org/licenses/LICENSE-2.0
9 %%
10 %% Unless required by applicable law or agreed to in writing, software
11 %% distributed under the License is distributed on an "AS IS" BASIS,
12 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 %% See the License for the specific language governing permissions and
14 %% limitations under the License.
15
16 %% @doc Mochiweb interface for webmachine.
17 -module(webmachine_mochiweb).
18 -author('Justin Sheehy <justin@basho.com>').
19 -author('Andy Gross <andy@basho.com>').
20 -export([start/1, stop/0, loop/2]).
21
22 %% The `log_dir' option is deprecated, but remove it from the
23 %% options list if it is present
24 -define(WM_OPTIONS, [error_handler,
25 log_dir,
26 rewrite_module,
27 resource_module_option]).
28
29 -define (WM_OPTION_DEFAULTS, [{error_handler, webmachine_error_handler}]).
30
31 start(Options) ->
32 {DispatchList, PName, DGroup, WMOptions, OtherOptions} = get_wm_options(Options),
33 webmachine_router:init_routes(DGroup, DispatchList),
34 [application_set_unless_env_or_undef(K, V) || {K, V} <- WMOptions],
35 MochiName = list_to_atom(to_list(PName) ++ "_mochiweb"),
36 LoopFun = fun(X) -> loop(DGroup, X) end,
37 mochiweb_http:start([{name, MochiName}, {loop, LoopFun} | OtherOptions]).
38
39 stop() ->
40 {registered_name, PName} = process_info(self(), registered_name),
41 MochiName = list_to_atom(atom_to_list(PName) ++ "_mochiweb"),
42 mochiweb_http:stop(MochiName).
43
44 loop(Name, MochiReq) ->
45 Req = webmachine:new_request(mochiweb, MochiReq),
46 DispatchList = webmachine_router:get_routes(Name),
47 Host = case host_headers(Req) of
48 [H|_] -> H;
49 [] -> []
50 end,
51 {Path, _} = Req:path(),
52 {RD, _} = Req:get_reqdata(),
53
54 %% Run the dispatch code, catch any errors...
55 try webmachine_dispatcher:dispatch(Host, Path, DispatchList, RD) of
56 {error, invalid_host} ->
57 handle_error(400, "Invalid Host", Req);
58 {no_dispatch_match, _UnmatchedHost, _UnmatchedPathTokens} ->
59 handle_error(404, {none, none, []}, Req);
60 {Mod, ModOpts, HostTokens, Port, PathTokens, Bindings,
61 AppRoot, StringPath} ->
62 BootstrapResource = webmachine_resource:new(x,x,x,x),
63 {ok,RS1} = Req:load_dispatch_data(Bindings,HostTokens,Port,
64 PathTokens,AppRoot,StringPath),
65 XReq1 = {webmachine_request,RS1},
66 try
67 {ok, Resource} = BootstrapResource:wrap(Mod, ModOpts),
68 {ok,RS2} = XReq1:set_metadata('resource_module',
69 resource_module(Mod, ModOpts)),
70 webmachine_decision_core:handle_request(Resource, RS2)
71 catch
72 error:Error ->
73 handle_error(500, {error, Error}, Req)
74 end
75 catch
76 Type : Error ->
77 handle_error(500, {Type, Error}, Req)
78 end.
79
80 handle_error(Code, Error, Req) ->
81 {ok, ErrorHandler} = application:get_env(webmachine, error_handler),
82 {ErrorHTML,ReqState1} =
83 ErrorHandler:render_error(Code, Req, Error),
84 Req1 = {webmachine_request,ReqState1},
85 {ok,ReqState2} = Req1:append_to_response_body(ErrorHTML),
86 Req2 = {webmachine_request,ReqState2},
87 {ok,ReqState3} = Req2:send_response(Code),
88 Req3 = {webmachine_request,ReqState3},
89 {LogData,_ReqState4} = Req3:log_data(),
90 spawn(webmachine_log, log_access, [LogData]).
91
92 get_wm_option(OptName, {WMOptions, OtherOptions}) ->
93 {Value, UpdOtherOptions} =
94 handle_get_option_result(get_option(OptName, OtherOptions), OptName),
95 {[{OptName, Value} | WMOptions], UpdOtherOptions}.
96
97 handle_get_option_result({undefined, Options}, Name) ->
98 {proplists:get_value(Name, ?WM_OPTION_DEFAULTS), Options};
99 handle_get_option_result(GetOptRes, _) ->
100 GetOptRes.
101
102 get_wm_options(Options) ->
103 {DispatchList, Options1} = get_option(dispatch, Options),
104 {Name, Options2} =
105 case get_option(name, Options1) of
106 {undefined, Opts2} ->
107 {webmachine, Opts2};
108 NRes -> NRes
109 end,
110 {DGroup, Options3} =
111 case get_option(dispatch_group, Options2) of
112 {undefined, Opts3} ->
113 {default, Opts3};
114 RRes -> RRes
115 end,
116 {WMOptions, RestOptions} = lists:foldl(fun get_wm_option/2, {[], Options3}, ?WM_OPTIONS),
117 {DispatchList, Name, DGroup, WMOptions, RestOptions}.
118
119 get_option(Option, Options) ->
120 case lists:keytake(Option, 1, Options) of
121 false -> {undefined, Options};
122 {value, {Option, Value}, NewOptions} -> {Value, NewOptions}
123 end.
124
125 application_set_unless_env_or_undef(_Var, undefined) ->
126 ok;
127 application_set_unless_env_or_undef(Var, Value) ->
128 application_set_unless_env(webmachine, Var, Value).
129
130 application_set_unless_env(App, Var, Value) ->
131 Current = application:get_all_env(App),
132 CurrentKeys = proplists:get_keys(Current),
133 case lists:member(Var, CurrentKeys) of
134 true ->
135 ok;
136 false ->
137 application:set_env(App, Var, Value)
138 end.
139
140 host_headers(Req) ->
141 [ V || {V,_ReqState} <- [Req:get_header_value(H)
142 || H <- ["x-forwarded-host",
143 "x-forwarded-server",
144 "host"]],
145 V /= undefined].
146
147 get_app_env(Key) ->
148 application:get_env(webmachine, Key).
149
150 %% @private
151 %% @doc This function is used for cases where it may be desirable to
152 %% override the value that is set in the request metadata under the
153 %% `resource_module' key. An example would be a pattern where a set of
154 %% resource modules shares a lot of common functionality that is
155 %% contained in a single module and is used as the resource in all
156 %% dispatch rules and the `ModOpts' are used to specify a smaller
157 %% set of callbacks for resource specialization.
158 resource_module(Mod, ModOpts) ->
159 resource_module(Mod, ModOpts, get_app_env(resource_module_option)).
160
161 resource_module(Mod, _, undefined) ->
162 Mod;
163 resource_module(Mod, ModOpts, {ok, OptionVal}) ->
164 proplists:get_value(OptionVal, ModOpts, Mod).
165
166 to_list(L) when is_list(L) ->
167 L;
168 to_list(A) when is_atom(A) ->
169 atom_to_list(A).
+0
-204
deps/webmachine/src/webmachine_multipart.erl less more
0 %% @author Justin Sheehy <justin@basho.com>
1 %% @author Andy Gross <andy@basho.com>
2 %% @copyright 2009 Basho Technologies
3
4 %% @doc Utility for parsing multipart form bodies.
5
6 %% Licensed under the Apache License, Version 2.0 (the "License");
7 %% you may not use this file except in compliance with the License.
8 %% You may obtain a copy of the License at
9
10 %% http://www.apache.org/licenses/LICENSE-2.0
11
12 %% Unless required by applicable law or agreed to in writing, software
13 %% distributed under the License is distributed on an "AS IS" BASIS,
14 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 %% See the License for the specific language governing permissions and
16 %% limitations under the License.
17
18 -module(webmachine_multipart).
19 -author('Justin Sheehy <justin@basho.com>').
20 -author('Andy Gross <andy@basho.com>').
21 -export([get_all_parts/2,stream_parts/2, find_boundary/1]).
22
23 % @type incoming_req_body() = binary().
24 % The request body, in "multipart/form-data" (rfc2388) form,
25
26 % @type boundary() = string().
27 % The multipart boundary, as taken from the containing message's content-type.
28
29 % @type fpart() = {fpartname(), {[fparam()],[fheader()]}, fcontent()}.
30 % A single part of a multipart form.
31
32 % @type fpartname() = string().
33 % The name from the form field of a form part.
34
35 % @type fparam() = {binary(), binary()}.
36 % A key-value parameter from the content-disposition header in a form part.
37
38 % @type fheader() = {binary(), binary()}.
39 % A header name and value supplied within a form part.
40
41 % @type fcontent() = binary().
42 % The body content within a form part.
43
44 % @doc Find the multipart boundary for a request.
45 % @spec find_boundary(wrq:wm_reqdata()) -> boundary()
46 find_boundary(ReqData) ->
47 ContentType = wrq:get_req_header("content-type", ReqData),
48 string:substr(ContentType, string:str(ContentType, "boundary=")
49 + length("boundary=")).
50
51 % @doc Turn a multipart form into component parts.
52 % @spec get_all_parts(incoming_req_body(), boundary()) -> [fpart()]
53 get_all_parts(Body, Boundary) when is_binary(Body), is_list(Boundary) ->
54 StreamStruct = send_streamed_body(Body,1024),
55 getparts1(stream_parts(StreamStruct, Boundary), []).
56
57 % @doc Similar to get_all_parts/2, but for streamed/chunked bodies.
58 % Takes as input the result of wrq:stream_req_body/2, and provides
59 % either the atom 'done_parts' when no more parts are available, or
60 % a tuple with the next part and a function. That function will
61 % have 0-arity and the same return type as stream_parts/2 itself.
62 % @spec stream_parts(wm_stream(), boundary()) ->
63 % 'done_parts' | {fpart(), function()}
64 stream_parts(StreamStruct, Boundary) ->
65 stream_form(StreamStruct, "--" ++ Boundary, []).
66
67 stream_form(_, _, [<<"----\n">>|_]) -> done_parts;
68 stream_form(_, _, [<<"--\n">>|_]) -> done_parts;
69 stream_form({Hunk, Next}, Boundary, []) ->
70 stream_form(get_more_data(Next), Boundary, re:split(Hunk, Boundary,[]));
71 stream_form({Hunk, Next}, Boundary, [<<>>|DQ]) ->
72 stream_form({Hunk, Next}, Boundary, DQ);
73 stream_form({Hunk, Next}, Boundary, [H|[T1|T2]]) ->
74 {make_part(H), fun() ->
75 stream_form({Hunk, Next}, Boundary, [T1|T2]) end};
76 stream_form({Hunk, really_done}, Boundary, DQ) ->
77 DQBin = iolist_to_binary(DQ),
78 FullHunk = <<DQBin/binary, Hunk/binary>>,
79 stream_parts(re:split(FullHunk, Boundary,[]));
80 stream_form({Hunk, Next}, Boundary, [Single]) ->
81 FullHunk = <<Single/binary, Hunk/binary>>,
82 stream_form(get_more_data(Next), Boundary, re:split(FullHunk, Boundary,[])).
83
84 stream_parts([]) -> done_parts;
85 % browsers are fun, and terminate posts slightly differently from each other:
86 stream_parts([<<"----\n">>]) -> done_parts;
87 stream_parts([<<"--\n">>]) -> done_parts;
88 stream_parts([<<"----\r\n">>]) -> done_parts;
89 stream_parts([<<"--\r\n">>]) -> done_parts;
90 stream_parts([<<"--\r\n--\n">>]) -> done_parts;
91 stream_parts([<<"--\r\n--\r\n">>]) -> done_parts;
92 stream_parts([H|T]) -> {make_part(H), fun() -> stream_parts(T) end}.
93
94 get_more_data(done) -> {<<"--\n">>, really_done};
95 get_more_data(Fun) -> Fun().
96
97 make_part(PartData) ->
98 %% Remove the trailing \r\n
99 [HeadData, BodyWithCRLF] = re:split(PartData, "\\r\\n\\r\\n", [{parts,2}]),
100 BodyLen = size(BodyWithCRLF) - 2,
101 <<Body:BodyLen/binary, _/binary>> = BodyWithCRLF,
102
103 HeadList = [list_to_binary(X) ||
104 X <- string:tokens(binary_to_list(HeadData), "\r\n")],
105 {Name, Params, Headers} = make_headers(HeadList),
106 {Name, {Params,Headers}, Body}.
107
108 make_headers(X) ->
109 make_headers(X, name_undefined, params_undefined, []).
110 make_headers([], Name, Params, Headers) -> {Name, Params, Headers};
111 make_headers([<<>>|HL], Name, Params, Headers) ->
112 make_headers(HL, Name, Params, Headers);
113 make_headers(
114 [<<"Content-Disposition: form-data; ", Names/binary>>|HL],
115 _, _, Headers) ->
116 {Name, Params} = extract_names(Names),
117 make_headers(HL, Name, Params, Headers);
118 make_headers([H|HL], Name, Params, Headers) ->
119 make_headers(HL, Name, Params, [cheap_parse_header(H)|Headers]).
120
121 extract_names(NamesString) ->
122 Params = [{K, V} ||
123 {K, [<<>>, V, <<>>]} <- [{K0, re:split(V0,"\"",[])} ||
124 [K0, V0] <- [re:split(N, "=", [{parts, 2}]) ||
125 N <- re:split(NamesString, "; ", [])]]],
126 Name = hd([binary_to_list(V) || {<<"name">>,V} <- Params]),
127 {Name, Params}.
128
129 cheap_parse_header(HeadBin) ->
130 [K,V] = re:split(HeadBin, ": ", [{parts,2}]),
131 {K,V}.
132
133 getparts1(done_parts, Acc) ->
134 lists:reverse(Acc);
135 getparts1({Part, Streamer}, Acc) ->
136 getparts1(Streamer(), [Part|Acc]).
137
138 send_streamed_body(Body, Max) ->
139 HunkLen=8*Max,
140 case Body of
141 <<A:HunkLen,Rest/binary>> ->
142 {<<A:HunkLen>>, fun() -> send_streamed_body(Rest,Max) end};
143 _ ->
144 {Body, done}
145 end.
146
147 %%
148 %% Tests
149 %%
150 -ifdef(TEST).
151 -include_lib("eunit/include/eunit.hrl").
152
153 body_test() ->
154 Body = <<"------------ae0gL6gL6Ij5KM7Ef1KM7ei4ae0cH2\r\nContent-Disposition: form-data; name=\"Filename\"\r\n\r\ntestfile.txt\r\n------------ae0gL6gL6Ij5KM7Ef1KM7ei4ae0cH2\r\nContent-Disposition: form-data; name=\"Filedata\"; filename=\"testfile.txt\"\r\nContent-Type: application/octet-stream\r\n\r\n%%% The contents of this file are a test,\n%%% do not be alarmed.\n\r\n------------ae0gL6gL6Ij5KM7Ef1KM7ei4ae0cH2\r\nContent-Disposition: form-data; name=\"Upload\"\r\n\r\nSubmit Query\r\n------------ae0gL6gL6Ij5KM7Ef1KM7ei4ae0cH2--">>,
155 Boundary = "----------ae0gL6gL6Ij5KM7Ef1KM7ei4ae0cH2",
156 ?assertEqual(
157 [{"Filename",
158 {[{<<"name">>,<<"Filename">>}],[]},
159 <<"testfile.txt">>},
160 {"Filedata",
161 {[{<<"name">>,<<"Filedata">>},
162 {<<"filename">>,<<"testfile.txt">>}],
163 [{<<"Content-Type">>,<<"application/octet-stream">>}]},
164 <<"%%% The contents of this file are a test,\n%%% do not be alarmed.\n">>},
165 {"Upload",{[{<<"name">>,<<"Upload">>}],[]},
166 <<"Submit Query">>}],
167 get_all_parts(Body, Boundary)).
168
169 body2_test() ->
170 Body = <<"-----------------------------89205314411538515011004844897\r\nContent-Disposition: form-data; name=\"Filedata\"; filename=\"akamai.txt\"\r\nContent-Type: text/plain\r\n\r\nCAMBRIDGE, MA - February 18, 2009 - Akamai Technologies, Inc. (NASDAQ: AKAM), the leader in powering rich media, dynamic transactions and enterprise applications online, today announced that its Service & Support organization was awarded top honors for Innovation in Customer Service at the 3rd Annual Stevie Awards for Sales & Customer Service, an international competition recognizing excellence in disciplines that are crucial to business success.\n\n\"We have always set incredibly high standards with respect to the service and support we provide our customers,\" said Sanjay Singh, vice president of Global Service & Support at Akamai. \"Our support team provides highly responsive service around the clock to our global customer base and, as a result, has become an extension of our customers' online businesses. This prestigious award is validation of Akamai's commitment to customer service and technical support.\"\n\nAkamai Service & Support professionals are dedicated to working with customers on a daily basis to fine tune, optimize, and support their Internet initiatives. Akamai's winning submission highlighted the key pillars of its service and support offering, as well as the initiatives established to meet customer requirements for proactive communication, simplification, and faster response times.\n\n\"This year's honorees demonstrate that even in challenging economic times, it's possible for organizations to continue to shine in sales and customer service, the two most important functions in business: acquiring and keeping customers,\" said Michael Gallagher, president of the Stevie Awards.\n\nThe awards are presented by the Stevie Awards, which organizes several of the world's leading business awards shows, including the prestigious American Business Awards. Nicknamed the Stevies for the Greek word \"crowned,\" winners were announced during a gala banquet on Monday, February 9 at Caesars Palace in Las Vegas. Nominated customer service and sales executives from the U.S.A. and several other countries attended. More than 500 entries from companies of all sizes and in virtually every industry were submitted to this year's competition. There are 27 categories for customer service professionals, as well as 41 categories for sales professionals.\n\nDetails about the Stevie Awards for Sales & Customer Service and the list of honorees in all categories are available at www.stevieawards.com/sales. \n\r\n-----------------------------89205314411538515011004844897--\r\n">>,
171 Boundary = "---------------------------89205314411538515011004844897",
172 ?assertEqual(
173 [{"Filedata",
174 {[{<<"name">>,<<"Filedata">>},
175 {<<"filename">>,<<"akamai.txt">>}],
176 [{<<"Content-Type">>,<<"text/plain">>}]},
177 <<"CAMBRIDGE, MA - February 18, 2009 - Akamai Technologies, Inc. (NASDAQ: AKAM), the leader in powering rich media, dynamic transactions and enterprise applications online, today announced that its Service & Support organization was awarded top honors for Innovation in Customer Service at the 3rd Annual Stevie Awards for Sales & Customer Service, an international competition recognizing excellence in disciplines that are crucial to business success.\n\n\"We have always set incredibly high standards with respect to the service and support we provide our customers,\" said Sanjay Singh, vice president of Global Service & Support at Akamai. \"Our support team provides highly responsive service around the clock to our global customer base and, as a result, has become an extension of our customers' online businesses. This prestigious award is validation of Akamai's commitment to customer service and technical support.\"\n\nAkamai Service & Support professionals are dedicated to working with customers on a daily basis to fine tune, optimize, and support their Internet initiatives. Akamai's winning submission highlighted the key pillars of its service and support offering, as well as the initiatives established to meet customer requirements for proactive communication, simplification, and faster response times.\n\n\"This year's honorees demonstrate that even in challenging economic times, it's possible for organizations to continue to shine in sales and customer service, the two most important functions in business: acquiring and keeping customers,\" said Michael Gallagher, president of the Stevie Awards.\n\nThe awards are presented by the Stevie Awards, which organizes several of the world's leading business awards shows, including the prestigious American Business Awards. Nicknamed the Stevies for the Greek word \"crowned,\" winners were announced during a gala banquet on Monday, February 9 at Caesars Palace in Las Vegas. Nominated customer service and sales executives from the U.S.A. and several other countries attended. More than 500 entries from companies of all sizes and in virtually every industry were submitted to this year's competition. There are 27 categories for customer service professionals, as well as 41 categories for sales professionals.\n\nDetails about the Stevie Awards for Sales & Customer Service and the list of honorees in all categories are available at www.stevieawards.com/sales. \n">>
178 }],
179 get_all_parts(Body,Boundary)).
180
181 firefox_test() ->
182 Body = <<"-----------------------------823378840143542612896544303\r\nContent-Disposition: form-data; name=\"upload-test\"; filename=\"abcdef.txt\"\r\nContent-Type: text/plain\r\n\r\n01234567890123456789012345678901234567890123456789\r\n-----------------------------823378840143542612896544303--\r\n">>,
183 Boundary = "---------------------------823378840143542612896544303",
184 ?assertEqual(
185 [{"upload-test",
186 {[{<<"name">>,<<"upload-test">>},
187 {<<"filename">>,<<"abcdef.txt">>}],
188 [{<<"Content-Type">>,<<"text/plain">>}]},
189 <<"01234567890123456789012345678901234567890123456789">>}],
190 get_all_parts(Body,Boundary)).
191
192 chrome_test() ->
193 Body = <<"------WebKitFormBoundaryIHB9Xyi7ZCNKJusP\r\nContent-Disposition: form-data; name=\"upload-test\"; filename=\"abcdef.txt\"\r\nContent-Type: text/plain\r\n\r\n01234567890123456789012345678901234567890123456789\r\n------WebKitFormBoundaryIHB9Xyi7ZCNKJusP--\r\n">>,
194 Boundary = "----WebKitFormBoundaryIHB9Xyi7ZCNKJusP",
195 ?assertEqual(
196 [{"upload-test",
197 {[{<<"name">>,<<"upload-test">>},
198 {<<"filename">>,<<"abcdef.txt">>}],
199 [{<<"Content-Type">>,<<"text/plain">>}]},
200 <<"01234567890123456789012345678901234567890123456789">>}],
201 get_all_parts(Body,Boundary)).
202
203 -endif.
+0
-114
deps/webmachine/src/webmachine_perf_log_handler.erl less more
0 %% Copyright (c) 2011-2013 Basho Technologies, Inc. All Rights Reserved.
1 %%
2 %% This file is provided to you under the Apache License,
3 %% Version 2.0 (the "License"); you may not use this file
4 %% except in compliance with the License. You may obtain
5 %% a copy of the License at
6 %%
7 %% http://www.apache.org/licenses/LICENSE-2.0
8 %%
9 %% Unless required by applicable law or agreed to in writing,
10 %% software distributed under the License is distributed on an
11 %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
12 %% KIND, either express or implied. See the License for the
13 %% specific language governing permissions and limitations
14 %% under the License.
15
16 %% @doc Default performance log handler for webmachine
17
18 -module(webmachine_perf_log_handler).
19
20 -behaviour(gen_event).
21
22 %% gen_event callbacks
23 -export([init/1,
24 handle_call/2,
25 handle_event/2,
26 handle_info/2,
27 terminate/2,
28 code_change/3]).
29
30 -include("webmachine_logger.hrl").
31
32 -ifdef(TEST).
33 -include_lib("eunit/include/eunit.hrl").
34 -endif.
35
36 -record(state, {hourstamp, filename, handle}).
37
38 -define(FILENAME, "perf.log").
39
40 %% ===================================================================
41 %% gen_event callbacks
42 %% ===================================================================
43
44 %% @private
45 init([BaseDir]) ->
46 webmachine_log:defer_refresh(?MODULE),
47 FileName = filename:join(BaseDir, ?FILENAME),
48 {Handle, DateHour} = webmachine_log:log_open(FileName),
49 {ok, #state{filename=FileName, handle=Handle, hourstamp=DateHour}}.
50
51 %% @private
52 handle_call({_Label, MRef, get_modules}, State) ->
53 {ok, {MRef, [?MODULE]}, State};
54 handle_call({refresh, Time}, State) ->
55 {ok, ok, webmachine_log:maybe_rotate(?MODULE, Time, State)};
56 handle_call(_Request, State) ->
57 {ok, ok, State}.
58
59 %% @private
60 handle_event({log_access, LogData}, State) ->
61 NewState = webmachine_log:maybe_rotate(?MODULE, os:timestamp(), State),
62 Msg = format_req(LogData),
63 webmachine_log:log_write(NewState#state.handle, Msg),
64 {ok, NewState};
65 handle_event(_Event, State) ->
66 {ok, State}.
67
68 %% @private
69 handle_info(_Info, State) ->
70 {ok, State}.
71
72 %% @private
73 terminate(_Reason, _State) ->
74 ok.
75
76 %% @private
77 code_change(_OldVsn, State, _Extra) ->
78 {ok, State}.
79
80 %% ===================================================================
81 %% Internal functions
82 %% ===================================================================
83
84 format_req(#wm_log_data{resource_module=Mod,
85 start_time=StartTime,
86 method=Method,
87 peer=Peer,
88 path=Path,
89 version=Version,
90 response_code=ResponseCode,
91 response_length=ResponseLength,
92 end_time=EndTime,
93 finish_time=FinishTime}) ->
94 Time = webmachine_log:fmtnow(),
95 Status = case ResponseCode of
96 {Code, _ReasonPhrase} when is_integer(Code) ->
97 integer_to_list(Code);
98 _ when is_integer(ResponseCode) ->
99 integer_to_list(ResponseCode);
100 _ ->
101 ResponseCode
102 end,
103 Length = integer_to_list(ResponseLength),
104 TTPD = webmachine_util:now_diff_milliseconds(EndTime, StartTime),
105 TTPS = webmachine_util:now_diff_milliseconds(FinishTime, EndTime),
106 fmt_plog(Time, Peer, atom_to_list(Method), Path, Version,
107 Status, Length, atom_to_list(Mod), integer_to_list(TTPD),
108 integer_to_list(TTPS)).
109
110 fmt_plog(Time, Ip, Method, Path, {VM,Vm}, Status, Length, Mod, TTPD, TTPS) ->
111 [webmachine_log:fmt_ip(Ip), " - ", [$\s], Time, [$\s, $"], Method, " ", Path,
112 " HTTP/", integer_to_list(VM), ".", integer_to_list(Vm), [$",$\s],
113 Status, [$\s], Length, " " , Mod, " ", TTPD, " ", TTPS, $\n].
+0
-962
deps/webmachine/src/webmachine_request.erl less more
0 %% @author Justin Sheehy <justin@basho.com>
1 %% @author Andy Gross <andy@basho.com>
2 %% @copyright 2007-2012 Basho Technologies
3 %% Based on mochiweb_request.erl, which is Copyright 2007 Mochi Media, Inc.
4 %%
5 %% Licensed under the Apache License, Version 2.0 (the "License");
6 %% you may not use this file except in compliance with the License.
7 %% You may obtain a copy of the License at
8 %%
9 %% http://www.apache.org/licenses/LICENSE-2.0
10 %%
11 %% Unless required by applicable law or agreed to in writing, software
12 %% distributed under the License is distributed on an "AS IS" BASIS,
13 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 %% See the License for the specific language governing permissions and
15 %% limitations under the License.
16
17 %% @doc Webmachine HTTP Request Abstraction. The functions in this module
18 %% can be invoked using either parameterized module syntax or regular
19 %% invocation syntax. Since the Ericsson OTP team is removing the
20 %% parameterized module syntax in version R16, we encourage you to write
21 %% your applications using regular function syntax.
22 %%
23 %% To use parameterized module syntax, you create an instance and then
24 %% invoke functions on that instance, like this:
25 %%
26 %% <pre><code>
27 %% Req = webmachine_request:new(ReqState),
28 %% Result = Req:some_fun(Args),
29 %% </code></pre>
30 %%
31 %% where `ReqState' is an instance of a `#wm_reqstate' record. The runtime
32 %% then ensures the `ReqState' variable is implicitly passed to each
33 %% function invoked through the `Req' instance.
34 %%
35 %% To call functions using regular syntax, simply explicitly pass the
36 %% `ReqState' variable yourself; note there's no need to call
37 %% `webmachine_request:new/1' to perform regular invocations.
38
39 -module(webmachine_request).
40 -author('Justin Sheehy <justin@basho.com>').
41 -author('Andy Gross <andy@basho.com>').
42
43 -export([get_peer/1]). % used in initialization
44 -export([call/2]). % internal switching interface, used by wrcall
45
46 % actual interface for resource functions
47 -export([
48 new/1,
49 trim_state/1,
50 get_reqdata/1,
51 set_reqdata/2,
52 socket/1,
53 method/1,
54 version/1,
55 disp_path/1,
56 path/1,
57 raw_path/1,
58 get_req_header/2,
59 req_headers/1,
60 req_body/2,
61 stream_req_body/2,
62 headers/1,
63 resp_headers/1,
64 out_headers/1,
65 get_out_header/2,
66 has_out_header/2,
67 peer/1,
68 get_header_value/2,
69 add_response_header/3,
70 add_response_headers/2,
71 remove_response_header/2,
72 merge_response_headers/2,
73 append_to_response_body/2,
74 send_response/2,
75 response_code/1,
76 set_response_code/2,
77 set_resp_body/2,
78 response_body/1,
79 has_response_body/1,
80 do_redirect/1,
81 resp_redirect/1,
82 set_metadata/3,
83 get_metadata/2,
84 get_path_info/1,
85 get_path_info/2,
86 load_dispatch_data/7,
87 get_path_tokens/1,
88 get_app_root/1,
89 parse_cookie/1,
90 get_cookie_value/2,
91 parse_qs/1,
92 get_qs_value/2,
93 get_qs_value/3,
94 range/1,
95 log_data/1
96 ]).
97
98 -include("webmachine_logger.hrl").
99 -include("wm_reqstate.hrl").
100 -include("wm_reqdata.hrl").
101
102 -define(WMVSN, "1.10.0").
103 -define(QUIP, "never breaks eye contact").
104 -define(IDLE_TIMEOUT, infinity).
105
106 new(#wm_reqstate{}=ReqState) ->
107 {?MODULE, ReqState}.
108
109 trim_state({?MODULE, ReqState}) ->
110 TrimData = (ReqState#wm_reqstate.reqdata)#wm_reqdata{wm_state='WMSTATE'},
111 webmachine_request:new(ReqState#wm_reqstate{reqdata=TrimData});
112 trim_state(ReqState) ->
113 trim_state({?MODULE, ReqState}).
114
115 get_peer({?MODULE, ReqState}=Req) ->
116 case ReqState#wm_reqstate.peer of
117 undefined ->
118 PeerName = case ReqState#wm_reqstate.socket of
119 testing -> {ok, {{127,0,0,1}, 80}};
120 {ssl,SslSocket} -> ssl:peername(SslSocket);
121 _ -> inet:peername(ReqState#wm_reqstate.socket)
122 end,
123 Peer = peer_from_peername(PeerName, Req),
124 NewReqState = ReqState#wm_reqstate{peer=Peer},
125 {Peer, NewReqState};
126 _ ->
127 {ReqState#wm_reqstate.peer, ReqState}
128 end;
129 get_peer(ReqState) ->
130 get_peer({?MODULE, ReqState}).
131
132 peer_from_peername({ok, {Addr={10, _, _, _}, _Port}}, Req) ->
133 x_peername(inet_parse:ntoa(Addr), Req);
134 peer_from_peername({ok, {Addr={172, Second, _, _}, _Port}}, Req)
135 when (Second > 15) andalso (Second < 32) ->
136 x_peername(inet_parse:ntoa(Addr), Req);
137 peer_from_peername({ok, {Addr={192, 168, _, _}, _Port}}, Req) ->
138 x_peername(inet_parse:ntoa(Addr), Req);
139 peer_from_peername({ok, {{127, 0, 0, 1}, _Port}}, Req) ->
140 x_peername("127.0.0.1", Req);
141 peer_from_peername({ok, {Addr, _Port}}, _Req) ->
142 inet_parse:ntoa(Addr).
143
144 x_peername(Default, Req) ->
145 case get_header_value("x-forwarded-for", Req) of
146 {undefined, _} ->
147 Default;
148 {Hosts, _} ->
149 string:strip(lists:last(string:tokens(Hosts, ",")))
150 end.
151
152 call(base_uri, {?MODULE, ReqState}) ->
153 {wrq:base_uri(ReqState#wm_reqstate.reqdata), ReqState};
154 call(socket, {?MODULE, ReqState}) -> {ReqState#wm_reqstate.socket,ReqState};
155 call(get_reqdata, {?MODULE, ReqState}) -> {ReqState#wm_reqstate.reqdata, ReqState};
156 call({set_reqdata, RD}, {?MODULE, ReqState}) ->
157 {ok, ReqState#wm_reqstate{reqdata=RD}};
158 call(method, {?MODULE, ReqState}) ->
159 {wrq:method(ReqState#wm_reqstate.reqdata), ReqState};
160 call(version, {?MODULE, ReqState}) ->
161 {wrq:version(ReqState#wm_reqstate.reqdata), ReqState};
162 call(raw_path, {?MODULE, ReqState}) ->
163 {wrq:raw_path(ReqState#wm_reqstate.reqdata), ReqState};
164 call(req_headers, {?MODULE, ReqState}) ->
165 {wrq:req_headers(ReqState#wm_reqstate.reqdata), ReqState};
166 call({req_body, MaxRecvBody}, {?MODULE, ReqState}) ->
167 case ReqState#wm_reqstate.bodyfetch of
168 stream ->
169 {stream_conflict, ReqState};
170 standard ->
171 {ReqState#wm_reqstate.reqbody, ReqState};
172 undefined ->
173 RD=(ReqState#wm_reqstate.reqdata)#wm_reqdata{
174 max_recv_body=MaxRecvBody},
175 NewReqState=ReqState#wm_reqstate{reqdata=RD},
176 NewBody = case get(req_body) of
177 undefined ->
178 NewB = do_recv_body(NewReqState),
179 put(req_body, NewB),
180 NewB;
181 B -> B
182 end,
183 NewRD = RD#wm_reqdata{req_body=NewBody},
184 {NewBody, NewReqState#wm_reqstate{
185 bodyfetch=standard,reqdata=NewRD,reqbody=NewBody}}
186 end;
187 call({stream_req_body, MaxHunk}, {?MODULE, ReqState}) ->
188 case ReqState#wm_reqstate.bodyfetch of
189 standard ->
190 {stream_conflict, ReqState};
191 _ ->
192 {recv_stream_body(ReqState, MaxHunk),
193 ReqState#wm_reqstate{bodyfetch=stream}}
194 end;
195 call(resp_headers, {?MODULE, ReqState}) ->
196 {wrq:resp_headers(ReqState#wm_reqstate.reqdata), ReqState};
197 call(resp_redirect, {?MODULE, ReqState}) ->
198 {wrq:resp_redirect(ReqState#wm_reqstate.reqdata), ReqState};
199 call({get_resp_header, HdrName}, {?MODULE, ReqState}) ->
200 Reply = mochiweb_headers:get_value(HdrName,
201 wrq:resp_headers(ReqState#wm_reqstate.reqdata)),
202 {Reply, ReqState};
203 call(get_path_info, {?MODULE, ReqState}) ->
204 PropList = orddict:to_list(wrq:path_info(ReqState#wm_reqstate.reqdata)),
205 {PropList, ReqState};
206 call({get_path_info, Key}, {?MODULE, ReqState}) ->
207 {wrq:path_info(Key, ReqState#wm_reqstate.reqdata), ReqState};
208 call(peer, Req) -> get_peer(Req);
209 call(range, Req) -> get_range(Req);
210 call(response_code, {?MODULE, ReqState}) ->
211 {wrq:response_code(ReqState#wm_reqstate.reqdata), ReqState};
212 call(app_root, {?MODULE, ReqState}) ->
213 {wrq:app_root(ReqState#wm_reqstate.reqdata), ReqState};
214 call(disp_path, {?MODULE, ReqState}) ->
215 {wrq:disp_path(ReqState#wm_reqstate.reqdata), ReqState};
216 call(path, {?MODULE, ReqState}) ->
217 {wrq:path(ReqState#wm_reqstate.reqdata), ReqState};
218 call({get_req_header, K}, {?MODULE, ReqState}) ->
219 {wrq:get_req_header(K, ReqState#wm_reqstate.reqdata), ReqState};
220 call({set_response_code, Code}, {?MODULE, ReqState}) ->
221 {ok, ReqState#wm_reqstate{reqdata=wrq:set_response_code(
222 Code, ReqState#wm_reqstate.reqdata)}};
223 call({set_resp_header, K, V}, {?MODULE, ReqState}) ->
224 {ok, ReqState#wm_reqstate{reqdata=wrq:set_resp_header(
225 K, V, ReqState#wm_reqstate.reqdata)}};
226 call({set_resp_headers, Hdrs}, {?MODULE, ReqState}) ->
227 {ok, ReqState#wm_reqstate{reqdata=wrq:set_resp_headers(
228 Hdrs, ReqState#wm_reqstate.reqdata)}};
229 call({remove_resp_header, K}, {?MODULE, ReqState}) ->
230 {ok, ReqState#wm_reqstate{reqdata=wrq:remove_resp_header(
231 K, ReqState#wm_reqstate.reqdata)}};
232 call({merge_resp_headers, Hdrs}, {?MODULE, ReqState}) ->
233 {ok, ReqState#wm_reqstate{reqdata=wrq:merge_resp_headers(
234 Hdrs, ReqState#wm_reqstate.reqdata)}};
235 call({append_to_response_body, Data}, {?MODULE, ReqState}) ->
236 {ok, ReqState#wm_reqstate{reqdata=wrq:append_to_response_body(
237 Data, ReqState#wm_reqstate.reqdata)}};
238 call({set_disp_path, P}, {?MODULE, ReqState}) ->
239 {ok, ReqState#wm_reqstate{reqdata=wrq:set_disp_path(
240 P, ReqState#wm_reqstate.reqdata)}};
241 call(do_redirect, {?MODULE, ReqState}) ->
242 {ok, ReqState#wm_reqstate{
243 reqdata=wrq:do_redirect(true, ReqState#wm_reqstate.reqdata)}};
244 call({send_response, Code}, Req) when is_integer(Code) ->
245 call({send_response, {Code, undefined}}, Req);
246 call({send_response, {Code, ReasonPhrase}=CodeAndReason}, Req) when is_integer(Code) ->
247 {Reply, NewState} =
248 case Code of
249 200 ->
250 send_ok_response(ReasonPhrase, Req);
251 _ ->
252 send_response(CodeAndReason, Req)
253 end,
254 LogData = NewState#wm_reqstate.log_data,
255 NewLogData = LogData#wm_log_data{finish_time=now()},
256 {Reply, NewState#wm_reqstate{log_data=NewLogData}};
257 call(resp_body, {?MODULE, ReqState}) ->
258 {wrq:resp_body(ReqState#wm_reqstate.reqdata), ReqState};
259 call({set_resp_body, Body}, {?MODULE, ReqState}) ->
260 {ok, ReqState#wm_reqstate{reqdata=wrq:set_resp_body(Body,
261 ReqState#wm_reqstate.reqdata)}};
262 call(has_resp_body, {?MODULE, ReqState}) ->
263 Reply = case wrq:resp_body(ReqState#wm_reqstate.reqdata) of
264 undefined -> false;
265 <<>> -> false;
266 _ -> true
267 end,
268 {Reply, ReqState};
269 call({get_metadata, Key}, {?MODULE, ReqState}) ->
270 Reply = case orddict:find(Key, ReqState#wm_reqstate.metadata) of
271 {ok, Value} -> Value;
272 error -> undefined
273 end,
274 {Reply, ReqState};
275 call({set_metadata, Key, Value}, {?MODULE, ReqState}) ->
276 NewDict = orddict:store(Key, Value, ReqState#wm_reqstate.metadata),
277 {ok, ReqState#wm_reqstate{metadata=NewDict}};
278 call(path_tokens, {?MODULE, ReqState}) ->
279 {wrq:path_tokens(ReqState#wm_reqstate.reqdata), ReqState};
280 call(req_cookie, {?MODULE, ReqState}) ->
281 {wrq:req_cookie(ReqState#wm_reqstate.reqdata), ReqState};
282 call(req_qs, {?MODULE, ReqState}) ->
283 {wrq:req_qs(ReqState#wm_reqstate.reqdata), ReqState};
284 call({load_dispatch_data, PathProps, HostTokens, Port,
285 PathTokens, AppRoot, DispPath}, {?MODULE, ReqState}) ->
286 PathInfo = orddict:from_list(PathProps),
287 NewState = ReqState#wm_reqstate{reqdata=wrq:load_dispatch_data(
288 PathInfo,HostTokens,Port,PathTokens,AppRoot,
289 DispPath,ReqState#wm_reqstate.reqdata)},
290 {ok, NewState};
291 call(log_data, {?MODULE, ReqState}) -> {ReqState#wm_reqstate.log_data, ReqState};
292 call(notes, {?MODULE, ReqState}) -> {wrq:get_notes(ReqState#wm_reqstate.reqdata), ReqState};
293 call(Arg, #wm_reqstate{}=ReqState) -> call(Arg, {?MODULE, ReqState}).
294
295 get_header_value(K, {?MODULE, ReqState}) ->
296 {wrq:get_req_header(K, ReqState#wm_reqstate.reqdata), ReqState};
297 get_header_value(K, ReqState) ->
298 get_header_value(K, {?MODULE, ReqState}).
299
300 get_outheader_value(K, {?MODULE, ReqState}) ->
301 {mochiweb_headers:get_value(K,
302 wrq:resp_headers(ReqState#wm_reqstate.reqdata)), ReqState};
303 get_outheader_value(K, ReqState) ->
304 get_outheader_value(K, {?MODULE, ReqState}).
305
306 send(Socket, Data) ->
307 case mochiweb_socket:send(Socket, iolist_to_binary(Data)) of
308 ok -> ok;
309 {error,closed} -> ok;
310 _ -> exit(normal)
311 end.
312
313 send_stream_body(Socket, X) -> send_stream_body(Socket, X, 0).
314 send_stream_body(Socket, {<<>>, done}, SoFar) ->
315 send_chunk(Socket, <<>>),
316 SoFar;
317 send_stream_body(Socket, {Data, done}, SoFar) ->
318 Size = send_chunk(Socket, Data),
319 send_chunk(Socket, <<>>),
320 Size + SoFar;
321 send_stream_body(Socket, {<<>>, Next}, SoFar) ->
322 send_stream_body(Socket, Next(), SoFar);
323 send_stream_body(Socket, {[], Next}, SoFar) ->
324 send_stream_body(Socket, Next(), SoFar);
325 send_stream_body(Socket, {Data, Next}, SoFar) ->
326 Size = send_chunk(Socket, Data),
327 send_stream_body(Socket, Next(), Size + SoFar).
328
329 send_stream_body_no_chunk(Socket, {Data, done}) ->
330 send(Socket, Data);
331 send_stream_body_no_chunk(Socket, {Data, Next}) ->
332 send(Socket, Data),
333 send_stream_body_no_chunk(Socket, Next()).
334
335 send_writer_body(Socket, {Encoder, Charsetter, BodyFun}) ->
336 put(bytes_written, 0),
337 Writer = fun(Data) ->
338 Size = send_chunk(Socket, Encoder(Charsetter(Data))),
339 put(bytes_written, get(bytes_written) + Size),
340 Size
341 end,
342 BodyFun(Writer),
343 send_chunk(Socket, <<>>),
344 get(bytes_written).
345
346 send_chunk(Socket, Data) ->
347 Size = iolist_size(Data),
348 send(Socket, [mochihex:to_hex(Size), <<"\r\n">>, Data, <<"\r\n">>]),
349 Size.
350
351 send_ok_response(ReasonPhrase, {?MODULE, ReqState}=Req) ->
352 RD0 = ReqState#wm_reqstate.reqdata,
353 {Range, State} = get_range(Req),
354 case Range of
355 X when X =:= undefined; X =:= fail; X =:= ignore ->
356 send_response({200, ReasonPhrase}, Req);
357 Ranges ->
358 {PartList, Size} = range_parts(RD0, Ranges),
359 case PartList of
360 [] -> %% no valid ranges
361 %% could be 416, for now we'll just return 200
362 send_response({200, ReasonPhrase}, Req);
363 PartList ->
364 {RangeHeaders, RangeBody} =
365 parts_to_body(PartList, Size, Req),
366 RespHdrsRD = wrq:set_resp_headers(
367 [{"Accept-Ranges", "bytes"} | RangeHeaders], RD0),
368 RespBodyRD = wrq:set_resp_body(
369 RangeBody, RespHdrsRD),
370 NewState = State#wm_reqstate{reqdata=RespBodyRD},
371 send_response({206, ReasonPhrase}, NewState, Req)
372 end
373 end.
374
375 send_response(Code, #wm_reqstate{}=ReqState) -> send_response(Code,ReqState,{?MODULE,ReqState});
376 send_response(Code, {?MODULE, ReqState}=Req) -> send_response(Code,ReqState,Req).
377 send_response(Code, PassedState=#wm_reqstate{reqdata=RD}, _Req) ->
378 Body0 = wrq:resp_body(RD),
379 {Body,Length} = case Body0 of
380 {stream, StreamBody} -> {{stream, StreamBody}, chunked};
381 {known_length_stream, Size, StreamBody} -> {{known_length_stream, StreamBody}, Size};
382 {stream, Size, Fun} -> {{stream, Fun(0, Size-1)}, chunked};
383 {writer, WriteBody} -> {{writer, WriteBody}, chunked};
384 _ -> {Body0, iolist_size([Body0])}
385 end,
386 send(PassedState#wm_reqstate.socket,
387 [make_version(wrq:version(RD)),
388 make_code(Code), <<"\r\n">> |
389 make_headers(Code, Length, RD)]),
390 FinalLength = case wrq:method(RD) of
391 'HEAD' -> Length;
392 _ ->
393 case Body of
394 {stream, Body2} ->
395 send_stream_body(PassedState#wm_reqstate.socket, Body2);
396 {known_length_stream, Body2} ->
397 send_stream_body_no_chunk(PassedState#wm_reqstate.socket, Body2),
398 Length;
399 {writer, Body2} ->
400 send_writer_body(PassedState#wm_reqstate.socket, Body2);
401 _ ->
402 send(PassedState#wm_reqstate.socket, Body),
403 Length
404 end
405 end,
406 InitLogData = PassedState#wm_reqstate.log_data,
407 FinalLogData = InitLogData#wm_log_data{response_code=Code,
408 response_length=FinalLength},
409 {ok, PassedState#wm_reqstate{reqdata=wrq:set_response_code(Code, RD),
410 log_data=FinalLogData}}.
411
412 %% @doc Infer body length from transfer-encoding and content-length headers.
413 body_length(Req) ->
414 case get_header_value("transfer-encoding", Req) of
415 {undefined, _} ->
416 case get_header_value("content-length", Req) of
417 {undefined, _} -> undefined;
418 {Length, _} -> list_to_integer(Length)
419 end;
420 {"chunked", _} -> chunked;
421 Unknown -> {unknown_transfer_encoding, Unknown}
422 end.
423
424 %% @doc Receive the body of the HTTP request (defined by Content-Length).
425 %% Will only receive up to the default max-body length
426 do_recv_body(PassedState=#wm_reqstate{reqdata=RD}) ->
427 MRH = RD#wm_reqdata.max_recv_hunk,
428 MRB = RD#wm_reqdata.max_recv_body,
429 read_whole_stream(recv_stream_body(PassedState, MRH), [], MRB, 0).
430
431 read_whole_stream({Hunk,_}, _, MaxRecvBody, SizeAcc)
432 when SizeAcc + byte_size(Hunk) > MaxRecvBody ->
433 {error, req_body_too_large};
434 read_whole_stream({Hunk,Next}, Acc0, MaxRecvBody, SizeAcc) ->
435 HunkSize = byte_size(Hunk),
436 if SizeAcc + HunkSize > MaxRecvBody ->
437 {error, req_body_too_large};
438 true ->
439 Acc = [Hunk|Acc0],
440 case Next of
441 done -> iolist_to_binary(lists:reverse(Acc));
442 _ -> read_whole_stream(Next(), Acc,
443 MaxRecvBody, SizeAcc + HunkSize)
444 end
445 end.
446
447 recv_stream_body(PassedState=#wm_reqstate{reqdata=RD}, MaxHunkSize) ->
448 put(mochiweb_request_recv, true),
449 case get_header_value("expect", PassedState) of
450 {"100-continue", _} ->
451 send(PassedState#wm_reqstate.socket,
452 [make_version(wrq:version(RD)),
453 make_code(100), <<"\r\n\r\n">>]);
454 _Else ->
455 ok
456 end,
457 case body_length(PassedState) of
458 {unknown_transfer_encoding, X} -> exit({unknown_transfer_encoding, X});
459 undefined -> {<<>>, done};
460 0 -> {<<>>, done};
461 chunked -> recv_chunked_body(PassedState#wm_reqstate.socket,
462 MaxHunkSize);
463 Length -> recv_unchunked_body(PassedState#wm_reqstate.socket,
464 MaxHunkSize, Length)
465 end.
466
467 recv_unchunked_body(Socket, MaxHunk, DataLeft) ->
468 case MaxHunk >= DataLeft of
469 true ->
470 {ok,Data1} = mochiweb_socket:recv(Socket,DataLeft,?IDLE_TIMEOUT),
471 {Data1, done};
472 false ->
473 {ok,Data2} = mochiweb_socket:recv(Socket,MaxHunk,?IDLE_TIMEOUT),
474 {Data2,
475 fun() -> recv_unchunked_body(Socket, MaxHunk, DataLeft-MaxHunk)
476 end}
477 end.
478
479 recv_chunked_body(Socket, MaxHunk) ->
480 case read_chunk_length(Socket, false) of
481 0 -> {<<>>, done};
482 ChunkLength -> recv_chunked_body(Socket,MaxHunk,ChunkLength)
483 end.
484 recv_chunked_body(Socket, MaxHunk, LeftInChunk) ->
485 case MaxHunk >= LeftInChunk of
486 true ->
487 {ok,Data1} = mochiweb_socket:recv(Socket,LeftInChunk,?IDLE_TIMEOUT),
488 {Data1,
489 fun() -> recv_chunked_body(Socket, MaxHunk)
490 end};
491 false ->
492 {ok,Data2} = mochiweb_socket:recv(Socket,MaxHunk,?IDLE_TIMEOUT),
493 {Data2,
494 fun() -> recv_chunked_body(Socket, MaxHunk, LeftInChunk-MaxHunk)
495 end}
496 end.
497
498 read_chunk_length(Socket, MaybeLastChunk) ->
499 mochiweb_socket:setopts(Socket, [{packet, line}]),
500 case mochiweb_socket:recv(Socket, 0, ?IDLE_TIMEOUT) of
501 {ok, Header} ->
502 mochiweb_socket:setopts(Socket, [{packet, raw}]),
503 Splitter = fun (C) ->
504 C =/= $\r andalso C =/= $\n andalso C =/= $
505 andalso C =/= 59 % semicolon
506 end,
507 {Hex, _Rest} = lists:splitwith(Splitter, binary_to_list(Header)),
508 case Hex of
509 [] ->
510 %% skip the \r\n at the end of a chunk, or
511 %% allow [badly formed] last chunk header to be
512 %% empty instead of '0' explicitly
513 if MaybeLastChunk -> 0;
514 true -> read_chunk_length(Socket, true)
515 end;
516 _ ->
517 erlang:list_to_integer(Hex, 16)
518 end;
519 _ ->
520 exit(normal)
521 end.
522
523 get_range({?MODULE, #wm_reqstate{reqdata = RD}=ReqState}=Req) ->
524 case RD#wm_reqdata.resp_range of
525 ignore_request ->
526 {ignore, ReqState#wm_reqstate{range=undefined}};
527 follow_request ->
528 case get_header_value("range", Req) of
529 {undefined, _} ->
530 {undefined, ReqState#wm_reqstate{range=undefined}};
531 {RawRange, _} ->
532 Range = mochiweb_http:parse_range_request(RawRange),
533 {Range, ReqState#wm_reqstate{range=Range}}
534 end
535 end.
536
537 range_parts(_RD=#wm_reqdata{resp_body={file, IoDevice}}, Ranges) ->
538 Size = mochiweb_io:iodevice_size(IoDevice),
539 F = fun (Spec, Acc) ->
540 case mochiweb_http:range_skip_length(Spec, Size) of
541 invalid_range ->
542 Acc;
543 V ->
544 [V | Acc]
545 end
546 end,
547 LocNums = lists:foldr(F, [], Ranges),
548 {ok, Data} = file:pread(IoDevice, LocNums),
549 Bodies = lists:zipwith(fun ({Skip, Length}, PartialBody) ->
550 {Skip, Skip + Length - 1, PartialBody}
551 end,
552 LocNums, Data),
553 {Bodies, Size};
554
555 range_parts(RD=#wm_reqdata{resp_body={stream, {Hunk,Next}}}, Ranges) ->
556 % for now, streamed bodies are read in full for range requests
557 MRB = RD#wm_reqdata.max_recv_body,
558 range_parts(read_whole_stream({Hunk,Next}, [], MRB, 0), Ranges);
559
560 range_parts(_RD=#wm_reqdata{resp_body={known_length_stream, Size, StreamBody}},
561 Ranges) ->
562 SkipLengths = [ mochiweb_http:range_skip_length(R, Size) || R <- Ranges],
563 {[ {Skip, Skip+Length-1, {known_length_stream, Length, StreamBody}} ||
564 {Skip, Length} <- SkipLengths ],
565 Size};
566
567 range_parts(_RD=#wm_reqdata{resp_body={stream, Size, StreamFun}}, Ranges) ->
568 SkipLengths = [ mochiweb_http:range_skip_length(R, Size) || R <- Ranges],
569 {[ {Skip, Skip+Length-1, StreamFun} || {Skip, Length} <- SkipLengths ],
570 Size};
571
572 range_parts(#wm_reqdata{resp_body=Body}, Ranges) when is_binary(Body); is_list(Body) ->
573 range_parts(Body, Ranges);
574
575 range_parts(Body0, Ranges) when is_binary(Body0); is_list(Body0) ->
576 Body = iolist_to_binary(Body0),
577 Size = size(Body),
578 F = fun(Spec, Acc) ->
579 case mochiweb_http:range_skip_length(Spec, Size) of
580 invalid_range ->
581 Acc;
582 {Skip, Length} ->
583 <<_:Skip/binary,
584 PartialBody:Length/binary,
585 _/binary>> = Body,
586 [{Skip, Skip + Length - 1, PartialBody} | Acc]
587 end
588 end,
589 {lists:foldr(F, [], Ranges), Size}.
590
591 parts_to_body([{Start, End, Body0}], Size, Req) ->
592 %% return body for a range reponse with a single body
593 ContentType =
594 case get_outheader_value("content-type", Req) of
595 {undefined, _} ->
596 "text/html";
597 {CT, _} ->
598 CT
599 end,
600 HeaderList = [{"Content-Type", ContentType},
601 {"Content-Range",
602 ["bytes ",
603 mochiweb_util:make_io(Start), "-",
604 mochiweb_util:make_io(End),
605 "/", mochiweb_util:make_io(Size)]}],
606 Body = case Body0 of
607 _ when is_function(Body0) ->
608 {known_length_stream, End - Start + 1, Body0(Start, End)};
609 {known_length_stream, ContentSize, StreamBody} ->
610 {known_length_stream, ContentSize, StreamBody};
611 _ ->
612 Body0
613 end,
614 {HeaderList, Body};
615 parts_to_body(BodyList, Size, Req) when is_list(BodyList) ->
616 %% return
617 %% header Content-Type: multipart/byteranges; boundary=441934886133bdee4
618 %% and multipart body
619 ContentType =
620 case get_outheader_value("content-type", Req) of
621 {undefined, _} ->
622 "text/html";
623 {CT, _} ->
624 CT
625 end,
626 Boundary = mochihex:to_hex(mochiweb_util:rand_bytes(8)),
627 HeaderList = [{"Content-Type",
628 ["multipart/byteranges; ",
629 "boundary=", Boundary]}],
630 MultiPartBody = case hd(BodyList) of
631 {_, _, Fun} when is_function(Fun) ->
632 stream_multipart_body(BodyList, ContentType,
633 Boundary, Size);
634 _ ->
635 multipart_body(BodyList, ContentType,
636 Boundary, Size)
637 end,
638 {HeaderList, MultiPartBody}.
639
640 multipart_body([], _ContentType, Boundary, _Size) ->
641 end_boundary(Boundary);
642 multipart_body([{Start, End, Body} | BodyList],
643 ContentType, Boundary, Size) ->
644 [part_preamble(Boundary, ContentType, Start, End, Size),
645 Body, <<"\r\n">>
646 | multipart_body(BodyList, ContentType, Boundary, Size)].
647
648 boundary(B) -> [<<"--">>, B, <<"\r\n">>].
649 end_boundary(B) -> [<<"--">>, B, <<"--\r\n">>].
650
651 part_preamble(Boundary, CType, Start, End, Size) ->
652 [boundary(Boundary),
653 <<"Content-Type: ">>, CType, <<"\r\n">>,
654 <<"Content-Range: bytes ">>,
655 mochiweb_util:make_io(Start), <<"-">>, mochiweb_util:make_io(End),
656 <<"/">>, mochiweb_util:make_io(Size),
657 <<"\r\n\r\n">>].
658
659 stream_multipart_body(BodyList, ContentType, Boundary, Size) ->
660 Helper = stream_multipart_body_helper(
661 BodyList, ContentType, Boundary, Size),
662 %% executing Helper() here is an optimization;
663 %% it's just as valid to say {<<>>, Helper}
664 {stream, Helper()}.
665
666 stream_multipart_body_helper([], _CType, Boundary, _Size) ->
667 fun() -> {end_boundary(Boundary), done} end;
668 stream_multipart_body_helper([{Start, End, Fun}|Rest],
669 CType, Boundary, Size) ->
670 fun() ->
671 {part_preamble(Boundary, CType, Start, End, Size),
672 stream_multipart_part_helper(
673 fun() -> Fun(Start, End) end,
674 Rest, CType, Boundary, Size)}
675 end.
676
677 stream_multipart_part_helper(Fun, Rest, CType, Boundary, Size) ->
678 fun() ->
679 case Fun() of
680 {Data, done} ->
681 %% when this part is done, start the next part
682 {[Data, <<"\r\n">>],
683 stream_multipart_body_helper(
684 Rest, CType, Boundary, Size)};
685 {Data, Next} ->
686 %% this subpart has more data coming
687 {Data, stream_multipart_part_helper(
688 Next, Rest, CType, Boundary, Size)}
689 end
690 end.
691
692 make_code({Code, undefined}) when is_integer(Code) ->
693 make_code({Code, httpd_util:reason_phrase(Code)});
694 make_code({Code, ReasonPhrase}) when is_integer(Code) ->
695 [integer_to_list(Code), [" ", ReasonPhrase]];
696 make_code(Code) when is_integer(Code) ->
697 make_code({Code, httpd_util:reason_phrase(Code)});
698 make_code(Io) when is_list(Io); is_binary(Io) ->
699 Io.
700
701 make_version({1, 0}) ->
702 <<"HTTP/1.0 ">>;
703 make_version(_) ->
704 <<"HTTP/1.1 ">>.
705
706 make_headers({Code, _ReasonPhrase}, Length, RD) ->
707 make_headers(Code, Length, RD);
708 make_headers(Code, Length, RD) when is_integer(Code) ->
709 Hdrs0 = case Code of
710 304 ->
711 mochiweb_headers:make(wrq:resp_headers(RD));
712 _ ->
713 case Length of
714 chunked ->
715 mochiweb_headers:enter(
716 "Transfer-Encoding","chunked",
717 mochiweb_headers:make(wrq:resp_headers(RD)));
718 _ ->
719 mochiweb_headers:enter(
720 "Content-Length",integer_to_list(Length),
721 mochiweb_headers:make(wrq:resp_headers(RD)))
722 end
723 end,
724 case application:get_env(webmachine, server_name) of
725 undefined -> ServerHeader = "MochiWeb/1.1 WebMachine/" ++ ?WMVSN ++ " (" ++ ?QUIP ++ ")";
726 {ok, ServerHeader} when is_list(ServerHeader) -> ok
727 end,
728 WithSrv = mochiweb_headers:enter("Server", ServerHeader, Hdrs0),
729 Hdrs = case mochiweb_headers:get_value("date", WithSrv) of
730 undefined ->
731 mochiweb_headers:enter("Date", httpd_util:rfc1123_date(), WithSrv);
732 _ ->
733 WithSrv
734 end,
735 F = fun({K, V}, Acc) ->
736 [mochiweb_util:make_io(K), <<": ">>, V, <<"\r\n">> | Acc]
737 end,
738 lists:foldl(F, [<<"\r\n">>], mochiweb_headers:to_list(Hdrs)).
739
740 get_reqdata(#wm_reqstate{}=ReqState) -> call(get_reqdata, {?MODULE, ReqState});
741 get_reqdata(Req) -> call(get_reqdata, Req).
742
743 set_reqdata(RD, #wm_reqstate{}=ReqState) -> call({set_reqdata, RD}, {?MODULE, ReqState});
744 set_reqdata(RD, Req) -> call({set_reqdata, RD}, Req).
745
746 socket(#wm_reqstate{}=ReqState) -> call(socket, {?MODULE, ReqState});
747 socket(Req) -> call(socket, Req).
748
749 method(#wm_reqstate{}=ReqState) -> call(method, {?MODULE, ReqState});
750 method(Req) -> call(method, Req).
751
752 version(#wm_reqstate{}=ReqState) -> call(version, {?MODULE, ReqState});
753 version(Req) -> call(version, Req).
754
755 disp_path(#wm_reqstate{}=ReqState) -> call(disp_path, {?MODULE, ReqState});
756 disp_path(Req) -> call(disp_path, Req).
757
758 path(#wm_reqstate{}=ReqState) -> call(path, {?MODULE, ReqState});
759 path(Req) -> call(path, Req).
760
761 raw_path(#wm_reqstate{}=ReqState) -> call(raw_path, {?MODULE, ReqState});
762 raw_path(Req) -> call(raw_path, Req).
763
764 req_headers(#wm_reqstate{}=ReqState) -> call(req_headers, {?MODULE, ReqState});
765 req_headers(Req) -> call(req_headers, Req).
766 headers(Req) -> req_headers(Req).
767
768 req_body(MaxRevBody, #wm_reqstate{}=ReqState) -> call({req_body,MaxRevBody}, {?MODULE, ReqState});
769 req_body(MaxRevBody, Req) -> call({req_body,MaxRevBody}, Req).
770 stream_req_body(MaxHunk, #wm_reqstate{}=ReqState) ->
771 call({stream_req_body, MaxHunk}, {?MODULE, ReqState});
772 stream_req_body(MaxHunk, Req) -> call({stream_req_body, MaxHunk}, Req).
773
774 resp_headers(#wm_reqstate{}=ReqState) -> call(resp_headers, {?MODULE, ReqState});
775 resp_headers(Req) -> call(resp_headers, Req).
776 out_headers(Req) -> resp_headers(Req).
777
778 get_resp_header(HeaderName, Req) ->
779 call({get_resp_header, HeaderName}, Req).
780 get_out_header(HeaderName, #wm_reqstate{}=ReqState) ->
781 get_resp_header(HeaderName, {?MODULE, ReqState});
782 get_out_header(HeaderName, Req) -> get_resp_header(HeaderName, Req).
783
784 has_resp_header(HeaderName, Req) ->
785 case get_out_header(HeaderName, Req) of
786 {undefined, _} -> false;
787 {_, _} -> true
788 end.
789 has_out_header(HeaderName, #wm_reqstate{}=ReqState) ->
790 has_resp_header(HeaderName, {?MODULE, ReqState});
791 has_out_header(HeaderName, Req) -> has_resp_header(HeaderName, Req).
792
793 has_resp_body(#wm_reqstate{}=ReqState) -> call(has_resp_body, {?MODULE, ReqState});
794 has_resp_body(Req) -> call(has_resp_body, Req).
795 has_response_body(Req) -> has_resp_body(Req).
796
797 response_code(#wm_reqstate{}=ReqState) -> call(response_code, {?MODULE, ReqState});
798 response_code(Req) -> call(response_code, Req).
799 set_response_code(Code, {?MODULE, ReqState}=Req) ->
800 call({ReqState, set_response_code, Code}, Req);
801 set_response_code(Code, ReqState) ->
802 set_response_code(Code, {?MODULE, ReqState}).
803
804 peer(#wm_reqstate{}=ReqState) -> call(peer, {?MODULE, ReqState});
805 peer(Req) -> call(peer, Req).
806
807 range(#wm_reqstate{}=ReqState) -> call(range, {?MODULE, ReqState});
808 range(Req) -> call(range, Req).
809
810 req_cookie(#wm_reqstate{}=ReqState) -> call(req_cookie, {?MODULE, ReqState});
811 req_cookie(Req) -> call(req_cookie, Req).
812 parse_cookie(Req) -> req_cookie(Req).
813 get_cookie_value(Key, #wm_reqstate{}=ReqState) -> get_cookie_value(Key, {?MODULE, ReqState});
814 get_cookie_value(Key, Req) ->
815 {ReqCookie, NewReqState} = req_cookie(Req),
816 case lists:keyfind(Key, 1, ReqCookie) of
817 false -> {undefined, NewReqState};
818 {Key, Value} -> {Value, NewReqState}
819 end.
820
821 req_qs(#wm_reqstate{}=ReqState) -> call(req_qs, {?MODULE, ReqState});
822 req_qs(Req) -> call(req_qs, Req).
823 parse_qs(Req) -> req_qs(Req).
824 get_qs_value(Key, #wm_reqstate{}=ReqState) -> get_qs_value(Key, {?MODULE, ReqState});
825 get_qs_value(Key, Req) ->
826 {ReqQS, NewReqState} = req_qs(Req),
827 case lists:keyfind(Key, 1, ReqQS) of
828 false -> {undefined, NewReqState};
829 {Key, Value} -> {Value, NewReqState}
830 end.
831 get_qs_value(Key, Default, #wm_reqstate{}=ReqState) ->
832 get_qs_value(Key, Default, {?MODULE, ReqState});
833 get_qs_value(Key, Default, Req) ->
834 {ReqQS, NewReqState} = req_qs(Req),
835 case lists:keyfind(Key, 1, ReqQS) of
836 false -> {Default, NewReqState};
837 {Key, Value} -> {Value, NewReqState}
838 end.
839 set_resp_body(Body, #wm_reqstate{}=ReqState) -> call({set_resp_body, Body}, {?MODULE, ReqState});
840 set_resp_body(Body, Req) -> call({set_resp_body, Body}, Req).
841 resp_body(#wm_reqstate{}=ReqState) -> call(resp_body, {?MODULE, ReqState});
842 resp_body(Req) -> call(resp_body, Req).
843 response_body(Req) -> resp_body(Req).
844
845 get_req_header(K, #wm_reqstate{}=ReqState) -> call({get_req_header, K}, {?MODULE, ReqState});
846 get_req_header(K, Req) -> call({get_req_header, K}, Req).
847
848 set_resp_header(K, V, Req) -> call({set_resp_header, K, V}, Req).
849 add_response_header(K, V, #wm_reqstate{}=ReqState) -> set_resp_header(K, V, {?MODULE, ReqState});
850 add_response_header(K, V, Req) -> set_resp_header(K, V, Req).
851
852 set_resp_headers(Hdrs, Req) -> call({set_resp_headers, Hdrs}, Req).
853 add_response_headers(Hdrs, #wm_reqstate{}=ReqState) -> set_resp_headers(Hdrs, {?MODULE, ReqState});
854 add_response_headers(Hdrs, Req) -> set_resp_headers(Hdrs, Req).
855
856 remove_resp_header(K, Req) -> call({remove_resp_header, K}, Req).
857 remove_response_header(K, #wm_reqstate{}=ReqState) -> remove_resp_header(K, {?MODULE, ReqState});
858 remove_response_header(K, Req) -> remove_resp_header(K, Req).
859
860 merge_resp_headers(Hdrs, Req) -> call({merge_resp_headers, Hdrs}, Req).
861 merge_response_headers(Hdrs, #wm_reqstate{}=ReqState) ->
862 merge_resp_headers(Hdrs, {?MODULE, ReqState});
863 merge_response_headers(Hdrs, Req) -> merge_resp_headers(Hdrs, Req).
864
865 append_to_response_body(Data, #wm_reqstate{}=ReqState) ->
866 call({append_to_response_body, Data}, {?MODULE, ReqState});
867 append_to_response_body(Data, Req) ->
868 call({append_to_response_body, Data}, Req).
869
870 do_redirect(#wm_reqstate{}=ReqState) -> call(do_redirect, {?MODULE, ReqState});
871 do_redirect(Req) -> call(do_redirect, Req).
872
873 resp_redirect(#wm_reqstate{}=ReqState) -> call(resp_redirect, {?MODULE, ReqState});
874 resp_redirect(Req) -> call(resp_redirect, Req).
875
876 get_metadata(Key, #wm_reqstate{}=ReqState) -> call({get_metadata, Key}, {?MODULE, ReqState});
877 get_metadata(Key, Req) -> call({get_metadata, Key}, Req).
878
879 set_metadata(Key, Value, #wm_reqstate{}=ReqState) ->
880 call({set_metadata, Key, Value}, {?MODULE, ReqState});
881 set_metadata(Key, Value, Req) -> call({set_metadata, Key, Value}, Req).
882
883 get_path_info(#wm_reqstate{}=ReqState) -> call(get_path_info, {?MODULE, ReqState});
884 get_path_info(Req) -> call(get_path_info, Req).
885
886 get_path_info(Key, #wm_reqstate{}=ReqState) -> call({get_path_info, Key}, {?MODULE, ReqState});
887 get_path_info(Key, Req) -> call({get_path_info, Key}, Req).
888
889 path_tokens(#wm_reqstate{}=ReqState) -> call(path_tokens, {?MODULE, ReqState});
890 path_tokens(Req) -> call(path_tokens, Req).
891 get_path_tokens(Req) -> path_tokens(Req).
892
893 app_root(#wm_reqstate{}=ReqState) -> call(app_root, {?MODULE, ReqState});
894 app_root(Req) -> call(app_root, Req).
895 get_app_root(Req) -> app_root(Req).
896
897 load_dispatch_data(Bindings, HostTokens, Port, PathTokens,
898 AppRoot, DispPath, #wm_reqstate{}=ReqState) ->
899 call({load_dispatch_data, Bindings, HostTokens, Port,
900 PathTokens, AppRoot, DispPath}, {?MODULE, ReqState});
901 load_dispatch_data(Bindings, HostTokens, Port, PathTokens,
902 AppRoot, DispPath, Req) ->
903 call({load_dispatch_data, Bindings, HostTokens, Port,
904 PathTokens, AppRoot, DispPath}, Req).
905
906 log_data(#wm_reqstate{}=ReqState) -> call(log_data, {?MODULE, ReqState});
907 log_data(Req) -> call(log_data, Req).
908
909 -ifdef(TEST).
910 -include_lib("eunit/include/eunit.hrl").
911
912 reqdata_test() ->
913 ReqData = #wm_reqdata{req_headers = mochiweb_headers:make([])},
914 {ok, ReqState} = set_reqdata(ReqData, #wm_reqstate{}),
915 ?assertEqual(ReqData, element(1, get_reqdata(ReqState))).
916
917 header_test() ->
918 HdrName = "Accept",
919 HdrValue = "application/json",
920 ReqData = #wm_reqdata{req_headers = mochiweb_headers:make([{HdrName, HdrValue}])},
921 {ok, ReqState} = set_reqdata(ReqData, #wm_reqstate{}),
922 ?assertEqual({HdrValue, ReqState}, get_header_value(HdrName, ReqState)),
923 ?assertEqual({HdrValue, ReqState}, get_req_header(HdrName, ReqState)).
924
925 metadata_test() ->
926 Key = "webmachine",
927 Value = "eunit",
928 {ok, ReqState} = set_metadata(Key, Value, #wm_reqstate{metadata=orddict:new()}),
929 ?assertEqual({Value, ReqState}, get_metadata(Key, ReqState)).
930
931 peer_test() ->
932 Self = self(),
933 Pid = spawn_link(fun() ->
934 {ok, LS} = gen_tcp:listen(0, [binary, {active, false}]),
935 {ok, {_, Port}} = inet:sockname(LS),
936 Self ! {port, Port},
937 {ok, S} = gen_tcp:accept(LS),
938 receive
939 stop ->
940 ok
941 after 2000 ->
942 ok
943 end,
944 gen_tcp:close(S),
945 gen_tcp:close(LS)
946 end),
947 receive
948 {port, Port} ->
949 {ok, S} = gen_tcp:connect({127,0,0,1}, Port, [binary, {active, false}]),
950 ReqData = #wm_reqdata{req_headers = mochiweb_headers:make([])},
951 ReqState = #wm_reqstate{socket=S, reqdata=ReqData},
952 ?assertEqual({S, ReqState}, socket(ReqState)),
953 {"127.0.0.1", NReqState} = get_peer(ReqState),
954 ?assertEqual("127.0.0.1", NReqState#wm_reqstate.peer),
955 Pid ! stop,
956 gen_tcp:close(S)
957 after 2000 ->
958 exit({error, listener_fail})
959 end.
960
961 -endif.
+0
-260
deps/webmachine/src/webmachine_resource.erl less more
0 %% @author Justin Sheehy <justin@basho.com>
1 %% @author Andy Gross <andy@basho.com>
2 %% @copyright 2007-2012 Basho Technologies
3 %%
4 %% Licensed under the Apache License, Version 2.0 (the "License");
5 %% you may not use this file except in compliance with the License.
6 %% You may obtain a copy of the License at
7 %%
8 %% http://www.apache.org/licenses/LICENSE-2.0
9 %%
10 %% Unless required by applicable law or agreed to in writing, software
11 %% distributed under the License is distributed on an "AS IS" BASIS,
12 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 %% See the License for the specific language governing permissions and
14 %% limitations under the License.
15
16 -module(webmachine_resource).
17 -author('Justin Sheehy <justin@basho.com>').
18 -author('Andy Gross <andy@basho.com>').
19 -export([new/4, wrap/2, wrap/3]).
20 -export([do/3,log_d/2,stop/1]).
21
22 -include("wm_resource.hrl").
23 -include("wm_reqdata.hrl").
24 -include("wm_reqstate.hrl").
25
26 new(R_Mod, R_ModState, R_ModExports, R_Trace) ->
27 {?MODULE, R_Mod, R_ModState, R_ModExports, R_Trace}.
28
29 default(ping) ->
30 no_default;
31 default(service_available) ->
32 true;
33 default(resource_exists) ->
34 true;
35 default(auth_required) ->
36 true;
37 default(is_authorized) ->
38 true;
39 default(forbidden) ->
40 false;
41 default(allow_missing_post) ->
42 false;
43 default(malformed_request) ->
44 false;
45 default(uri_too_long) ->
46 false;
47 default(known_content_type) ->
48 true;
49 default(valid_content_headers) ->
50 true;
51 default(valid_entity_length) ->
52 true;
53 default(options) ->
54 [];
55 default(allowed_methods) ->
56 ['GET', 'HEAD'];
57 default(known_methods) ->
58 ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE', 'CONNECT', 'OPTIONS'];
59 default(content_types_provided) ->
60 [{"text/html", to_html}];
61 default(content_types_accepted) ->
62 [];
63 default(delete_resource) ->
64 false;
65 default(delete_completed) ->
66 true;
67 default(post_is_create) ->
68 false;
69 default(create_path) ->
70 undefined;
71 default(base_uri) ->
72 undefined;
73 default(process_post) ->
74 false;
75 default(language_available) ->
76 true;
77 default(charsets_provided) ->
78 no_charset; % this atom causes charset-negotation to short-circuit
79 % the default setting is needed for non-charset responses such as image/png
80 % an example of how one might do actual negotiation
81 % [{"iso-8859-1", fun(X) -> X end}, {"utf-8", make_utf8}];
82 default(encodings_provided) ->
83 [{"identity", fun(X) -> X end}];
84 % this is handy for auto-gzip of GET-only resources:
85 % [{"identity", fun(X) -> X end}, {"gzip", fun(X) -> zlib:gzip(X) end}];
86 default(variances) ->
87 [];
88 default(is_conflict) ->
89 false;
90 default(multiple_choices) ->
91 false;
92 default(previously_existed) ->
93 false;
94 default(moved_permanently) ->
95 false;
96 default(moved_temporarily) ->
97 false;
98 default(last_modified) ->
99 undefined;
100 default(expires) ->
101 undefined;
102 default(generate_etag) ->
103 undefined;
104 default(finish_request) ->
105 true;
106 default(validate_content_checksum) ->
107 not_validated;
108 default(_) ->
109 no_default.
110
111 wrap(Mod, Args, {?MODULE, _, _, _, _}) ->
112 wrap(Mod, Args).
113
114 wrap(Mod, Args) ->
115 case Mod:init(Args) of
116 {ok, ModState} ->
117 {ok, webmachine_resource:new(Mod, ModState,
118 orddict:from_list(Mod:module_info(exports)), false)};
119 {{trace, Dir}, ModState} ->
120 {ok, File} = open_log_file(Dir, Mod),
121 log_decision(File, v3b14),
122 log_call(File, attempt, Mod, init, Args),
123 log_call(File, result, Mod, init, {{trace, Dir}, ModState}),
124 {ok, webmachine_resource:new(Mod, ModState,
125 orddict:from_list(Mod:module_info(exports)), File)};
126 _ ->
127 {stop, bad_init_arg}
128 end.
129
130 do(#wm_resource{}=Res, Fun, ReqProps) ->
131 #wm_resource{module=R_Mod, modstate=R_ModState,
132 modexports=R_ModExports, trace=R_Trace} = Res,
133 do(Fun, ReqProps, {?MODULE, R_Mod, R_ModState, R_ModExports, R_Trace});
134 do(Fun, ReqProps, {?MODULE, R_Mod, _, R_ModExports, R_Trace}=Req)
135 when is_atom(Fun) andalso is_list(ReqProps) ->
136 case lists:keyfind(reqstate, 1, ReqProps) of
137 false -> RState0 = undefined;
138 {reqstate, RState0} -> ok
139 end,
140 put(tmp_reqstate, empty),
141 {Reply, ReqData, NewModState} = handle_wm_call(Fun,
142 (RState0#wm_reqstate.reqdata)#wm_reqdata{wm_state=RState0},
143 Req),
144 ReqState = case get(tmp_reqstate) of
145 empty -> RState0;
146 X -> X
147 end,
148 %% Do not need the embedded state anymore
149 TrimData = ReqData#wm_reqdata{wm_state=undefined},
150 {Reply,
151 webmachine_resource:new(R_Mod, NewModState, R_ModExports, R_Trace),
152 ReqState#wm_reqstate{reqdata=TrimData}}.
153
154 handle_wm_call(Fun, ReqData, {?MODULE,R_Mod,R_ModState,R_ModExports,R_Trace}=Req) ->
155 case default(Fun) of
156 no_default ->
157 resource_call(Fun, ReqData, Req);
158 Default ->
159 case orddict:is_key(Fun, R_ModExports) of
160 true ->
161 resource_call(Fun, ReqData, Req);
162 false ->
163 if is_pid(R_Trace) ->
164 log_call(R_Trace,
165 not_exported,
166 R_Mod, Fun, [ReqData, R_ModState]);
167 true -> ok
168 end,
169 {Default, ReqData, R_ModState}
170 end
171 end.
172
173 trim_trace([{M,F,[RD = #wm_reqdata{},S]}|STRest]) ->
174 TrimState = (RD#wm_reqdata.wm_state)#wm_reqstate{reqdata='REQDATA'},
175 TrimRD = RD#wm_reqdata{wm_state=TrimState},
176 [{M,F,[TrimRD,S]}|STRest];
177 trim_trace(X) -> X.
178
179 resource_call(F, ReqData, {?MODULE, R_Mod, R_ModState, _, R_Trace}) ->
180 case R_Trace of
181 false -> nop;
182 _ -> log_call(R_Trace, attempt, R_Mod, F, [ReqData, R_ModState])
183 end,
184 Result = try
185 apply(R_Mod, F, [ReqData, R_ModState])
186 catch C:R ->
187 Reason = {C, R, trim_trace(erlang:get_stacktrace())},
188 {{error, Reason}, ReqData, R_ModState}
189 end,
190 case R_Trace of
191 false -> nop;
192 _ -> log_call(R_Trace, result, R_Mod, F, Result)
193 end,
194 Result.
195
196 log_d(#wm_resource{}=Res, DecisionID) ->
197 #wm_resource{module=R_Mod, modstate=R_ModState,
198 modexports=R_ModExports, trace=R_Trace} = Res,
199 log_d(DecisionID, {?MODULE, R_Mod, R_ModState, R_ModExports, R_Trace});
200 log_d(DecisionID, {?MODULE, _, _, _, R_Trace}) ->
201 case R_Trace of
202 false -> nop;
203 _ -> log_decision(R_Trace, DecisionID)
204 end.
205
206 stop(#wm_resource{trace=R_Trace}) -> close_log_file(R_Trace);
207 stop({?MODULE, _, _, _, R_Trace}) -> close_log_file(R_Trace).
208
209 log_call(File, Type, M, F, Data) ->
210 io:format(File,
211 "{~p, ~p, ~p,~n ~p}.~n",
212 [Type, M, F, escape_trace_data(Data)]).
213
214 escape_trace_data(Fun) when is_function(Fun) ->
215 {'WMTRACE_ESCAPED_FUN',
216 [erlang:fun_info(Fun, module),
217 erlang:fun_info(Fun, name),
218 erlang:fun_info(Fun, arity),
219 erlang:fun_info(Fun, type)]};
220 escape_trace_data(Pid) when is_pid(Pid) ->
221 {'WMTRACE_ESCAPED_PID', pid_to_list(Pid)};
222 escape_trace_data(Port) when is_port(Port) ->
223 {'WMTRACE_ESCAPED_PORT', erlang:port_to_list(Port)};
224 escape_trace_data(List) when is_list(List) ->
225 escape_trace_list(List, []);
226 escape_trace_data(R=#wm_reqstate{}) ->
227 list_to_tuple(
228 escape_trace_data(
229 tuple_to_list(R#wm_reqstate{reqdata='WMTRACE_NESTED_REQDATA'})));
230 escape_trace_data(Tuple) when is_tuple(Tuple) ->
231 list_to_tuple(escape_trace_data(tuple_to_list(Tuple)));
232 escape_trace_data(Other) ->
233 Other.
234
235 escape_trace_list([Head|Tail], Acc) ->
236 escape_trace_list(Tail, [escape_trace_data(Head)|Acc]);
237 escape_trace_list([], Acc) ->
238 %% proper, nil-terminated list
239 lists:reverse(Acc);
240 escape_trace_list(Final, Acc) ->
241 %% non-nil-terminated list, like the dict module uses
242 lists:reverse(tl(Acc))++[hd(Acc)|escape_trace_data(Final)].
243
244 log_decision(File, DecisionID) ->
245 io:format(File, "{decision, ~p}.~n", [DecisionID]).
246
247 open_log_file(Dir, Mod) ->
248 Now = {_,_,US} = now(),
249 {{Y,M,D},{H,I,S}} = calendar:now_to_universal_time(Now),
250 Filename = io_lib:format(
251 "~s/~p-~4..0B-~2..0B-~2..0B"
252 "-~2..0B-~2..0B-~2..0B.~6..0B.wmtrace",
253 [Dir, Mod, Y, M, D, H, I, S, US]),
254 file:open(Filename, [write]).
255
256 close_log_file(File) when is_pid(File) ->
257 file:close(File);
258 close_log_file(_) ->
259 ok.
+0
-276
deps/webmachine/src/webmachine_router.erl less more
0 %% @author Kevin A. Smith <ksmith@basho.com>
1 %% @copyright 2007-2010 Basho Technologies
2 %%
3 %% Licensed under the Apache License, Version 2.0 (the "License");
4 %% you may not use this file except in compliance with the License.
5 %% You may obtain a copy of the License at
6 %%
7 %% http://www.apache.org/licenses/LICENSE-2.0
8 %%
9 %% Unless required by applicable law or agreed to in writing, software
10 %% distributed under the License is distributed on an "AS IS" BASIS,
11 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 %% See the License for the specific language governing permissions and
13 %% limitations under the License.
14
15 %% @doc Module to add and remove dynamic routes to webmachine's routing
16 %% table. Dynamic routes are not persistent between executions of
17 %% a webmachine application. They will need to be added to the
18 %% the table each time webmachine restarts.
19 -module(webmachine_router).
20
21 -behaviour(gen_server).
22
23 %% API
24 -export([start_link/0,
25 add_route/1,
26 add_route/2,
27 remove_route/1,
28 remove_route/2,
29 remove_resource/1,
30 remove_resource/2,
31 get_routes/0,
32 get_routes/1,
33 init_routes/1,
34 init_routes/2
35 ]).
36
37 %% gen_server callbacks
38 -export([init/1,
39 handle_call/3,
40 handle_cast/2,
41 handle_info/2,
42 terminate/2,
43 code_change/3]).
44
45 %% @type hostmatchterm() = {hostmatch(), [pathmatchterm()]}.
46 % The dispatch configuration contains a list of these terms, and the
47 % first one whose host and one pathmatchterm match is used.
48
49 %% @type pathmatchterm() = {[pathterm()], matchmod(), matchopts()}.
50 % The dispatch configuration contains a list of these terms, and the
51 % first one whose list of pathterms matches the input path is used.
52
53 %% @type pathterm() = '*' | string() | atom().
54 % A list of pathterms is matched against a '/'-separated input path.
55 % The '*' pathterm matches all remaining tokens.
56 % A string pathterm will match a token of exactly the same string.
57 % Any atom pathterm other than '*' will match any token and will
58 % create a binding in the result if a complete match occurs.
59
60 %% @type matchmod() = atom().
61 % This atom, if present in a successful matchterm, will appear in
62 % the resulting dispterm. In Webmachine this is used to name the
63 % resource module that will handle the matching request.
64
65 %% @type matchopts() = [term()].
66 % This term, if present in a successful matchterm, will appear in
67 % the resulting dispterm. In Webmachine this is used to provide
68 % arguments to the resource module handling the matching request.
69
70 -define(SERVER, ?MODULE).
71
72 %% @spec add_route(hostmatchterm() | pathmatchterm()) -> ok
73 %% @doc Adds a route to webmachine's route table. The route should
74 %% be the format documented here:
75 %% http://bitbucket.org/justin/webmachine/wiki/DispatchConfiguration
76 add_route(Route) ->
77 add_route(default, Route).
78
79 add_route(Name, Route) ->
80 gen_server:call(?SERVER, {add_route, Name, Route}, infinity).
81
82 %% @spec remove_route(hostmatchterm() | pathmatchterm()) -> ok
83 %% @doc Removes a route from webamchine's route table. The route
84 %% route must be properly formatted
85 %% @see add_route/2
86 remove_route(Route) ->
87 remove_route(default, Route).
88
89 remove_route(Name, Route) ->
90 gen_server:call(?SERVER, {remove_route, Name, Route}, infinity).
91
92 %% @spec remove_resource(atom()) -> ok
93 %% @doc Removes all routes for a specific resource module.
94 remove_resource(Resource) when is_atom(Resource) ->
95 remove_resource(default, Resource).
96
97 remove_resource(Name, Resource) when is_atom(Resource) ->
98 gen_server:call(?SERVER, {remove_resource, Name, Resource}, infinity).
99
100 %% @spec get_routes() -> [{[], res, []}]
101 %% @doc Retrieve a list of routes and resources set in webmachine's
102 %% route table.
103 get_routes() ->
104 get_routes(default).
105
106 get_routes(Name) ->
107 get_dispatch_list(Name).
108
109 %% @spec init_routes() -> ok
110 %% @doc Set the default routes, unless the routing table isn't empty.
111 init_routes(DefaultRoutes) ->
112 init_routes(default, DefaultRoutes).
113
114 init_routes(Name, DefaultRoutes) ->
115 gen_server:call(?SERVER, {init_routes, Name, DefaultRoutes}, infinity).
116
117 %% @spec start_link() -> {ok, pid()} | {error, any()}
118 %% @doc Starts the webmachine_router gen_server.
119 start_link() ->
120 %% We expect to only be called from webmachine_sup
121 %%
122 %% Set up the ETS configuration table.
123 try ets:new(?MODULE, [named_table, public, set, {keypos, 1},
124 {read_concurrency, true}]) of
125 _Result ->
126 ok
127 catch
128 error:badarg ->
129 %% The table already exists, which is fine. The webmachine_router
130 %% probably crashed and this is a restart.
131 ok
132 end,
133 gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
134
135 %% @private
136 init([]) ->
137 {ok, undefined}.
138
139 %% @private
140 handle_call({remove_resource, Name, Resource}, _From, State) ->
141 DL = filter_by_resource(Resource, get_dispatch_list(Name)),
142 {reply, set_dispatch_list(Name, DL), State};
143
144 handle_call({remove_route, Name, Route}, _From, State) ->
145 DL = [D || D <- get_dispatch_list(Name),
146 D /= Route],
147 {reply, set_dispatch_list(Name, DL), State};
148
149 handle_call({add_route, Name, Route}, _From, State) ->
150 DL = [Route|[D || D <- get_dispatch_list(Name),
151 D /= Route]],
152 {reply, set_dispatch_list(Name, DL), State};
153
154 handle_call({init_routes, Name, DefaultRoutes}, _From, State) ->
155 %% if the table lacks a dispatch_list row, set it
156 ets:insert_new(?MODULE, {Name, DefaultRoutes}),
157 {reply, ok, State};
158
159 handle_call(_Request, _From, State) ->
160 {reply, ignore, State}.
161
162 %% @private
163 handle_cast(_Msg, State) ->
164 {noreply, State}.
165
166 %% @private
167 handle_info(_Info, State) ->
168 {noreply, State}.
169
170 %% @private
171 terminate(_Reason, _State) ->
172 ok.
173
174 %% @private
175 code_change(_OldVsn, State, _Extra) ->
176 {ok, State}.
177
178 %% Internal functions
179
180 %% @doc Remove any dispatch rule that directs requests to `Resource'
181 filter_by_resource(Resource, Dispatch) ->
182 lists:foldr(filter_by_resource(Resource), [], Dispatch).
183
184 filter_by_resource(Resource) ->
185 fun({_, R, _}, Acc) when R == Resource -> % basic dispatch
186 Acc;
187 ({_, _, R, _}, Acc) when R == Resource -> % guarded dispatch
188 Acc;
189 ({Host, Disp}, Acc) -> % host-based dispatch
190 [{Host, filter_by_resource(Resource, Disp)}|Acc];
191 (Other, Acc) -> % dispatch not mentioning this resource
192 [Other|Acc]
193 end.
194
195 get_dispatch_list(Name) ->
196 case ets:lookup(?MODULE, Name) of
197 [{Name, Dispatch}] ->
198 Dispatch;
199 [] ->
200 []
201 end.
202
203 set_dispatch_list(Name, DispatchList) ->
204 true = ets:insert(?MODULE, {Name, DispatchList}),
205 ok.
206
207 %%
208 %% Tests
209 %%
210 -ifdef(TEST).
211 -include_lib("eunit/include/eunit.hrl").
212
213 add_remove_route_test() ->
214 {ok, Pid} = webmachine_router:start_link(),
215 unlink(Pid),
216 PathSpec = {["foo"], foo, []},
217 webmachine_router:add_route(PathSpec),
218 [PathSpec] = get_routes(),
219 webmachine_router:remove_route(PathSpec),
220 [] = get_routes(),
221 exit(Pid, kill).
222
223 add_remove_resource_test() ->
224 {ok, Pid} = webmachine_router:start_link(),
225 unlink(Pid),
226 PathSpec1 = {["foo"], foo, []},
227 PathSpec2 = {["bar"], foo, []},
228 PathSpec3 = {["baz"], bar, []},
229 PathSpec4 = {["foo"], fun(_) -> true end, foo, []},
230 PathSpec5 = {["foo"], {webmachine_router, test_guard}, foo, []},
231 webmachine_router:add_route(PathSpec1),
232 webmachine_router:add_route(PathSpec2),
233 webmachine_router:add_route(PathSpec3),
234 webmachine_router:remove_resource(foo),
235 [PathSpec3] = get_routes(),
236 webmachine_router:add_route(PathSpec4),
237 webmachine_router:remove_resource(foo),
238 [PathSpec3] = get_routes(),
239 webmachine_router:add_route(PathSpec5),
240 webmachine_router:remove_resource(foo),
241 [PathSpec3] = get_routes(),
242 webmachine_router:remove_route(PathSpec3),
243 [begin
244 PathSpec = {"localhost", [HostPath]},
245 webmachine_router:add_route(PathSpec),
246 webmachine_router:remove_resource(foo),
247 [{"localhost", []}] = get_routes(),
248 webmachine_router:remove_route({"localhost", []})
249 end || HostPath <- [PathSpec1, PathSpec4, PathSpec5]],
250 exit(Pid, kill).
251
252 no_dupe_path_test() ->
253 {ok, Pid} = webmachine_router:start_link(),
254 unlink(Pid),
255 PathSpec = {["foo"], foo, []},
256 webmachine_router:add_route(PathSpec),
257 webmachine_router:add_route(PathSpec),
258 [PathSpec] = get_routes(),
259 exit(Pid, kill).
260
261 supervisor_restart_keeps_routes_test() ->
262 {ok, Pid} = webmachine_router:start_link(),
263 unlink(Pid),
264 PathSpec = {["foo"], foo, []},
265 webmachine_router:add_route(PathSpec),
266 [PathSpec] = get_routes(),
267 OldRouter = whereis(webmachine_router),
268 exit(whereis(webmachine_router), kill),
269 timer:sleep(100),
270 NewRouter = whereis(webmachine_router),
271 ?assert(OldRouter /= NewRouter),
272 [PathSpec] = get_routes(),
273 exit(Pid, kill).
274
275 -endif.
+0
-65
deps/webmachine/src/webmachine_sup.erl less more
0 %% @author Justin Sheehy <justin@basho.com>
1 %% @author Andy Gross <andy@basho.com>
2 %% @copyright 2007-2008 Basho Technologies
3 %%
4 %% Licensed under the Apache License, Version 2.0 (the "License");
5 %% you may not use this file except in compliance with the License.
6 %% You may obtain a copy of the License at
7 %%
8 %% http://www.apache.org/licenses/LICENSE-2.0
9 %%
10 %% Unless required by applicable law or agreed to in writing, software
11 %% distributed under the License is distributed on an "AS IS" BASIS,
12 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 %% See the License for the specific language governing permissions and
14 %% limitations under the License.
15
16 %% @doc Supervisor for the webmachine application.
17
18 -module(webmachine_sup).
19
20 -behaviour(supervisor).
21
22 %% External exports
23 -export([start_link/0, upgrade/0]).
24
25 %% supervisor callbacks
26 -export([init/1]).
27
28 -include("webmachine_logger.hrl").
29
30 %% @spec start_link() -> ServerRet
31 %% @doc API for starting the supervisor.
32 start_link() ->
33 supervisor:start_link({local, ?MODULE}, ?MODULE, []).
34
35 %% @spec upgrade() -> ok
36 %% @doc Add processes if necessary.
37 upgrade() ->
38 {ok, {_, Specs}} = init([]),
39
40 Old = sets:from_list(
41 [Name || {Name, _, _, _} <- supervisor:which_children(?MODULE)]),
42 New = sets:from_list([Name || {Name, _, _, _, _, _} <- Specs]),
43 Kill = sets:subtract(Old, New),
44
45 sets:fold(fun (Id, ok) ->
46 supervisor:terminate_child(?MODULE, Id),
47 supervisor:delete_child(?MODULE, Id),
48 ok
49 end, ok, Kill),
50
51 [supervisor:start_child(?MODULE, Spec) || Spec <- Specs],
52 ok.
53
54 %% @spec init([]) -> SupervisorTree
55 %% @doc supervisor callback.
56 init([]) ->
57 Router = {webmachine_router,
58 {webmachine_router, start_link, []},
59 permanent, 5000, worker, [webmachine_router]},
60 LogHandler = [{webmachine_logger, {gen_event, start_link, [{local, ?EVENT_LOGGER}]},
61 permanent, 5000, worker, [dynamic]},
62 {webmachine_logger_watcher_sup, {webmachine_logger_watcher_sup, start_link, []},
63 permanent, 5000, supervisor, [webmachine_logger_watcher_sup]}],
64 {ok, {{one_for_one, 9, 10}, LogHandler ++ [Router]}}.
+0
-549
deps/webmachine/src/webmachine_util.erl less more
0 %% @author Justin Sheehy <justin@basho.com>
1 %% @author Andy Gross <andy@basho.com>
2 %% @copyright 2007-2008 Basho Technologies
3 %% (guess_mime/1 derived from code copyright 2007 Mochi Media, Inc.)
4 %%
5 %% Licensed under the Apache License, Version 2.0 (the "License");
6 %% you may not use this file except in compliance with the License.
7 %% You may obtain a copy of the License at
8 %%
9 %% http://www.apache.org/licenses/LICENSE-2.0
10 %%
11 %% Unless required by applicable law or agreed to in writing, software
12 %% distributed under the License is distributed on an "AS IS" BASIS,
13 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 %% See the License for the specific language governing permissions and
15 %% limitations under the License.
16
17 %% @doc Utilities for parsing, quoting, and negotiation.
18
19 -module(webmachine_util).
20 -export([guess_mime/1]).
21 -export([convert_request_date/1, compare_ims_dates/2]).
22 -export([rfc1123_date/1]).
23 -export([choose_media_type/2, format_content_type/1]).
24 -export([choose_charset/2]).
25 -export([choose_encoding/2]).
26 -export([now_diff_milliseconds/2]).
27 -export([media_type_to_detail/1,
28 quoted_string/1,
29 split_quoted_strings/1]).
30 -export([parse_range/2]).
31
32 -ifdef(TEST).
33 -ifdef(EQC).
34 -include_lib("eqc/include/eqc.hrl").
35 -endif.
36 -include_lib("eunit/include/eunit.hrl").
37 -export([accept_header_to_media_types/1]).
38 -endif.
39
40 convert_request_date(Date) ->
41 try
42 case httpd_util:convert_request_date(Date) of
43 ReqDate -> ReqDate
44 end
45 catch
46 error:_ -> bad_date
47 end.
48
49 %% returns true if D1 > D2
50 compare_ims_dates(D1, D2) ->
51 GD1 = calendar:datetime_to_gregorian_seconds(D1),
52 GD2 = calendar:datetime_to_gregorian_seconds(D2),
53 GD1 > GD2.
54
55 %% @doc Convert tuple style GMT datetime to RFC1123 style one
56 rfc1123_date({{YYYY, MM, DD}, {Hour, Min, Sec}}) ->
57 DayNumber = calendar:day_of_the_week({YYYY, MM, DD}),
58 lists:flatten(io_lib:format("~s, ~2.2.0w ~3.s ~4.4.0w ~2.2.0w:~2.2.0w:~2.2.0w GMT",
59 [httpd_util:day(DayNumber), DD, httpd_util:month(MM),
60 YYYY, Hour, Min, Sec])).
61
62 %% @spec guess_mime(string()) -> string()
63 %% @doc Guess the mime type of a file by the extension of its filename.
64 guess_mime(File) ->
65 case filename:extension(File) of
66 ".bz2" ->
67 "application/x-bzip2";
68 ".css" ->
69 "text/css";
70 ".eot" ->
71 "application/vnd.ms-fontobject";
72 ".gif" ->
73 "image/gif";
74 ".gz" ->
75 "application/x-gzip";
76 ".htc" ->
77 "text/x-component";
78 ".html" ->
79 "text/html";
80 ".ico" ->
81 "image/x-icon";
82 ".jpeg" ->
83 "image/jpeg";
84 ".jpg" ->
85 "image/jpeg";
86 ".js" ->
87 "application/x-javascript";
88 ".less" ->
89 "text/css";
90 ".m4v" ->
91 "video/mp4";
92 ".manifest" ->
93 "text/cache-manifest";
94 ".mp4" ->
95 "video/mp4";
96 ".oga" ->
97 "audio/ogg";
98 ".ogg" ->
99 "audio/ogg";
100 ".ogv" ->
101 "video/ogg";
102 ".otf" ->
103 "font/opentyp";
104 ".png" ->
105 "image/png";
106 ".svg" ->
107 "image/svg+xml";
108 ".svgz" ->
109 "image/svg+xml";
110 ".swf" ->
111 "application/x-shockwave-flash";
112 ".tar" ->
113 "application/x-tar";
114 ".tgz" ->
115 "application/x-gzip";
116 ".ttc" ->
117 "application/x-font-ttf";
118 ".ttf" ->
119 "application/x-font-ttf";
120 ".vcf" ->
121 "text/x-vcard";
122 ".webm" ->
123 "video/web";
124 ".webp" ->
125 "image/web";
126 ".woff" ->
127 "application/x-font-woff";
128 ".xhtml" ->
129 "application/xhtml+xml";
130 ".xml" ->
131 "application/xml";
132 ".zip" ->
133 "application/zip";
134 _ ->
135 "text/plain"
136 end.
137
138 choose_media_type(Provided,AcceptHead) ->
139 % Return the Content-Type we will serve for a request.
140 % If there is no acceptable/available match, return the atom "none".
141 % AcceptHead is the value of the request's Accept header
142 % Provided is a list of media types the resource can provide.
143 % each is either a string e.g. -- "text/html"
144 % or a string and parameters e.g. -- {"text/html",[{level,1}]}
145 % (the plain string case with no parameters is much more common)
146 Requested = accept_header_to_media_types(AcceptHead),
147 Prov1 = normalize_provided(Provided),
148 choose_media_type1(Prov1,Requested).
149 choose_media_type1(_Provided,[]) ->
150 none;
151 choose_media_type1(Provided,[H|T]) ->
152 {_Pri,Type,Params} = H,
153 case media_match({Type,Params}, Provided) of
154 [] -> choose_media_type1(Provided,T);
155 [{CT_T,CT_P}|_] -> format_content_type(CT_T,CT_P)
156 end.
157
158 media_match(_,[]) -> [];
159 media_match({"*/*",[]},[H|_]) -> [H];
160 media_match({Type,Params},Provided) ->
161 [{T1,P1} || {T1,P1} <- Provided,
162 media_type_match(Type,T1), media_params_match(Params,P1)].
163 media_type_match(Req,Prov) ->
164 case Req of
165 "*" -> % might as well not break for lame (Gomez) clients
166 true;
167 "*/*" ->
168 true;
169 Prov ->
170 true;
171 _ ->
172 [R1|R2] = string:tokens(Req,"/"),
173 [P1,_P2] = string:tokens(Prov,"/"),
174 case R2 of
175 ["*"] ->
176 case R1 of
177 P1 -> true;
178 _ -> false
179 end;
180 _ -> false
181 end
182 end.
183 media_params_match(Req,Prov) ->
184 lists:sort(Req) =:= lists:sort(Prov).
185
186 prioritize_media(TyParam) ->
187 {Type, Params} = TyParam,
188 prioritize_media(Type,Params,[]).
189 prioritize_media(Type,Params,Acc) ->
190 case Params of
191 [] ->
192 {1, Type, Acc};
193 _ ->
194 [{Tok,Val}|Rest] = Params,
195 case Tok of
196 "q" ->
197 QVal = case Val of
198 "1" ->
199 1;
200 "0" ->
201 0;
202 [$.|_] ->
203 %% handle strange FeedBurner Accept
204 list_to_float([$0|Val]);
205 _ -> list_to_float(Val)
206 end,
207 {QVal, Type, Rest ++ Acc};
208 _ ->
209 prioritize_media(Type,Rest,[{Tok,Val}|Acc])
210 end
211 end.
212
213 media_type_to_detail(MType) ->
214 mochiweb_util:parse_header(MType).
215
216 accept_header_to_media_types(HeadVal) ->
217 % given the value of an accept header, produce an ordered list
218 % based on the q-values. Results are [{Type,Params}] with the
219 % head of the list being the highest-priority requested type.
220 try
221 lists:reverse(lists:keysort(1,
222 [prioritize_media(media_type_to_detail(MType)) ||
223 MType <- [string:strip(X) || X <- string:tokens(HeadVal, ",")]]))
224 catch _:_ -> []
225 end.
226
227 normalize_provided(Provided) ->
228 [normalize_provided1(X) || X <- Provided].
229 normalize_provided1(Type) when is_list(Type) -> {Type, []};
230 normalize_provided1({Type,Params}) -> {Type, normalize_media_params(Params)}.
231
232 normalize_media_params(Params) ->
233 normalize_media_params(Params,[]).
234
235 normalize_media_params([],Acc) ->
236 Acc;
237 normalize_media_params([{K,V}|T], Acc) when is_atom(K) ->
238 normalize_media_params(T,[{atom_to_list(K),V}|Acc]);
239 normalize_media_params([H|T], Acc) ->
240 normalize_media_params(T, [H|Acc]).
241
242
243 format_content_type(Type) when is_list(Type) ->
244 Type;
245 format_content_type({Type,Params}) ->
246 format_content_type(Type,Params).
247
248 format_content_type(Type,[]) -> Type;
249 format_content_type(Type,[{K,V}|T]) when is_atom(K) ->
250 format_content_type(Type, [{atom_to_list(K),V}|T]);
251 format_content_type(Type,[{K,V}|T]) ->
252 format_content_type(Type ++ "; " ++ K ++ "=" ++ V, T).
253
254 choose_charset(CSets, AccCharHdr) -> do_choose(CSets, AccCharHdr, "ISO-8859-1").
255
256 choose_encoding(Encs, AccEncHdr) -> do_choose(Encs, AccEncHdr, "identity").
257
258 do_choose(Choices, Header, Default) ->
259 Accepted = build_conneg_list(string:tokens(Header, ",")),
260 DefaultPrio = [P || {P,C} <- Accepted, C =:= Default],
261 StarPrio = [P || {P,C} <- Accepted, C =:= "*"],
262 DefaultOkay = case DefaultPrio of
263 [] ->
264 case StarPrio of
265 [0.0] -> no;
266 _ -> yes
267 end;
268 [0.0] -> no;
269 _ -> yes
270 end,
271 AnyOkay = case StarPrio of
272 [] -> no;
273 [0.0] -> no;
274 _ -> yes
275 end,
276 do_choose(Default, DefaultOkay, AnyOkay, Choices, Accepted).
277 do_choose(_Default, _DefaultOkay, _AnyOkay, [], []) ->
278 none;
279 do_choose(_Default, _DefaultOkay, _AnyOkay, [], _Accepted) ->
280 none;
281 do_choose(Default, DefaultOkay, AnyOkay, Choices, []) ->
282 case AnyOkay of
283 yes -> hd(Choices);
284 no ->
285 case DefaultOkay of
286 yes ->
287 case lists:member(Default, Choices) of
288 true -> Default;
289 _ -> none
290 end;
291 no -> none
292 end
293 end;
294 do_choose(Default, DefaultOkay, AnyOkay, Choices, [AccPair|AccRest]) ->
295 {Prio, Acc} = AccPair,
296 case Prio of
297 0.0 ->
298 do_choose(Default, DefaultOkay, AnyOkay,
299 lists:delete(Acc, Choices), AccRest);
300 _ ->
301 LAcc = string:to_lower(Acc),
302 LChoices = [string:to_lower(X) || X <- Choices],
303 % doing this a little more work than needed in
304 % order to be easily insensitive but preserving
305 case lists:member(LAcc, LChoices) of
306 true ->
307 hd([X || X <- Choices,
308 string:to_lower(X) =:= LAcc]);
309 false -> do_choose(Default, DefaultOkay, AnyOkay,
310 Choices, AccRest)
311 end
312 end.
313
314 build_conneg_list(AccList) ->
315 build_conneg_list(AccList, []).
316 build_conneg_list([], Result) -> lists:reverse(lists:sort(Result));
317 build_conneg_list([Acc|AccRest], Result) ->
318 XPair = list_to_tuple([string:strip(X) || X <- string:tokens(Acc, ";")]),
319 Pair = case XPair of
320 {Choice, "q=" ++ PrioStr} ->
321 case PrioStr of
322 "0" -> {0.0, Choice};
323 "1" -> {1.0, Choice};
324 [$.|_] ->
325 %% handle strange FeedBurner Accept
326 {list_to_float([$0|PrioStr]), Choice};
327 _ -> {list_to_float(PrioStr), Choice}
328 end;
329 {Choice} ->
330 {1.0, Choice}
331 end,
332 build_conneg_list(AccRest,[Pair|Result]).
333
334
335 quoted_string([$" | _Rest] = Str) ->
336 Str;
337 quoted_string(Str) ->
338 escape_quotes(Str, [$"]). % Initialize Acc with opening quote
339
340 escape_quotes([], Acc) ->
341 lists:reverse([$" | Acc]); % Append final quote
342 escape_quotes([$\\, Char | Rest], Acc) ->
343 escape_quotes(Rest, [Char, $\\ | Acc]); % Any quoted char should be skipped
344 escape_quotes([$" | Rest], Acc) ->
345 escape_quotes(Rest, [$", $\\ | Acc]); % Unquoted quotes should be escaped
346 escape_quotes([Char | Rest], Acc) ->
347 escape_quotes(Rest, [Char | Acc]).
348
349 split_quoted_strings(Str) ->
350 split_quoted_strings(Str, []).
351
352 split_quoted_strings([], Acc) ->
353 lists:reverse(Acc);
354 split_quoted_strings([$" | Rest], Acc) ->
355 {Str, Cont} = unescape_quoted_string(Rest, []),
356 split_quoted_strings(Cont, [Str | Acc]);
357 split_quoted_strings([_Skip | Rest], Acc) ->
358 split_quoted_strings(Rest, Acc).
359
360 unescape_quoted_string([], Acc) ->
361 {lists:reverse(Acc), []};
362 unescape_quoted_string([$\\, Char | Rest], Acc) -> % Any quoted char should be unquoted
363 unescape_quoted_string(Rest, [Char | Acc]);
364 unescape_quoted_string([$" | Rest], Acc) -> % Quote indicates end of this string
365 {lists:reverse(Acc), Rest};
366 unescape_quoted_string([Char | Rest], Acc) ->
367 unescape_quoted_string(Rest, [Char | Acc]).
368
369
370 %% @type now() = {MegaSecs, Secs, MicroSecs}
371
372 %% This is faster than timer:now_diff() because it does not use bignums.
373 %% But it returns *milliseconds* (timer:now_diff returns microseconds.)
374 %% From http://www.erlang.org/ml-archive/erlang-questions/200205/msg00027.html
375
376 %% @doc Compute the difference between two now() tuples, in milliseconds.
377 %% @spec now_diff_milliseconds(now(), now()) -> integer()
378 now_diff_milliseconds(undefined, undefined) ->
379 0;
380 now_diff_milliseconds(undefined, T2) ->
381 now_diff_milliseconds(os:timestamp(), T2);
382 now_diff_milliseconds({M,S,U}, {M,S1,U1}) ->
383 ((S-S1) * 1000) + ((U-U1) div 1000);
384 now_diff_milliseconds({M,S,U}, {M1,S1,U1}) ->
385 ((M-M1)*1000000+(S-S1))*1000 + ((U-U1) div 1000).
386
387 -spec parse_range(RawRange::string(), ResourceLength::non_neg_integer()) ->
388 [{Start::non_neg_integer(), End::non_neg_integer()}].
389 parse_range(RawRange, ResourceLength) when is_list(RawRange) ->
390 parse_range(mochiweb_http:parse_range_request(RawRange), ResourceLength, []).
391
392 parse_range([], _ResourceLength, Acc) ->
393 lists:reverse(Acc);
394 parse_range([Spec | Rest], ResourceLength, Acc) ->
395 case mochiweb_http:range_skip_length(Spec, ResourceLength) of
396 invalid_range ->
397 parse_range(Rest, ResourceLength, Acc);
398 {Skip, Length} ->
399 parse_range(Rest, ResourceLength, [{Skip, Skip + Length - 1} | Acc])
400 end.
401
402 %%
403 %% TEST
404 %%
405 -ifdef(TEST).
406
407 choose_media_type_test() ->
408 Provided = "text/html",
409 ShouldMatch = ["*", "*/*", "text/*", "text/html"],
410 WantNone = ["foo", "text/xml", "application/*", "foo/bar/baz"],
411 [ ?assertEqual(Provided, choose_media_type([Provided], I))
412 || I <- ShouldMatch ],
413 [ ?assertEqual(none, choose_media_type([Provided], I))
414 || I <- WantNone ].
415
416 choose_media_type_qval_test() ->
417 Provided = ["text/html", "image/jpeg"],
418 HtmlMatch = ["image/jpeg;q=0.5, text/html",
419 "text/html, image/jpeg; q=0.5",
420 "text/*; q=0.8, image/*;q=0.7",
421 "text/*;q=.8, image/*;q=.7"], %% strange FeedBurner format
422 JpgMatch = ["image/*;q=1, text/html;q=0.9",
423 "image/png, image/*;q=0.3"],
424 [ ?assertEqual("text/html", choose_media_type(Provided, I))
425 || I <- HtmlMatch ],
426 [ ?assertEqual("image/jpeg", choose_media_type(Provided, I))
427 || I <- JpgMatch ].
428
429 accept_header_to_media_types_test() ->
430 Header1 = "text/html,application/xhtml+xml,application/xml,application/x-javascript,*/*;q=0.5",
431 Header2 = "audio/*; q=0, audio/basic",
432 OddHeader = "text/html,application/xhtml+xml,application/xml,application/x-javascript,*/*;q=0,5",
433 Result1 = accept_header_to_media_types(Header1),
434 Result2 = accept_header_to_media_types(Header2),
435 Result3 = accept_header_to_media_types(OddHeader),
436 ExpResult1 = [{1,"application/x-javascript", []},
437 {1,"application/xml",[]},
438 {1,"application/xhtml+xml",[]},
439 {1,"text/html",[]},
440 {0.5,"*/*",[]}],
441 ExpResult2 = [{1,"audio/basic",[]},{0,"audio/*",[]}],
442 ExpResult3 = [{1, "5", []},
443 {1,"application/x-javascript", []},
444 {1,"application/xml",[]},
445 {1,"application/xhtml+xml",[]},
446 {1,"text/html",[]},
447 {0,"*/*",[]}],
448 ?assertEqual(ExpResult1, Result1),
449 ?assertEqual(ExpResult2, Result2),
450 ?assertEqual(ExpResult3, Result3).
451
452 media_type_extra_whitespace_test() ->
453 MType = "application/x-www-form-urlencoded ; charset = utf8",
454 ?assertEqual({"application/x-www-form-urlencoded",[{"charset","utf8"}]},
455 webmachine_util:media_type_to_detail(MType)).
456
457 format_content_type_test() ->
458 Types = ["audio/vnd.wave; codec=31",
459 "text/x-okie; charset=iso-8859-1; declaration=<f950118.AEB0@XIson.com>"],
460 [?assertEqual(Type, format_content_type(
461 webmachine_util:media_type_to_detail(Type)))
462 || Type <- Types],
463 ?assertEqual(hd(Types), format_content_type("audio/vnd.wave", [{codec, "31"}])).
464
465 convert_request_date_test() ->
466 ?assertMatch({{_,_,_},{_,_,_}},
467 convert_request_date("Wed, 30 Dec 2009 14:39:02 GMT")),
468 ?assertMatch(bad_date,
469 convert_request_date(<<"does not handle binaries">>)).
470
471 compare_ims_dates_test() ->
472 Late = {{2009,12,30},{14,39,02}},
473 Early = {{2009,12,30},{13,39,02}},
474 ?assertEqual(true, compare_ims_dates(Late, Early)),
475 ?assertEqual(false, compare_ims_dates(Early, Late)).
476
477 rfc1123_date_test() ->
478 ?assertEqual("Thu, 11 Jul 2013 04:33:19 GMT",
479 rfc1123_date({{2013, 7, 11}, {4, 33, 19}})).
480
481 guess_mime_test() ->
482 TextTypes = [".html",".css",".htc",".manifest",".txt"],
483 AppTypes = [".xhtml",".xml",".js",".swf",".zip",".bz2",
484 ".gz",".tar",".tgz"],
485 ImgTypes = [".jpg",".jpeg",".gif",".png",".ico",".svg"],
486 ?assertEqual([], [ T || T <- TextTypes,
487 1 /= string:str(guess_mime(T),"text/") ]),
488 ?assertEqual([], [ T || T <- AppTypes,
489 1 /= string:str(guess_mime(T),"application/") ]),
490 ?assertEqual([], [ T || T <- ImgTypes,
491 1 /= string:str(guess_mime(T),"image/") ]).
492
493
494 now_diff_milliseconds_test() ->
495 Late = {10, 10, 10},
496 Early1 = {10, 9, 9},
497 Early2 = {9, 9, 9},
498 ?assertEqual(1000, now_diff_milliseconds(Late, Early1)),
499 ?assertEqual(1000001000, now_diff_milliseconds(Late, Early2)).
500
501 -ifdef(EQC).
502
503 -define(QC_OUT(P),
504 eqc:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)).
505
506 prop_quoted_string() ->
507 ?FORALL(String0, non_empty(list(oneof([char(), $", [$\\, char()]]))),
508 begin
509 String = lists:flatten(String0),
510
511 Quoted = quoted_string(String),
512 case String of
513 [$" | _] ->
514 ?assertEqual(String, Quoted),
515 true;
516 _ ->
517 %% Properties:
518 %% * strings must begin/end with quote
519 %% * All other quotes should be escaped
520 ?assertEqual($", hd(Quoted)),
521 ?assertEqual($", lists:last(Quoted)),
522 Partial = lists:reverse(tl(lists:reverse(tl(Quoted)))),
523 case check_quote(Partial) of
524 true ->
525 true;
526 false ->
527 io:format(user, "----\n", []),
528 io:format(user, "In: ~p\n", [[integer_to_list(C) || C <- String]]),
529 io:format(user, "Out: ~p\n", [[integer_to_list(C) || C <- Quoted]]),
530 false
531 end
532 end
533 end).
534
535 check_quote([]) ->
536 true;
537 check_quote([$\\, _Any | Rest]) ->
538 check_quote(Rest);
539 check_quote([$" | _Rest]) ->
540 false;
541 check_quote([_Any | Rest]) ->
542 check_quote(Rest).
543
544 prop_quoted_string_test() ->
545 ?assert(eqc:quickcheck(?QC_OUT(prop_quoted_string()))).
546
547 -endif. % EQC
548 -endif. % TEST
+0
-351
deps/webmachine/src/wmtrace_resource.erl less more
0 %% @author Bryan Fink <bryan@basho.com>
1 %% @doc Webmachine trace file interpretter.
2 -module(wmtrace_resource).
3
4 -export([add_dispatch_rule/2,
5 remove_dispatch_rules/0]).
6
7 -export([ping/2,
8 init/1,
9 resource_exists/2,
10 content_types_provided/2,
11 produce_html/2,
12 produce_javascript/2,
13 produce_map/2,
14 produce_css/2]).
15
16 -include("wm_reqdata.hrl").
17
18 -record(ctx, {trace_dir, trace}).
19
20 -define(MAP_EXTERNAL, "static/map.png").
21 -define(MAP_INTERNAL, "http-headers-status-v3.png").
22 -define(SCRIPT_EXTERNAL, "static/wmtrace.js").
23 -define(SCRIPT_INTERNAL, "wmtrace.js").
24 -define(STYLE_EXTERNAL, "static/wmtrace.css").
25 -define(STYLE_INTERNAL, "wmtrace.css").
26
27 %%
28 %% Dispatch Modifiers
29 %%
30
31 %% @spec add_dispatch_rule(string(), string()) -> ok
32 %% @doc Add a dispatch rule to point at wmtrace_resource.
33 %% Example: to serve wmtrace_resource from
34 %% http://yourhost/dev/wmtrace/
35 %% with trace files on disk at
36 %% priv/traces
37 %% call:
38 %% add_dispatch_rule("dev/wmtrace", "priv/traces")
39 add_dispatch_rule(BasePath, TracePath) when is_list(BasePath),
40 is_list(TracePath) ->
41 Parts = string:tokens(BasePath, "/"),
42 webmachine_router:add_route({Parts ++ ['*'], ?MODULE, [{trace_dir, TracePath}]}).
43
44 %% @spec remove_dispatch_rules() -> ok
45 %% @doc Remove all dispatch rules pointing to wmtrace_resource.
46 remove_dispatch_rules() ->
47 webmachine_router:remove_resource(?MODULE).
48
49 %%
50 %% Resource
51 %%
52
53 ping(ReqData, State) ->
54 {pong, ReqData, State}.
55
56 init(Config) ->
57 {trace_dir, TraceDir} = proplists:lookup(trace_dir, Config),
58 {trace_dir_exists, true} = {trace_dir_exists, filelib:is_dir(TraceDir)},
59 {ok, #ctx{trace_dir=TraceDir}}.
60
61 resource_exists(RD, Ctx) ->
62 case wrq:disp_path(RD) of
63 [] ->
64 case lists:reverse(wrq:raw_path(RD)) of
65 [$/|_] ->
66 {true, RD, Ctx};
67 _ ->
68 {{halt, 303},
69 wrq:set_resp_header("Location",
70 wrq:raw_path(RD)++"/",
71 RD),
72 Ctx}
73 end;
74 ?MAP_EXTERNAL ->
75 {filelib:is_file(wm_path(?MAP_INTERNAL)), RD, Ctx};
76 ?SCRIPT_EXTERNAL ->
77 {filelib:is_file(wm_path(?SCRIPT_INTERNAL)), RD, Ctx};
78 ?STYLE_EXTERNAL ->
79 {filelib:is_file(wm_path(?STYLE_INTERNAL)), RD, Ctx};
80 TraceName ->
81 TracePath = filename:join([Ctx#ctx.trace_dir, TraceName]),
82 {filelib:is_file(TracePath), RD, Ctx#ctx{trace=TracePath}}
83 end.
84
85 wm_path(File) ->
86 filename:join([code:priv_dir(webmachine), "trace", File]).
87
88 content_types_provided(RD, Ctx) ->
89 case wrq:disp_path(RD) of
90 ?MAP_EXTERNAL ->
91 {[{"image/png", produce_map}], RD, Ctx};
92 ?SCRIPT_EXTERNAL ->
93 {[{"text/javascript", produce_javascript}], RD, Ctx};
94 ?STYLE_EXTERNAL ->
95 {[{"text/css", produce_css}], RD, Ctx};
96 _ ->
97 {[{"text/html", produce_html}], RD, Ctx}
98 end.
99
100 produce_html(RD, Ctx=#ctx{trace=undefined}) ->
101 Dir = filename:absname(Ctx#ctx.trace_dir),
102 Files = lists:reverse(
103 lists:sort(
104 filelib:fold_files(Dir,
105 ".*\.wmtrace",
106 false,
107 fun(F, Acc) ->
108 [filename:basename(F)|Acc]
109 end,
110 []))),
111 {trace_list_html(Dir, Files), RD, Ctx};
112 produce_html(RD, Ctx) ->
113 Filename = filename:absname(Ctx#ctx.trace),
114 {ok, Data} = file:consult(Filename),
115 {trace_html(Filename, Data), RD, Ctx}.
116
117 trace_list_html(Dir, Files) ->
118 html([],
119 [head([],
120 title([], ["Webmachine Trace List for ",Dir])),
121 body([],
122 [h1([], ["Traces in ",Dir]),
123 ul([],
124 [ li([], a([{"href", F}], F)) || F <- Files ])
125 ])
126 ]).
127
128 trace_html(Filename, Data) ->
129 {Request, Response, Trace} = encode_trace(Data),
130 html([],
131 [head([],
132 [title([],["Webmachine Trace ",Filename]),
133 linkblock([{"rel", "stylesheet"},
134 {"type", "text/css"},
135 {"href", "static/wmtrace.css"}],
136 []),
137 script([{"type", "text/javascript"},
138 {"src", "static/wmtrace.js"}],
139 []),
140 script([{"type", "text/javascript"}],
141 mochiweb_html:escape(
142 lists:flatten(
143 ["var request=",Request,";\n"
144 "var response=",Response,";\n"
145 "var trace=",Trace,";"])))
146 ]),
147 body([],
148 [divblock([{"id", "zoompanel"}],
149 [button([{"id", "zoomout"}], ["zoom out"]),
150 button([{"id", "zoomin"}], ["zoom in"])
151 ]),
152 canvas([{"id", "v3map"},
153 {"width", "3138"},
154 {"height", "2184"}],
155 []),
156 divblock([{"id", "sizetest"}], []),
157 divblock([{"id", "preview"}],
158 [divblock([{"id", "previewid"}],[]),
159 ul([{"id", "previewcalls"}], [])
160 ]),
161 divblock([{"id", "infopanel"}],
162 [divblock([{"id", "infocontrols"}],
163 [divblock([{"id", "requesttab"},
164 {"class", "selectedtab"}],"Q"),
165 divblock([{"id", "responsetab"}], "R"),
166 divblock([{"id", "decisiontab"}], "D")
167 ]),
168 divblock([{"id", "requestdetail"}],
169 [divblock([],
170 [span([{"id", "requestmethod"}], []),
171 " ",
172 span([{"id", "requestpath"}], [])]),
173 ul([{"id", "requestheaders"}], []),
174 divblock([{"id", "requestbody"}],
175 [])
176 ]),
177 divblock([{"id", "responsedetail"}],
178 [divblock([{"id", "responsecode"}], []),
179 ul([{"id", "responseheaders"}], []),
180 divblock([{"id", "responsebody"}], [])
181 ]),
182 divblock([{"id", "decisiondetail"}],
183 [divblock([],
184 ["Decision: ",
185 select([{"id", "decisionid"}], [])
186 ]),
187 divblock([],
188 ["Calls:",
189 select([{"id", "decisioncalls"}], [])
190 ]),
191 divblock([], "Input:"),
192 pre([{"id", "callinput"}], []),
193 divblock([], "Output:"),
194 pre([{"id", "calloutput"}], [])
195 ])
196 ])
197 ])
198 ]).
199
200 produce_javascript(RD, Ctx) ->
201 {ok, Script} = file:read_file(wm_path(?SCRIPT_INTERNAL)),
202 {Script, RD, Ctx}.
203
204 produce_map(RD, Ctx) ->
205 {ok, Map} = file:read_file(wm_path(?MAP_INTERNAL)),
206 {Map, RD, Ctx}.
207
208 produce_css(RD, Ctx) ->
209 {ok, Script} = file:read_file(wm_path(?STYLE_INTERNAL)),
210 {Script, RD, Ctx}.
211
212 %%
213 %% Trace Encoding
214 %%
215
216 encode_trace(Data) ->
217 {Request, Response, Trace} = aggregate_trace(Data),
218 {mochijson:encode(encode_request(Request)),
219 mochijson:encode(encode_response(Response)),
220 mochijson:encode({array, [ encode_trace_part(P) || P <- Trace ]})}.
221
222 aggregate_trace(RawTrace) ->
223 {Request, Response, Trace} = lists:foldl(fun aggregate_trace_part/2,
224 {undefined, 500, []},
225 RawTrace),
226 {Request, Response, lists:reverse(Trace)}.
227
228 aggregate_trace_part({decision, Decision}, {Q, R, Acc}) ->
229 BDN = base_decision_name(Decision),
230 case Acc of
231 [{BDN,_}|_] -> {Q, R, Acc}; %% subdecision (ex. v3b13b)
232 _ ->
233 {Q, R, [{base_decision_name(Decision), []}|Acc]}
234 end;
235 aggregate_trace_part({attempt, Module, Function, Args},
236 {Q, R, [{Decision,Calls}|Acc]}) ->
237 {maybe_extract_request(Function, Args, Q),
238 R, [{Decision,[{Module, Function, Args, wmtrace_null}|Calls]}|Acc]};
239 aggregate_trace_part({result, Module, Function, Result},
240 {Q, R, [{Decision,[{Module,Function,Args,_}|Calls]}|Acc]}) ->
241 {Q, maybe_extract_response(Function, Result, R),
242 [{Decision,[{Module, Function, Args, Result}|Calls]}|Acc]};
243 aggregate_trace_part({not_exported, Module, Function, Args},
244 {Q, R, [{Decision,Calls}|Acc]}) ->
245 {Q, maybe_extract_response(Function, Args, R),
246 [{Decision,[{Module, Function, Args, wmtrace_not_exported}|Calls]}
247 |Acc]}.
248
249 maybe_extract_request(ping, [ReqData,_], _) ->
250 ReqData;
251 maybe_extract_request(_, _, R) ->
252 R.
253
254 maybe_extract_response(finish_request, [ReqData,_], _) ->
255 ReqData;
256 maybe_extract_response(finish_request, {_, ReqData, _}, _) ->
257 ReqData;
258 maybe_extract_response(_, _, R) ->
259 R.
260
261 base_decision_name(Decision) ->
262 [$v,$3|D] = atom_to_list(Decision), %% strip 'v3'
263 case lists:reverse(D) of
264 [A|RD] when A >= $a, A =< $z ->
265 lists:reverse(RD); %% strip 'b' off end of some
266 _ ->
267 D
268 end.
269
270 encode_request(ReqData) when is_record(ReqData, wm_reqdata) ->
271 {struct, [{"method", atom_to_list(
272 wrq:method(ReqData))},
273 {"path", wrq:raw_path(ReqData)},
274 {"headers", encode_headers(wrq:req_headers(ReqData))},
275 {"body", case ReqData#wm_reqdata.req_body of
276 undefined -> [];
277 Body when is_atom(Body) ->
278 atom_to_list(Body);
279 Body -> lists:flatten(io_lib:format("~s", [Body]))
280 end}]}.
281
282 encode_response(ReqData) ->
283 {struct, [{"code", integer_to_list(
284 wrq:response_code(ReqData))},
285 {"headers", encode_headers(wrq:resp_headers(ReqData))},
286 {"body", lists:flatten(io_lib:format("~s", [wrq:resp_body(ReqData)]))}]}.
287
288 encode_headers(Headers) when is_list(Headers) ->
289 {struct, [ {N, V} || {N, V} <- Headers ]};
290 encode_headers(Headers) ->
291 encode_headers(mochiweb_headers:to_list(Headers)).
292
293 encode_trace_part({Decision, Calls}) ->
294 {struct, [{"d", Decision},
295 {"calls",
296 {array, [ {struct,
297 [{"module", Module},
298 {"function", Function},
299 {"input", encode_trace_io(Input)},
300 {"output", encode_trace_io(Output)}]}
301 || {Module, Function, Input, Output}
302 <- lists:reverse(Calls) ]}}]}.
303
304 encode_trace_io(wmtrace_null) -> null;
305 encode_trace_io(wmtrace_not_exported) -> "wmtrace_not_exported";
306 encode_trace_io(Data) ->
307 lists:flatten(io_lib:format("~p", [Data])).
308
309 %%
310 %% HTML Building
311 %%
312
313 -define(TAG(T), T(Attrs, Content) ->
314 tag(??T, Attrs, Content)).
315
316 ?TAG(head).
317 ?TAG(script).
318 ?TAG(title).
319 ?TAG(body).
320 ?TAG(h1).
321 ?TAG(ul).
322 ?TAG(li).
323 ?TAG(a).
324 ?TAG(canvas).
325 ?TAG(select).
326 ?TAG(pre).
327 ?TAG(span).
328 ?TAG(button).
329
330 html(_Attrs, Content) ->
331 [<<"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">">>,
332 <<"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">">>,
333 Content,
334 <<"</html>">>].
335
336 divblock(Attrs, Content) ->
337 tag("div", Attrs, Content). %% div is a reserved word
338
339 linkblock(Attrs, Content) ->
340 tag("link", Attrs, Content). %% link is a reserved word
341
342 tag(Name, Attrs, Content) ->
343 ["<",Name,
344 [ [" ",K,"=\"",V,"\""] || {K, V} <- Attrs ],
345 if Content == empty -> "/>";
346 true ->
347 [">",
348 Content,
349 "</",Name,">"]
350 end].
+0
-338
deps/webmachine/src/wrq.erl less more
0 %% @author Justin Sheehy <justin@basho.com>
1 %% @copyright 2007-2009 Basho Technologies
2 %%
3 %% Licensed under the Apache License, Version 2.0 (the "License");
4 %% you may not use this file except in compliance with the License.
5 %% You may obtain a copy of the License at
6 %%
7 %% http://www.apache.org/licenses/LICENSE-2.0
8 %%
9 %% Unless required by applicable law or agreed to in writing, software
10 %% distributed under the License is distributed on an "AS IS" BASIS,
11 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 %% See the License for the specific language governing permissions and
13 %% limitations under the License.
14
15 -module(wrq).
16 -author('Justin Sheehy <justin@basho.com>').
17
18 -export([create/4, create/5,load_dispatch_data/7]).
19 -export([method/1,scheme/1,version/1,peer/1,disp_path/1,path/1,raw_path/1,
20 path_info/1,response_code/1,req_cookie/1,req_qs/1,req_headers/1,
21 req_body/1,stream_req_body/2,resp_redirect/1,resp_headers/1,
22 resp_body/1,app_root/1,path_tokens/1, host_tokens/1, port/1,
23 base_uri/1]).
24 -export([path_info/2,get_req_header/2,do_redirect/2,fresh_resp_headers/2,
25 get_resp_header/2,set_resp_header/3,set_resp_headers/2,
26 set_disp_path/2,set_req_body/2,set_resp_body/2,set_response_code/2,
27 merge_resp_headers/2,remove_resp_header/2,
28 append_to_resp_body/2,append_to_response_body/2, set_resp_range/2,
29 max_recv_body/1,set_max_recv_body/2,
30 get_cookie_value/2,get_qs_value/2,get_qs_value/3,set_peer/2,
31 add_note/3, get_notes/1]).
32
33 % @type reqdata(). The opaque data type used for req/resp data structures.
34 -include("wm_reqdata.hrl").
35 -include("wm_reqstate.hrl").
36
37
38 create(Method,Version,RawPath,Headers) ->
39 create(Method,http,Version,RawPath,Headers).
40 create(Method,Scheme,Version,RawPath,Headers) ->
41 create(#wm_reqdata{method=Method,scheme=Scheme,version=Version,
42 raw_path=RawPath,req_headers=Headers,
43 wm_state=defined_on_call,
44 path="defined_in_create",
45 req_cookie=defined_in_create,
46 req_qs=defined_in_create,
47 peer="defined_in_wm_req_srv_init",
48 req_body=not_fetched_yet,
49 max_recv_body=(1024*(1024*1024)),
50 % Stolen from R13B03 inet_drv.c's TCP_MAX_PACKET_SIZE definition
51 max_recv_hunk=(64*(1024*1024)),
52 app_root="defined_in_load_dispatch_data",
53 path_info=orddict:new(),
54 path_tokens=defined_in_load_dispatch_data,
55 disp_path=defined_in_load_dispatch_data,
56 resp_redirect=false, resp_headers=mochiweb_headers:empty(),
57 resp_body = <<>>, response_code=500,
58 resp_range = follow_request,
59 notes=[]}).
60 create(RD = #wm_reqdata{raw_path=RawPath}) ->
61 {Path, _, _} = mochiweb_util:urlsplit_path(RawPath),
62 Cookie = case get_req_header("cookie", RD) of
63 undefined -> [];
64 Value -> mochiweb_cookies:parse_cookie(Value)
65 end,
66 {_, QueryString, _} = mochiweb_util:urlsplit_path(RawPath),
67 ReqQS = mochiweb_util:parse_qs(QueryString),
68 RD#wm_reqdata{path=Path,req_cookie=Cookie,req_qs=ReqQS}.
69 load_dispatch_data(PathInfo, HostTokens, Port, PathTokens, AppRoot,
70 DispPath, RD) ->
71 RD#wm_reqdata{path_info=PathInfo,host_tokens=HostTokens,
72 port=Port,path_tokens=PathTokens,
73 app_root=AppRoot,disp_path=DispPath}.
74
75 method(_RD = #wm_reqdata{method=Method}) -> Method.
76
77 scheme(_RD = #wm_reqdata{scheme=Scheme}) -> Scheme.
78
79 version(_RD = #wm_reqdata{version=Version})
80 when is_tuple(Version), size(Version) == 2,
81 is_integer(element(1,Version)), is_integer(element(2,Version)) -> Version.
82
83 peer(_RD = #wm_reqdata{peer=Peer}) when is_list(Peer) -> Peer.
84
85 app_root(_RD = #wm_reqdata{app_root=AR}) when is_list(AR) -> AR.
86
87 % all three paths below are strings
88 disp_path(_RD = #wm_reqdata{disp_path=DP}) when is_list(DP) -> DP.
89
90 path(_RD = #wm_reqdata{path=Path}) when is_list(Path) -> Path.
91
92 raw_path(_RD = #wm_reqdata{raw_path=RawPath}) when is_list(RawPath) -> RawPath.
93
94 path_info(_RD = #wm_reqdata{path_info=PathInfo}) -> PathInfo. % dict
95
96 path_tokens(_RD = #wm_reqdata{path_tokens=PathT}) -> PathT. % list of strings
97
98 host_tokens(_RD = #wm_reqdata{host_tokens=HostT}) -> HostT. % list of strings
99
100 port(_RD = #wm_reqdata{port=Port}) -> Port. % integer
101
102 response_code(_RD = #wm_reqdata{response_code={C,_ReasonPhrase}}) when is_integer(C) -> C;
103 response_code(_RD = #wm_reqdata{response_code=C}) when is_integer(C) -> C.
104
105 req_cookie(_RD = #wm_reqdata{req_cookie=C}) when is_list(C) -> C. % string
106
107 %% @spec req_qs(reqdata()) -> [{Key, Value}]
108 req_qs(_RD = #wm_reqdata{req_qs=QS}) when is_list(QS) -> QS.
109
110 req_headers(_RD = #wm_reqdata{req_headers=ReqH}) -> ReqH. % mochiheaders
111
112 req_body(_RD = #wm_reqdata{wm_state=ReqState0,max_recv_body=MRB}) ->
113 Req = webmachine_request:new(ReqState0),
114 {ReqResp, ReqState} = Req:req_body(MRB),
115 put(tmp_reqstate, ReqState),
116 maybe_conflict_body(ReqResp).
117
118 stream_req_body(_RD = #wm_reqdata{wm_state=ReqState0}, MaxHunk) ->
119 Req = webmachine_request:new(ReqState0),
120 {ReqResp, ReqState} = Req:stream_req_body(MaxHunk),
121 put(tmp_reqstate, ReqState),
122 maybe_conflict_body(ReqResp).
123
124 max_recv_body(_RD = #wm_reqdata{max_recv_body=X}) when is_integer(X) -> X.
125
126 set_max_recv_body(X, RD) when is_integer(X) -> RD#wm_reqdata{max_recv_body=X}.
127
128 maybe_conflict_body(BodyResponse) ->
129 case BodyResponse of
130 stream_conflict ->
131 exit("wrq:req_body and wrq:stream_req_body conflict");
132 {error, req_body_too_large} ->
133 exit("request body too large");
134 _ ->
135 BodyResponse
136 end.
137
138 resp_redirect(_RD = #wm_reqdata{resp_redirect=true}) -> true;
139 resp_redirect(_RD = #wm_reqdata{resp_redirect=false}) -> false.
140
141 resp_headers(_RD = #wm_reqdata{resp_headers=RespH}) -> RespH. % mochiheaders
142
143 resp_body(_RD = #wm_reqdata{resp_body=undefined}) -> undefined;
144 resp_body(_RD = #wm_reqdata{resp_body={stream,X}}) -> {stream,X};
145 resp_body(_RD = #wm_reqdata{resp_body={known_length_stream,X,Y}}) -> {known_length_stream,X,Y};
146 resp_body(_RD = #wm_reqdata{resp_body={stream,X,Y}}) -> {stream,X,Y};
147 resp_body(_RD = #wm_reqdata{resp_body={writer,X}}) -> {writer,X};
148 resp_body(_RD = #wm_reqdata{resp_body=RespB}) when is_binary(RespB) -> RespB;
149 resp_body(_RD = #wm_reqdata{resp_body=RespB}) -> iolist_to_binary(RespB).
150
151 %% --
152
153 path_info(Key, RD) when is_atom(Key) ->
154 case orddict:find(Key, path_info(RD)) of
155 {ok, Value} when is_list(Value); is_integer(Value) ->
156 Value; % string (for host or path match)
157 % or integer (for port match)
158 error -> undefined
159 end.
160
161 get_req_header(HdrName, RD) -> % string->string
162 mochiweb_headers:get_value(HdrName, req_headers(RD)).
163
164 do_redirect(true, RD) -> RD#wm_reqdata{resp_redirect=true};
165 do_redirect(false, RD) -> RD#wm_reqdata{resp_redirect=false}.
166
167 set_peer(P, RD) when is_list(P) -> RD#wm_reqdata{peer=P}. % string
168
169 set_disp_path(P, RD) when is_list(P) -> RD#wm_reqdata{disp_path=P}. % string
170
171 set_req_body(Body, RD) -> RD#wm_reqdata{req_body=Body}.
172
173 set_resp_body(Body, RD) -> RD#wm_reqdata{resp_body=Body}.
174
175 set_response_code({Code, _ReasonPhrase}=CodeAndReason, RD) when is_integer(Code) ->
176 RD#wm_reqdata{response_code=CodeAndReason};
177 set_response_code(Code, RD) when is_integer(Code) ->
178 RD#wm_reqdata{response_code=Code}.
179
180 get_resp_header(HdrName, _RD=#wm_reqdata{resp_headers=RespH}) ->
181 mochiweb_headers:get_value(HdrName, RespH).
182 set_resp_header(K, V, RD=#wm_reqdata{resp_headers=RespH})
183 when is_list(K),is_list(V) ->
184 RD#wm_reqdata{resp_headers=mochiweb_headers:enter(K, V, RespH)}.
185 set_resp_headers(Hdrs, RD=#wm_reqdata{resp_headers=RespH}) ->
186 F = fun({K, V}, Acc) -> mochiweb_headers:enter(K, V, Acc) end,
187 RD#wm_reqdata{resp_headers=lists:foldl(F, RespH, Hdrs)}.
188 fresh_resp_headers(Hdrs, RD) ->
189 F = fun({K, V}, Acc) -> mochiweb_headers:enter(K, V, Acc) end,
190 RD#wm_reqdata{resp_headers=lists:foldl(F, mochiweb_headers:empty(), Hdrs)}.
191 remove_resp_header(K, RD=#wm_reqdata{resp_headers=RespH}) when is_list(K) ->
192 RD#wm_reqdata{resp_headers=mochiweb_headers:from_list(
193 proplists:delete(K,
194 mochiweb_headers:to_list(RespH)))}.
195
196 merge_resp_headers(Hdrs, RD=#wm_reqdata{resp_headers=RespH}) ->
197 F = fun({K, V}, Acc) -> mochiweb_headers:insert(K, V, Acc) end,
198 NewHdrs = lists:foldl(F, RespH, Hdrs),
199 RD#wm_reqdata{resp_headers=NewHdrs}.
200
201 append_to_resp_body(Data, RD) -> append_to_response_body(Data, RD).
202 append_to_response_body(Data, RD=#wm_reqdata{resp_body=RespB}) ->
203 case is_binary(Data) of
204 true ->
205 Data0 = RespB,
206 Data1 = <<Data0/binary,Data/binary>>,
207 RD#wm_reqdata{resp_body=Data1};
208 false -> % MUST BE an iolist! else, fail.
209 append_to_response_body(iolist_to_binary(Data), RD)
210 end.
211
212 -spec set_resp_range(follow_request | ignore_request, #wm_reqdata{}) -> #wm_reqdata{}.
213 %% follow_request : range responce for range request, normal responce for non-range one
214 %% ignore_request : normal resopnse for either range reuqest or non-range one
215 set_resp_range(RespRange, RD)
216 when RespRange =:= follow_request orelse RespRange =:= ignore_request ->
217 RD#wm_reqdata{resp_range = RespRange}.
218
219 get_cookie_value(Key, RD) when is_list(Key) -> % string
220 case lists:keyfind(Key, 1, req_cookie(RD)) of
221 false -> undefined;
222 {Key, Value} -> Value
223 end.
224
225 get_qs_value(Key, RD) when is_list(Key) -> % string
226 case lists:keyfind(Key, 1, req_qs(RD)) of
227 false -> undefined;
228 {Key, Value} -> Value
229 end.
230
231 get_qs_value(Key, Default, RD) when is_list(Key) ->
232 case lists:keyfind(Key, 1, req_qs(RD)) of
233 false -> Default;
234 {Key, Value} -> Value
235 end.
236 add_note(K, V, RD) -> RD#wm_reqdata{notes=[{K, V} | RD#wm_reqdata.notes]}.
237
238 get_notes(RD) -> RD#wm_reqdata.notes.
239
240 base_uri(RD) ->
241 Scheme = erlang:atom_to_list(RD#wm_reqdata.scheme),
242 Host = string:join(RD#wm_reqdata.host_tokens, "."),
243 PortString = port_string(RD#wm_reqdata.scheme, RD#wm_reqdata.port),
244 Scheme ++ "://" ++ Host ++ PortString.
245
246 port_string(Scheme, Port) ->
247 case Scheme of
248 http ->
249 case Port of
250 80 -> "";
251 _ -> ":" ++ erlang:integer_to_list(Port)
252 end;
253 https ->
254 case Port of
255 443 -> "";
256 _ -> ":" ++ erlang:integer_to_list(Port)
257 end;
258 _ -> ":" ++ erlang:integer_to_list(Port)
259 end.
260
261 %%
262 %% Tests
263 %%
264
265 -ifdef(TEST).
266 -include_lib("eunit/include/eunit.hrl").
267
268 make_wrq(Method, RawPath, Headers) ->
269 make_wrq(Method, http, RawPath, Headers).
270
271 make_wrq(Method, Scheme, RawPath, Headers) ->
272 create(Method, Scheme, {1,1}, RawPath, mochiweb_headers:from_list(Headers)).
273
274 accessor_test() ->
275 R0 = make_wrq('GET', "/foo?a=1&b=2", [{"Cookie", "foo=bar"}]),
276 R = set_peer("127.0.0.1", R0),
277 ?assertEqual('GET', method(R)),
278 ?assertEqual({1,1}, version(R)),
279 ?assertEqual("/foo", path(R)),
280 ?assertEqual("/foo?a=1&b=2", raw_path(R)),
281 ?assertEqual([{"a", "1"}, {"b", "2"}], req_qs(R)),
282 ?assertEqual({"1", "2"}, {get_qs_value("a", R), get_qs_value("b", R)}),
283 ?assertEqual("3", get_qs_value("c", "3", R)),
284 ?assertEqual([{"foo", "bar"}], req_cookie(R)),
285 ?assertEqual("bar", get_cookie_value("foo", R)),
286 ?assertEqual("127.0.0.1", peer(R)).
287
288 simple_dispatch_test() ->
289 R0 = make_wrq('GET', "/foo?a=1&b=2", [{"Cookie", "foo=bar"}]),
290 R1 = set_peer("127.0.0.1", R0),
291 {_, _, HostTokens, Port, PathTokens, Bindings, AppRoot, StringPath} =
292 webmachine_dispatcher:dispatch("127.0.0.1", "/foo",
293 [{["foo"], foo_resource, []}], R1),
294 R = load_dispatch_data(Bindings,
295 HostTokens,
296 Port,
297 PathTokens,
298 AppRoot,
299 StringPath,
300 R1),
301 ?assertEqual(".", app_root(R)),
302 ?assertEqual(80, port(R)),
303 ?assertEqual("http://127.0.0.1", base_uri(R)).
304
305 base_uri_test_() ->
306 Make_req =
307 fun(Scheme, Host) ->
308 R0 = make_wrq('GET', Scheme, "/foo?a=1&b=2",
309 [{"Cookie", "foo=bar"}]),
310 R1 = set_peer("127.0.0.1", R0),
311 DispatchRule = {["foo"], foo_resource, []},
312 {_, _, HostTokens, Port, PathTokens,
313 Bindings, AppRoot,StringPath} =
314 webmachine_dispatcher:dispatch(Host, "/foo", [DispatchRule],
315 R1),
316 load_dispatch_data(Bindings,
317 HostTokens,
318 Port,
319 PathTokens,
320 AppRoot,
321 StringPath,
322 R1)
323 end,
324 Tests = [{{http, "somewhere.com:8080"}, "http://somewhere.com:8080"},
325 {{https, "somewhere.com:8080"}, "https://somewhere.com:8080"},
326
327 {{http, "somewhere.com"}, "http://somewhere.com"},
328 {{https, "somewhere.com"}, "https://somewhere.com"},
329
330 {{http, "somewhere.com:80"}, "http://somewhere.com"},
331 {{https, "somewhere.com:443"}, "https://somewhere.com"},
332 {{https, "somewhere.com:80"}, "https://somewhere.com:80"},
333 {{http, "somewhere.com:443"}, "http://somewhere.com:443"}],
334 [ ?_assertEqual(Expect, base_uri(Make_req(Scheme, Host)))
335 || {{Scheme, Host}, Expect} <- Tests ].
336
337 -endif.
+0
-3
deps/webmachine/start-dev.sh less more
0 #!/bin/sh
1 cd `dirname $0`
2 exec erl -pa $PWD/ebin $PWD/deps/*/ebin -boot start_sasl -s reloader -s webmachine
+0
-3
deps/webmachine/start.sh less more
0 #!/bin/sh
1 cd `dirname $0`
2 exec erl -pa $PWD/ebin $PWD/deps/*/ebin -boot start_sasl -s webmachine
+0
-67
deps/webmachine/www/blogs.html less more
0 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
1 <html xmlns="http://www.w3.org/1999/xhtml">
2 <head>
3 <meta name="author" content="Basho Technologies" />
4 <meta name="description" content="Webmachine blogging" />
5 <meta name="keywords" content="webmachine http rest web" />
6 <meta http-equiv="content-type" content="text/html;charset=utf-8" />
7 <link rel="stylesheet" href="css/style-1c.css" type="text/css" />
8 <title>other writing about Webmachine</title>
9 </head>
10 <body>
11 <div id="content">
12 <h1><span class="hr"></span><a href="/">webmachine</a></h1>
13 <ul id="top">
14 <li><a href="/">Home</a></li>
15 <li><a href="http://bitbucket.org/justin/webmachine/">Source Code</a></li>
16 <li><a href="contact.html">Contact</a></li>
17 </ul>
18 <div id="left">
19 <h3>What else can I read?</h3>
20
21 <p>
22 In addition to the documentation on this site and the source code on bitbucket, some useful Webmachine writing can be found on blogs and elsewhere. A small sampling of these is here:
23 </p>
24 <p>
25 The <a href="http://blog.therestfulway.com/">restful way</a> blog is almost entirely dedicated to Webmachine.
26 </p>
27 <p>
28 The <a href="http://blog.beerriot.com/">BeerRiot blog</a> contains multiple topics, including a wealth of examples and discussion of Webmachine.
29 </p>
30 <p>
31 The blog of <a href="http://blog.argv0.net/">Andy Gross</a> has some
32 useful hints and tutorials.
33 </p>
34 <p>
35 Paul Mineiro posted about
36 <a href="http://dukesoferl.blogspot.com/2009/08/dynamically-loading-webmachine.html">dynamically loading webmachine resources</a>.
37 </p>
38 <p>
39 <a href="http://weblog.hypotheticalabs.com/?page_id=413">Kevin Smith</a>
40 teaches an excellent Erlang training class which often culminates with
41 development of applications in Webmachine.
42 </p>
43 <p>
44 There is a fairly low traffic
45 <a href="http://lists.therestfulway.com/mailman/listinfo/webmachine_lists.therestfulway.com">mailing list</a>
46 for technical discussion of Webmachine.
47 </p>
48 </div>
49 <div id="footer">
50
51 </div>
52 </div>
53
54 <script type="text/javascript">
55 var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
56 document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
57 </script>
58 <script type="text/javascript">
59 try {
60 var pageTracker = _gat._getTracker("UA-4979965-5");
61 pageTracker._trackPageview();
62 } catch(err) {}</script>
63
64 </body>
65 </html>
66
+0
-48
deps/webmachine/www/contact.html less more
0 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
1 <html xmlns="http://www.w3.org/1999/xhtml">
2 <head>
3 <meta name="author" content="Basho Technologies" />
4 <meta name="description" content="Webmachine contact info" />
5 <meta name="keywords" content="webmachine http rest web" />
6 <meta http-equiv="content-type" content="text/html;charset=utf-8" />
7 <link rel="stylesheet" href="css/style-1c.css" type="text/css" />
8 <title>Webmachine contact information</title>
9 </head>
10 <body>
11 <div id="content">
12 <h1><span class="hr"></span><a href="/">webmachine</a></h1>
13 <ul id="top">
14 <li><a href="/">Home</a></li>
15 <li><a href="http://bitbucket.org/justin/webmachine/">Source Code</a></li>
16 <li><a href="contact.html">Contact</a></li>
17 </ul>
18 <div id="left">
19 <h3>Webmachine contact information</h3>
20
21 <p>
22 Have questions about Webmachine that aren't answered here? We'd love
23 to hear from you.
24 </p>
25 <p>
26 Subscribers to the <a href="http://lists.therestfulway.com/mailman/listinfo/webmachine_lists.therestfulway.com">Webmachine mailing list</a> include both users and the core development team.
27 </p>
28
29 </div>
30 <div id="footer">
31
32 </div>
33 </div>
34
35 <script type="text/javascript">
36 var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
37 document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
38 </script>
39 <script type="text/javascript">
40 try {
41 var pageTracker = _gat._getTracker("UA-4979965-5");
42 pageTracker._trackPageview();
43 } catch(err) {}</script>
44
45 </body>
46 </html>
47
+0
-103
deps/webmachine/www/css/style-1c.css less more
0 /* global reset */
1 *{ margin: 0; padding: 0; }*
2 :focus, :active { outline: 0; }
3
4 body { font: .9em Georgia, "Times New Roman", Sans-Serif; background: #fff url(../images/bg.gif) repeat-x; color: #333; }
5 a { color: #3333FF; text-decoration: none; }
6 img { border: 0; }
7 h1 { float: left; margin: 20px 0 50px; font-size: 4em; color: #fff; }
8 h2 { font-size: 2.4em; font-weight: normal; margin: 0 0 20px; }
9 h2 a:hover { background: #3333FF; color: #fff; }
10 .hr { color: #ccc; }
11 p { margin: 5px 0 15px; line-height: 1.6em; }
12 #content { margin: 0 auto; width: 900px; }
13 #top { float: right; margin: 38px 0 30px 0;}
14 #top li { list-style: none; display: inline; }
15 #top li a { float: left; padding: 6px 20px; margin: 3px 2px 0 0; color: #3333FF; }
16 #top li a.current { color: #fff; background: #3333FF; }
17 #top li a:hover { background: #808080; color: #fff; }
18 #intro { clear: both; padding: 15px 0 1px 20px; border: 1px solid #dedede; font-size: 1.3em; background: #eee; margin: 0 0 30px; }
19 #left { float: left; width: 830px; margin: 0 0 15px; }
20 #right { float: right; width: 0px; }
21 #right h3 { border-bottom: 1px solid #ccc; margin: 0 0 10px; }
22 #right img { margin: 0 3px 3px 0; border: 2px solid #eee; padding: 2px; }
23 #right li { list-style: none; }
24 #right li a { display: block; border-bottom: 1px solid #ccc; padding: 5px 5px; }
25 #footer { clear: both; padding: 15px 0; border-top: 1px solid #ccc; }
26 #r { float: right; }
27 dt { font-weight:bold; }
28 dd { margin: 0.5em 0 0.5em 1em; }
29
30 /* table */
31
32 .fwf {
33 font: 12px "Courier";
34 color: #111;
35 }
36
37 .lhcol {
38 width: 200px;
39 }
40
41 .x_check {
42 padding-left:3px;
43 font: 12px "Courier";
44 }
45
46 table {
47 width: 100%;
48 padding: 0;
49 border-spacing: 0px;
50 border-collapse: collapse;
51 margin: 5px;
52 margin-bottom: 15px;
53 }
54
55 th {
56 font: bold 12px "Georgia", Verdana, Arial, Helvetica, sans-serif;
57 /* color: #4f6b72; */
58 border: 1px solid #999;
59 letter-spacing: 2px;
60 text-transform: uppercase;
61 text-align: left;
62 padding: 6px 6px 6px 12px;
63 background: #ddd;
64 }
65
66 th.nobg {
67 border-top: 0;
68 border-left: 0;
69 border-right: 1px solid #999;
70 background: none;
71 }
72
73 td {
74 border: 1px solid #999;
75 background: #fff;
76 padding: 6px 6px 6px 12px;
77 vertical-align: top;
78 font: inherit;
79 color: #222;
80 }
81
82
83 td.alt {
84 background: #F5FAFA;
85 color: #797268;
86 }
87
88 th.spec {
89 border-left: 1px solid #999;
90 border-top: 0;
91 background: #fff url(images/bullet1.gif) no-repeat;
92 font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
93 }
94
95 th.specalt {
96 border-left: 1px solid #999;
97 border-top: 0;
98 background: #f5fafa url(images/bullet2.gif) no-repeat;
99 font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
100 color: #797268;
101 }
102
+0
-103
deps/webmachine/www/css/style.css less more
0 /* global reset */
1 *{ margin: 0; padding: 0; }*
2 :focus, :active { outline: 0; }
3
4 body { font: .9em Georgia, "Times New Roman", Sans-Serif; background: #fff url(../images/bg.gif) repeat-x; color: #333; }
5 a { color: #3333FF; text-decoration: none; }
6 img { border: 0; }
7 h1 { float: left; margin: 20px 0 50px; font-size: 4em; color: #fff; }
8 h2 { font-size: 2.4em; font-weight: normal; margin: 0 0 20px; }
9 h2 a:hover { background: #3333FF; color: #fff; }
10 .hr { color: #ccc; }
11 p { margin: 5px 0 15px; line-height: 1.6em; }
12 #content { margin: 0 auto; width: 900px; }
13 #top { float: right; margin: 38px 0 30px 0;}
14 #top li { list-style: none; display: inline; }
15 #top li a { float: left; padding: 6px 20px; margin: 3px 2px 0 0; color: #3333FF; }
16 #top li a.current { color: #fff; background: #3333FF; }
17 #top li a:hover { background: #808080; color: #fff; }
18 #intro { clear: both; padding: 15px 0 1px 20px; border: 1px solid #dedede; font-size: 1.3em; background: #eee; margin: 0 0 30px; }
19 #left { float: left; width: 550px; margin: 0 0 15px; }
20 #right { float: right; width: 280px; }
21 #right h3 { border-bottom: 1px solid #ccc; margin: 0 0 10px; }
22 #right img { margin: 0 3px 3px 0; border: 0 solid #eee; padding: 2px; }
23 #right li { list-style: none; }
24 #right li a { display: block; border-bottom: 1px solid #ccc; padding: 5px 5px; }
25 #footer { clear: both; padding: 15px 0; border-top: 1px solid #ccc; }
26 #r { float: right; }
27 dt { font-weight:bold; }
28 dd { margin: 0.5em 0 0.5em 1em; }
29
30 /* table */
31
32 .fwf {
33 font: 12px "Courier";
34 color: #111;
35 }
36
37 .lhcol {
38 width: 200px;
39 }
40
41 .x_check {
42 padding-left:3px;
43 font: 12px "Courier";
44 }
45
46 table {
47 width: 100%;
48 padding: 0;
49 border-spacing: 0px;
50 border-collapse: collapse;
51 margin: 5px;
52 margin-bottom: 15px;
53 }
54
55 th {
56 font: bold 12px "Georgia", Verdana, Arial, Helvetica, sans-serif;
57 /* color: #4f6b72; */
58 border: 1px solid #999;
59 letter-spacing: 2px;
60 text-transform: uppercase;
61 text-align: left;
62 padding: 6px 6px 6px 12px;
63 background: #ddd;
64 }
65
66 th.nobg {
67 border-top: 0;
68 border-left: 0;
69 border-right: 1px solid #999;
70 background: none;
71 }
72
73 td {
74 border: 1px solid #999;
75 background: #fff;
76 padding: 6px 6px 6px 12px;
77 vertical-align: top;
78 font: inherit;
79 color: #222;
80 }
81
82
83 td.alt {
84 background: #F5FAFA;
85 color: #797268;
86 }
87
88 th.spec {
89 border-left: 1px solid #999;
90 border-top: 0;
91 background: #fff url(images/bullet1.gif) no-repeat;
92 font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
93 }
94
95 th.specalt {
96 border-left: 1px solid #999;
97 border-top: 0;
98 background: #f5fafa url(images/bullet2.gif) no-repeat;
99 font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
100 color: #797268;
101 }
102
+0
-292
deps/webmachine/www/debugging.html less more
0 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
1 <html xmlns="http://www.w3.org/1999/xhtml">
2 <head>
3 <meta name="author" content="Basho Technologies" />
4 <meta name="description" content="Webmachine debugging" />
5 <meta name="keywords" content="webmachine http rest web" />
6 <meta http-equiv="content-type" content="text/html;charset=utf-8" />
7 <link rel="stylesheet" href="css/style-1c.css" type="text/css" />
8 <title>Webmachine debugging</title>
9 </head>
10 <body>
11 <div id="content">
12 <h1><span class="hr"></span><a href="/">webmachine</a></h1>
13 <ul id="top">
14 <li><a href="/">Home</a></li>
15 <li><a href="http://bitbucket.org/justin/webmachine/">Source Code</a></li>
16 <li><a href="contact.html">Contact</a></li>
17 </ul>
18 <div id="left">
19 <h3>Webmachine debugging</h3>
20
21 <p>
22 Having trouble with your webmachine resource? Try debugging it with
23 the webmachine trace utility!
24 </p>
25
26 <h2>Basic Use</h2>
27
28 <p>
29 To get started, first change your resource's <code>init/1</code>
30 function to return <code>{trace, Path}</code> instead of
31 <code>ok</code>. For example:
32 </p>
33
34 <pre>
35 init(Config) ->
36 {{trace, "/tmp"}, Config}. %% debugging code
37 %%{ok, Config}. %% regular code
38 </pre>
39
40 <p>
41 Rebuild and reload the module, then in your webmachine application's
42 shell, type:
43 </p>
44
45 <pre>
46 wmtrace_resource:add_dispatch_rule("wmtrace", "/tmp").
47 </pre>
48
49 <p>
50 Now issue the HTTP request that you're trying to debug. Once it has
51 finished, point your browser at <code>http://YOUR_HOST/wmtrace/</code>.
52 You'll see one or more trace files available for inspection.
53 Click on one of them to navigate to the trace inspection utility,
54 which will look something like this:
55 </p>
56
57 <p><img src="images/basic-trace-labeled.png" alt="Basic Trace" title="Basic Trace" /></p>
58
59 <p>
60 The example above is a trace of a resource that responded to a GET of
61 the root path (as you can see in the <em>Detail Panel</em>), and ran
62 all the way to a 200 response code (as you can see in the <em>Decision
63 Graph</em>).
64 </p>
65
66 <p>
67 The graph starts small, so you can get a quick view of the path your
68 resource took through it. You can zoom in and out of the <em>Decision
69 Graph</em> by using the <em>Zoom Controls</em>.
70 </p>
71
72 <p>
73 The path your resource took through the graph is highlighted with a
74 dark grey line. Hovering your mouse over the outlined decision points
75 along that line will pop up a tool tip with the name of the decision,
76 and the names of the functions in your resource that were called at
77 that point. Clicking on a decision will flip the <em>Detail
78 Panel</em> to the <em>Decision Tab</em>, where information about that
79 decision will be displayed.
80 </p>
81 <p>
82 If your resource traversed the graph all the way to one of the
83 standard, non-error return codes, the box for that return code will be
84 outlined. If your resource instead returned a non-standard or error
85 code, a red circle will be drawn next to the last decision point your
86 resource reached. Clicking on either of these types of endpoints will
87 flip the <em>Detail Panel</em> to the <em>Response Tab</em>, where
88 information about the response will be displayed.
89 </p>
90 <p>
91 The <em>Detail Panel</em> has three tabs: Request (labeled
92 <em>Q</em>), Response (labeled <em>R</em>), and Decision (labeled
93 <em>D</em>). Clicking each of these will show information about the
94 request, response, or current decision, respectively.
95 </p>
96
97 <p><img src="images/basic-trace-request-tab.png" alt="Detail Panel Request Tab" title="Detail Panel Request Tab" /></p>
98
99 <p>
100 The <em>Request Tab</em> shows details about what the client
101 requested. The method and path are displayed at the top, headers
102 below that, and body (if available) at the bottom.
103 </p>
104
105 <p><img src="images/basic-trace-response-tab.png" alt="Detail Panel Response Tab" title="Detail Panel Response Tab" /></p>
106
107 <p>
108 The <em>Response Tab</em> shows details about how your resource
109 responded. The response code is displayed at the top, headers below
110 that, and body (if available) at the bottom.
111 </p>
112
113 <p><img src="images/basic-trace-decision-tab.png" alt="Detail Panel Decision Tab" title="Detail Panel Decision Tab" /></p>
114
115 <p>
116 The <em>Decision Tab</em> shows details about the currently selected
117 decision. The decision's name is displayed in a dropdown at the top
118 (changing this dropdown or clicking on a decision in the graph will
119 change which decision is displayed). The list of the functions called
120 in the resource's module is displayed in a dropdown below the
121 decision. The arguments with which the function was called are
122 displayed just below the function's name, and the return value of the
123 function is displayed at the bottom of the panel.
124 </p>
125 <p>
126 The <em>Detail Panel</em> can be resized by clicking and dragging the
127 tabs or the dark grey border to the left or right. Clicking the
128 border will toggle the panel's visibility.
129 </p>
130
131 <h2>Configuration Details</h2>
132
133 <p>
134 The Webmachine trace tool is divided into two parts: one produces the
135 trace logs, while the other produces the visualization.
136 </p>
137
138 <h3>Trace Log Production Configuration</h3>
139
140 <p>
141 You may configure the path under which trace files are stored by
142 specifying that path as the <code>Path</code> part of your resource
143 module's <code>init/1</code> return value. Every time a request is
144 made to that resource, a new trace file will be created in the
145 specified directory.
146 </p>
147 <p>
148 <strong><em>Warning</em></strong>: Trace files can be large. It is
149 advised that you do not leave tracing enabled on a
150 large-content-producing or high-hit-rate resource.
151 </p>
152 <p>
153 The path may be either absolute:
154 </p>
155 <pre>
156 init(Config) ->
157 {{trace, "/tmp/traces"}, Config}. %% absolute path /tmp/traces
158 </pre>
159
160 <p>or relative to your application's root:</p>
161
162 <p>
163 <pre>
164 init(Config) ->
165 {{trace, "traces"}, Config}. %% "traces" directory in application's root
166 </pre>
167 </p>
168
169 <h3>Trace Viewer Configuration</h3>
170
171 <p>
172 The viewer is configured by its entry in the
173 <a href="dispatcher.html">dispatch list</a>.
174 Two functions make modifying that entry easy:
175 <code>wmtrace_resource:add_dispatch_rule/2</code>
176 and <code>wmtrace_resource:remove_dispatch_rules/0</code>.
177 </p>
178 <p>
179 Call <code>add_dispatch_rule/2</code> with the HTTP-exported path, and
180 the path to the trace files. For example, to expose the viewer at
181 <code>http://YOUR_HOST/dev/wmtrace/</code> and point it at the trace
182 files in <code>/tmp/traces</code>, type in your application's erlang
183 shell:
184 </p>
185
186 <pre>
187 wmtrace_resource:add_dispatch_rule("dev/wmtrace", "/tmp/traces").
188 </pre>
189
190 <p>
191 If you know that you always want the viewer enabled and configured in
192 a specific way, you can also add a line like the following to your
193 application's dispatch list:
194 </p>
195
196 <pre>
197 {["dev", "wmtrace", '*'], wmtrace_resource, [{trace_dir, "/tmp/traces"}]}
198 </pre>
199
200 <p>
201 To disable all trace viewer resources at any point, just execute
202 <code>wmtrace_resource:remove_dispatch_rules/0</code> in your
203 application's erlang shell.
204 </p>
205
206 <h2>Trace Log Format</h2>
207
208 <p>
209 The trace file is fairly straightforward, if you want to read it with
210 <code>less</code>:
211 </p>
212
213 <ul><li><code> {decision, X}. </code> indicates that a decision point was reached
214
215 </li><li><code> {attempt, Module, Function, Args}. </code> indicates that a call to <code> Module:Function(Args) </code> was made.
216 </li><li><code> {result, Module, Function, Result}. </code> indicates that the call to <code> Module:Function(Args) </code> returned <code> Result </code>.
217
218 </li><li><code> {not_expored, Module, Function, Args}. </code> indicates that <code> Module:Function(Args) </code> would have been called, but it was not exported (or not defined) by the module
219 </li></ul>
220
221 <p>
222 The format should be such that a <code>file:consult/1</code> will
223 give you a list of the lines as erlang terms.
224 </p>
225
226 <h2>Special Handling for Funs and Pids</h2>
227
228 <p>
229 Funs and Pids don't roundtrip through file serialization very well
230 (<code>file:consult/1</code> will blow up on a fun or pid written to a
231 file with <code>io:format("~p", [FunOrPid])</code>). To deal with
232 this, the trace logger prints a recognizable tuple translation instead
233 of the fun or pid.
234 </p>
235
236 <h3>Fun Translation</h3>
237
238 <p>
239 Funs you might see in Erlang source as <code>fun io:format/2</code>
240 will appear in a trace log as:
241 </p>
242
243 <pre>
244 {'WMTRACE_ESCAPED_FUN',[{module,io},
245 {name,format},
246 {arity,2},
247 {type,external}]}
248 </pre>
249
250 <p>
251 Those that would be in Erlang source as <code> fun() -&gt; ok end
252 </code> will appear in a trace log as:
253 </p>
254
255 <pre>
256 {'WMTRACE_ESCAPED_FUN',[{module,sampletrace_resource},
257 {name,'-to_html/2-fun-0-'},
258 {arity,0},
259 {type,local}]}
260 </pre>
261
262 <h3>Pid Translation</h3>
263
264 <p>
265 Pids are simply logged in a marked tuple, after being run through
266 <code>erlang:pid_to_list/1</code>:
267 </p>
268
269 <pre>
270 {'WMTRACE_ESCAPED_PID',"<0.74.0>"}
271 </pre>
272
273 </div>
274 <div id="footer">
275
276 </div>
277 </div>
278
279 <script type="text/javascript">
280 var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
281 document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
282 </script>
283 <script type="text/javascript">
284 try {
285 var pageTracker = _gat._getTracker("UA-4979965-5");
286 pageTracker._trackPageview();
287 } catch(err) {}</script>
288
289 </body>
290 </html>
291
+0
-57
deps/webmachine/www/diagram.html less more
0 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
1 <html xmlns="http://www.w3.org/1999/xhtml">
2 <head>
3 <meta name="author" content="Basho Technologies" />
4 <meta name="description" content="Webmachine decision flow" />
5 <meta name="keywords" content="webmachine http rest web" />
6 <meta http-equiv="content-type" content="text/html;charset=utf-8" />
7 <link rel="stylesheet" href="css/style-1c.css" type="text/css" />
8 <title>Webmachine decision flow</title>
9 </head>
10 <body>
11 <div id="content">
12 <h1><span class="hr"></span><a href="/">webmachine</a></h1>
13 <ul id="top">
14 <li><a href="/">Home</a></li>
15 <li><a href="http://bitbucket.org/justin/webmachine/">Source Code</a></li>
16 <li><a href="contact.html">Contact</a></li>
17 </ul>
18 <div id="left">
19 <h3>Webmachine decision flow</h3>
20
21 <p>
22 This diagram is illustrative of the flow of processing that a
23 <a href="resources.html">webmachine resource</a> goes through
24 from inception to response.
25 </p>
26 <p>
27 Version 1 of this diagram, from Alan Dean, was the inspiration for
28 webmachine_decision_core. Versions 2 and 3 were created in
29 collaboration between Alan Dean and Justin Sheehy.
30 </p>
31 <p>
32 A copy of v3 is found in the webmachine source tree for convenience.
33 </p>
34 <p>
35 <img src="images/http-headers-status-v3.png" alt="http diagram v3" title="http diagram v3" />
36 </p>
37
38 </div>
39 <div id="footer">
40
41 </div>
42 </div>
43
44 <script type="text/javascript">
45 var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
46 document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
47 </script>
48 <script type="text/javascript">
49 try {
50 var pageTracker = _gat._getTracker("UA-4979965-5");
51 pageTracker._trackPageview();
52 } catch(err) {}</script>
53
54 </body>
55 </html>
56
+0
-121
deps/webmachine/www/dispatcher.html less more
0 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
1 <html xmlns="http://www.w3.org/1999/xhtml">
2 <head>
3 <meta name="author" content="Basho Technologies" />
4 <meta name="description" content="Webmachine request dispatching" />
5 <meta name="keywords" content="webmachine http rest web" />
6 <meta http-equiv="content-type" content="text/html;charset=utf-8" />
7 <link rel="stylesheet" href="css/style-1c.css" type="text/css" />
8 <title>Webmachine request dispatching</title>
9 </head>
10 <body>
11 <div id="content">
12 <h1><span class="hr"></span><a href="/">webmachine</a></h1>
13 <ul id="top">
14 <li><a href="/">Home</a></li>
15 <li><a href="http://bitbucket.org/justin/webmachine/">Source Code</a></li>
16 <li><a href="contact.html">Contact</a></li>
17 </ul>
18 <div id="left">
19 <h3>Webmachine request dispatching</h3>
20
21 <p>
22 This page describes the configuration of URI dispatch to resources
23 in a webmachine application. The dispatch map data structure is a list
24 of 3-tuples, where each entry is of the form {pathspec, resource,
25 args}. The first pathspec in the list that matches the URI for a
26 request will cause the corresponding resource to be used in handling
27 that request.
28
29 </p>
30 <p>
31
32 A <code>pathspec</code> is a list of pathterms. A pathterm is any of
33 [string,atom,star] where star is just the atom of "*". The
34 pathspec-matching is done by breaking up the request URI into tokens
35 via the "/" separator and matching those tokens against the
36 pathterms. A string pathterm will match a token if the token is equal
37 to that string. A non-star atom will match any single token. The star
38 atom (* in single quotes) will match any number of tokens, but may
39 only be present as the last pathterm in a pathspec. If all tokens are
40 matched and all pathterms are used, then the pathspec matches. The
41 tokens used are available in <code>wrq:path_tokens(ReqData)</code>
42 in the resource functions.
43
44 </p>
45 <p>
46
47 Any atom pathterms that were used in a match will cause a binding in
48 the path_info element of the request's
49 <a href="reqdata.html">ReqData</a>. If
50 there was a <code>foo</code> atom that matched the token
51 <code>"bar"</code>, then <code>wrq:path_info(foo, ReqData)</code> will
52 return <code>"bar"</code> inside the resource calls, and in any case
53 <code>wrq:path_info(ReqData)</code> will return a Dict term with all
54 the bindings, accessible via the <code>dict</code> standard library
55 module. If there was a star pathterm in the pathspec, then
56 <code>wrq:disp_path(ReqData)</code> in a resource function will return
57 the URI portion that was matched by the star.
58
59 </p>
60 <p>
61
62 The <code> resource </code> is an atom identifying a
63 <a href="resources.html">resource</a> that
64 should handle a matching request. It will have the <code>args</code>
65 (which must be a list) passed to its init function before request
66 handling begins.
67
68 </p>
69 <p>
70
71 In the default directory structure for a new webmachine application,
72 the dispatch terms will be in file:consult form in
73 "priv/dispatch.conf" under the application root.
74
75 </p>
76 <h3 id="examples">Examples</h3>
77
78 <p>
79
80 The examples below are taken from
81 <a href="http://www.erlang-factory.com/conference/SFBayAreaErlangFactory2009/speakers/justinsheehy">Justin Sheehy's slide at Erlang Factory 2009</a>
82 </p>
83
84 <table><tr><th>Dispatch Rule</th><th>URL</th><th>wrq:disp_path</th><th>wrq:path</th><th>wrq:path_info</th><th>wrq:path_tokens</th></tr>
85 <tr><td>{["a"], some_resource, []}</td><td>/a</td><td>""</td><td>"/a"</td><td>[]</td><td>[]</td></tr>
86
87 <tr><td>{["a", '*'], some_resource, []}</td><td>/a</td><td>""</td><td>"/a"</td><td>[]</td><td>[]</td></tr>
88 <tr><td>{["a", '*'], some_resource, []}</td><td>/a/b/c</td><td>"b/c"</td><td>"/a/b/c"</td><td>[]</td><td>["b", "c"]</td></tr>
89 <tr><td>{["a", foo], some_resource, []}</td><td>/a/b</td><td>""</td><td>"/a/b"</td><td>[{foo, "b"}]</td><td>[]</td></tr>
90
91 <tr><td>{["a", foo, '*'], some_resource, []}</td><td>/a/b</td><td>""</td><td>"/a/b"</td><td>[{foo, "b"}]</td><td>[]</td></tr>
92 <tr><td>{["a", foo, '*'], some_resource, []}</td><td>/a/b/c/d</td><td>"c/d"</td><td>"/a/b/c/d"</td><td>[{foo, "b"}]</td><td>["c", "d"]</td></tr>
93 </table>
94
95 <p>Query strings are easy too:</p>
96
97 <ul><li>Given rule: {["a", foo, '*'], some_resource, []}
98 </li><li>And URL: /a/b/c/d?fee=ah&amp;fie=ha
99 </li><li>Then wrq:get_qs_value("fie",ReqData) -&gt; "ha"
100 </li></ul>
101
102 </div>
103 <div id="footer">
104
105 </div>
106 </div>
107
108 <script type="text/javascript">
109 var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
110 document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
111 </script>
112 <script type="text/javascript">
113 try {
114 var pageTracker = _gat._getTracker("UA-4979965-5");
115 pageTracker._trackPageview();
116 } catch(err) {}</script>
117
118 </body>
119 </html>
120
+0
-58
deps/webmachine/www/docs.html less more
0 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
1 <html xmlns="http://www.w3.org/1999/xhtml">
2 <head>
3 <meta name="author" content="Basho Technologies" />
4 <meta name="description" content="Webmachine documentation" />
5 <meta name="keywords" content="webmachine http rest web" />
6 <meta http-equiv="content-type" content="text/html;charset=utf-8" />
7 <link rel="stylesheet" href="css/style-1c.css" type="text/css" />
8 <title>Webmachine documentation</title>
9 </head>
10 <body>
11 <div id="content">
12 <h1><span class="hr"></span><a href="/">webmachine</a></h1>
13 <ul id="top">
14 <li><a href="/">Home</a></li>
15 <li><a href="http://bitbucket.org/justin/webmachine/">Source Code</a></li>
16 <li><a href="contact.html">Contact</a></li>
17 </ul>
18 <div id="left">
19 <h3>documentation</h3>
20
21 <p>
22 There are a lot of places you can choose to start reading about
23 Webmachine. A few of your options are:
24 </p>
25
26 <ul>
27 <li><a href="quickstart.html">get started right away</a></li>
28 <li><a href="example_resources.html">read some examples</a></li>
29 <li><a href="mechanics.html">understand Webmachine's execution model</a></li>
30 <li><a href="dispatcher.html">configure your URL dispatching</a></li>
31 <li><a href="resources.html">learn about the functions that can make up a resource</a></li>
32 <li><a href="reqdata.html">see how your resource can access the HTTP Request</a></li>
33 <li><a href="http://blog.therestfulway.com/2009/05/video-slideshow-introducing-webmachine.html">watch a video</a></li>
34 <li><a href="http://lists.therestfulway.com/mailman/listinfo/webmachine_lists.therestfulway.com">join the mailing list</a></li>
35 <li><a href="debugging.html">debug your application </a></li>
36 <li><a href="blogs.html">check out some blogs </a></li>
37 </ul>
38
39 </div>
40 <div id="footer">
41
42 </div>
43 </div>
44
45 <script type="text/javascript">
46 var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
47 document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
48 </script>
49 <script type="text/javascript">
50 try {
51 var pageTracker = _gat._getTracker("UA-4979965-5");
52 pageTracker._trackPageview();
53 } catch(err) {}</script>
54
55 </body>
56 </html>
57
+0
-250
deps/webmachine/www/example_resources.html less more
0 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
1 <html xmlns="http://www.w3.org/1999/xhtml">
2 <head>
3 <meta name="author" content="Basho Technologies" />
4 <meta name="description" content="Webmachine examples" />
5 <meta name="keywords" content="webmachine http rest web" />
6 <meta http-equiv="content-type" content="text/html;charset=utf-8" />
7 <link rel="stylesheet" href="css/style-1c.css" type="text/css" />
8 <title>Webmachine examples</title>
9 </head>
10 <body>
11 <div id="content">
12 <h1><span class="hr"></span><a href="/">webmachine</a></h1>
13 <ul id="top">
14 <li><a href="/">Home</a></li>
15 <li><a href="http://bitbucket.org/justin/webmachine/">Source Code</a></li>
16 <li><a href="contact.html">Contact</a></li>
17 </ul>
18 <div id="left">
19 <h3>Webmachine examples</h3>
20
21 <p>
22 The simplest possible example is the one produced via the
23 <a href="quickstart.html">quickstart</a>.
24 </p>
25 <p>
26 For an example of a read/write filesystem server showing several
27 interesting features and supporting GET, PUT, DELETE, and POST, see
28 <a href="http://bitbucket.org/justin/webmachine/src/tip/demo/src/demo_fs_resource.erl">demo_fs_resource</a>.
29 </p>
30 <p>
31 For a very simple resource demonstrating content negotiation, basic
32 auth, and some caching headers, see
33 <a href="http://bitbucket.org/justin/webmachine/src/tip/demo/src/webmachine_demo_resource.erl">webmachine_demo_resource</a>.
34 </p>
35 <p>
36 Some example code based on webmachine_demo_resource follows.
37 </p>
38 <p>
39 The simplest working resource could export only one function in
40 addition to init/1:
41 </p>
42
43 <pre>
44 -module(webmachine_demo_resource).
45 -export([init/1, to_html/2]).
46 -include_lib("webmachine/include/webmachine.hrl").
47
48 init([]) -> {ok, undefined}.
49
50 to_html(ReqData, Context) -> {"<html><body>Hello, new world</body></html>", ReqData, Context}.
51 </pre>
52
53 <p>
54 That's really it -- a working webmachine resource. That resource will
55 respond to all valid GET requests with the exact same response.
56 </p>
57 <p>
58 Many interesting bits of HTTP are handled automatically by
59 Webmachine. For instance, if a client sends a request to that trivial
60 resource with an Accept header that does not allow for a text/html
61 response, they will receive a 406 Not Acceptable.
62 </p>
63 <p>
64 Suppose I wanted to serve a plaintext client as well. I could note
65 that I provide more than just HTML:
66 </p>
67
68 <pre>
69 content_types_provided(ReqData, Context) ->
70 {[{"text/html", to_html},{"text/plain",to_text}], ReqData, Context}.
71 </pre>
72
73 <p>
74 I already have my HTML representation produced, so I add a text one:
75 (and while I'm at it, I'll show that it's trivial to produce dynamic content as well)
76 </p>
77
78 <pre>
79 to_text(ReqData, Context) ->
80 Path = wrq:disp_path(ReqData),
81 Body = io_lib:format("Hello ~s from webmachine.~n", [Path]),
82 {Body, ReqData, Context}.
83 </pre>
84
85 <p>
86 Now that this resource provides multiple media types, it automatically performs conneg:
87 </p>
88
89 <pre>
90 $ telnet localhost 8000
91 Trying 127.0.0.1...
92 Connected to localhost.
93 Escape character is '^]'.
94 GET /demo/a/resource/path HTTP/1.1
95 Accept: text/plain
96
97 HTTP/1.1 200 OK
98 Vary: Accept
99 Server: MochiWeb/1.1 WebMachine/0.97
100 Date: Sun, 15 Mar 2009 02:54:02 GMT
101 Content-Type: text/plain
102 Content-Length: 39
103
104 Hello a/resource/path from webmachine.
105 </pre>
106
107 <p>
108 What about authorization? Webmachine resources default to assuming the
109 client is authorized, but that can easily be overridden. Here's an
110 overly simplistic but illustrative example:
111 </p>
112
113 <pre>
114 is_authorized(ReqData, Context) ->
115 case wrq:disp_path(ReqData) of
116 "authdemo" ->
117 case wrq:get_req_header("authorization", ReqData) of
118 "Basic "++Base64 ->
119 Str = base64:mime_decode_to_string(Base64),
120 case string:tokens(Str, ":") of
121 ["authdemo", "demo1"] ->
122 {true, ReqData, Context};
123 _ ->
124 {"Basic realm=webmachine", ReqData, Context}
125 end;
126 _ ->
127 {"Basic realm=webmachine", ReqData, Context}
128 end;
129 _ -> {true, ReqData, Context}
130 end.
131 </pre>
132
133 <p>
134 With that function in the resource, all paths except
135 <code>/authdemo</code> from this resource's root are authorized.
136 For that one path,
137 the UA will be asked to do basic authorization with the user/pass of
138 authdemo/demo1. It should go without saying that this isn't quite the
139 same function that we use in our real apps, but it is nice and simple.
140 </p>
141 <p>
142 If you've generated the application from the
143 <a href="quickstart.html">quickstart</a>, make sure
144 you've added this line to your dispatch.conf file:
145 </p>
146 <pre>
147 {["demo", '*'], mywebdemo_resource, []}.
148 </pre>
149 <p>
150 Now you can point your browser at
151 <code>http://localhost:8000/demo/authdemo</code> with the demo app running:
152 </p>
153
154 <pre>
155 $ curl -v http://localhost:8000/demo/authdemo
156 &gt; GET /demo/authdemo HTTP/1.1
157 &gt; Host: localhost:8000
158 &gt; Accept: */*
159 &gt;
160 &lt; HTTP/1.1 401 Unauthorized
161 &lt; WWW-Authenticate: Basic realm=webmachine
162 &lt; Server: MochiWeb/1.1 WebMachine/0.97
163 &lt; Date: Sun, 15 Mar 2009 02:57:43 GMT
164 &lt; Content-Length: 0
165
166 &lt;
167 </pre>
168 <p></p>
169 <pre>
170 $ curl -v -u authdemo:demo1 http://localhost:8000/demo/authdemo
171 * Server auth using Basic with user 'authdemo'
172 &gt; GET /demo/authdemo HTTP/1.1
173 &gt; Authorization: Basic YXV0aGRlbW86ZGVtbzE=
174 &gt; Host: localhost:8000
175 &gt; Accept: */*
176 &gt;
177 &lt; HTTP/1.1 200 OK
178 &lt; Vary: Accept
179 &lt; Server: MochiWeb/1.1 WebMachine/0.97
180
181 &lt; Date: Sun, 15 Mar 2009 02:59:02 GMT
182 &lt; Content-Type: text/html
183 &lt; Content-Length: 59
184 &lt;
185 &lt;html&gt;&lt;body&gt;Hello authdemo from webmachine.
186 &lt;/body&gt;&lt;/html&gt;
187 </pre>
188
189 <p>
190 HTTP caching support is also quite easy, with functions allowing
191 resources to define (e.g.) <code>last_modified</code>,
192 <code>expires</code>, and <code>generate_etag.</code> For instance, since
193 representations of this resource vary only by URI Path, I could use an
194 extremely simple entity tag unfit for most real applications but
195 sufficient for this example:
196 </p>
197
198 <pre>
199 generate_etag(ReqData, Context) -> {wrq:raw_path(ReqData), ReqData, Context}.
200 </pre>
201
202 <p>Similarly, here's a trivial expires rule:</p>
203
204 <pre>
205 expires(ReqData, Context) -> {{{2021,1,1},{0,0,0}}, ReqData, Context}.
206 </pre>
207
208 <p>
209 And now the response from our earlier request is appropriately tagged:
210 </p>
211
212 <pre>
213 HTTP/1.1 200 OK
214 Vary: Accept
215 Server: MochiWeb/1.1 WebMachine/0.97
216 Expires: Fri, 01 Jan 2021 00:00:00 GMT
217 ETag: /demo/authdemo
218 Date: Sun, 15 Mar 2009 02:59:02 GMT
219 Content-Type: text/html
220 Content-Length: 59
221
222 &lt;html&gt;&lt;body&gt;Hello authdemo from webmachine.
223 &lt;/body&gt;&lt;/html&gt;
224 </pre>
225
226 <p>
227 For more details, read the source of the resources linked at the top
228 of this page.
229 </p>
230
231 </div>
232 <div id="footer">
233
234 </div>
235 </div>
236
237 <script type="text/javascript">
238 var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
239 document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
240 </script>
241 <script type="text/javascript">
242 try {
243 var pageTracker = _gat._getTracker("UA-4979965-5");
244 pageTracker._trackPageview();
245 } catch(err) {}</script>
246
247 </body>
248 </html>
249
deps/webmachine/www/favicon.ico less more
Binary diff not shown
deps/webmachine/www/images/WM200-crop.png less more
Binary diff not shown
deps/webmachine/www/images/basho-landscape.gif less more
Binary diff not shown
deps/webmachine/www/images/basic-trace-decision-tab.png less more
Binary diff not shown
deps/webmachine/www/images/basic-trace-labeled.png less more
Binary diff not shown
deps/webmachine/www/images/basic-trace-request-tab.png less more
Binary diff not shown
deps/webmachine/www/images/basic-trace-response-tab.png less more
Binary diff not shown
deps/webmachine/www/images/bg.gif less more
Binary diff not shown
deps/webmachine/www/images/blankbox.gif less more
Binary diff not shown
deps/webmachine/www/images/chash.gif less more
Binary diff not shown
deps/webmachine/www/images/easy-ops.gif less more
Binary diff not shown
deps/webmachine/www/images/gossip4.gif less more
Binary diff not shown
deps/webmachine/www/images/halfblankbox.gif less more
Binary diff not shown
deps/webmachine/www/images/http-headers-status-v3.png less more
Binary diff not shown
deps/webmachine/www/images/more.gif less more
Binary diff not shown
deps/webmachine/www/images/site.gif less more
Binary diff not shown
deps/webmachine/www/images/splash250.gif less more
Binary diff not shown
deps/webmachine/www/images/vclock.gif less more
Binary diff not shown
+0
-73
deps/webmachine/www/index.html less more
0 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
1 <html xmlns="http://www.w3.org/1999/xhtml">
2 <head>
3 <meta name="author" content="Basho Technologies" />
4 <meta name="description" content="Webmachine - software shaped like the Web - basho technologies" />
5 <meta name="keywords" content="webmachine http rest web" />
6 <meta http-equiv="content-type" content="text/html;charset=utf-8" />
7 <link rel="stylesheet" href="css/style.css" type="text/css" />
8 <title>Webmachine - software shaped like the Web</title>
9 </head>
10 <body>
11 <div id="content">
12 <h1><span class="hr"></span><a href="/">webmachine</a></h1>
13 <ul id="top">
14 <li><a class="current" href="/">Home</a></li>
15 <li><a href="http://bitbucket.org/justin/webmachine/">Source Code</a></li>
16 <li><a href="contact.html">Contact</a></li>
17 </ul>
18
19 <div id="intro">
20 <p>Webmachine is not much like the Web frameworks you're used to. You can call Webmachine a REST toolkit if you like, and we won't argue with you.</p>
21 </div>
22 <div id="left">
23 <p>
24 Webmachine is an application layer that adds HTTP semantic awareness on top of the excellent bit-pushing and HTTP syntax-management provided by
25 <a href="http://code.google.com/p/mochiweb/">mochiweb</a>,
26 and provides a simple and clean way to connect that to your
27 application's behavior.
28 </p>
29 <p>
30 A Webmachine application is a set of resources, each of which is a set of
31 <a href="resources.html">functions</a> over the state of the resource. We really mean functions here, not object-methods, infinite-server-loops, or any other such construction. This aspect of Webmachine is one of the reasons why Webmachine applications are relatively easy to understand and extend.
32 </p>
33 <p>
34 These functions give you a place to define the representations and other Web-relevant properties of your application's resources -- with the emphasis that the first-class things on the Web are resources and that their essential properties of interaction are already quite well defined and usefully constrained.
35 </p>
36 <p>
37 For most Webmachine applications, most of the functions are quite small and isolated. One of the nice effects of this is that a quick reading of a resource will give you an understanding of the application, its Web behavior, and the relationship between them. Since these functions are usually <a href="reftrans.html">referentially transparent</a>, Webmachine applications can be quite easy to test. There's no need for mock objects, fake database connections, or any other wastes of time when you can write tests against each component of your application in terms of the input and output to various functions.
38 </p>
39 <p>
40 We believe that by giving Web developers a
41 <a href="mechanics.html">system</a> with conventions that
42 <a href="diagram.html">directly map to HTTP</a>
43 and REST, we help them to write and extend Web applications quickly while not dictating the shape of the rest of their application. The resulting applications are straightforward to examine and maintain, and have very easily understood HTTP semantics.
44 </p>
45 </div>
46 <div id="right">
47 <h3>more information</h3>
48 <ul>
49 <li><a href="intros.html">introduction</a></li>
50 <li><a href="docs.html">documentation</a></li>
51 <li><a href="blogs.html">other writing</a></li>
52 </ul>
53 <a href="diagram.html"><img src="images/WM200-crop.png" alt="Webmachine Diagram" /></a>
54 <a href="http://www.basho.com"><img src="images/basho-landscape.gif" alt="Basho" /></a>
55 </div>
56 <div id="footer">
57
58 </div>
59 </div>
60
61 <script type="text/javascript">
62 var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
63 document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
64 </script>
65 <script type="text/javascript">
66 try {
67 var pageTracker = _gat._getTracker("UA-4979965-5");
68 pageTracker._trackPageview();
69 } catch(err) {}</script>
70
71 </body>
72 </html>
+0
-62
deps/webmachine/www/intros.html less more
0 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
1 <html xmlns="http://www.w3.org/1999/xhtml">
2 <head>
3 <meta name="author" content="Basho Technologies" />
4 <meta name="description" content="Webmachine introductions" />
5 <meta name="keywords" content="webmachine http rest web" />
6 <meta http-equiv="content-type" content="text/html;charset=utf-8" />
7 <link rel="stylesheet" href="css/style-1c.css" type="text/css" />
8 <title>introductions to Webmachine</title>
9 </head>
10 <body>
11 <div id="content">
12 <h1><span class="hr"></span><a href="/">webmachine</a></h1>
13 <ul id="top">
14 <li><a href="/">Home</a></li>
15 <li><a href="http://bitbucket.org/justin/webmachine/">Source Code</a></li>
16 <li><a href="contact.html">Contact</a></li>
17 </ul>
18 <div id="left">
19 <h3>How do I get started?</h3>
20
21 <p>
22 If you want to jump in and start coding right away, the
23 <a href="quickstart.html">quickstart</a> is the way to go.
24 </p>
25 <p>
26 If you would prefer to watch a narrated slideshow introduction, this is roughly similar to the presentation that was given at
27 <a href="http://www.erlang-factory.com/conference/SFBayAreaErlangFactory2009">Bay Area Erlang Factory 2009</a>:
28 </p>
29
30 <object width="512" height="322"><param name="movie" value="http://d.yimg.com/static.video.yahoo.com/yep/YV_YEP.swf?ver=2.2.40" /><param name="allowFullScreen" value="true" /><param name="AllowScriptAccess" VALUE="always" /><param name="bgcolor" value="#000000" /><param name="flashVars" value="id=13693397&vid=5178506&lang=en-us&intl=us&thumbUrl=http%3A//l.yimg.com/a/p/i/bcst/videosearch/9129/86376656.jpeg&embed=1" /><embed src="http://d.yimg.com/static.video.yahoo.com/yep/YV_YEP.swf?ver=2.2.40" type="application/x-shockwave-flash" width="512" height="322" allowFullScreen="true" AllowScriptAccess="always" bgcolor="#000000" flashVars="id=13693397&vid=5178506&lang=en-us&intl=us&thumbUrl=http%3A//l.yimg.com/a/p/i/bcst/videosearch/9129/86376656.jpeg&embed=1" ></embed></object>
31
32 <p>
33 Some <a href="blogs.html">blogs</a> also have posts that can serve as
34 useful introductions to Webmachine, if you prefer to start that way.
35 </p>
36
37 <p>
38 No matter how you get started, you'll probably want to come back and
39 read <a href="docs.html">more documentation</a> once you get up and running.
40 <p>
41
42
43 </div>
44 <div id="footer">
45
46 </div>
47 </div>
48
49 <script type="text/javascript">
50 var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
51 document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
52 </script>
53 <script type="text/javascript">
54 try {
55 var pageTracker = _gat._getTracker("UA-4979965-5");
56 pageTracker._trackPageview();
57 } catch(err) {}</script>
58
59 </body>
60 </html>
61
+0
-108
deps/webmachine/www/mechanics.html less more
0 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
1 <html xmlns="http://www.w3.org/1999/xhtml">
2 <head>
3 <meta name="author" content="Basho Technologies" />
4 <meta name="description" content="Webmachine mechanics" />
5 <meta name="keywords" content="webmachine http rest web" />
6 <meta http-equiv="content-type" content="text/html;charset=utf-8" />
7 <link rel="stylesheet" href="css/style-1c.css" type="text/css" />
8 <title>Webmachine mechanics</title>
9 </head>
10 <body>
11 <div id="content">
12 <h1><span class="hr"></span><a href="/">webmachine</a></h1>
13 <ul id="top">
14 <li><a href="/">Home</a></li>
15 <li><a href="http://bitbucket.org/justin/webmachine/">Source Code</a></li>
16 <li><a href="contact.html">Contact</a></li>
17 </ul>
18 <div id="left">
19 <h3>How does this Webmachine thing work, anyway?</h3>
20
21 <p>
22 This page describes the basic mechanics of Webmachine from the point
23 of view of a single incoming HTTP Request, documenting the behavior of
24 Webmachine through to the HTTP Response.
25 </p>
26 <p>
27 (This is a bit different from what you might get with a "Web
28 Framework" as we're not going to talk about MVC, ORMs, or anything
29 else about the rest of the shape of your application. We believe that
30 you know better than we do how to structure your own app --
31 Webmachine's job is to help you make sure that your app's presence on
32 the Web is well-behaved and well-structured.)
33 </p>
34 <p>
35 When a request is initially received by Webmachine it is handled by the
36 <a href="dispatcher.html">dispatcher</a>.
37 If the dispatcher does not find a matching resource then it will
38 immediately respond with a 404 Not Found. If a match is found then a
39 <a href="reqdata.html">request data record</a>
40 is created and the matching
41 <a href="resources.html">resource</a> is
42 kicked off via its <code>init/1</code> function.
43 </p>
44 <p>
45 The resource then flows through the decision core, which is
46 effectively just running the request through the
47 <a href="diagram.html">HTTP flowchart</a>. At
48 each decision point (diamond) in the diagram, Webmachine will
49 determine which path to follow. In some cases it can determine the
50 path purely from the request data -- for instance, the path from
51 decision <code>C3</code> depends purely on whether the client sent
52 an <code>Accept</code> header. In many cases, however, the decision
53 is application-specific -- the path from <code>B10</code> depends on
54 the value the
55 <a href="resources.html">resource</a> module
56 returns from <code>allowed_methods.</code> Eventually the chosen path
57 will terminate at one of the rectangles on the diagram. At that point
58 Webmachine will send an appropriate HTTP response, with the headers
59 and body dependent on the path through the diagram and the values
60 returned by the resource's functions.
61 </p>
62 <p>
63 Most of the time you don't need to worry about this big diagram,
64 though -- just define the
65 <a href="resources.html">resource functions</a>
66 relevant to your app and Webmachine will do the rest. A
67 good understanding of this central mechanism in Webmachine is most
68 useful when
69 <a href="debugging.html">debugging your resources</a>.
70 </p>
71 <p>
72 From the way that webmachine's decision core works, it follows that
73 Webmachine's HTTP behavior is transactional. Each HTTP Request is
74 fully received, and the resulting HTTP Response is then fully
75 constructed before being returned. This means that while Webmachine
76 is suitable for a great many Web applications it is not a good fit for
77 an application that will gradually or continuously stream responses
78 back to clients inside the context of a single HTTP Request.
79 </p>
80 <p>
81 A useful way to build Webmachine applications is often just to write a
82 single function such as <code>to_html</code> to provide the most
83 basic of stubs; when that function exists (or any other returned by
84 <code>content_types_provided</code>) you can produce <code>200 OK</code>
85 responses. After that, you can easily extend your
86 application's Web behavior simply by filling in the other
87 <a href="resources.html">resource functions</a> as desired.
88 </p>
89 </div>
90 <div id="footer">
91
92 </div>
93 </div>
94
95 <script type="text/javascript">
96 var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
97 document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
98 </script>
99 <script type="text/javascript">
100 try {
101 var pageTracker = _gat._getTracker("UA-4979965-5");
102 pageTracker._trackPageview();
103 } catch(err) {}</script>
104
105 </body>
106 </html>
107
+0
-77
deps/webmachine/www/quickstart.html less more
0 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
1 <html xmlns="http://www.w3.org/1999/xhtml">
2 <head>
3 <meta name="author" content="Basho Technologies" />
4 <meta name="description" content="getting started quickly with Webmachine" />
5 <meta name="keywords" content="webmachine http rest web" />
6 <meta http-equiv="content-type" content="text/html;charset=utf-8" />
7 <link rel="stylesheet" href="css/style-1c.css" type="text/css" />
8 <title>getting started quickly with Webmachine</title>
9 </head>
10 <body>
11 <div id="content">
12 <h1><span class="hr"></span><a href="/">webmachine</a></h1>
13 <ul id="top">
14 <li><a href="/">Home</a></li>
15 <li><a href="http://bitbucket.org/justin/webmachine/">Source Code</a></li>
16 <li><a href="contact.html">Contact</a></li>
17 </ul>
18 <div id="left">
19 <h3>getting started quickly with Webmachine</h3>
20
21
22 <p>Make sure that you have a working Erlang/OTP release, R12B5 or later.</p>
23
24 <p>Get the webmachine code:</p>
25
26 <p><pre>
27 hg clone http://bitbucket.org/justin/webmachine/ webmachine-read-only
28 </pre></p>
29
30 <p>Build webmachine:</p>
31
32 <p><pre>
33 cd webmachine-read-only
34 make
35 </pre></p>
36
37 <p>Create, build, and start the skeleton resource:</p>
38
39 <p><pre>
40 ./scripts/new_webmachine.sh mywebdemo /tmp
41 cd /tmp/mywebdemo
42 make
43 ./start.sh
44 </pre></p>
45
46 <p>Take a look! Point a web browser at <a href="http://localhost:8000/">http://localhost:8000/</a></p>
47
48 <p>To make this resource handle URI paths other than /, add more
49 <a href="dispatcher.html">dispatch</a> terms in
50 /tmp/mywebdemo/priv/dispatch.conf; to make that resource to more
51 interesting things, modify the
52 <a href="resources.html">resource</a> itself
53 at /tmp/mywebdemo/src/mywebdemo_resource.erl.</p>
54
55 <p>To learn how to do more interesting things, check out <a href="example_resources.html">some examples</a> or read <a href="docs.html">more documentation</a>.</p>
56
57
58 </div>
59 <div id="footer">
60
61 </div>
62 </div>
63
64 <script type="text/javascript">
65 var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
66 document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
67 </script>
68 <script type="text/javascript">
69 try {
70 var pageTracker = _gat._getTracker("UA-4979965-5");
71 pageTracker._trackPageview();
72 } catch(err) {}</script>
73
74 </body>
75 </html>
76
+0
-52
deps/webmachine/www/reftrans.html less more
0 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
1 <html xmlns="http://www.w3.org/1999/xhtml">
2 <head>
3 <meta name="author" content="Basho Technologies" />
4 <meta name="description" content="Webmachine's approach to resource functions and referential transparency" />
5 <meta name="keywords" content="webmachine http rest web" />
6 <meta http-equiv="content-type" content="text/html;charset=utf-8" />
7 <link rel="stylesheet" href="css/style-1c.css" type="text/css" />
8 <title>Webmachine's approach to resource functions and referential transparency</title>
9 </head>
10 <body>
11 <div id="content">
12 <h1><span class="hr"></span><a href="/">webmachine</a></h1>
13 <ul id="top">
14 <li><a href="/">Home</a></li>
15 <li><a href="http://bitbucket.org/justin/webmachine/">Source Code</a></li>
16 <li><a href="contact.html">Contact</a></li>
17 </ul>
18 <div id="left">
19 <h3>Webmachine's approach to resource functions and referential transparency</h3>
20
21 <p>
22 Webmachine goes to great lengths to help your <a href="resources.html">resource functions</a> to be as referentially transparent as possible. By "referentially transparent" we mean that given the same input <code> {ReqData, Context} </code> the function will return the same output <code> {Result, ReqData, Context} </code> and that side effects will be insignificant from the point of view of Webmachine's execution.
23 </p>
24 <p>
25 We don't try to force you into pure referential transparency; we give you as big a hole as you want via <code>Context</code>. As that term is application-specific, you can put database handles, server process identifiers, or anything else you like in there and we won't try to stop you.
26 </p>
27 <p>
28 However, all Webmachine really cares about is the rest of the terms. Since resource functions are generally referentially transparent at least with regard to those terms, many things are easier -- testing, <a href="debugging.html">debugging</a>, and even static analysis and reasoning about your Web application.
29 </p>
30 <p>
31 Note that there is one important exception to this. The <a href="streambody.html">streamed body feature</a> exists to allow resources to consume or produce request/response bodies a hunk at a time without ever having the whole thing in memory. While the continuation-passing style used in the streaming API is friendly to general functional analysis, due to the necessary side-effect of reading or writing to sockets the stream bodies cannot be treated in quite the same way as other uses of the <code>ReqData</code> interface. Luckily, it is easy to inspect a <code>ReqData</code> to see if this is the case in any individual resource or request instance.
32 </p>
33 </div>
34 <div id="footer">
35
36 </div>
37 </div>
38
39 <script type="text/javascript">
40 var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
41 document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
42 </script>
43 <script type="text/javascript">
44 try {
45 var pageTracker = _gat._getTracker("UA-4979965-5");
46 pageTracker._trackPageview();
47 } catch(err) {}</script>
48
49 </body>
50 </html>
51
+0
-143
deps/webmachine/www/reqdata.html less more
0 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
1 <html xmlns="http://www.w3.org/1999/xhtml">
2 <head>
3 <meta name="author" content="Basho Technologies" />
4 <meta name="description" content="Webmachine request/response data" />
5 <meta name="keywords" content="webmachine http rest web" />
6 <meta http-equiv="content-type" content="text/html;charset=utf-8" />
7 <link rel="stylesheet" href="css/style-1c.css" type="text/css" />
8 <title>Webmachine request/response data</title>
9 </head>
10 <body>
11 <div id="content">
12 <h1><span class="hr"></span><a href="/">webmachine</a></h1>
13 <ul id="top">
14 <li><a href="/">Home</a></li>
15 <li><a href="http://bitbucket.org/justin/webmachine/">Source Code</a></li>
16 <li><a href="contact.html">Contact</a></li>
17 </ul>
18 <div id="left">
19 <h3>Webmachine request/response data</h3>
20
21 <p>
22
23 This is documentation of the Webmachine Request Data API as embodied
24 by the <code>"wrq"</code> module. This module is the means by which
25 resources access and manipulate the state of the request they are
26 handling.
27
28 </p>
29 <p>
30
31 Given that all webmachine resource functions have this signature:
32
33 </p>
34
35 <span class="fwf">f(ReqData, Context) -> {Result, ReqData, Context}</span>
36
37 <p>
38
39 we should explain in detail the <code>ReqData</code> input and output
40 parameter. This is a data structure used to represent the request
41 sent by the client as well as the response being built by the
42 resource. The <code>wrq</code> module is used to access the values in
43 the input parameter. Most functions in most resources have no need to
44 modify the output <code>ReqData</code> and can simply pass along the
45 one received as input. However, in some cases a resource will need to
46 make some update to the response other than that implied by
47 <code>Result</code> and in those cases it should use the
48 <code>wrq</code> module to build a modified <code>ReqData</code> from
49 the original one for the return value.
50
51 </p>
52 <p>
53
54 A couple of nonstandard types are assumed here:
55
56 </p>
57
58 <table><tr><th>Type</th><th>Description</th></tr>
59 <tr><td>string()</td><td>a list() with all elements in the ASCII range</td></tr>
60 <tr><td>rd()</td><td>opaque record, used as the input/output <code>ReqData</code></td></tr>
61 <tr><td>streambody()</td><td>A webmachine <a href="streambody.html">streamed body format</a></td></tr>
62 <tr><td>mochiheaders()</td><td>a structure used in mochiweb for HTTP header storage</td></tr>
63 </table>
64
65 <p>The accessors are:</p>
66
67 <table><tr><th>Function</th><th>Description</th></tr>
68 <tr><td><code> method(rd()) -&gt; 'DELETE' | 'GET' | 'HEAD' | 'OPTIONS' | 'POST' | 'PUT' | 'TRACE' </code></td><td>The HTTP method used by the client. (note that it is an <code> atom() </code>)</td></tr>
69
70 <tr><td><code> version(rd()) -&gt; {integer(),integer()} </code></td><td>The HTTP version used by the client. Most often <code> {1,1} </code>.</td></tr>
71 <tr><td><code> peer(rd()) -&gt; string() </code></td><td>The IP address of the client.</td></tr>
72 <tr><td><code> disp_path(rd()) -&gt; string() </code></td><td>The "local" path of the resource URI; the part after any prefix used in <a href="dispatcher.html">dispatch configuration</a>. Of the three path accessors, this is the one you usually want. This is also the one that will change after <code>create_path</code> is called in your <a href="resources.html">resource</a>.</td></tr>
73
74 <tr><td><code> path(rd()) -&gt; string() </code></td><td>The path part of the URI -- after the host and port, but not including any query string.</td></tr>
75 <tr><td><code> raw_path(rd()) -&gt; string() </code></td><td>The entire path part of the URI, including any query string present.</td></tr>
76 <tr><td><code> path_info(atom(),rd()) -&gt; 'undefined' | string() </code></td><td>Looks up a binding as described in <a href="dispatcher.html">dispatch configuration</a>.</td></tr>
77
78 <tr><td><code> path_info(rd()) -&gt; any() </code></td><td>The dictionary of bindings as described in <a href="dispatcher.html">dispatch configuration</a>.</td></tr>
79 <tr><td><code> path_tokens(rd()) -&gt; list() </code></td><td>This is a list of <code> string() </code> terms, the disp_path components split by "/".</td></tr>
80
81 <tr><td><code> get_req_header(string(),rd()) -&gt; 'undefined' | string() </code></td><td>Look up the value of an incoming request header.</td></tr>
82 <tr><td><code> req_headers(rd()) -&gt; mochiheaders() </code></td><td>The incoming HTTP headers. Generally, get_req_header is more useful.</td></tr>
83 <tr><td><code> req_body(rd()) -&gt; 'undefined' | binary() </code></td><td>The incoming request body, if any.</td></tr>
84
85 <tr><td><code> stream_req_body(rd(),integer()) -&gt; streambody() </code></td><td>The incoming request body in <a href="streambody.html">streamed</a> form, with hunks no bigger than the integer argument.</td></tr>
86 <tr><td><code> get_cookie_value(string(),rd()) -&gt; string() </code></td><td>Look up the named value in the incoming request cookie header.</td></tr>
87 <tr><td><code> req_cookie(rd()) -&gt; string() </code></td><td>The raw value of the cookie header. Note that get_cookie_value is often more useful.</td></tr>
88
89 <tr><td><code> get_qs_value(string(),rd()) -&gt; 'undefined' | string() </code></td><td>Given the name of a key, look up the corresponding value in the query string.</td></tr>
90 <tr><td><code> get_qs_value(string(),string(),rd()) -&gt; string() </code></td><td>Given the name of a key and a default value if not present, look up the corresponding value in the query string.</td></tr>
91 <tr><td><code> req_qs(rd()) -&gt; [{string(), string()}] </code></td><td>The parsed query string, if any. Note that get_qs_value is often more useful.</td></tr>
92
93 <tr><td><code> get_resp_header(string(),rd()) -&gt; string() </code></td><td>Look up the current value of an outgoing request header.</td></tr>
94 <tr><td><code> resp_redirect(rd()) -&gt; bool() </code></td><td>the last value passed to do_redirect, false otherwise -- if true, then some responses will be 303 instead of 2xx where applicable</td></tr>
95 <tr><td><code> resp_headers(rd()) -&gt; mochiheaders() </code></td><td>The outgoing HTTP headers. Generally, get_resp_header is more useful.</td></tr>
96
97 <tr><td><code> resp_body(rd()) -&gt; 'undefined' | binary() </code></td><td>The outgoing response body, if one has been set. Usually, append_to_response_body is the best way to set this.</td></tr>
98 <tr><td><code> app_root(rd()) -&gt; string() </code></td><td>Indicates the "height" above the requested URI that this resource is dispatched from. Typical values are <code> "." </code>, <code> ".." </code>, <code> "../.." </code> and so on.</td></tr>
99
100 </table>
101
102
103 <p
104 >The functions for (nondestructive) modification of <code> rd() </code> terms are:
105 </p>
106
107
108 <table><tr><th>Function</th><th>Description</th></tr>
109 <tr><td><code> set_resp_header(string(),string(),rd()) -&gt; rd() </code></td><td>Given a header name and value, set an outgoing request header to that value.</td></tr>
110 <tr><td><code> append_to_response_body(binary(),rd()) -&gt; rd() </code></td><td>Append the given value to the body of the outgoing response.</td></tr>
111
112 <tr><td><code> do_redirect(bool(),rd()) -&gt; rd() </code></td><td>see resp_redirect; this sets that value.</td></tr>
113 <tr><td><code> set_disp_path(string(),rd()) -&gt; rd() </code></td><td>The disp_path is the only path that can be changed during a request. This function will do so.</td></tr>
114 <tr><td><code> set_req_body(binary(),rd()) -&gt; rd() </code></td><td>Replace the incoming request body with this for the rest of the processing.</td></tr>
115
116 <tr><td><code> set_resp_body(binary(),rd()) -&gt; rd() </code></td><td>Set the outgoing response body to this value.</td></tr>
117 <tr><td><code> set_resp_body(streambody(),rd()) -&gt; rd() </code></td><td>Use this <a href="streambody.html">streamed body</a> to produce the outgoing response body on demand.</td></tr>
118 <tr><td><code> set_resp_headers([{string(),string()}],rd()) -&gt; rd() </code></td><td>Given a list of two-tuples of {headername,value}, set those outgoing response headers.</td></tr>
119
120 <tr><td><code> remove_resp_header(string(),rd()) -&gt; rd() </code></td><td>Remove the named outgoing response header.</td></tr>
121 </table>
122
123
124 </div>
125 <div id="footer">
126
127 </div>
128 </div>
129
130 <script type="text/javascript">
131 var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
132 document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
133 </script>
134 <script type="text/javascript">
135 try {
136 var pageTracker = _gat._getTracker("UA-4979965-5");
137 pageTracker._trackPageview();
138 } catch(err) {}</script>
139
140 </body>
141 </html>
142
+0
-142
deps/webmachine/www/resources.html less more
0 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
1 <html xmlns="http://www.w3.org/1999/xhtml">
2 <head>
3 <meta name="author" content="Basho Technologies" />
4 <meta name="description" content="Webmachine resource functions" />
5 <meta name="keywords" content="webmachine http rest web" />
6 <meta http-equiv="content-type" content="text/html;charset=utf-8" />
7 <link rel="stylesheet" href="css/style-1c.css" type="text/css" />
8 <title>Webmachine resource functions</title>
9 </head>
10 <body>
11 <div id="content">
12 <h1><span class="hr"></span><a href="/">webmachine</a></h1>
13 <ul id="top">
14 <li><a href="/">Home</a></li>
15 <li><a href="http://bitbucket.org/justin/webmachine/">Source Code</a></li>
16 <li><a href="contact.html">Contact</a></li>
17 </ul>
18 <div id="left">
19 <h3>Webmachine resource functions</h3>
20
21 <p>All webmachine resources should include the webmachine resource library:</p>
22
23 <p>
24 <span class="fwf">
25 -include_lib("webmachine/include/webmachine.hrl").
26 </span>
27 </p>
28
29 <p>
30 All webmachine resources should define and export <span class="fwf">init/1</span>, which will receive a configuration property list from the <a href="dispatcher.html">dispatcher</a> as its argument. This function should, if successful, return <span class="fwf">{ok, Context}</span>. Context is any term, and will be threaded through all of the other webmachine resource functions. Alternately, the resource can go into debugging mode by returning <span class="fwf">{{trace, Dir}, Context}</span> instead -- see <a href="debugging.html">the tracing documentation</a> for more information.</p>
31
32 <p>All webmachine resource functions are of the signature:</p>
33
34 <p>
35 <span class="fwf">
36 f(ReqData, Context) -> {Result, ReqData, Context}
37 </span>
38 </p>
39
40
41 <p>
42 <span class="fwf">Context</span> is an arbitrary <span class="fwf">term()</span> that is specific to your application. Webmachine will never do anything with this term other than threading it through the various functions of your resource. This is the means by which transient application-specific request state is passed along between functions.
43 </p>
44 <p>
45 <span class="fwf">ReqData</span> is a <span class="fwf">#wm_reqdata{}</span> term, and is manipulated via the <a href="reqdata.html">wrq</a> interface. A resource function may access request data (such as header values) from the input value. If a resource function wishes to affect the response data in some way other than that implied by its return value (e.g. adding an X-Header) then it should modify the returned <span class="fwf">ReqData</span> term accordingly.
46 </p>
47 <p>
48 The rest of this document is about the effects produced by different values in the <span class="fwf">Result</span> term.
49 </p>
50 <p>
51 There are over 30 resource functions you can define, but any of them can be omitted as they have reasonable defaults.
52 </p>
53 <p>
54 Each function is described below, showing the default and allowed values that may be in the <span class="fwf">Result</span> term. The default will be used if a resource does not export the function. If a function has an "X" in the "Halt" column, it also has the option of returning either of the two following special values for <span class="fwf">Result</span>:
55
56 </p>
57
58 <table><tr><th>Result</th><th>Effect</th></tr>
59 <tr><td class="fwf lhcol">{error, Err::term()}</td><td>Immediately end processing of this request, returning a 500 response code. The response body will contain the <span class="fwf"> Err </span> term.</td></tr>
60 <tr><td class="fwf lhcol">{halt, Code::integer()}</td><td>Immediately end processing of this request, returning response code Code. It is the responsibility of the resource to ensure that all necessary response header and body elements are filled in <span class="fwf"> ReqData </span> in order to make that reponse code valid.</td></tr>
61 <tr><td class="fwf lhcol">{halt, {Code::integer(), ReasonPhrase::iolist()}}</td><td>Same as <span class="fwf"> {halt, Code::integer()} </span> but supply a custom reason phrase for the HTTP status code as well.</td></tr>
62 </table>
63
64 <p>
65 Any function which has no description is optional and the effect of its return value should be evident from examining the <a href="diagram.html">diagram</a>.
66 </p>
67
68 <table>
69 <tr><th>Function</th><th>Default</th><th>Halt</th><th>Allowed</th><th>Description</th></tr>
70
71 <tr><td class="fwf">resource_exists</td><td class="fwf">true</td><td align='center' class='x_check'>X</td><td class="fwf">true <span class="fwf">|</span> false</td><td>Returning non-true values will result in 404 Not Found.</td></tr>
72
73 <tr><td class="fwf">service_available</td><td class="fwf">true</td><td align='center' class='x_check'>X</td><td class="fwf">true <span class="fwf">|</span> false</td><td></td></tr>
74 <tr><td class="fwf">is_authorized</td><td class="fwf">true</td><td align='center' class='x_check'>X</td><td class="fwf">true <span class="fwf">|</span> AuthHead</td><td>If this returns anything other than true, the response will be 401 Unauthorized. The AuthHead return value will be used as the value in the WWW-Authenticate header.</td></tr>
75
76 <tr><td class="fwf">forbidden</td><td class="fwf">false</td><td align='center' class='x_check'>X</td><td class="fwf">true <span class="fwf">|</span> false</td><td></td></tr>
77 <tr><td class="fwf">allow_missing_post</td><td class="fwf">false</td><td align='center' class='x_check'>X</td><td class="fwf">true <span class="fwf">|</span> false</td><td>If the resource accepts POST requests to nonexistent resources, then this should return true.</td></tr>
78
79 <tr><td class="fwf">malformed_request</td><td class="fwf">false</td><td align='center' class='x_check'>X</td><td class="fwf">true <span class="fwf">|</span> false</td><td></td></tr>
80 <tr><td class="fwf">uri_too_long</td><td class="fwf">false</td><td align='center' class='x_check'>X</td><td class="fwf">true <span class="fwf">|</span> false</td><td></td></tr>
81 <tr><td class="fwf">known_content_type</td><td class="fwf">true</td><td align='center' class='x_check'>X</td><td class="fwf">true <span class="fwf">|</span> false</td><td></td></tr>
82
83 <tr><td class="fwf">valid_content_headers</td><td class="fwf">true</td><td align='center' class='x_check'>X</td><td class="fwf">true <span class="fwf">|</span> false</td><td></td></tr>
84 <tr><td class="fwf">valid_entity_length</td><td class="fwf">true</td><td align='center' class='x_check'>X</td><td class="fwf">true <span class="fwf">|</span> false</td><td></td></tr>
85 <tr><td class="fwf">options</td><td class="fwf">[]</td><td></td><td class="fwf">[Header]</td><td>If the OPTIONS method is supported and is used, the return value of this function is expected to be a list of pairs representing header names and values that should appear in the response.</td></tr>
86
87 <tr><td class="fwf">allowed_methods</td><td class="fwf">['GET', 'HEAD']</td><td></td><td class="fwf">[Method]</td><td>If a Method not in this list is requested, then a 405 Method Not Allowed will be sent. Note that these are all-caps and are atoms. (single-quoted)</td></tr>
88 <tr><td class="fwf">delete_resource</td><td class="fwf">false</td><td align='center' class='x_check'>X</td><td class="fwf">true <span class="fwf">|</span> false</td><td>This is called when a DELETE request should be enacted, and should return true if the deletion succeeded.</td></tr>
89 <tr><td class="fwf">delete_completed</td><td class="fwf">true</td><td align='center' class='x_check'>X</td><td class="fwf">true <span class="fwf">|</span> false</td><td>This is only called after a successful delete_resource call, and should return false if the deletion was accepted but cannot yet be guaranteed to have finished.</td></tr>
90
91 <tr><td class="fwf">post_is_create</td><td class="fwf">false</td><td></td><td class="fwf">true <span class="fwf">|</span> false</td><td>If POST requests should be treated as a request to put content into a (potentially new) resource as opposed to being a generic submission for processing, then this function should return true. If it does return true, then create_path will be called and the rest of the request will be treated much like a PUT to the Path entry returned by that call.</td></tr>
92 <tr><td class="fwf">create_path</td><td class="fwf">undefined</td><td></td><td class="fwf">Path</td><td>This will be called on a POST request if post_is_create returns true. It is an error for this function to not produce a Path if post_is_create returns true. The Path returned should be a valid URI part following the dispatcher prefix. That Path will replace the previous one in the return value of <span class="fwf"> wrq:disp_path(ReqData) </span> for all subsequent resource function calls in the course of this request.</td></tr>
93
94 <tr><td class="fwf">process_post</td><td class="fwf">false</td><td align='center' class='x_check'>X</td><td class="fwf">true <span class="fwf">|</span> false</td><td>If post_is_create returns false, then this will be called to process any POST requests. If it succeeds, it should return true.</td></tr>
95 <tr><td class="fwf">content_types_provided</td><td><span class="fwf"> [{"text/html", to_html}] </span></td><td></td><td><span class="fwf"> [{Mediatype, Handler}] </span></td><td>This should return a list of pairs where each pair is of the form <span class="fwf"> {Mediatype, Handler} </span> where <span class="fwf">Mediatype</span> is a string of content-type format and the <span class="fwf">Handler</span> is an atom naming the function which can provide a resource representation in that media type. Content negotiation is driven by this return value. For example, if a client request includes an Accept header with a value that does not appear as a first element in any of the return tuples, then a 406 Not Acceptable will be sent.</td></tr>
96
97 <tr><td class="fwf">content_types_accepted</td><td><span class="fwf"> [] </span></td><td></td><td><span class="fwf"> [{Mediatype, Handler}] </span></td><td>This is used similarly to content_types_provided, except that it is for incoming resource representations -- for example, PUT requests. Handler functions usually want to use <span class="fwf"> wrq:req_body(ReqData) </span> to access the incoming request body.</td></tr>
98 <tr><td class="fwf">charsets_provided</td><td class="fwf">no_charset</td><td></td><td>no_charset <span class="fwf">|</span> <span class="fwf"> [{Charset, CharsetConverter}] </span></td><td>If this is anything other than the atom no_charset, it must be a list of pairs where each pair is of the form Charset, Converter where Charset is a string naming a charset and Converter is a callable function in the resource which will be called on the produced body in a GET and ensure that it is in Charset.</td></tr>
99
100 <tr><td class="fwf">encodings_provided</td><td><span class="fwf"> [{"identity", fun(X) -&gt; X end}] </span></td><td></td><td><span class="fwf"> [{Encoding, Encoder}] </span></td><td>This must be a list of pairs where in each pair Encoding is a string naming a valid content encoding and Encoder is a callable function in the resource which will be called on the produced body in a GET and ensure that it is so encoded. One useful setting is to have the function check on method, and on GET requests return <span class="fwf"> [{"identity", fun(X) -&gt; X end}, {"gzip", fun(X) -&gt; zlib:gzip(X) end}] </span> as this is all that is needed to support gzip content encoding.</td></tr>
101
102 <tr><td class="fwf">variances</td><td><span class="fwf"> [] </span></td><td></td><td><span class="fwf"> [HeaderName] </span></td><td>If this function is implemented, it should return a list of strings with header names that should be included in a given response's Vary header. The standard conneg headers (Accept, Accept-Encoding, Accept-Charset, Accept-Language) do not need to be specified here as Webmachine will add the correct elements of those automatically depending on resource behavior.</td></tr>
103 <tr><td class="fwf">is_conflict</td><td class="fwf">false</td><td></td><td class="fwf">true <span class="fwf">|</span> false</td><td>If this returns true, the client will receive a 409 Conflict.</td></tr>
104 <tr><td class="fwf">multiple_choices</td><td class="fwf">false</td><td align='center' class='x_check'>X</td><td class="fwf">true <span class="fwf">|</span> false</td><td>If this returns true, then it is assumed that multiple representations of the response are possible and a single one cannot be automatically chosen, so a 300 Multiple Choices will be sent instead of a 200.</td></tr>
105
106 <tr><td class="fwf">previously_existed</td><td class="fwf">false</td><td align='center' class='x_check'>X</td><td class="fwf">true <span class="fwf">|</span> false</td><td></td></tr>
107 <tr><td class="fwf">moved_permanently</td><td class="fwf">false</td><td align='center' class='x_check'>X</td><td class="fwf"><span class="fwf"> {true, MovedURI} </span> <span class="fwf">|</span> false</td><td></td></tr>
108
109 <tr><td class="fwf">moved_temporarily</td><td class="fwf">false</td><td align='center' class='x_check'>X</td><td class="fwf"><span class="fwf"> {true, MovedURI} </span> <span class="fwf">|</span> false</td><td></td></tr>
110 <tr><td class="fwf">last_modified</td><td class="fwf">undefined</td><td></td><td class="fwf">undefined <span class="fwf">|</span> <span class="fwf"> {{YYYY,MM,DD}, {Hour,Min,Sec}} </span></td><td></td></tr>
111
112 <tr><td class="fwf">expires</td><td class="fwf">undefined</td><td></td><td class="fwf">undefined <span class="fwf">|</span> <span class="fwf"> {{YYYY,MM,DD}, {Hour,Min,Sec}} </span></td><td></td></tr>
113 <tr><td class="fwf">generate_etag</td><td class="fwf">undefined</td><td></td><td class="fwf">undefined <span class="fwf">|</span> ETag</td><td>If this returns a value, it will be used as the value of the ETag header and for comparison in conditional requests.</td></tr>
114 <tr><td class="fwf">finish_request</td><td class="fwf">true</td><td></td><td class="fwf">true <span class="fwf">|</span> false</td><td>This function, if exported, is called just before the final response is constructed and sent. The <span class="fwf"> Result </span> is ignored, so any effect of this function must be by returning a modified <span class="fwf"> ReqData </span>.</td></tr>
115
116 <tr><td class="fwf">body-producing function named as a Handler by content_types_provided</td><td></td><td align='center' class='x_check'>X</td><td class="fwf"><span class="fwf"> Body </span></td><td>The Body should be either an <span class="fwf">iolist()</span> or <a href="streambody.html">{stream,streambody()}</a></td></tr>
117 <tr><td class="fwf">POST-processing function named as a Handler by content_types_accepted</td><td></td><td align='center' class='x_check'>X</td><td class="fwf"><span class="fwf"> true </span></td><td></td></tr>
118 </table>
119
120 <p>
121 The above are all of the supported predefined resource functions. In addition to whichever of these a resource wishes to use, it also must export all of the functions named in the return values of the content_types_provided and content_types_accepted functions with behavior as described in the bottom two rows of the table.
122 </p>
123 </div>
124 <div id="footer">
125
126 </div>
127 </div>
128
129 <script type="text/javascript">
130 var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
131 document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
132 </script>
133 <script type="text/javascript">
134 try {
135 var pageTracker = _gat._getTracker("UA-4979965-5");
136 pageTracker._trackPageview();
137 } catch(err) {}</script>
138
139 </body>
140 </html>
141
+0
-176
deps/webmachine/www/streambody.html less more
0 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
1 <html xmlns="http://www.w3.org/1999/xhtml">
2 <head>
3 <meta name="author" content="Basho Technologies" />
4 <meta name="description" content="Webmachine streamed bodies" />
5 <meta name="keywords" content="webmachine http rest web" />
6 <meta http-equiv="content-type" content="text/html;charset=utf-8" />
7 <link rel="stylesheet" href="css/style-1c.css" type="text/css" />
8 <title>Webmachine streamed bodies</title>
9 </head>
10 <body>
11 <div id="content">
12 <h1><span class="hr"></span><a href="/">webmachine</a></h1>
13 <ul id="top">
14 <li><a href="/">Home</a></li>
15 <li><a href="http://bitbucket.org/justin/webmachine/">Source Code</a></li>
16 <li><a href="contact.html">Contact</a></li>
17 </ul>
18 <div id="left">
19 <h3>Webmachine streamed bodies</h3>
20
21 <p>
22
23 Webmachine allows the resource developer to handle request and
24 response bodies as either whole units (binary or iolist) to be handed
25 around at once, or else to choose to "stream" the body.
26
27 </p>
28 <p>
29
30 The body-handling functions are:
31
32 </p>
33
34 <ul><li><code> wrq:req_body/1 </code>
35 </li><li><code> wrq:stream_req_body/2 </code>
36 </li><li><code> wrq:set_resp_body/2 </code>
37 </li></ul>
38
39 <p>
40
41 The last of these, <code>wrq:set_resp_body/2</code>, is also called
42 implicitly with the return value of any content-producing function
43 such as <code>to_html/2</code>.
44
45 </p>
46 <p>
47
48 The first of these (<code>req_body</code>) is the simplest. It will
49 provide the whole incoming request body as a binary. (Unless the body
50 is too large, as set by <code>wrq:set_max_recv_body/2</code> or
51 defaulting to 50M) For the majority of resources, this is the easiest
52 way to handle the incoming request body.
53
54 </p>
55 <p>
56
57 If a resource wants to handle the incoming request body a hunk at a
58 time, it may call <code>wrq:stream_req_body/2</code> instead. Instead
59 of a binary, this produces a <code>StreamBody</code> structure.
60
61 </p>
62 <p>
63
64 (It is an error to call both <code>wrq:req_body/1</code> and
65 <code>wrq:stream_req_body/2</code> in the execution of a single
66 resource.)
67
68 </p>
69 <p>
70
71 A <code>StreamBody</code> is a pair of the form
72 <code>{Data,Next}</code> where <code>Data</code> is a binary and
73 <code>Next</code> is either the atom <code>done</code> signifying the
74 end of the body or else a 0-arity function that, when called, will
75 produce the "next" <code>StreamBody</code> structure.
76
77 </p>
78 <p>
79
80 The integer parameter to <code>wrq:stream_req_body/2</code> indicates
81 the maximum size in bytes of any <code>Hunk</code> from the resulting
82 <code>StreamBody</code>.
83
84 </p>
85 <p>
86
87 When a resource provides a body to be sent in the response, it should
88 use <code>wrq:set_resp_body/2</code>. The parameter to this function
89 may be either an iolist, representing the entire body, or else a pair
90 of the form <code>{stream, StreamBody}</code>.
91
92 </p>
93 <p>
94
95 An example may make the usage of this API clearer. A complete and
96 working resource module using this API in both directions:
97
98 </p>
99 <p>
100
101 <pre>
102 -module(mywebdemo_resource).
103 -export([init/1, allowed_methods/2, process_post/2]).
104
105 -include_lib("webmachine/include/webmachine.hrl").
106
107 init([]) -> {ok, undefined}.
108
109 allowed_methods(ReqData, State) -> {['POST'], ReqData, State}.
110
111 process_post(ReqData, State) ->
112 Body = get_streamed_body(wrq:stream_req_body(ReqData, 3), []),
113 {true, wrq:set_resp_body({stream, send_streamed_body(Body,4)},ReqData), State}.
114
115 send_streamed_body(Body, Max) ->
116 HunkLen=8*Max,
117 case Body of
118 <<A:HunkLen,Rest/binary>> ->
119 io:format("SENT ~p~n",[<<A:HunkLen>>]),
120 {<<A:HunkLen>>, fun() -> send_streamed_body(Rest,Max) end};
121 _ ->
122 io:format("SENT ~p~n",[Body]),
123 {Body, done}
124 end.
125
126 get_streamed_body({Hunk,done},Acc) ->
127 io:format("RECEIVED ~p~n",[Hunk]),
128 iolist_to_binary(lists:reverse([Hunk|Acc]));
129 get_streamed_body({Hunk,Next},Acc) ->
130 io:format("RECEIVED ~p~n",[Hunk]),
131 get_streamed_body(Next(),[Hunk|Acc]).
132 </pre>
133
134 </p>
135 <p>
136
137 If you use this resource in place of the file
138 <code>/tmp/mywebdemo/src/mywebdemo_resource.erl</code> in the
139 <a href="quickstart.html">quickstart</a> setup, you should then be able
140 to issue <code>curl -d '1234567890' http://127.0.0.1:8000/</code> on
141 the command line and the <code>io:format</code> calls will show you
142 what is going on.
143
144 </p>
145 <p>
146
147 Obviously, a realistic resource wouldn't use this API just to collect
148 the whole binary into memory or break one up that is already present
149 -- you'd use <code>req_body</code> and put a simple iolist into
150 <code>set_resp_body</code> instead. Also, the choices of 3 and 4
151 bytes as hunk size are far from optimal for most reasonable uses.
152 This resource is intended only as a demonstration of the API, not as a
153 real-world use of streaming request/response bodies.
154
155 </p>
156
157 </div>
158 <div id="footer">
159
160 </div>
161 </div>
162
163 <script type="text/javascript">
164 var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
165 document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
166 </script>
167 <script type="text/javascript">
168 try {
169 var pageTracker = _gat._getTracker("UA-4979965-5");
170 pageTracker._trackPageview();
171 } catch(err) {}</script>
172
173 </body>
174 </html>
175
0 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
0 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
11 #
22 # Permission to use, copy, modify, and/or distribute this software for any
33 # purpose with or without fee is hereby granted, provided that the above
1111 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1212 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1313
14 .PHONY: all app apps deps search rel docs install-docs check tests clean distclean help erlang-mk
14 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
1515
1616 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
17
18 ERLANG_MK_VERSION = 2.0.0-pre.2-144-g647ffd1
17 export ERLANG_MK_FILENAME
18
19 ERLANG_MK_VERSION = 2.0.0-pre.2-256-g2cce185
20 ERLANG_MK_WITHOUT =
21
22 # Make 3.81 and 3.82 are deprecated.
23
24 ifeq ($(MAKE_VERSION),3.81)
25 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
26 endif
27
28 ifeq ($(MAKE_VERSION),3.82)
29 $(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
30 endif
1931
2032 # Core configuration.
2133
2436
2537 PROJECT_VERSION ?= rolling
2638 PROJECT_MOD ?= $(PROJECT)_app
39 PROJECT_ENV ?= []
2740
2841 # Verbosity.
2942
8497 rel::
8598 $(verbose) :
8699
100 relup:: deps app
101
87102 check:: tests
88103
89104 clean:: clean-crashdump
101116 help::
102117 $(verbose) printf "%s\n" \
103118 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
104 "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
119 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
105120 "" \
106121 "Usage: [V=1] $(MAKE) [target]..." \
107122 "" \
149164 core_native_path = $1
150165 endif
151166
152 ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
153 define core_http_get
154 wget --no-check-certificate -O $(1) $(2)|| rm $(1)
155 endef
156 else
157 define core_http_get.erl
158 ssl:start(),
159 inets:start(),
160 case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
161 {ok, {{_, 200, _}, _, Body}} ->
162 case file:write_file("$(1)", Body) of
163 ok -> ok;
164 {error, R1} -> halt(R1)
165 end;
166 {error, R2} ->
167 halt(R2)
168 end,
169 halt(0).
170 endef
171
172 define core_http_get
173 $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
174 endef
175 endif
167 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
176168
177169 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
178170
192184 ERLANG_MK_BUILD_CONFIG ?= build.config
193185 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
194186
187 erlang-mk: WITHOUT ?= $(ERLANG_MK_WITHOUT)
195188 erlang-mk:
196189 git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
197190 ifdef ERLANG_MK_COMMIT
198191 cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
199192 endif
200193 if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
201 $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
194 $(MAKE) -C $(ERLANG_MK_BUILD_DIR) WITHOUT='$(strip $(WITHOUT))'
202195 cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
203196 rm -rf $(ERLANG_MK_BUILD_DIR)
204197
285278 pkg_apns_repo = https://github.com/inaka/apns4erl
286279 pkg_apns_commit = master
287280
281 PACKAGES += asciideck
282 pkg_asciideck_name = asciideck
283 pkg_asciideck_description = Asciidoc for Erlang.
284 pkg_asciideck_homepage = https://ninenines.eu
285 pkg_asciideck_fetch = git
286 pkg_asciideck_repo = https://github.com/ninenines/asciideck
287 pkg_asciideck_commit = master
288
288289 PACKAGES += azdht
289290 pkg_azdht_name = azdht
290291 pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
405406 pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
406407 pkg_bootstrap_commit = master
407408
409 PACKAGES += boss
410 pkg_boss_name = boss
411 pkg_boss_description = Erlang web MVC, now featuring Comet
412 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
413 pkg_boss_fetch = git
414 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
415 pkg_boss_commit = master
416
408417 PACKAGES += boss_db
409418 pkg_boss_db_name = boss_db
410419 pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
413422 pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
414423 pkg_boss_db_commit = master
415424
416 PACKAGES += boss
417 pkg_boss_name = boss
418 pkg_boss_description = Erlang web MVC, now featuring Comet
419 pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
420 pkg_boss_fetch = git
421 pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
422 pkg_boss_commit = master
423
424425 PACKAGES += brod
425426 pkg_brod_name = brod
426427 pkg_brod_description = Kafka client in Erlang
565566 pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
566567 pkg_cloudi_service_api_requests_commit = master
567568
569 PACKAGES += cloudi_service_db
570 pkg_cloudi_service_db_name = cloudi_service_db
571 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
572 pkg_cloudi_service_db_homepage = http://cloudi.org/
573 pkg_cloudi_service_db_fetch = git
574 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
575 pkg_cloudi_service_db_commit = master
576
577 PACKAGES += cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
579 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
580 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
581 pkg_cloudi_service_db_cassandra_fetch = git
582 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
583 pkg_cloudi_service_db_cassandra_commit = master
584
568585 PACKAGES += cloudi_service_db_cassandra_cql
569586 pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
570587 pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
573590 pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
574591 pkg_cloudi_service_db_cassandra_cql_commit = master
575592
576 PACKAGES += cloudi_service_db_cassandra
577 pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
578 pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
579 pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
580 pkg_cloudi_service_db_cassandra_fetch = git
581 pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
582 pkg_cloudi_service_db_cassandra_commit = master
583
584593 PACKAGES += cloudi_service_db_couchdb
585594 pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
586595 pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
637646 pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
638647 pkg_cloudi_service_db_tokyotyrant_commit = master
639648
640 PACKAGES += cloudi_service_db
641 pkg_cloudi_service_db_name = cloudi_service_db
642 pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
643 pkg_cloudi_service_db_homepage = http://cloudi.org/
644 pkg_cloudi_service_db_fetch = git
645 pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
646 pkg_cloudi_service_db_commit = master
647
648649 PACKAGES += cloudi_service_filesystem
649650 pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
650651 pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
10371038 pkg_edown_repo = https://github.com/uwiger/edown
10381039 pkg_edown_commit = master
10391040
1041 PACKAGES += eep
1042 pkg_eep_name = eep
1043 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1044 pkg_eep_homepage = https://github.com/virtan/eep
1045 pkg_eep_fetch = git
1046 pkg_eep_repo = https://github.com/virtan/eep
1047 pkg_eep_commit = master
1048
10401049 PACKAGES += eep_app
10411050 pkg_eep_app_name = eep_app
10421051 pkg_eep_app_description = Embedded Event Processing
10451054 pkg_eep_app_repo = https://github.com/darach/eep-erl
10461055 pkg_eep_app_commit = master
10471056
1048 PACKAGES += eep
1049 pkg_eep_name = eep
1050 pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
1051 pkg_eep_homepage = https://github.com/virtan/eep
1052 pkg_eep_fetch = git
1053 pkg_eep_repo = https://github.com/virtan/eep
1054 pkg_eep_commit = master
1055
10561057 PACKAGES += efene
10571058 pkg_efene_name = efene
10581059 pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
12371238 pkg_eqm_repo = https://github.com/loucash/eqm
12381239 pkg_eqm_commit = master
12391240
1241 PACKAGES += eredis
1242 pkg_eredis_name = eredis
1243 pkg_eredis_description = Erlang Redis client
1244 pkg_eredis_homepage = https://github.com/wooga/eredis
1245 pkg_eredis_fetch = git
1246 pkg_eredis_repo = https://github.com/wooga/eredis
1247 pkg_eredis_commit = master
1248
12401249 PACKAGES += eredis_pool
12411250 pkg_eredis_pool_name = eredis_pool
12421251 pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
12451254 pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
12461255 pkg_eredis_pool_commit = master
12471256
1248 PACKAGES += eredis
1249 pkg_eredis_name = eredis
1250 pkg_eredis_description = Erlang Redis client
1251 pkg_eredis_homepage = https://github.com/wooga/eredis
1252 pkg_eredis_fetch = git
1253 pkg_eredis_repo = https://github.com/wooga/eredis
1254 pkg_eredis_commit = master
1255
12561257 PACKAGES += erl_streams
12571258 pkg_erl_streams_name = erl_streams
12581259 pkg_erl_streams_description = Streams in Erlang
15411542 pkg_etap_repo = https://github.com/ngerakines/etap
15421543 pkg_etap_commit = master
15431544
1545 PACKAGES += etest
1546 pkg_etest_name = etest
1547 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1548 pkg_etest_homepage = https://github.com/wooga/etest
1549 pkg_etest_fetch = git
1550 pkg_etest_repo = https://github.com/wooga/etest
1551 pkg_etest_commit = master
1552
15441553 PACKAGES += etest_http
15451554 pkg_etest_http_name = etest_http
15461555 pkg_etest_http_description = etest Assertions around HTTP (client-side)
15491558 pkg_etest_http_repo = https://github.com/wooga/etest_http
15501559 pkg_etest_http_commit = master
15511560
1552 PACKAGES += etest
1553 pkg_etest_name = etest
1554 pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
1555 pkg_etest_homepage = https://github.com/wooga/etest
1556 pkg_etest_fetch = git
1557 pkg_etest_repo = https://github.com/wooga/etest
1558 pkg_etest_commit = master
1559
15601561 PACKAGES += etoml
15611562 pkg_etoml_name = etoml
15621563 pkg_etoml_description = TOML language erlang parser
15651566 pkg_etoml_repo = https://github.com/kalta/etoml
15661567 pkg_etoml_commit = master
15671568
1569 PACKAGES += eunit
1570 pkg_eunit_name = eunit
1571 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1572 pkg_eunit_homepage = https://github.com/richcarl/eunit
1573 pkg_eunit_fetch = git
1574 pkg_eunit_repo = https://github.com/richcarl/eunit
1575 pkg_eunit_commit = master
1576
15681577 PACKAGES += eunit_formatters
15691578 pkg_eunit_formatters_name = eunit_formatters
15701579 pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
15731582 pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
15741583 pkg_eunit_formatters_commit = master
15751584
1576 PACKAGES += eunit
1577 pkg_eunit_name = eunit
1578 pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
1579 pkg_eunit_homepage = https://github.com/richcarl/eunit
1580 pkg_eunit_fetch = git
1581 pkg_eunit_repo = https://github.com/richcarl/eunit
1582 pkg_eunit_commit = master
1583
15841585 PACKAGES += euthanasia
15851586 pkg_euthanasia_name = euthanasia
15861587 pkg_euthanasia_description = Merciful killer for your Erlang processes
15981599 pkg_evum_commit = master
15991600
16001601 PACKAGES += exec
1601 pkg_exec_name = exec
1602 pkg_exec_name = erlexec
16021603 pkg_exec_description = Execute and control OS processes from Erlang/OTP.
16031604 pkg_exec_homepage = http://saleyn.github.com/erlexec
16041605 pkg_exec_fetch = git
17171718 pkg_fn_repo = https://github.com/reiddraper/fn
17181719 pkg_fn_commit = master
17191720
1721 PACKAGES += folsom
1722 pkg_folsom_name = folsom
1723 pkg_folsom_description = Expose Erlang Events and Metrics
1724 pkg_folsom_homepage = https://github.com/boundary/folsom
1725 pkg_folsom_fetch = git
1726 pkg_folsom_repo = https://github.com/boundary/folsom
1727 pkg_folsom_commit = master
1728
17201729 PACKAGES += folsom_cowboy
17211730 pkg_folsom_cowboy_name = folsom_cowboy
17221731 pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
17251734 pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
17261735 pkg_folsom_cowboy_commit = master
17271736
1728 PACKAGES += folsom
1729 pkg_folsom_name = folsom
1730 pkg_folsom_description = Expose Erlang Events and Metrics
1731 pkg_folsom_homepage = https://github.com/boundary/folsom
1732 pkg_folsom_fetch = git
1733 pkg_folsom_repo = https://github.com/boundary/folsom
1734 pkg_folsom_commit = master
1735
17361737 PACKAGES += folsomite
17371738 pkg_folsomite_name = folsomite
17381739 pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
20932094 pkg_jesse_repo = https://github.com/for-GET/jesse
20942095 pkg_jesse_commit = master
20952096
2097 PACKAGES += jiffy
2098 pkg_jiffy_name = jiffy
2099 pkg_jiffy_description = JSON NIFs for Erlang.
2100 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2101 pkg_jiffy_fetch = git
2102 pkg_jiffy_repo = https://github.com/davisp/jiffy
2103 pkg_jiffy_commit = master
2104
20962105 PACKAGES += jiffy_v
20972106 pkg_jiffy_v_name = jiffy_v
20982107 pkg_jiffy_v_description = JSON validation utility
21012110 pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
21022111 pkg_jiffy_v_commit = master
21032112
2104 PACKAGES += jiffy
2105 pkg_jiffy_name = jiffy
2106 pkg_jiffy_description = JSON NIFs for Erlang.
2107 pkg_jiffy_homepage = https://github.com/davisp/jiffy
2108 pkg_jiffy_fetch = git
2109 pkg_jiffy_repo = https://github.com/davisp/jiffy
2110 pkg_jiffy_commit = master
2111
21122113 PACKAGES += jobs
21132114 pkg_jobs_name = jobs
21142115 pkg_jobs_description = a Job scheduler for load regulation
21252126 pkg_joxa_repo = https://github.com/joxa/joxa
21262127 pkg_joxa_commit = master
21272128
2129 PACKAGES += json
2130 pkg_json_name = json
2131 pkg_json_description = a high level json library for erlang (17.0+)
2132 pkg_json_homepage = https://github.com/talentdeficit/json
2133 pkg_json_fetch = git
2134 pkg_json_repo = https://github.com/talentdeficit/json
2135 pkg_json_commit = master
2136
21282137 PACKAGES += json_rec
21292138 pkg_json_rec_name = json_rec
21302139 pkg_json_rec_description = JSON to erlang record
21332142 pkg_json_rec_repo = https://github.com/justinkirby/json_rec
21342143 pkg_json_rec_commit = master
21352144
2136 PACKAGES += json
2137 pkg_json_name = json
2138 pkg_json_description = a high level json library for erlang (17.0+)
2139 pkg_json_homepage = https://github.com/talentdeficit/json
2140 pkg_json_fetch = git
2141 pkg_json_repo = https://github.com/talentdeficit/json
2142 pkg_json_commit = master
2143
21442145 PACKAGES += jsone
21452146 pkg_jsone_name = jsone
21462147 pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
21812182 pkg_jsx_repo = https://github.com/talentdeficit/jsx
21822183 pkg_jsx_commit = master
21832184
2185 PACKAGES += kafka
2186 pkg_kafka_name = kafka
2187 pkg_kafka_description = Kafka consumer and producer in Erlang
2188 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2189 pkg_kafka_fetch = git
2190 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2191 pkg_kafka_commit = master
2192
21842193 PACKAGES += kafka_protocol
21852194 pkg_kafka_protocol_name = kafka_protocol
21862195 pkg_kafka_protocol_description = Kafka protocol Erlang library
21892198 pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
21902199 pkg_kafka_protocol_commit = master
21912200
2192 PACKAGES += kafka
2193 pkg_kafka_name = kafka
2194 pkg_kafka_description = Kafka consumer and producer in Erlang
2195 pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
2196 pkg_kafka_fetch = git
2197 pkg_kafka_repo = https://github.com/wooga/kafka-erlang
2198 pkg_kafka_commit = master
2199
22002201 PACKAGES += kai
22012202 pkg_kai_name = kai
22022203 pkg_kai_description = DHT storage by Takeshi Inoue
22932294 pkg_kvs_repo = https://github.com/synrc/kvs
22942295 pkg_kvs_commit = master
22952296
2297 PACKAGES += lager
2298 pkg_lager_name = lager
2299 pkg_lager_description = A logging framework for Erlang/OTP.
2300 pkg_lager_homepage = https://github.com/basho/lager
2301 pkg_lager_fetch = git
2302 pkg_lager_repo = https://github.com/basho/lager
2303 pkg_lager_commit = master
2304
22962305 PACKAGES += lager_amqp_backend
22972306 pkg_lager_amqp_backend_name = lager_amqp_backend
22982307 pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
23092318 pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
23102319 pkg_lager_syslog_commit = master
23112320
2312 PACKAGES += lager
2313 pkg_lager_name = lager
2314 pkg_lager_description = A logging framework for Erlang/OTP.
2315 pkg_lager_homepage = https://github.com/basho/lager
2316 pkg_lager_fetch = git
2317 pkg_lager_repo = https://github.com/basho/lager
2318 pkg_lager_commit = master
2319
23202321 PACKAGES += lambdapad
23212322 pkg_lambdapad_name = lambdapad
23222323 pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
25732574 pkg_mixer_repo = https://github.com/chef/mixer
25742575 pkg_mixer_commit = master
25752576
2577 PACKAGES += mochiweb
2578 pkg_mochiweb_name = mochiweb
2579 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2580 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2581 pkg_mochiweb_fetch = git
2582 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2583 pkg_mochiweb_commit = master
2584
25762585 PACKAGES += mochiweb_xpath
25772586 pkg_mochiweb_xpath_name = mochiweb_xpath
25782587 pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
25812590 pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
25822591 pkg_mochiweb_xpath_commit = master
25832592
2584 PACKAGES += mochiweb
2585 pkg_mochiweb_name = mochiweb
2586 pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
2587 pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
2588 pkg_mochiweb_fetch = git
2589 pkg_mochiweb_repo = https://github.com/mochi/mochiweb
2590 pkg_mochiweb_commit = master
2591
25922593 PACKAGES += mockgyver
25932594 pkg_mockgyver_name = mockgyver
25942595 pkg_mockgyver_description = A mocking library for Erlang
30613062 pkg_quickrand_repo = https://github.com/okeuday/quickrand
30623063 pkg_quickrand_commit = master
30633064
3065 PACKAGES += rabbit
3066 pkg_rabbit_name = rabbit
3067 pkg_rabbit_description = RabbitMQ Server
3068 pkg_rabbit_homepage = https://www.rabbitmq.com/
3069 pkg_rabbit_fetch = git
3070 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3071 pkg_rabbit_commit = master
3072
30643073 PACKAGES += rabbit_exchange_type_riak
30653074 pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
30663075 pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
30693078 pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
30703079 pkg_rabbit_exchange_type_riak_commit = master
30713080
3072 PACKAGES += rabbit
3073 pkg_rabbit_name = rabbit
3074 pkg_rabbit_description = RabbitMQ Server
3075 pkg_rabbit_homepage = https://www.rabbitmq.com/
3076 pkg_rabbit_fetch = git
3077 pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
3078 pkg_rabbit_commit = master
3079
30803081 PACKAGES += rack
30813082 pkg_rack_name = rack
30823083 pkg_rack_description = Rack handler for erlang
34933494 pkg_smother_repo = https://github.com/ramsay-t/Smother
34943495 pkg_smother_commit = master
34953496
3497 PACKAGES += snappyer
3498 pkg_snappyer_name = snappyer
3499 pkg_snappyer_description = Snappy as nif for Erlang
3500 pkg_snappyer_homepage = https://github.com/zmstone/snappyer
3501 pkg_snappyer_fetch = git
3502 pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
3503 pkg_snappyer_commit = master
3504
34963505 PACKAGES += social
34973506 pkg_social_name = social
34983507 pkg_social_description = Cowboy handler for social login via OAuth2 providers
35413550 pkg_stable_repo = https://github.com/dvv/stable
35423551 pkg_stable_commit = master
35433552
3553 PACKAGES += statebox
3554 pkg_statebox_name = statebox
3555 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3556 pkg_statebox_homepage = https://github.com/mochi/statebox
3557 pkg_statebox_fetch = git
3558 pkg_statebox_repo = https://github.com/mochi/statebox
3559 pkg_statebox_commit = master
3560
35443561 PACKAGES += statebox_riak
35453562 pkg_statebox_riak_name = statebox_riak
35463563 pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
35493566 pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
35503567 pkg_statebox_riak_commit = master
35513568
3552 PACKAGES += statebox
3553 pkg_statebox_name = statebox
3554 pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
3555 pkg_statebox_homepage = https://github.com/mochi/statebox
3556 pkg_statebox_fetch = git
3557 pkg_statebox_repo = https://github.com/mochi/statebox
3558 pkg_statebox_commit = master
3559
35603569 PACKAGES += statman
35613570 pkg_statman_name = statman
35623571 pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
37253734 pkg_tirerl_repo = https://github.com/inaka/tirerl
37263735 pkg_tirerl_commit = master
37273736
3737 PACKAGES += toml
3738 pkg_toml_name = toml
3739 pkg_toml_description = TOML (0.4.0) config parser
3740 pkg_toml_homepage = http://dozzie.jarowit.net/trac/wiki/TOML
3741 pkg_toml_fetch = git
3742 pkg_toml_repo = https://github.com/dozzie/toml
3743 pkg_toml_commit = v0.2.0
3744
37283745 PACKAGES += traffic_tools
37293746 pkg_traffic_tools_name = traffic_tools
37303747 pkg_traffic_tools_description = Simple traffic limiting library
40614078 pkg_zucchini_repo = https://github.com/devinus/zucchini
40624079 pkg_zucchini_commit = master
40634080
4064 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
40654082 # This file is part of erlang.mk and subject to the terms of the ISC License.
40664083
40674084 .PHONY: search
40884105 $(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
40894106 endif
40904107
4091 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4108 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
40924109 # This file is part of erlang.mk and subject to the terms of the ISC License.
40934110
4094 .PHONY: distclean-deps
4111 .PHONY: distclean-deps clean-tmp-deps.log
40954112
40964113 # Configuration.
40974114
41104127
41114128 REBAR_DEPS_DIR = $(DEPS_DIR)
41124129 export REBAR_DEPS_DIR
4130
4131 # External "early" plugins (see core/plugins.mk for regular plugins).
4132 # They both use the core_dep_plugin macro.
4133
4134 define core_dep_plugin
4135 ifeq ($(2),$(PROJECT))
4136 -include $$(patsubst $(PROJECT)/%,%,$(1))
4137 else
4138 -include $(DEPS_DIR)/$(1)
4139
4140 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4141 endif
4142 endef
4143
4144 DEP_EARLY_PLUGINS ?=
4145
4146 $(foreach p,$(DEP_EARLY_PLUGINS),\
4147 $(eval $(if $(findstring /,$p),\
4148 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4149 $(call core_dep_plugin,$p/early-plugins.mk,$p))))
41134150
41144151 dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1)))
41154152 dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
41164153 $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
41174154 dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit)))
41184155
4156 LOCAL_DEPS_DIRS = $(foreach a,$(LOCAL_DEPS),$(if $(wildcard $(APPS_DIR)/$(a)),$(APPS_DIR)/$(a)))
41194157 ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d)))
41204158 ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep))))
41214159
41384176
41394177 # Core targets.
41404178
4141 ifdef IS_APP
4142 apps::
4143 else
4144 apps:: $(ALL_APPS_DIRS)
4179 apps:: $(ALL_APPS_DIRS) clean-tmp-deps.log
41454180 ifeq ($(IS_APP)$(IS_DEP),)
41464181 $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
41474182 endif
41494184 # Create ebin directory for all apps to make sure Erlang recognizes them
41504185 # as proper OTP applications when using -include_lib. This is a temporary
41514186 # fix, a proper fix would be to compile apps/* in the right order.
4152 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4153 mkdir -p $$dep/ebin || exit $$?; \
4187 ifndef IS_APP
4188 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4189 mkdir -p $$dep/ebin; \
41544190 done
4155 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4191 endif
4192 # at the toplevel: if LOCAL_DEPS is defined with at least one local app, only
4193 # compile that list of apps. otherwise, compile everything.
4194 # within an app: compile all LOCAL_DEPS that are (uncompiled) local apps
4195 $(verbose) set -e; for dep in $(if $(LOCAL_DEPS_DIRS)$(IS_APP),$(LOCAL_DEPS_DIRS),$(ALL_APPS_DIRS)) ; do \
41564196 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
41574197 :; \
41584198 else \
41594199 echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
4160 $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
4200 $(MAKE) -C $$dep IS_APP=1; \
41614201 fi \
41624202 done
4203
4204 clean-tmp-deps.log:
4205 ifeq ($(IS_APP)$(IS_DEP),)
4206 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
41634207 endif
41644208
41654209 ifneq ($(SKIP_DEPS),)
41664210 deps::
41674211 else
4168 deps:: $(ALL_DEPS_DIRS) apps
4169 ifeq ($(IS_APP)$(IS_DEP),)
4170 $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
4171 endif
4212 deps:: $(ALL_DEPS_DIRS) apps clean-tmp-deps.log
41724213 $(verbose) mkdir -p $(ERLANG_MK_TMP)
4173 $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
4214 $(verbose) set -e; for dep in $(ALL_DEPS_DIRS) ; do \
41744215 if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
41754216 :; \
41764217 else \
41774218 echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
41784219 if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
4179 $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
4220 $(MAKE) -C $$dep IS_DEP=1; \
41804221 else \
4181 echo "Error: No Makefile to build dependency $$dep."; \
4222 echo "Error: No Makefile to build dependency $$dep." >&2; \
41824223 exit 2; \
41834224 fi \
41844225 fi \
41924233 # in practice only Makefile is needed so far.
41934234 define dep_autopatch
41944235 if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
4236 rm -rf $(DEPS_DIR)/$1/ebin/; \
41954237 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
41964238 $(call dep_autopatch_erlang_mk,$(1)); \
41974239 elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
4198 if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
4240 if [ -f $(DEPS_DIR)/$1/rebar.lock ]; then \
4241 $(call dep_autopatch2,$1); \
4242 elif [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
41994243 $(call dep_autopatch2,$(1)); \
4200 elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
4244 elif [ 0 != `grep -ci "^[^#].*rebar" $(DEPS_DIR)/$(1)/Makefile` ]; then \
42014245 $(call dep_autopatch2,$(1)); \
4202 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
4246 elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i "^[^#].*rebar" '{}' \;`" ]; then \
42034247 $(call dep_autopatch2,$(1)); \
4204 else \
4205 $(call erlang,$(call dep_autopatch_app.erl,$(1))); \
42064248 fi \
42074249 else \
42084250 if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
42144256 endef
42154257
42164258 define dep_autopatch2
4259 ! test -f $(DEPS_DIR)/$1/ebin/$1.app || \
4260 mv -n $(DEPS_DIR)/$1/ebin/$1.app $(DEPS_DIR)/$1/src/$1.app.src; \
4261 rm -f $(DEPS_DIR)/$1/ebin/$1.app; \
42174262 if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \
42184263 $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \
42194264 fi; \
42204265 $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
4221 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
4266 if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script -o -f $(DEPS_DIR)/$1/rebar.lock ]; then \
42224267 $(call dep_autopatch_fetch_rebar); \
42234268 $(call dep_autopatch_rebar,$(1)); \
42244269 else \
42304275 printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
42314276 endef
42324277
4233 # Overwrite erlang.mk with the current file by default.
4278 # Replace "include erlang.mk" with a line that will load the parent Erlang.mk
4279 # if given. Do it for all 3 possible Makefile file names.
42344280 ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
42354281 define dep_autopatch_erlang_mk
4236 echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \
4237 > $(DEPS_DIR)/$1/erlang.mk
4282 $t for f in Makefile makefile GNUmakefile; do \
4283 if [ -f $(DEPS_DIR)/$1/$$f ]; then \
4284 sed -i.bak s/'include *erlang.mk'/'include $$(if $$(ERLANG_MK_FILENAME),$$(ERLANG_MK_FILENAME),erlang.mk)'/ $(DEPS_DIR)/$1/$$f; \
4285 fi \
4286 done
42384287 endef
42394288 else
42404289 define dep_autopatch_erlang_mk
42534302 if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
42544303 git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
42554304 cd $(ERLANG_MK_TMP)/rebar; \
4256 git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
4305 git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
42574306 $(MAKE); \
42584307 cd -; \
42594308 fi
42704319 define dep_autopatch_rebar.erl
42714320 application:load(rebar),
42724321 application:set_env(rebar, log_level, debug),
4322 rmemo:start(),
42734323 Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
42744324 {ok, Conf0} -> Conf0;
42754325 _ -> []
44234473 [] -> ok;
44244474 _ ->
44254475 Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
4426 PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
4476 PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
44274477 [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
4428 PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L \\"~s\\" -lerl_interface -lei\n",
4478 PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
44294479 [code:lib_dir(erl_interface, lib)])),
44304480 [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
44314481 FilterEnv = fun(Env) ->
44644514 "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44654515 "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
44664516 "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
4467 [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
4517 [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
44684518 Output, ": $$\(foreach ext,.c .C .cc .cpp,",
44694519 "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
44704520 "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
44764526 end,
44774527 [PortSpec(S) || S <- PortSpecs]
44784528 end,
4479 Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"),
4529 Write("\ninclude $$\(if $$\(ERLANG_MK_FILENAME),$$\(ERLANG_MK_FILENAME),erlang.mk)"),
44804530 RunPlugin = fun(Plugin, Step) ->
44814531 case erlang:function_exported(Plugin, Step, 2) of
44824532 false -> ok;
45244574 halt()
45254575 endef
45264576
4527 define dep_autopatch_app.erl
4528 UpdateModules = fun(App) ->
4529 case filelib:is_regular(App) of
4530 false -> ok;
4531 true ->
4532 {ok, [{application, '$(1)', L0}]} = file:consult(App),
4533 Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true,
4534 fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
4535 L = lists:keystore(modules, 1, L0, {modules, Mods}),
4536 ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}]))
4537 end
4538 end,
4539 UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"),
4540 halt()
4541 endef
4542
45434577 define dep_autopatch_appsrc_script.erl
45444578 AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)",
45454579 AppSrcScript = AppSrc ++ ".script",
45874621 cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
45884622 endef
45894623
4590 define dep_fetch_hex.erl
4591 ssl:start(),
4592 inets:start(),
4593 {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
4594 {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
4595 [], [{body_format, binary}]),
4596 {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
4597 {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
4598 ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
4599 halt()
4624 define dep_fetch_ln
4625 ln -s $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
46004626 endef
46014627
46024628 # Hex only has a package version. No need to look in the Erlang.mk packages.
46034629 define dep_fetch_hex
4604 $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
4630 mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
4631 $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
4632 https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
4633 tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
46054634 endef
46064635
46074636 define dep_fetch_fail
46314660 $(eval DEP_NAME := $(call dep_name,$1))
46324661 $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))"))
46334662 $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \
4634 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \
4663 echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)." >&2; \
46354664 exit 17; \
46364665 fi
46374666 $(verbose) mkdir -p $(DEPS_DIR)
46734702 clean:: clean-apps
46744703
46754704 clean-apps:
4676 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4677 $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \
4705 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4706 $(MAKE) -C $$dep clean IS_APP=1; \
46784707 done
46794708
46804709 distclean:: distclean-apps
46814710
46824711 distclean-apps:
4683 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
4684 $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \
4712 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
4713 $(MAKE) -C $$dep distclean IS_APP=1; \
46854714 done
46864715 endif
46874716
47014730 ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
47024731 ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
47034732
4704 # External plugins.
4705
4706 DEP_PLUGINS ?=
4707
4708 define core_dep_plugin
4709 -include $(DEPS_DIR)/$(1)
4710
4711 $(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ;
4712 endef
4713
4714 $(foreach p,$(DEP_PLUGINS),\
4715 $(eval $(if $(findstring /,$p),\
4716 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
4717 $(call core_dep_plugin,$p/plugins.mk,$p))))
4718
4719 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4720 # This file is part of erlang.mk and subject to the terms of the ISC License.
4721
4722 # Configuration.
4723
4724 DTL_FULL_PATH ?=
4725 DTL_PATH ?= templates/
4726 DTL_SUFFIX ?= _dtl
4727 DTL_OPTS ?=
4728
4729 # Verbosity.
4730
4731 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
4732 dtl_verbose = $(dtl_verbose_$(V))
4733
4734 # Core targets.
4735
4736 DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl))
4737
4738 ifneq ($(DTL_FILES),)
4739
4740 ifdef DTL_FULL_PATH
4741 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
4742 else
4743 BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
4744 endif
4745
4746 ifneq ($(words $(DTL_FILES)),0)
4747 # Rebuild templates when the Makefile changes.
4748 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
4749 @mkdir -p $(ERLANG_MK_TMP)
4750 @if test -f $@; then \
4751 touch $(DTL_FILES); \
4752 fi
4753 @touch $@
4754
4755 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
4756 endif
4757
4758 define erlydtl_compile.erl
4759 [begin
4760 Module0 = case "$(strip $(DTL_FULL_PATH))" of
4761 "" ->
4762 filename:basename(F, ".dtl");
4763 _ ->
4764 "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
4765 re:replace(F2, "/", "_", [{return, list}, global])
4766 end,
4767 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
4768 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
4769 ok -> ok;
4770 {ok, _} -> ok
4771 end
4772 end || F <- string:tokens("$(1)", " ")],
4773 halt().
4774 endef
4775
4776 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
4777 $(if $(strip $?),\
4778 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
4779
4780 endif
4781
4782 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
4733 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
47834734 # This file is part of erlang.mk and subject to the terms of the ISC License.
47844735
47854736 # Verbosity.
47984749
47994750 define compile_proto.erl
48004751 [begin
4801 Dir = filename:dirname(filename:dirname(F)),
48024752 protobuffs_compile:generate_source(F,
4803 [{output_include_dir, Dir ++ "/include"},
4804 {output_src_dir, Dir ++ "/ebin"}])
4753 [{output_include_dir, "./include"},
4754 {output_src_dir, "./ebin"}])
48054755 end || F <- string:tokens("$(1)", " ")],
48064756 halt().
48074757 endef
48114761 $(if $(strip $?),$(call compile_proto,$?))
48124762 endif
48134763
4814 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
4764 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
48154765 # This file is part of erlang.mk and subject to the terms of the ISC License.
48164766
48174767 .PHONY: clean-app
48254775 ERLC_EXCLUDE ?=
48264776 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
48274777
4778 ERLC_ASN1_OPTS ?=
4779
48284780 ERLC_MIB_OPTS ?=
48294781 COMPILE_MIB_FIRST ?=
48304782 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
48744826
48754827 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
48764828 define app_file
4877 {application, $(PROJECT), [
4829 {application, '$(PROJECT)', [
48784830 {description, "$(PROJECT_DESCRIPTION)"},
48794831 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48804832 {id$(comma)$(space)"$(1)"}$(comma))
48814833 {modules, [$(call comma_list,$(2))]},
48824834 {registered, []},
4883 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
4835 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4836 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48844837 ]}.
48854838 endef
48864839 else
48874840 define app_file
4888 {application, $(PROJECT), [
4841 {application, '$(PROJECT)', [
48894842 {description, "$(PROJECT_DESCRIPTION)"},
48904843 {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
48914844 {id$(comma)$(space)"$(1)"}$(comma))
48924845 {modules, [$(call comma_list,$(2))]},
48934846 {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
48944847 {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
4895 {mod, {$(PROJECT_MOD), []}}
4848 {mod, {$(PROJECT_MOD), []}},
4849 {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
48964850 ]}.
48974851 endef
48984852 endif
49024856
49034857 # Source files.
49044858
4905 ERL_FILES = $(sort $(call core_find,src/,*.erl))
4906 CORE_FILES = $(sort $(call core_find,src/,*.core))
4859 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
4860
4861 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
4862 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
49074863
49084864 # ASN.1 files.
49094865
49134869
49144870 define compile_asn1
49154871 $(verbose) mkdir -p include/
4916 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1)
4872 $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
49174873 $(verbose) mv asn1/*.erl src/
49184874 $(verbose) mv asn1/*.hrl include/
49194875 $(verbose) mv asn1/*.asn1db include/
49364892
49374893 # Leex and Yecc files.
49384894
4939 XRL_FILES = $(sort $(call core_find,src/,*.xrl))
4895 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
49404896 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
49414897 ERL_FILES += $(XRL_ERL_FILES)
49424898
4943 YRL_FILES = $(sort $(call core_find,src/,*.yrl))
4899 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
49444900 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
49454901 ERL_FILES += $(YRL_ERL_FILES)
49464902
49474903 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
4948 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
4904 $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
49494905
49504906 # Erlang and Core Erlang files.
49514907
49954951 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49964952 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
49974953 (F, Mod, import, {Imp, _}) ->
4998 case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of
4954 IsFile =
4955 case lists:keyfind(Imp, 1, Modules) of
4956 false -> false;
4957 {_, FilePath} -> filelib:is_file(FilePath)
4958 end,
4959 case IsFile of
49994960 false -> ok;
50004961 true -> Add(Mod, Imp)
50014962 end;
50194980 end || F <- ErlFiles],
50204981 Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
50214982 CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
4983 TargetPath = fun(Target) ->
4984 case lists:keyfind(Target, 1, Modules) of
4985 false -> "";
4986 {_, DepFile} ->
4987 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
4988 string:join(DirSubname ++ [atom_to_list(Target)], "/")
4989 end
4990 end,
50224991 ok = file:write_file("$(1)", [
50234992 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
5024 "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n"
4993 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
50254994 ]),
50264995 halt()
50274996 endef
50345003 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
50355004 # Rebuild everything when the Makefile changes.
50365005 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
5037 @mkdir -p $(ERLANG_MK_TMP)
5038 @if test -f $@; then \
5006 $(verbose) mkdir -p $(ERLANG_MK_TMP)
5007 $(verbose) if test -f $@; then \
50395008 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
50405009 touch -c $(PROJECT).d; \
50415010 fi
5042 @touch $@
5011 $(verbose) touch $@
50435012
50445013 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
50455014 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
50465015 endif
50475016
5048 -include $(PROJECT).d
5017 include $(wildcard $(PROJECT).d)
50495018
50505019 ebin/$(PROJECT).app:: ebin/
50515020
50645033 $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
50655034 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
50665035 ifeq ($(wildcard src/$(PROJECT).app.src),)
5067 $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
5036 $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
50685037 > ebin/$(PROJECT).app
50695038 else
50705039 $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
50885057
50895058 endif
50905059
5060 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
50915061 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
50925062 # This file is part of erlang.mk and subject to the terms of the ISC License.
50935063
51055075 doc-deps:
51065076 else
51075077 doc-deps: $(ALL_DOC_DEPS_DIRS)
5108 $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5109 endif
5110
5111 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5078 $(verbose) set -e; for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5079 endif
5080
5081 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51125082 # This file is part of erlang.mk and subject to the terms of the ISC License.
51135083
51145084 .PHONY: rel-deps
51255095 rel-deps:
51265096 else
51275097 rel-deps: $(ALL_REL_DEPS_DIRS)
5128 $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5129 endif
5130
5131 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5098 $(verbose) set -e; for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
5099 endif
5100
5101 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51325102 # This file is part of erlang.mk and subject to the terms of the ISC License.
51335103
51345104 .PHONY: test-deps test-dir test-build clean-test-dir
51505120 test-deps:
51515121 else
51525122 test-deps: $(ALL_TEST_DEPS_DIRS)
5153 $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
5123 $(verbose) set -e; for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
51545124 endif
51555125
51565126 ifneq ($(wildcard $(TEST_DIR)),)
51835153 endif
51845154 endif
51855155
5186 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5156 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
51875157 # This file is part of erlang.mk and subject to the terms of the ISC License.
51885158
51895159 .PHONY: rebar.config
52195189 rebar.config:
52205190 $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
52215191
5222 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5192 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
52235193 # This file is part of erlang.mk and subject to the terms of the ISC License.
52245194
5225 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
5226
5227 MAN_INSTALL_PATH ?= /usr/local/share/man
5228 MAN_SECTIONS ?= 3 7
5195 ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
5196
5197 .PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
5198
5199 # Core targets.
52295200
52305201 docs:: asciidoc
52315202
5203 distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
5204
5205 # Plugin-specific targets.
5206
52325207 asciidoc: asciidoc-guide asciidoc-manual
5208
5209 # User guide.
52335210
52345211 ifeq ($(wildcard doc/src/guide/book.asciidoc),)
52355212 asciidoc-guide:
52365213 else
5237 asciidoc-guide: distclean-asciidoc doc-deps
5214 asciidoc-guide: distclean-asciidoc-guide doc-deps
52385215 a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
52395216 a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
5240 endif
5241
5242 ifeq ($(wildcard doc/src/manual/*.asciidoc),)
5217
5218 distclean-asciidoc-guide:
5219 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
5220 endif
5221
5222 # Man pages.
5223
5224 ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
5225
5226 ifeq ($(ASCIIDOC_MANUAL_FILES),)
52435227 asciidoc-manual:
52445228 else
5245 asciidoc-manual: distclean-asciidoc doc-deps
5246 for f in doc/src/manual/*.asciidoc ; do \
5247 a2x -v -f manpage $$f ; \
5248 done
5249 for s in $(MAN_SECTIONS); do \
5250 mkdir -p doc/man$$s/ ; \
5251 mv doc/src/manual/*.$$s doc/man$$s/ ; \
5252 gzip doc/man$$s/*.$$s ; \
5253 done
5229
5230 # Configuration.
5231
5232 MAN_INSTALL_PATH ?= /usr/local/share/man
5233 MAN_SECTIONS ?= 3 7
5234 MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
5235 MAN_VERSION ?= $(PROJECT_VERSION)
5236
5237 # Plugin-specific targets.
5238
5239 define asciidoc2man.erl
5240 try
5241 [begin
5242 io:format(" ADOC ~s~n", [F]),
5243 ok = asciideck:to_manpage(asciideck:parse_file(F), #{
5244 compress => gzip,
5245 outdir => filename:dirname(F),
5246 extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
5247 extra3 => "$(MAN_PROJECT) Function Reference"
5248 })
5249 end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
5250 halt(0)
5251 catch C:E ->
5252 io:format("Exception ~p:~p~nStacktrace: ~p~n", [C, E, erlang:get_stacktrace()]),
5253 halt(1)
5254 end.
5255 endef
5256
5257 asciidoc-manual:: doc-deps
5258
5259 asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
5260 $(call erlang,$(call asciidoc2man.erl,$?))
5261 $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
52545262
52555263 install-docs:: install-asciidoc
52565264
52575265 install-asciidoc: asciidoc-manual
5258 for s in $(MAN_SECTIONS); do \
5259 mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
5260 install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
5261 done
5262 endif
5263
5264 distclean:: distclean-asciidoc
5265
5266 distclean-asciidoc:
5267 $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
5268
5269 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5266 $(foreach s,$(MAN_SECTIONS),\
5267 mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
5268 install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
5269
5270 distclean-asciidoc-manual:
5271 $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
5272 endif
5273 endif
5274
5275 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
52705276 # This file is part of erlang.mk and subject to the terms of the ISC License.
52715277
52725278 .PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
53235329 define bs_Makefile
53245330 PROJECT = $p
53255331 PROJECT_DESCRIPTION = New project
5326 PROJECT_VERSION = 0.0.1
5332 PROJECT_VERSION = 0.1.0
53275333
53285334 # Whitespace to be used when creating files from templates.
53295335 SP = $(SP)
53335339 define bs_Makefile
53345340 PROJECT = $p
53355341 PROJECT_DESCRIPTION = New project
5336 PROJECT_VERSION = 0.0.1
5342 PROJECT_VERSION = 0.1.0
53375343
53385344 endef
53395345 endif
53415347 define bs_apps_Makefile
53425348 PROJECT = $p
53435349 PROJECT_DESCRIPTION = New project
5344 PROJECT_VERSION = 0.0.1
5350 PROJECT_VERSION = 0.1.0
53455351
53465352 include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
53475353 endef
53615367 endef
53625368
53635369 define bs_relx_config
5364 {release, {$p_release, "1"}, [$p]}.
5370 {release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
53655371 {extended_start_script, true}.
53665372 {sys_config, "rel/sys.config"}.
53675373 {vm_args, "rel/vm.args"}.
57125718 ifndef t
57135719 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57145720 endif
5715 ifndef tpl_$(t)
5716 $(error Unknown template)
5717 endif
57185721 ifndef n
57195722 $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP])
57205723 endif
57275730 list-templates:
57285731 $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
57295732
5730 # Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
5733 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
57315734 # This file is part of erlang.mk and subject to the terms of the ISC License.
57325735
57335736 .PHONY: clean-c_src distclean-c_src-env
59625965 $(call render_template,bs_erl_nif,src/$n.erl)
59635966 endif
59645967
5965 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
5968 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
59665969 # This file is part of erlang.mk and subject to the terms of the ISC License.
59675970
5968 .PHONY: ci ci-setup distclean-kerl
5969
5970 KERL ?= $(CURDIR)/kerl
5971 export KERL
5972
5973 KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl
5974
5975 OTP_GIT ?= https://github.com/erlang/otp
5976
5977 CI_INSTALL_DIR ?= $(HOME)/erlang
5971 .PHONY: ci ci-prepare ci-setup distclean-kerl
5972
59785973 CI_OTP ?=
5979
5980 ifeq ($(strip $(CI_OTP)),)
5974 CI_HIPE ?=
5975 CI_ERLLVM ?=
5976
5977 ifeq ($(CI_VM),native)
5978 ERLC_OPTS += +native
5979 TEST_ERLC_OPTS += +native
5980 else ifeq ($(CI_VM),erllvm)
5981 ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5982 TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
5983 endif
5984
5985 ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
59815986 ci::
59825987 else
5983 ci:: $(addprefix ci-,$(CI_OTP))
5984
5985 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
5988
5989 ifeq ($(strip $(KERL)),)
5990 KERL := $(ERLANG_MK_TMP)/kerl/kerl
5991 endif
5992
5993 export KERL
5994
5995 KERL_GIT ?= https://github.com/kerl/kerl
5996 KERL_COMMIT ?= master
5997
5998 KERL_MAKEFLAGS ?=
5999
6000 OTP_GIT ?= https://github.com/erlang/otp
6001
6002 CI_INSTALL_DIR ?= $(HOME)/erlang
6003
6004 ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
6005
6006 ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
59866007
59876008 ci-setup::
6009
6010 ci-extra::
59886011
59896012 ci_verbose_0 = @echo " CI " $(1);
59906013 ci_verbose = $(ci_verbose_$(V))
59916014
59926015 define ci_target
5993 ci-$(1): $(CI_INSTALL_DIR)/$(1)
6016 ci-$1: $(CI_INSTALL_DIR)/$2
6017 $(verbose) $(MAKE) --no-print-directory clean
59946018 $(ci_verbose) \
5995 PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
5996 CI_OTP_RELEASE="$(1)" \
5997 CT_OPTS="-label $(1)" \
5998 $(MAKE) clean ci-setup tests
5999 endef
6000
6001 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
6019 PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
6020 CI_OTP_RELEASE="$1" \
6021 CT_OPTS="-label $1" \
6022 CI_VM="$3" \
6023 $(MAKE) ci-setup tests
6024 $(verbose) $(MAKE) --no-print-directory ci-extra
6025 endef
6026
6027 $(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
6028 $(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
6029 $(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
60026030
60036031 define ci_otp_target
60046032 ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
60056033 $(CI_INSTALL_DIR)/$(1): $(KERL)
6006 $(KERL) build git $(OTP_GIT) $(1) $(1)
6034 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $(1) $(1)
60076035 $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
60086036 endif
60096037 endef
60106038
60116039 $(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
60126040
6041 define ci_hipe_target
6042 ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
6043 $(CI_INSTALL_DIR)/$1-native: $(KERL)
6044 KERL_CONFIGURE_OPTIONS=--enable-native-libs \
6045 MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
6046 $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
6047 endif
6048 endef
6049
6050 $(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
6051
60136052 $(KERL):
6014 $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
6053 $(verbose) mkdir -p $(ERLANG_MK_TMP)
6054 $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
6055 $(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
60156056 $(verbose) chmod +x $(KERL)
60166057
60176058 help::
60286069 $(gen_verbose) rm -rf $(KERL)
60296070 endif
60306071
6031 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6072 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
60326073 # This file is part of erlang.mk and subject to the terms of the ISC License.
60336074
60346075 .PHONY: ct apps-ct distclean-ct
60366077 # Configuration.
60376078
60386079 CT_OPTS ?=
6080
60396081 ifneq ($(wildcard $(TEST_DIR)),)
6040 CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6041 else
6042 CT_SUITES ?=
6043 endif
6082 ifndef CT_SUITES
6083 CT_SUITES := $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
6084 endif
6085 endif
6086 CT_SUITES ?=
6087 CT_LOGS_DIR ?= $(CURDIR)/logs
60446088
60456089 # Core targets.
60466090
60636107 -noinput \
60646108 -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
60656109 -dir $(TEST_DIR) \
6066 -logdir $(CURDIR)/logs
6110 -logdir $(CT_LOGS_DIR)
60676111
60686112 ifeq ($(CT_SUITES),)
60696113 ct: $(if $(IS_APP),,apps-ct)
60706114 else
6115 # We do not run tests if we are in an apps/* with no test directory.
6116 ifneq ($(IS_APP)$(wildcard $(TEST_DIR)),1)
60716117 ct: test-build $(if $(IS_APP),,apps-ct)
6072 $(verbose) mkdir -p $(CURDIR)/logs/
6118 $(verbose) mkdir -p $(CT_LOGS_DIR)
60736119 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
6120 endif
60746121 endif
60756122
60766123 ifneq ($(ALL_APPS_DIRS),)
60976144
60986145 define ct_suite_target
60996146 ct-$(1): test-build
6100 $(verbose) mkdir -p $(CURDIR)/logs/
6147 $(verbose) mkdir -p $(CT_LOGS_DIR)
61016148 $(gen_verbose) $(CT_RUN) -sname ct_$(PROJECT) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS)
61026149 endef
61036150
61046151 $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
61056152
61066153 distclean-ct:
6107 $(gen_verbose) rm -rf $(CURDIR)/logs/
6108
6109 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6154 $(gen_verbose) rm -rf $(CT_LOGS_DIR)
6155
6156 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61106157 # This file is part of erlang.mk and subject to the terms of the ISC License.
61116158
61126159 .PHONY: plt distclean-plt dialyze
61506197 endef
61516198
61526199 $(DIALYZER_PLT): deps app
6153 $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
6200 $(eval DEPS_LOG := $(shell test -f $(ERLANG_MK_TMP)/deps.log && \
6201 while read p; do test -d $$p/ebin && echo $$p/ebin; done <$(ERLANG_MK_TMP)/deps.log))
6202 $(verbose) dialyzer --build_plt --apps erts kernel stdlib \
6203 $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS_LOG)
61546204
61556205 plt: $(DIALYZER_PLT)
61566206
61646214 endif
61656215 $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
61666216
6167 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6217 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61686218 # This file is part of erlang.mk and subject to the terms of the ISC License.
61696219
61706220 .PHONY: distclean-edoc edoc
61726222 # Configuration.
61736223
61746224 EDOC_OPTS ?=
6225 EDOC_SRC_DIRS ?=
6226
6227 define edoc.erl
6228 SrcPaths = lists:foldl(fun(P, Acc) ->
6229 filelib:wildcard(atom_to_list(P) ++ "/{src,c_src}") ++ Acc
6230 end, [], [$(call comma_list,$(patsubst %,'%',$(EDOC_SRC_DIRS)))]),
6231 DefaultOpts = [{source_path, SrcPaths}, {subpackages, false}],
6232 edoc:application($(1), ".", [$(2)] ++ DefaultOpts),
6233 halt(0).
6234 endef
61756235
61766236 # Core targets.
61776237
6178 ifneq ($(wildcard doc/overview.edoc),)
6238 ifneq ($(strip $(EDOC_SRC_DIRS)$(wildcard doc/overview.edoc)),)
61796239 docs:: edoc
61806240 endif
61816241
61846244 # Plugin-specific targets.
61856245
61866246 edoc: distclean-edoc doc-deps
6187 $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
6247 $(gen_verbose) $(call erlang,$(call edoc.erl,$(PROJECT),$(EDOC_OPTS)))
61886248
61896249 distclean-edoc:
61906250 $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
61916251
6192 # Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
6252 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
61936253 # This file is part of erlang.mk and subject to the terms of the ISC License.
61946254
6195 .PHONY: distclean-escript escript
6255 # Configuration.
6256
6257 DTL_FULL_PATH ?=
6258 DTL_PATH ?= templates/
6259 DTL_SUFFIX ?= _dtl
6260 DTL_OPTS ?=
6261
6262 # Verbosity.
6263
6264 dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F));
6265 dtl_verbose = $(dtl_verbose_$(V))
6266
6267 # Core targets.
6268
6269 DTL_PATH := $(abspath $(DTL_PATH))
6270 DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
6271
6272 ifneq ($(DTL_FILES),)
6273
6274 DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
6275 DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
6276 BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
6277
6278 ifneq ($(words $(DTL_FILES)),0)
6279 # Rebuild templates when the Makefile changes.
6280 $(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
6281 @mkdir -p $(ERLANG_MK_TMP)
6282 @if test -f $@; then \
6283 touch $(DTL_FILES); \
6284 fi
6285 @touch $@
6286
6287 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
6288 endif
6289
6290 define erlydtl_compile.erl
6291 [begin
6292 Module0 = case "$(strip $(DTL_FULL_PATH))" of
6293 "" ->
6294 filename:basename(F, ".dtl");
6295 _ ->
6296 "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
6297 re:replace(F2, "/", "_", [{return, list}, global])
6298 end,
6299 Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
6300 case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
6301 ok -> ok;
6302 {ok, _} -> ok
6303 end
6304 end || F <- string:tokens("$(1)", " ")],
6305 halt().
6306 endef
6307
6308 ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
6309 $(if $(strip $?),\
6310 $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
6311 -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
6312
6313 endif
6314
6315 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6316 # Copyright (c) 2014, Dave Cottlehuber <dch@skunkwerks.at>
6317 # This file is part of erlang.mk and subject to the terms of the ISC License.
6318
6319 .PHONY: distclean-escript escript escript-zip
61966320
61976321 # Configuration.
61986322
61996323 ESCRIPT_NAME ?= $(PROJECT)
62006324 ESCRIPT_FILE ?= $(ESCRIPT_NAME)
62016325
6326 ESCRIPT_SHEBANG ?= /usr/bin/env escript
62026327 ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
6203
6204 ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
6205 ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
6206 ESCRIPT_EMU_ARGS ?= -pa . \
6207 -sasl errlog_type error \
6208 -escript main $(ESCRIPT_NAME)
6209 ESCRIPT_SHEBANG ?= /usr/bin/env escript
6210 ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
6328 ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
6329
6330 ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
6331 ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
62116332
62126333 # Core targets.
62136334
62206341
62216342 # Plugin-specific targets.
62226343
6223 # Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
6224 # Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
6225 # Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
6226 # Software may only be used for the great good and the true happiness of all
6227 # sentient beings.
6228
6229 define ESCRIPT_RAW
6230 'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
6231 'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
6232 ' [F || F <- A, not filelib:is_dir(F) ] end,'\
6233 'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
6234 'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
6235 'Ez = fun(Escript) ->'\
6236 ' Static = Files([$(ESCRIPT_STATIC)]),'\
6237 ' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
6238 ' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
6239 ' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
6240 ' {archive, Archive, [memory]},'\
6241 ' {shebang, "$(ESCRIPT_SHEBANG)"},'\
6242 ' {comment, "$(ESCRIPT_COMMENT)"},'\
6243 ' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
6244 ' ]),'\
6245 ' file:change_mode(Escript, 8#755)'\
6246 'end,'\
6247 'Ez("$(ESCRIPT_FILE)"),'\
6248 'halt().'
6249 endef
6250
6251 ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
6252
6253 escript:: distclean-escript deps app
6254 $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
6344 escript-zip:: deps app
6345 $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
6346 $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
6347 $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
6348 ifneq ($(DEPS),)
6349 $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
6350 `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
6351 endif
6352
6353 escript:: escript-zip
6354 $(gen_verbose) printf "%s\n" \
6355 "#!$(ESCRIPT_SHEBANG)" \
6356 "%% $(ESCRIPT_COMMENT)" \
6357 "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
6358 $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
6359 $(verbose) chmod +x $(ESCRIPT_FILE)
62556360
62566361 distclean-escript:
6257 $(gen_verbose) rm -f $(ESCRIPT_NAME)
6258
6362 $(gen_verbose) rm -f $(ESCRIPT_FILE)
6363
6364 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
62596365 # Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
6260 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
62616366 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
62626367
62636368 .PHONY: eunit apps-eunit
63216426
63226427 ifneq ($(ALL_APPS_DIRS),)
63236428 apps-eunit:
6324 $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done
6325 endif
6326 endif
6327
6328 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
6429 $(verbose) eunit_retcode=0 ; for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; \
6430 [ $$? -ne 0 ] && eunit_retcode=1 ; done ; \
6431 exit $$eunit_retcode
6432 endif
6433 endif
6434
6435 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
63296436 # This file is part of erlang.mk and subject to the terms of the ISC License.
63306437
6331 .PHONY: relx-rel distclean-relx-rel distclean-relx run
6438 .PHONY: relx-rel relx-relup distclean-relx-rel run
63326439
63336440 # Configuration.
63346441
6335 RELX ?= $(CURDIR)/relx
6442 RELX ?= $(ERLANG_MK_TMP)/relx
63366443 RELX_CONFIG ?= $(CURDIR)/relx.config
63376444
63386445 RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
63396446 RELX_OPTS ?=
63406447 RELX_OUTPUT_DIR ?= _rel
6448 RELX_REL_EXT ?=
6449 RELX_TAR ?= 1
6450
6451 ifdef SFX
6452 RELX_TAR = 1
6453 endif
63416454
63426455 ifeq ($(firstword $(RELX_OPTS)),-o)
63436456 RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
63506463 ifeq ($(IS_DEP),)
63516464 ifneq ($(wildcard $(RELX_CONFIG)),)
63526465 rel:: relx-rel
6353 endif
6354 endif
6355
6356 distclean:: distclean-relx-rel distclean-relx
6466
6467 relup:: relx-relup
6468 endif
6469 endif
6470
6471 distclean:: distclean-relx-rel
63576472
63586473 # Plugin-specific targets.
63596474
63626477 $(verbose) chmod +x $(RELX)
63636478
63646479 relx-rel: $(RELX) rel-deps app
6365 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
6480 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release $(if $(filter 1,$(RELX_TAR)),tar)
6481
6482 relx-relup: $(RELX) rel-deps app
6483 $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup $(if $(filter 1,$(RELX_TAR)),tar)
63666484
63676485 distclean-relx-rel:
63686486 $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
6369
6370 distclean-relx:
6371 $(gen_verbose) rm -rf $(RELX)
63726487
63736488 # Run target.
63746489
63776492 else
63786493
63796494 define get_relx_release.erl
6380 {ok, Config} = file:consult("$(RELX_CONFIG)"),
6381 {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
6382 io:format("~s", [Name]),
6495 {ok, Config} = file:consult("$(call core_native_path,$(RELX_CONFIG))"),
6496 {release, {Name, Vsn0}, _} = lists:keyfind(release, 1, Config),
6497 Vsn = case Vsn0 of
6498 {cmd, Cmd} -> os:cmd(Cmd);
6499 semver -> "";
6500 {semver, _} -> "";
6501 VsnStr -> Vsn0
6502 end,
6503 io:format("~s ~s", [Name, Vsn]),
63836504 halt(0).
63846505 endef
63856506
6386 RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
6507 RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
6508 RELX_REL_NAME := $(word 1,$(RELX_REL))
6509 RELX_REL_VSN := $(word 2,$(RELX_REL))
6510
6511 ifeq ($(PLATFORM),msys2)
6512 RELX_REL_EXT := .cmd
6513 endif
63876514
63886515 run: all
6389 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
6516 $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME)$(RELX_REL_EXT) console
63906517
63916518 help::
63926519 $(verbose) printf "%s\n" "" \
63956522
63966523 endif
63976524
6525 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
63986526 # Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
6399 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
64006527 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
64016528
64026529 .PHONY: shell
64216548 $(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))
64226549
64236550 build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
6424 $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
6551 $(verbose) set -e; for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done
64256552
64266553 shell: build-shell-deps
64276554 $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
64286555
6429 # Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
6556 # Copyright (c) 2017, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
6557 # This file is contributed to erlang.mk and subject to the terms of the ISC License.
6558
6559 .PHONY: show-ERL_LIBS show-ERLC_OPTS show-TEST_ERLC_OPTS
6560
6561 show-ERL_LIBS:
6562 @echo $(ERL_LIBS)
6563
6564 show-ERLC_OPTS:
6565 @$(foreach opt,$(ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6566
6567 show-TEST_ERLC_OPTS:
6568 @$(foreach opt,$(TEST_ERLC_OPTS) -pa ebin -I include,echo "$(opt)";)
6569
6570 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
64306571 # This file is part of erlang.mk and subject to the terms of the ISC License.
64316572
64326573 ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
64376578 tests:: triq
64386579
64396580 define triq_check.erl
6440 code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
6581 code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
64416582 try
64426583 case $(1) of
64436584 all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
64696610 endif
64706611 endif
64716612
6613 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
64726614 # Copyright (c) 2015, Erlang Solutions Ltd.
64736615 # This file is part of erlang.mk and subject to the terms of the ISC License.
64746616
64856627 XREFR ?= $(CURDIR)/xrefr
64866628 export XREFR
64876629
6488 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr
6630 XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/1.1.0/xrefr
64896631
64906632 # Core targets.
64916633
64926634 help::
6493 $(verbose) printf "%s\n" "" \
6494 "Xref targets:" \
6495 " xref Run Xrefr using $XREF_CONFIG as config file if defined"
6635 $(verbose) printf '%s\n' '' \
6636 'Xref targets:' \
6637 ' xref Run Xrefr using $$XREF_CONFIG as config file if defined'
64966638
64976639 distclean:: distclean-xref
64986640
65086650 distclean-xref:
65096651 $(gen_verbose) rm -rf $(XREFR)
65106652
6511 # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
6653 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6654 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
65126655 # This file is part of erlang.mk and subject to the terms of the ISC License.
65136656
65146657 COVER_REPORT_DIR = cover
65176660
65186661 ifdef COVER
65196662 ifdef CT_RUN
6663 ifneq ($(wildcard $(TEST_DIR)),)
65206664 # All modules in 'ebin'
65216665 COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))
65226666
65316675 CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
65326676 endif
65336677 endif
6678 endif
65346679
65356680 # Core targets
65366681
65526697 "Cover targets:" \
65536698 " cover-report Generate a HTML coverage report from previously collected" \
65546699 " cover data." \
6555 " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
6700 " all.coverdata Merge all coverdata files into all.coverdata." \
65566701 "" \
65576702 "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
65586703 "target tests additionally generates a HTML coverage report from the combined" \
65656710
65666711 .PHONY: coverdata-clean
65676712 coverdata-clean:
6568 $(gen_verbose) rm -f *.coverdata ct.cover.spec
6713 $(gen_verbose) rm -f *.coverdata $(TEST_DIR)/ct.cover.spec
65696714
65706715 # Merge all coverdata files into one.
6716 define cover_export.erl
6717 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
6718 cover:export("$@"), halt(0).
6719 endef
6720
65716721 all.coverdata: $(COVERDATA)
6572 $(gen_verbose) $(ERL) -eval ' \
6573 $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
6574 cover:export("$@"), halt(0).'
6722 $(gen_verbose) $(call erlang,$(cover_export.erl))
65756723
65766724 # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
65776725 # empty if you want the coverdata files but not the HTML report.
65896737 # Modules which include eunit.hrl always contain one line without coverage
65906738 # because eunit defines test/0 which is never called. We compensate for this.
65916739 EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
6592 grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
6740 grep -H -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
65936741 | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
65946742
65956743 define cover_report.erl
66246772 endef
66256773
66266774 cover-report:
6627 $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
6775 $(verbose) mkdir -p $(COVER_REPORT_DIR)
66286776 $(gen_verbose) $(call erlang,$(cover_report.erl))
66296777
66306778 endif
66316779 endif # ifneq ($(COVER_REPORT_DIR),)
6780
6781 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
6782 # This file is part of erlang.mk and subject to the terms of the ISC License.
6783
6784 .PHONY: sfx
6785
6786 ifdef RELX_REL
6787 ifdef SFX
6788
6789 # Configuration.
6790
6791 SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
6792 SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
6793
6794 # Core targets.
6795
6796 rel:: sfx
6797
6798 # Plugin-specific targets.
6799
6800 define sfx_stub
6801 #!/bin/sh
6802
6803 TMPDIR=`mktemp -d`
6804 ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
6805 FILENAME=$$(basename $$0)
6806 REL=$${FILENAME%.*}
6807
6808 tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
6809
6810 $$TMPDIR/bin/$$REL console
6811 RET=$$?
6812
6813 rm -rf $$TMPDIR
6814
6815 exit $$RET
6816
6817 __ARCHIVE_BELOW__
6818 endef
6819
6820 sfx:
6821 $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
6822 $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
6823 $(verbose) chmod +x $(SFX_OUTPUT_FILE)
6824
6825 endif
6826 endif
6827
6828 # Copyright (c) 2013-2017, Loïc Hoguin <essen@ninenines.eu>
6829 # This file is part of erlang.mk and subject to the terms of the ISC License.
6830
6831 # External plugins.
6832
6833 DEP_PLUGINS ?=
6834
6835 $(foreach p,$(DEP_PLUGINS),\
6836 $(eval $(if $(findstring /,$p),\
6837 $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
6838 $(call core_dep_plugin,$p/plugins.mk,$p))))
66326839
66336840 # Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
66346841 # Copyright (c) 2015-2016, Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>
66976904 $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
66986905 endif
66996906 ifndef IS_APP
6700 $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
6907 $(verbose) set -e; for dep in $(ALL_APPS_DIRS) ; do \
67016908 $(MAKE) -C $$dep $@ \
67026909 IS_APP=1 \
6703 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6704 || exit $$?; \
6910 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67056911 done
67066912 endif
6707 $(verbose) for dep in $^ ; do \
6913 $(verbose) set -e; for dep in $^ ; do \
67086914 if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
67096915 echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
6710 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
6916 if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk|.*ERLANG_MK_FILENAME.*)$$" \
67116917 $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
67126918 $(MAKE) -C $$dep fetch-deps \
67136919 IS_DEP=1 \
6714 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
6715 || exit $$?; \
6920 ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST); \
67166921 fi \
67176922 fi \
67186923 done
0 rabbitmq_server_release ad4565fa57e98c9183db8e9f7acc9ce6ce1ee48f rabbitmq_v3_6_6
1 amqp_client ca59f72a6f7c151f1eaeb0131163909791ae6284 rabbitmq_v3_6_6
2 cowboy b8e4115eb13488c517d8d8ef33c47d0eaa7838c6 1.0.3
3 cowlib 7d8a571b1e50602d701ca203fbf28036b2cf80f5 1.0.1
4 mochiweb a1ed381e1c4f56c7d1eaee2c2cb725719905a84a master
5 rabbit 3be6cd4bb31f4a7a99fe1e5cd4652766a08c3c40 rabbitmq_v3_6_6
6 rabbit_common 97e014fc01136bbd6da31fb3490f6e0209a386e2 rabbitmq_v3_6_6
7 rabbitmq_amqp1_0 102609e93e84d2935698bf9d07ad5ba7ccae1133 rabbitmq_v3_6_6
8 rabbitmq_auth_backend_ldap 1ee91f8022d1c2e4b825144faf9d7efe65146a37 rabbitmq_v3_6_6
9 rabbitmq_auth_mechanism_ssl 4538833b754e581a1c4b5000b8ab3a3752b0551e rabbitmq_v3_6_6
10 rabbitmq_codegen 4e725d8cafeaca969082beb0b5fa7d48f7f738fe rabbitmq_v3_6_6
11 rabbitmq_consistent_hash_exchange a0a239bbfb3ae17ac9b01e20ea43175acd990e65 rabbitmq_v3_6_6
12 rabbitmq_event_exchange b4b6331b060ab7a6db37298227bbef4905df641a rabbitmq_v3_6_6
13 rabbitmq_federation f80e793331ef80fb96949f0b927cd609e0fdd952 rabbitmq_v3_6_6
14 rabbitmq_federation_management 354f9bf661cc19f1f53bc277be25676e272a584b rabbitmq_v3_6_6
15 rabbitmq_jms_topic_exchange a549a8a1a434a52d904cee92566442f025c0059b rabbitmq_v3_6_6
16 rabbitmq_management a5f9d42785a251383bfe1e673df9ab260e1a778d rabbitmq_v3_6_6
17 rabbitmq_management_agent 82c31a6d4a3c740d65b50dadfc04b46c4ca3fc02 rabbitmq_v3_6_6
18 rabbitmq_management_visualiser 4211cbb1108ac3562f02c899ee217444d3a86bfd rabbitmq_v3_6_6
19 rabbitmq_mqtt 0cee514e400c57061b4c2663e9170059ea1dfadf rabbitmq_v3_6_6
20 rabbitmq_recent_history_exchange 9881652e4af40964bbe804a4dcd70fd745d4d171 rabbitmq_v3_6_6
21 rabbitmq_sharding 7beaab51586015445edac2e31553289925fc252a rabbitmq_v3_6_6
22 rabbitmq_shovel 6749519c6833881886f354092d49ab9036d8f90d rabbitmq_v3_6_6
23 rabbitmq_shovel_management 715cd498cbd24e288ef501214714baf57af17941 rabbitmq_v3_6_6
24 rabbitmq_stomp 215dde6d30d2b4a302930528e2a1b0debcc15446 rabbitmq_v3_6_6
25 rabbitmq_top d12a3bf21ebd0a6aa5d2bc76474c1f5d2ef2040a rabbitmq_v3_6_6
26 rabbitmq_tracing b9900be98f2929a8c472393b40ac7ec74e5cf7ff rabbitmq_v3_6_6
27 rabbitmq_trust_store 5a1ce3e2354e421423d7ea2048daa9d6d8216cbe rabbitmq_v3_6_6
28 rabbitmq_web_dispatch 98b24e030c043456645fbbaa9df9f2268c4d460f rabbitmq_v3_6_6
29 rabbitmq_web_stomp 64d3b9c829f92c087b295f0347efce2d23d14e15 rabbitmq_v3_6_6
30 rabbitmq_web_stomp_examples 85f33ca3ccce7bba094a657300c43054af8a0af6 rabbitmq_v3_6_6
31 ranch a5d2efcde9a34ad38ab89a26d98ea5335e88625a 1.2.1
32 sockjs 7e7112a4935a9aaa89e97954eb612534fa0f6229 master
33 webmachine 6b5210c0ed07159f43222255e05a90bbef6c8cbe
0 RabbitMQ Server 3.6.10
1 rabbitmq_server_release 122123aedd2302da85e3bc3fdf61df3a573528b2 rabbitmq_v3_6_10
2 amqp_client 28bedb1e4e8813fee1d2896302c28c2e9700190e rabbitmq_v3_6_10
3 cowboy d08c2ab39d38c181abda279d5c2cadfac33a50c1 1.0.4
4 cowlib 45f750db410a4b08c68d142ad0af839f544c5d3d 1.0.2
5 rabbit 92702e4c5093ecb4ad911603c2357ccadebc52e9 rabbitmq_v3_6_10
6 rabbit_common 928070fbf3e837950eb36fedb80a64ca84490656 rabbitmq_v3_6_10
7 rabbitmq_amqp1_0 85601575e22d74f0fb071109a20d2d7be1304c2b rabbitmq_v3_6_10
8 rabbitmq_auth_backend_ldap f02b5de03426ff220f7f3bbd349e353b851c4a2a rabbitmq_v3_6_10
9 rabbitmq_auth_mechanism_ssl 233c78b824be6d2a266459fdb31de73c85f74e7c rabbitmq_v3_6_10
10 rabbitmq_codegen fc9bb3361535b6096c75d3cd44f94e7f534188cd rabbitmq_v3_6_10
11 rabbitmq_consistent_hash_exchange 1b214b202d6e80abde2ac384316a549fd29715ce rabbitmq_v3_6_10
12 rabbitmq_event_exchange 77c52e806591c5dcc33bd8fa1d2c4d36e4d45c33 rabbitmq_v3_6_10
13 rabbitmq_federation 636f28321e3a78e88565fe0d6d1a695e83ec029a rabbitmq_v3_6_10
14 rabbitmq_federation_management 40fd04c55637c76415f2e818c1faa9cdfff30e46 rabbitmq_v3_6_10
15 rabbitmq_jms_topic_exchange 50cea6517071f91dee8a806fa0d026f2b6e29b2a rabbitmq_v3_6_10
16 rabbitmq_management 4b73d4f7c1d424e82f3069e89628172f0e6488aa rabbitmq_v3_6_10
17 rabbitmq_management_agent 53678d8eb2761896e38efa22bb62ec652b0132b9 rabbitmq_v3_6_10
18 rabbitmq_management_visualiser e86b75adeedb9246f65e4a8be16c36aa8918219b rabbitmq_v3_6_10
19 rabbitmq_mqtt 9751d1d59fc97c7753232ca4189a2d7633aa08e2 rabbitmq_v3_6_10
20 rabbitmq_recent_history_exchange d653c8ed41457b1233ea24715ad453f8ad3c6cdf rabbitmq_v3_6_10
21 rabbitmq_sharding 7372fda92f4f53d2100e75c3c26dde487726193b rabbitmq_v3_6_10
22 rabbitmq_shovel 15a39b947e8ef0d828b25a5b18eb31086a05b77b rabbitmq_v3_6_10
23 rabbitmq_shovel_management 064c8d251bfb1f92d0b234cd31081885bb3279fe rabbitmq_v3_6_10
24 rabbitmq_stomp cd7af6e0f665ad2be6ddfee3768bbd26c7e32c05 rabbitmq_v3_6_10
25 rabbitmq_top 58baec4ce949a331897fedf27ad19d2c3bed644f rabbitmq_v3_6_10
26 rabbitmq_tracing e45469a0aaa901911b20dd73771a17b9b8efdc25 rabbitmq_v3_6_10
27 rabbitmq_trust_store 27885bf633d0a42965b1251e15b51d0ac150c630 rabbitmq_v3_6_10
28 rabbitmq_web_dispatch 48965be4ecdd46ee618f7039e50ad922fea34c85 rabbitmq_v3_6_10
29 rabbitmq_web_mqtt 5e9af147a88a7c64ec4ec5ade1d590685d7c10eb rabbitmq_v3_6_10
30 rabbitmq_web_mqtt_examples d2cd1642c4568039f6d37c4312592682f96d693e rabbitmq_v3_6_10
31 rabbitmq_web_stomp 7db44fc6daa40e6fd7336e0be38708cc305bbfab rabbitmq_v3_6_10
32 rabbitmq_web_stomp_examples 00e1713771ebbfc4077b8d132d97e2721e990eb4 rabbitmq_v3_6_10
33 ranch 8a07098b31ebdf37bf2e72fdd4b25a0f0052c849 1.3.1
34 sockjs 405990ea62353d98d36dbf5e1e64942d9b0a1daf master
0 PLUGINS := rabbitmq_amqp1_0 rabbitmq_auth_backend_ldap rabbitmq_auth_mechanism_ssl rabbitmq_consistent_hash_exchange rabbitmq_event_exchange rabbitmq_federation rabbitmq_federation_management rabbitmq_jms_topic_exchange rabbitmq_management rabbitmq_management_agent rabbitmq_management_visualiser rabbitmq_mqtt rabbitmq_recent_history_exchange rabbitmq_sharding rabbitmq_shovel rabbitmq_shovel_management rabbitmq_stomp rabbitmq_top rabbitmq_tracing rabbitmq_trust_store rabbitmq_web_dispatch rabbitmq_web_mqtt rabbitmq_web_mqtt_examples rabbitmq_web_stomp rabbitmq_web_stomp_examples
22 # before the inclusion of erlang.mk leading to the wrong target becoming
33 # the default.
44 .DEFAULT_GOAL = all
5 endif
6
7 # PROJECT_VERSION defaults to:
8 # 1. the version exported by rabbitmq-server-release;
9 # 2. the version stored in `git-revisions.txt`, if it exists;
10 # 3. a version based on git-describe(1), if it is a Git clone;
11 # 4. 0.0.0
12
13 PROJECT_VERSION := $(RABBITMQ_VERSION)
14
15 ifeq ($(PROJECT_VERSION),)
16 PROJECT_VERSION := $(shell \
17 if test -f git-revisions.txt; then \
18 head -n1 git-revisions.txt | \
19 awk '{print $$$(words $(PROJECT_DESCRIPTION) version);}'; \
20 else \
21 (git describe --dirty --abbrev=7 --tags --always --first-parent \
22 2>/dev/null || echo rabbitmq_v0_0_0) | \
23 sed -e 's/^rabbitmq_v//' -e 's/^v//' -e 's/_/./g' -e 's/-/+/' \
24 -e 's/-/./g'; \
25 fi)
526 endif
627
728 # --------------------------------------------------------------------
2041 dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
2142 dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
2243 dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
44 dep_rabbitmq_auth_backend_cache = git_rmq rabbitmq-auth-backend-cache $(current_rmq_ref) $(base_rmq_ref) master
2345 dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
2446 dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
2547 dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
2648 dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
2749 dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
50 dep_rabbitmq_cli = git_rmq rabbitmq-cli $(current_rmq_ref) $(base_rmq_ref) master
2851 dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
2952 dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
53 dep_rabbitmq_ct_client_helpers = git_rmq rabbitmq-ct-client-helpers $(current_rmq_ref) $(base_rmq_ref) master
3054 dep_rabbitmq_ct_helpers = git_rmq rabbitmq-ct-helpers $(current_rmq_ref) $(base_rmq_ref) master
3155 dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
3256 dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
3559 dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
3660 dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
3761 dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master
62 dep_rabbitmq_jms_cts = git_rmq rabbitmq-jms-cts $(current_rmq_ref) $(base_rmq_ref) master
3863 dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master
3964 dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
4065 dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
7095
7196 dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
7297
73 # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
74 # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
75 # needs to add "ranch" as a BUILD_DEPS. The list of projects needing
76 # this workaround are:
77 # o rabbitmq-web-stomp
78 dep_ranch = git https://github.com/ninenines/ranch 1.2.1
98 # Third-party dependencies version pinning.
99 #
100 # We do that in this file, which is copied in all projects, to ensure
101 # all projects use the same versions. It avoids conflicts and makes it
102 # possible to work with rabbitmq-public-umbrella.
103
104 dep_cowboy_commit = 1.0.4
105 dep_mochiweb = git git://github.com/basho/mochiweb.git v2.9.0p2
106 dep_ranch_commit = 1.3.1
107 dep_webmachine_commit = 1.10.8p2
79108
80109 RABBITMQ_COMPONENTS = amqp_client \
81110 rabbit \
82111 rabbit_common \
83112 rabbitmq_amqp1_0 \
84113 rabbitmq_auth_backend_amqp \
114 rabbitmq_auth_backend_cache \
85115 rabbitmq_auth_backend_http \
86116 rabbitmq_auth_backend_ldap \
87117 rabbitmq_auth_mechanism_ssl \
88118 rabbitmq_boot_steps_visualiser \
89119 rabbitmq_clusterer \
120 rabbitmq_cli \
90121 rabbitmq_codegen \
91122 rabbitmq_consistent_hash_exchange \
123 rabbitmq_ct_client_helpers \
92124 rabbitmq_ct_helpers \
93125 rabbitmq_delayed_message_exchange \
94126 rabbitmq_dotnet_client \
97129 rabbitmq_federation_management \
98130 rabbitmq_java_client \
99131 rabbitmq_jms_client \
132 rabbitmq_jms_cts \
100133 rabbitmq_jms_topic_exchange \
101134 rabbitmq_lvc \
102135 rabbitmq_management \
4141 OCF_RESKEY_mnesia_schema_base_default="/var/lib/rabbitmq"
4242 OCF_RESKEY_host_ip_default="127.0.0.1"
4343 OCF_RESKEY_node_port_default=5672
44 OCF_RESKEY_default_vhost_default="/"
4445 OCF_RESKEY_erlang_cookie_default=false
4546 OCF_RESKEY_erlang_cookie_file_default="/var/lib/rabbitmq/.erlang.cookie"
4647 OCF_RESKEY_use_fqdn_default=false
4950 OCF_RESKEY_policy_file_default="/usr/local/sbin/set_rabbitmq_policy"
5051 OCF_RESKEY_rmq_feature_health_check_default=true
5152 OCF_RESKEY_rmq_feature_local_list_queues_default=true
53 OCF_RESKEY_limit_nofile_default=65535
5254
5355 : ${HA_LOGTAG="lrmd"}
5456 : ${HA_LOGFACILITY="daemon"}
6567 : ${OCF_RESKEY_mnesia_schema_base=${OCF_RESKEY_mnesia_schema_base_default}}
6668 : ${OCF_RESKEY_pid_file=${OCF_RESKEY_pid_file_default}}
6769 : ${OCF_RESKEY_node_port=${OCF_RESKEY_node_port_default}}
70 : ${OCF_RESKEY_default_vhost=${OCF_RESKEY_default_vhost_default}}
6871 : ${OCF_RESKEY_erlang_cookie=${OCF_RESKEY_erlang_cookie_default}}
6972 : ${OCF_RESKEY_erlang_cookie_file=${OCF_RESKEY_erlang_cookie_file_default}}
7073 : ${OCF_RESKEY_use_fqdn=${OCF_RESKEY_use_fqdn_default}}
7376 : ${OCF_RESKEY_policy_file=${OCF_RESKEY_policy_file_default}}
7477 : ${OCF_RESKEY_rmq_feature_health_check=${OCF_RESKEY_rmq_feature_health_check_default}}
7578 : ${OCF_RESKEY_rmq_feature_local_list_queues=${OCF_RESKEY_rmq_feature_local_list_queues_default}}
79 : ${OCF_RESKEY_limit_nofile=${OCF_RESKEY_limit_nofile_default}}
7680
7781 #######################################################################
7882
8488 : ${OCF_RESKEY_command_timeout=${OCF_RESKEY_command_timeout_default}}
8589 TIMEOUT_ARG=$((OCF_RESKEY_CRM_meta_timeout / 6000 + 30))
8690 COMMAND_TIMEOUT="/usr/bin/timeout ${OCF_RESKEY_command_timeout} ${TIMEOUT_ARG}"
91 RESOURCE_NAME=`echo $OCF_RESOURCE_INSTANCE | cut -d ":" -f 1`
8792
8893 #######################################################################
8994
259264 <content type="boolean" default="${OCF_RESKEY_node_port_default}" />
260265 </parameter>
261266
267 <parameter name="default_vhost" unique="0" required="0">
268 <longdesc lang="en">
269 Default virtual host used for monitoring if a node is fully synchronized with
270 the rest of the cluster. In normal operation, the resource agent will wait for
271 queues from this virtual host on this node to be synchronized elsewhere before
272 stopping RabbitMQ. This also means queues in other virtual hosts may not be
273 fully synchronized on stop operations.
274 </longdesc>
275 <shortdesc lang="en">Default virtual host used for waiting for synchronization</shortdesc>
276 <content type="string" default="${OCF_RESKEY_default_vhost_default}" />
277 </parameter>
278
262279 <parameter name="erlang_cookie" unique="0" required="0">
263280 <longdesc lang="en">
264281 Erlang cookie for clustering. If specified, will be updated at the mnesia reset
329346 </longdesc>
330347 <shortdesc lang="en">Use --local option for list_queues</shortdesc>
331348 <content type="boolean" default="${OCF_RESKEY_rmq_feature_local_list_queues_default}" />
349 </parameter>
350
351 <parameter name="limit_nofile" unique="0" required="0">
352 <longdesc lang="en">
353 Soft and hard limit for NOFILE
354 </longdesc>
355 <shortdesc lang="en">NOFILE limit</shortdesc>
356 <content type="string" default="${OCF_RESKEY_limit_nofile_default}" />
332357 </parameter>
333358
334359 $EXTENDED_OCF_PARAMS
555580 date -u +%s
556581 }
557582
583 set_limits() {
584 local current_limit=$(su $OCF_RESKEY_username -s /bin/sh -c "ulimit -n")
585 if [ ! -z $OCF_RESKEY_limit_nofile -a $OCF_RESKEY_limit_nofile -gt $current_limit ] ; then
586 ulimit -n $OCF_RESKEY_limit_nofile
587 fi
588 }
589
558590 master_score() {
559591 local LH="${LL} master_score():"
560592 local score=$1
622654 }
623655
624656 get_node_master_score() {
625 get_integer_node_attr $1 'master-p_rabbitmq-server'
657 get_integer_node_attr $1 "master-${RESOURCE_NAME}"
626658 }
627659
628660 # Return either rabbit node name as FQDN or shortname, depends on the OCF_RESKEY_use_fqdn.
11651197
11661198 [ -f /etc/default/rabbitmq-server ] && . /etc/default/rabbitmq-server
11671199
1200 # RabbitMQ requires high soft and hard limits for NOFILE
1201 set_limits
1202
11681203 # run beam process
11691204 command="${OCF_RESKEY_binary} >> \"${OCF_RESKEY_log_dir}/startup_log\" 2>/dev/null"
11701205 RABBITMQ_NODE_ONLY=1 su rabbitmq -s /bin/sh -c "${command}"&
14701505 opt_arg="--local"
14711506 fi
14721507
1473 queues="${COMMAND_TIMEOUT} ${OCF_RESKEY_ctl} list_queues $opt_arg name state"
1508 queues="${COMMAND_TIMEOUT} ${OCF_RESKEY_ctl} -p ${OCF_RESKEY_default_vhost} list_queues $opt_arg name state"
14741509
14751510 su_rabbit_cmd -t "${wait_time}" "sh -c \"while ${queues} | grep -q 'syncing,'; \
14761511 do sleep 2; done\""
17721807 local queues
17731808 local rc_queues
17741809 local timeout_queues
1775 queues=`su_rabbit_cmd "${OCF_RESKEY_ctl} -q list_queues memory messages consumer_utilisation"`
1810 queues=`su_rabbit_cmd "${OCF_RESKEY_ctl} -q -p ${OCF_RESKEY_default_vhost} list_queues memory messages consumer_utilisation"`
17761811 rc_queues=$?
17771812 check_timeouts $rc_queues "rabbit_list_queues_timeouts" "list_queues"
17781813 timeout_queues=$?
2929 ## OCF_RESKEY_mnesia_base
3030 ## OCF_RESKEY_server_start_args
3131 ## OCF_RESKEY_pid_file
32 ## OCF_RESKEY_limit_nofile
3233
3334 #######################################################################
3435 # Initialization:
4344 OCF_RESKEY_nodename_default="rabbit@localhost"
4445 OCF_RESKEY_log_base_default="/var/log/rabbitmq"
4546 OCF_RESKEY_pid_file_default="/var/run/rabbitmq/pid"
47 OCF_RESKEY_limit_nofile_default=65535
4648 : ${OCF_RESKEY_server=${OCF_RESKEY_server_default}}
4749 : ${OCF_RESKEY_ctl=${OCF_RESKEY_ctl_default}}
4850 : ${OCF_RESKEY_nodename=${OCF_RESKEY_nodename_default}}
4951 : ${OCF_RESKEY_log_base=${OCF_RESKEY_log_base_default}}
5052 : ${OCF_RESKEY_pid_file=${OCF_RESKEY_pid_file_default}}
53 : ${OCF_RESKEY_limit_nofile=${OCF_RESKEY_limit_nofile_default}}
5154
5255 meta_data() {
5356 cat <<END
141144 </longdesc>
142145 <shortdesc lang="en">Pid file path</shortdesc>
143146 <content type="string" default="${OCF_RESKEY_pid_file_default}" />
147 </parameter>
148
149 <parameter name="limit_nofile" unique="0" required="0">
150 <longdesc lang="en">
151 Soft and hard limit for NOFILE
152 </longdesc>
153 <shortdesc lang="en">NOFILE limit</shortdesc>
154 <content type="string" default="${OCF_RESKEY_limit_nofile_default}" />
144155 </parameter>
145156
146157 </parameters>
175186 RABBITMQ_MNESIA_BASE=$OCF_RESKEY_mnesia_base
176187 RABBITMQ_SERVER_START_ARGS=$OCF_RESKEY_server_start_args
177188 RABBITMQ_PID_FILE=$OCF_RESKEY_pid_file
189 RABBITMQ_LIMIT_NOFILE=$OCF_RESKEY_limit_nofile
178190 [ ! -z $RABBITMQ_NODENAME ] && NODENAME_ARG="-n $RABBITMQ_NODENAME"
179191 [ ! -z $RABBITMQ_NODENAME ] && export RABBITMQ_NODENAME
180192
203215 [ ! -z $RABBITMQ_PID_FILE ] && ensure_pid_dir && export RABBITMQ_PID_FILE
204216 }
205217
218 set_limits() {
219 local current_limit=$(su rabbitmq -s /bin/sh -c "ulimit -n")
220 if [ ! -z $RABBITMQ_LIMIT_NOFILE -a $RABBITMQ_LIMIT_NOFILE -gt $current_limit ] ; then
221 ulimit -n $RABBITMQ_LIMIT_NOFILE
222 fi
223 }
224
206225 rabbit_validate_partial() {
207226 if [ ! -x $RABBITMQ_SERVER ]; then
208227 ocf_log err "rabbitmq-server server $RABBITMQ_SERVER does not exist or is not executable";
248267 local rc
249268 local action
250269 action=$@
251 $RABBITMQ_CTL $NODENAME_ARG $action > /dev/null 2> /dev/null
270 ocf_run -q -info $RABBITMQ_CTL $NODENAME_ARG $action
252271 rc=$?
253272 case "$rc" in
254273 0)
255274 ocf_log debug "RabbitMQ server is running normally"
256275 return $OCF_SUCCESS
257276 ;;
258 2)
277 1|2)
259278 ocf_log debug "RabbitMQ server is not running"
260279 return $OCF_NOT_RUNNING
261280 ;;
274293 fi
275294
276295 export_vars
296
297 # RabbitMQ requires high soft and hard limits for NOFILE
298 set_limits
277299
278300 setsid sh -c "$RABBITMQ_SERVER > ${RABBITMQ_LOG_BASE}/startup_log 2> ${RABBITMQ_LOG_BASE}/startup_err" &
279301
298320 return $OCF_SUCCESS
299321 fi
300322
301 $RABBITMQ_CTL stop ${RABBITMQ_PID_FILE}
323 rabbitmqctl_action stop ${RABBITMQ_PID_FILE}
302324 rc=$?
303325
304326 if [ "$rc" != 0 ]; then
+0
-30
scripts/travis_test_ocf_ra.sh less more
0 #!/bin/sh -eux
1 # Prepare and run a smoke test against the RabbitMQ OCF RA only if
2 # the scripts/rabbitmq-server-ha.ocf has changes
3 if ! git diff HEAD~ --name-only | grep -q scripts/rabbitmq-server-ha.ocf
4 then
5 exit 0
6 fi
7
8 export VAGRANT_VERSION=1.8.1
9 export DOCKER_IMAGE=bogdando/rabbitmq-cluster-ocf-wily
10 export UPLOAD_METHOD=none
11 export DOCKER_MOUNTS="$(pwd)/scripts/rabbitmq-server-ha.ocf:/tmp/rabbitmq-server-ha"
12
13 # Install vagrant and requirements
14 sudo apt-get install -qq git wget
15 wget --no-verbose https://releases.hashicorp.com/vagrant/${VAGRANT_VERSION}/vagrant_${VAGRANT_VERSION}_x86_64.deb
16 sudo dpkg -i --force-all ./vagrant_${VAGRANT_VERSION}_x86_64.deb
17 vagrant plugin install vagrant-triggers
18
19 # Update docker and prepare images
20 sudo apt-get update
21 sudo DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install --only-upgrade docker-engine
22 sudo service docker restart
23 docker pull $DOCKER_IMAGE
24
25 # Prepare and run a smoke test for a rabbitmq cluster by the OCF RA
26 git clone https://github.com/bogdando/rabbitmq-cluster-ocf-vagrant.git
27 cd ./rabbitmq-cluster-ocf-vagrant
28 vagrant up --provider docker
29 docker exec -it n1 /bin/bash /vagrant/vagrant_script/test_rabbitcluster.sh rabbit@n1 rabbit@n2
+0
-109
upgrade/Makefile less more
0
1 UPGRADE_FROM = 3.6.5
2
3 # UPGRADE_FROM_SCRIPT specifies a script name to use.
4 # it should not be changed for every UPGRADE_FROM version
5 UPGRADE_FROM_SCRIPT = 3.5
6 UPGRADE_TO_SCRIPT = 3.6
7
8 RELEASE_FOR_UPGRADE = $(CURDIR)/rabbitmq_r_$(UPGRADE_FROM)
9 RELEASE_ARCHIVE = rabbitmq-server-generic-unix-$(UPGRADE_FROM).tar.xz
10 RELEASE_FOR_UPGRADE_URL = "http://www.rabbitmq.com/releases/rabbitmq-server/v$(UPGRADE_FROM)/$(RELEASE_ARCHIVE)"
11 RELEASE_UPGRADE_DATA = $(CURDIR)/rabbitmq_server_upgrade_data/rabbitmq-test-instances
12 UPGRADE_CONFIG_DIR = $(CURDIR)/config
13 UPGRADE_SCRIPTS_DIR = $(CURDIR)/scripts
14
15 QUEUES_COUNT_TRANSIENT ?= 1
16 QUEUES_COUNT_DURABLE ?= 1
17 MSGS_COUNT_NON_PERSISTENT ?= 10
18 MSGS_COUNT_PERSISTENT_INDEX ?= 10
19 MSGS_COUNT_PERSISTENT_STORE ?= 10
20 INDEX_MSG_SIZE ?= 50
21 STORE_MSG_SIZE ?= 150
22
23 # Clean up erlang.mk exported variables
24 ifneq (0,${MAKELEVEL})
25 export ERL_LIBS=
26 export RABBITMQ_SCRIPTS_DIR=
27 endif
28
29 all: verify-and-cleanup
30
31 $(RELEASE_ARCHIVE):
32 wget $(RELEASE_FOR_UPGRADE_URL) -O $(RELEASE_ARCHIVE)
33
34 $(RELEASE_FOR_UPGRADE): $(RELEASE_ARCHIVE)
35 mkdir $(RELEASE_FOR_UPGRADE)
36 tar -xvf $(RELEASE_ARCHIVE)
37 mv rabbitmq_server-*/* $(RELEASE_FOR_UPGRADE)
38
39 run-release-for-upgrade: $(RELEASE_FOR_UPGRADE)
40 rm -rf $(RELEASE_UPGRADE_DATA)
41 mkdir -p $(RELEASE_UPGRADE_DATA)
42 cd $(RELEASE_FOR_UPGRADE) && \
43 RABBITMQ_HOME=$(RELEASE_FOR_UPGRADE) \
44 RABBITMQ_CONFIG_FILE=$(UPGRADE_CONFIG_DIR)/rabbitmq \
45 RABBITMQ_NODENAME=rabbit_upgrade \
46 RABBITMQ_ENABLED_PLUGINS_FILE=$(UPGRADE_CONFIG_DIR)/enabled_plugins \
47 RABBITMQ_LOG_BASE=$(RELEASE_UPGRADE_DATA)/rabbit_upgrade/log \
48 RABBITMQ_MNESIA_BASE=$(RELEASE_UPGRADE_DATA)/rabbit_upgrade/mnesia \
49 RABBITMQ_MNESIA_DIR=$(RELEASE_UPGRADE_DATA)/rabbit_upgrade/mnesia/rabbit_upgrade \
50 RABBITMQ_SCHEMA_DIR=$(RELEASE_UPGRADE_DATA)/rabbit_upgrade/schema \
51 RABBITMQ_PLUGINS_EXPAND_DIR=$(RELEASE_UPGRADE_DATA)/rabbit_upgrade/plugins \
52 RABBITMQ_PLUGINS_DIR=$(RELEASE_FOR_UPGRADE)/plugins \
53 ./sbin/rabbitmq-server -detached
54 sleep 10
55
56 stop-release-for-upgrade:
57 cd $(RELEASE_FOR_UPGRADE) && \
58 RABBITMQ_HOME=$(RELEASE_FOR_UPGRADE) \
59 ./sbin/rabbitmqctl -n rabbit_upgrade stop
60
61 setup-release-for-upgrade: run-release-for-upgrade
62 RABBITMQCTL="$(RELEASE_FOR_UPGRADE)/sbin/rabbitmqctl -n rabbit_upgrade" \
63 UPGRADE_FROM_SCRIPT=$(UPGRADE_FROM_SCRIPT) \
64 QUEUES_COUNT_TRANSIENT=$(QUEUES_COUNT_TRANSIENT) \
65 QUEUES_COUNT_DURABLE=$(QUEUES_COUNT_DURABLE) \
66 MSGS_COUNT_NON_PERSISTENT=$(MSGS_COUNT_NON_PERSISTENT) \
67 MSGS_COUNT_PERSISTENT_INDEX=$(MSGS_COUNT_PERSISTENT_INDEX) \
68 MSGS_COUNT_PERSISTENT_STORE=$(MSGS_COUNT_PERSISTENT_STORE) \
69 INDEX_MSG_SIZE=$(INDEX_MSG_SIZE) \
70 STORE_MSG_SIZE=$(STORE_MSG_SIZE) \
71 bash $(UPGRADE_SCRIPTS_DIR)/upgrade-from.sh
72
73 prepare-release-for-upgrade: setup-release-for-upgrade stop-release-for-upgrade
74
75 run-broker-upgrade:
76 $(MAKE) -C ../ run-background-broker \
77 RABBITMQ_CONFIG_FILE=$(UPGRADE_CONFIG_DIR)/rabbitmq \
78 RABBITMQ_NODENAME=rabbit_upgrade \
79 TMPDIR=`dirname $(RELEASE_UPGRADE_DATA)` \
80 PLUGINS=rabbitmq_management
81 sleep 10
82
83 verify-upgrade: prepare-release-for-upgrade run-broker-upgrade
84 RABBITMQCTL="$(RELEASE_FOR_UPGRADE)/sbin/rabbitmqctl -n rabbit_upgrade" \
85 UPGRADE_TO_SCRIPT=$(UPGRADE_TO_SCRIPT) \
86 QUEUES_COUNT_TRANSIENT=$(QUEUES_COUNT_TRANSIENT) \
87 QUEUES_COUNT_DURABLE=$(QUEUES_COUNT_DURABLE) \
88 MSGS_COUNT_NON_PERSISTENT=$(MSGS_COUNT_NON_PERSISTENT) \
89 MSGS_COUNT_PERSISTENT_INDEX=$(MSGS_COUNT_PERSISTENT_INDEX) \
90 MSGS_COUNT_PERSISTENT_STORE=$(MSGS_COUNT_PERSISTENT_STORE) \
91 INDEX_MSG_SIZE=$(INDEX_MSG_SIZE) \
92 STORE_MSG_SIZE=$(STORE_MSG_SIZE) \
93 $(UPGRADE_SCRIPTS_DIR)/upgrade-to.sh
94
95 verify-and-stop: verify-upgrade
96 -$(MAKE) stop-release-for-upgrade
97
98 verify-and-cleanup: verify-and-stop
99 $(MAKE) clean
100
101 clean:
102 -$(MAKE) stop-release-for-upgrade
103 rm -rf rabbitmq_server*
104 rm -rf rabbitmq_r*
105 rm -rf rabbitmqadmin
106
107 distclean: clean
108 rm -rf rabbitmq-server-generic-unix-*
+0
-64
upgrade/README.md less more
0 ## Testing RabbitMQ upgrades.
1
2 This tool can be used to test upgrade of RabbitMQ to a branch version.
3 `rabbitmq-server` dependency should be checked out to the tested branch.
4
5 ### How it works:
6
7 This tool use GNU Make to run following steps:
8
9 - install generic unix release for specified version to `rabbitmq_r_<version>`
10 - start a server with configuration from `config` directory and data directory in `rabbitmq_server_upgrade_data`
11 - set up vhost, user, policies, exchange (fanout), queues (transient and durable)
12 - publish messages to queues (persistent to queue index, persistent to storage, not persistent)
13 - stop the server
14 - start local branch server using `run-background-broker`
15 - verify everything is in place (transient queues and messages are expected to be lost)
16 - stop local server
17
18 By default it will also clean up the test data.
19
20 ### Custom targets and configuration
21
22 Make targets:
23
24 - `run-release-for-upgrade` - download release and run it in `rabbitmq_r_<version>` with data in `rabbitmq_server_upgrade_data`
25 - `setup-release-for-upgrade` - run previous step and set up test data
26 - `prepare-release-for-upgrade` - run previous step and stop the server (can be used to build "pre upgrade" state)
27 - `run-broker-upgrade` - run previous step and run a current branch server (can be used to observe "after upgrade" state for manual verification)
28 - `verify-upgrade` - run previous step and verification script (that will consume published messages and delete some vhosts)
29 - `verify-and-stop` - run previous stap and stop the server
30 - `verify-and-cleanup` - run previous step and delete test data **this is the default step**
31
32 Additional targets:
33
34 - `clean` - stop server (if running) and delete temporary data
35 - `distclean` - same as `clean`, but also removes downloaded release package
36
37 Environment:
38
39 Following environment parameters can be used to configure upgrade validation:
40
41 | parameter | default | description |
42 |-----------------------------|---------|----------------------------------------------------------------|
43 | UPGRADE_FROM | 3.6.5 | Release version to upgrade from |
44 | QUEUES_COUNT_TRANSIENT | 1 | Number of transient queues |
45 | QUEUES_COUNT_DURABLE | 1 | Number of durable queues |
46 | MSGS_COUNT_NON_PERSISTENT | 10 | Number of transient messages to publish |
47 | MSGS_COUNT_PERSISTENT_INDEX | 10 | Number of persistent messages to publish to queue index |
48 | MSGS_COUNT_PERSISTENT_STORE | 10 | Number of persistent messages to publish to message store |
49 | INDEX_MSG_SIZE | 50 | Message size to fit queue index (depends on configuration) |
50 | STORE_MSG_SIZE | 150 | Message size to not fit queue index (depends on configuration) |
51
52 `INDEX_MSG_SIZE` and `STORE_MSG_SIZE` should be set to be more and less than `queue_index_embed_msgs_below` setting in `config/rabbitmq.config` file respectively.
53
54 Unsafe. Do not change without need:
55
56 | parameter | default | description |
57 |-----------|---------|-------------|
58 | UPGRADE_FROM_SCRIPT | 3.5 | Script to use for data setup. It's only `3.5` now used for both `3.5.x` and `3.6.x` releases |
59 | UPGRADE_TO_SCRIPT | 3.6 | Script to use for verification. Should correspond with branch version |
60 | RELEASE_ARCHIVE | rabbitmq-server-generic-unix-$(UPGRADE_FROM).tar.xz | Filename for release archive |
61 | RELEASE_FOR_UPGRADE_URL | http://www.rabbitmq.com/releases/rabbitmq-server/v$(UPGRADE_FROM)/$(RELEASE_ARCHIVE) | URL to load $RELEASE_ARCHIVE from. Should point to directly accessible (via wget) generic unix archive |
62
63
+0
-1
upgrade/config/enabled_plugins less more
0 [rabbitmq_management].
+0
-1
upgrade/config/rabbitmq.config less more
0 [{rabbit, [{queue_index_embed_msgs_below, 100}]}].
+0
-80
upgrade/scripts/upgrade-from-3.5-helpers.sh less more
0 setup_steps() {
1 local test_vhost='test'
2 local test_user='test_user'
3
4 ${RABBITMQCTL} add_user $test_user $test_user
5 ${RABBITMQCTL} set_user_tags $test_user policymaker
6 ${RABBITMQCTL} add_vhost $test_vhost
7 ${RABBITMQCTL} set_permissions -p $test_vhost $test_user '.*' '.*' '.*'
8 ${RABBITMQCTL} set_policy -p $test_vhost my_policy_name "policy.*" '{"max-length":300}'
9
10 # TODO: create exchanges, queues, publish messages
11 local exchange_name="test_exchange"
12 local queue_name_base="test_queue"
13
14 prepare_rabbitmqadmin
15
16 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost declare exchange name=$exchange_name type=fanout
17 for i in `seq 1 $QUEUES_COUNT_TRANSIENT`
18 do
19 local queue_name="${queue_name_base}_trans_${i}"
20 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost declare queue name=$queue_name durable=false
21 ./rabbitmqadmin -utest_user -ptest_user -V $test_vhost declare binding source=$exchange_name destination=$queue_name
22 done
23
24 for i in `seq 1 $QUEUES_COUNT_DURABLE`
25 do
26 local queue_name="${queue_name_base}_dur_${i}"
27 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost declare queue name=$queue_name durable=true
28 ./rabbitmqadmin -utest_user -ptest_user -V $test_vhost declare binding source=$exchange_name destination=$queue_name
29 done
30
31 local sequence_index=`seq 1 $INDEX_MSG_SIZE`
32 local msg_payload_index=`printf '=%.0s' $sequence_index`
33 local sequence_store=`seq 1 $STORE_MSG_SIZE`
34 local msg_payload_store=`printf '+%.0s' $sequence_store`
35 for i in `seq 1 $MSGS_COUNT_NON_PERSISTENT`
36 do
37 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost publish routing_key=any exchange=$exchange_name payload=$msg_payload
38 done
39
40 for i in `seq 1 $MSGS_COUNT_PERSISTENT_INDEX`
41 do
42 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost publish routing_key=any exchange=$exchange_name payload=$msg_payload_index properties='{"delivery_mode":2}'
43 done
44
45 for i in `seq 1 $MSGS_COUNT_PERSISTENT_STORE`
46 do
47 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost publish routing_key=any exchange=$exchange_name payload=$msg_payload_store properties='{"delivery_mode":2}'
48 done
49
50 # Second vhost to test data isolation
51
52 local test_vhost1="test_vhost_1"
53 local msg_payload_index_1=`printf '_%.0s' $sequence_index`
54 local msg_payload_store_1=`printf '0%.0s' $sequence_store`
55
56 ${RABBITMQCTL} add_vhost $test_vhost1
57 ${RABBITMQCTL} set_permissions -p $test_vhost1 $test_user '.*' '.*' '.*'
58
59 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost1 declare exchange name=$exchange_name type=fanout
60 for i in `seq 1 $QUEUES_COUNT_DURABLE`
61 do
62 local queue_name="${queue_name_base}_dur_vhost1_${i}"
63 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost1 declare queue name=$queue_name durable=true
64 ./rabbitmqadmin -utest_user -ptest_user -V $test_vhost1 declare binding source=$exchange_name destination=$queue_name
65 done
66 for i in `seq 1 $MSGS_COUNT_PERSISTENT_INDEX`
67 do
68 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost1 publish routing_key=any exchange=$exchange_name payload=$msg_payload_index_1 properties='{"delivery_mode":2}'
69 done
70
71 for i in `seq 1 $MSGS_COUNT_PERSISTENT_STORE`
72 do
73 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost1 publish routing_key=any exchange=$exchange_name payload=$msg_payload_store_1 properties='{"delivery_mode":2}'
74 done
75
76 ./rabbitmqadmin -utest_user -ptest_user -V $test_vhost list queues name durable messages
77 ./rabbitmqadmin -utest_user -ptest_user -V $test_vhost list exchanges
78 }
79
+0
-10
upgrade/scripts/upgrade-from.sh less more
0 #!/usr/bin/env bash
1
2 set -ex
3
4 echo "UPGRADE_FROM ${UPGRADE_FROM}"
5
6 . "$(dirname "$0")/upgrade-helpers.sh"
7 . "$(dirname "$0")/upgrade-from-${UPGRADE_FROM_SCRIPT}-helpers.sh"
8
9 setup_steps
+0
-5
upgrade/scripts/upgrade-helpers.sh less more
0 prepare_rabbitmqadmin() {
1 rm -rf rabbitmqadmin
2 wget localhost:15672/cli/rabbitmqadmin
3 chmod +x rabbitmqadmin
4 }
+0
-101
upgrade/scripts/upgrade-to-3.6-helpers.sh less more
0 verify_steps() {
1 local test_vhost='test'
2 local test_user='test_user'
3
4 ${RABBITMQCTL} list_users | grep $test_user
5 ${RABBITMQCTL} list_vhosts | grep $test_vhost
6 ${RABBITMQCTL} list_user_permissions $test_user | grep $test_vhost | grep -F ".*"
7 ${RABBITMQCTL} list_permissions -p $test_vhost | grep $test_user | grep -F ".*"
8 ${RABBITMQCTL} list_policies -p $test_vhost | \
9 grep $test_vhost | \
10 grep my_policy_name | \
11 grep -F "policy.*" | \
12 grep -F '{"max-length":300}'
13
14 local exchange_name="test_exchange"
15 local queue_name_base="test_queue"
16
17 prepare_rabbitmqadmin
18 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost list exchanges | grep $exchange_name
19
20 local sequence_index=`seq 1 $INDEX_MSG_SIZE`
21 local msg_payload_index=`printf '=%.0s' $sequence_index`
22 local sequence_store=`seq 1 $STORE_MSG_SIZE`
23 local msg_payload_store=`printf '+%.0s' $sequence_store`
24
25 # Durable queues survive upgrade
26 for i in `seq 1 $QUEUES_COUNT_DURABLE`
27 do
28 local queue_name="${queue_name_base}_dur_${i}"
29 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost list queues name durable | \
30 grep $queue_name | grep True
31 # Each queue have $MSGS_COUNT_PERSISTENT_INDEX + $MSGS_COUNT_PERSISTENT_STORE messages
32 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost list queues name messages | \
33 grep $queue_name | grep `expr $MSGS_COUNT_PERSISTENT_INDEX + $MSGS_COUNT_PERSISTENT_STORE`
34
35 # Drain persistent messages from queue index
36 for j in `seq 1 $MSGS_COUNT_PERSISTENT_INDEX`
37 do
38 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost get queue=$queue_name count=1 requeue=false | \
39 grep $msg_payload_index | grep $INDEX_MSG_SIZE | grep $exchange_name
40 done
41 # Drain persistent messages from message store
42 for j in `seq 1 $MSGS_COUNT_PERSISTENT_STORE`
43 do
44 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost get queue=$queue_name count=1 requeue=false | \
45 grep $msg_payload_store | grep $STORE_MSG_SIZE | grep $exchange_name
46 done
47 # No more messages
48 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost get queue=$queue_name count=1 requeue=false | \
49 grep "No items"
50
51 done
52
53 # Transient queues are deleted
54 (! ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost list queues name | grep "${queue_name_base}_trans_") || exit 2
55
56 # Test first vhost and check second
57 ${RABBITMQCTL} delete_vhost $test_vhost
58
59 # Also delete default vhost
60 ${RABBITMQCTL} delete_vhost /
61
62 local test_vhost1="test_vhost_1"
63 local msg_payload_index_1=`printf '_%.0s' $sequence_index`
64 local msg_payload_store_1=`printf '0%.0s' $sequence_store`
65
66 ${RABBITMQCTL} list_vhosts | grep $test_vhost1
67 ${RABBITMQCTL} list_user_permissions $test_user | grep $test_vhost1 | grep -F ".*"
68 ${RABBITMQCTL} list_permissions -p $test_vhost1 | grep $test_user | grep -F ".*"
69
70 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost1 list exchanges | grep $exchange_name
71
72 # Durable queues
73 for i in `seq 1 $QUEUES_COUNT_DURABLE`
74 do
75 local queue_name="${queue_name_base}_dur_vhost1_${i}"
76 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost1 list queues name durable | \
77 grep $queue_name | grep True
78 # Each queue have $MSGS_COUNT_PERSISTENT messages
79 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost1 list queues name messages | \
80 grep $queue_name | grep `expr $MSGS_COUNT_PERSISTENT_INDEX + $MSGS_COUNT_PERSISTENT_STORE`
81
82 # Drain persistent messages from queue index
83 for j in `seq 1 $MSGS_COUNT_PERSISTENT_INDEX`
84 do
85 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost1 get queue=$queue_name count=1 requeue=false | \
86 grep $msg_payload_index_1 | grep $INDEX_MSG_SIZE | grep $exchange_name
87 done
88 # Drain persistent messages from message store
89 for j in `seq 1 $MSGS_COUNT_PERSISTENT_STORE`
90 do
91 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost1 get queue=$queue_name count=1 requeue=false | \
92 grep $msg_payload_store_1 | grep $STORE_MSG_SIZE | grep $exchange_name
93 done
94 # No more messages
95 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost1 get queue=$queue_name count=1 requeue=false | \
96 grep "No items"
97 done
98
99 echo "Finish verify"
100 }
+0
-104
upgrade/scripts/upgrade-to-3.7-helpers.sh less more
0 verify_steps() {
1 local test_vhost='test'
2 local test_user='test_user'
3
4 ${RABBITMQCTL} list_users | grep $test_user
5 ${RABBITMQCTL} list_vhosts | grep $test_vhost
6 ${RABBITMQCTL} list_user_permissions $test_user | grep $test_vhost | grep -F ".*"
7 ${RABBITMQCTL} list_permissions -p $test_vhost | grep $test_user | grep -F ".*"
8 ${RABBITMQCTL} list_policies -p $test_vhost | \
9 grep $test_vhost | \
10 grep my_policy_name | \
11 grep -F "policy.*" | \
12 grep -F '{"max-length":300}'
13
14 local exchange_name="test_exchange"
15 local queue_name_base="test_queue"
16
17 prepare_rabbitmqadmin
18 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost list exchanges | grep $exchange_name
19
20 local sequence_index=`seq 1 $INDEX_MSG_SIZE`
21 local msg_payload_index=`printf '=%.0s' $sequence_index`
22 local sequence_store=`seq 1 $STORE_MSG_SIZE`
23 local msg_payload_store=`printf '+%.0s' $sequence_store`
24
25 # Durable queues survive upgrade
26 for i in `seq 1 $QUEUES_COUNT_DURABLE`
27 do
28 local queue_name="${queue_name_base}_dur_${i}"
29 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost list queues name durable | \
30 grep $queue_name | grep True
31 # Each queue have $MSGS_COUNT_PERSISTENT_INDEX + $MSGS_COUNT_PERSISTENT_STORE messages
32 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost list queues name messages | \
33 grep $queue_name | grep `expr $MSGS_COUNT_PERSISTENT_INDEX + $MSGS_COUNT_PERSISTENT_STORE`
34
35 # Drain persistent messages from queue index
36 for j in `seq 1 $MSGS_COUNT_PERSISTENT_INDEX`
37 do
38 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost get queue=$queue_name count=1 ackmode=ack_requeue_false | \
39 grep $msg_payload_index | grep $INDEX_MSG_SIZE | grep $exchange_name
40 done
41 # Drain persistent messages from message store
42 for j in `seq 1 $MSGS_COUNT_PERSISTENT_STORE`
43 do
44 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost get queue=$queue_name count=1 ackmode=ack_requeue_false | \
45 grep $msg_payload_store | grep $STORE_MSG_SIZE | grep $exchange_name
46 done
47 # No more messages
48 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost get queue=$queue_name count=1 ackmode=ack_requeue_false | \
49 grep "No items"
50
51 done
52
53 # Transient queues are deleted
54 (! ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost list queues name | grep "${queue_name_base}_trans_") || exit 2
55
56 # Test first vhost and check second
57 ${RABBITMQCTL} delete_vhost $test_vhost
58
59 # Also delete default vhost
60 ${RABBITMQCTL} delete_vhost /
61
62 local test_vhost1="test_vhost_1"
63 local msg_payload_index_1=`printf '_%.0s' $sequence_index`
64 local msg_payload_store_1=`printf '0%.0s' $sequence_store`
65
66 ${RABBITMQCTL} list_vhosts | grep $test_vhost1
67 ${RABBITMQCTL} list_user_permissions $test_user | grep $test_vhost1 | grep -F ".*"
68 ${RABBITMQCTL} list_permissions -p $test_vhost1 | grep $test_user | grep -F ".*"
69
70 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost1 list exchanges | grep $exchange_name
71
72 # Durable queues
73 for i in `seq 1 $QUEUES_COUNT_DURABLE`
74 do
75 local queue_name="${queue_name_base}_dur_vhost1_${i}"
76 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost1 list queues name durable | \
77 grep $queue_name | grep True
78 # Each queue have $MSGS_COUNT_PERSISTENT_INDEX + $MSGS_COUNT_PERSISTENT_STORE messages
79 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost1 list queues name messages | \
80 grep $queue_name | grep `expr $MSGS_COUNT_PERSISTENT_INDEX + $MSGS_COUNT_PERSISTENT_STORE`
81
82 # Drain persistent messages from queue index
83 for j in `seq 1 $MSGS_COUNT_PERSISTENT_INDEX`
84 do
85 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost1 get queue=$queue_name count=1 ackmode=ack_requeue_false | \
86 grep $msg_payload_index_1 | grep $INDEX_MSG_SIZE | grep $exchange_name
87 done
88 # Drain persistent messages from message store
89 for j in `seq 1 $MSGS_COUNT_PERSISTENT_STORE`
90 do
91 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost1 get queue=$queue_name count=1 ackmode=ack_requeue_false | \
92 grep $msg_payload_store_1 | grep $STORE_MSG_SIZE | grep $exchange_name
93 done
94 # No more messages
95 ./rabbitmqadmin -u $test_user -p $test_user -V $test_vhost1 get queue=$queue_name count=1 ackmode=ack_requeue_false | \
96 grep "No items"
97 done
98
99 echo "Finish verify"
100 }
101
102
103 ackmode=ack_requeue_false
+0
-8
upgrade/scripts/upgrade-to.sh less more
0 #!/usr/bin/env bash
1
2 set -ex
3
4 . "$(dirname "$0")/upgrade-helpers.sh"
5 . "$(dirname "$0")/upgrade-to-${UPGRADE_TO_SCRIPT}-helpers.sh"
6
7 verify_steps