Replaced exhaustive search with a binary lookup in watch list.
[erlang-gen_inotify.git] / erlang.mk
1 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
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 .PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
16
17 ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
18
19 ERLANG_MK_VERSION = 2017.04.25-6-g5922969
20
21 # Make 3.81 and 3.82 are deprecated.
22
23 #ifeq ($(MAKE_VERSION),3.81)
24 #$(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
25 #endif
26
27 #ifeq ($(MAKE_VERSION),3.82)
28 #$(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
29 #endif
30
31 # Core configuration.
32
33 PROJECT ?= $(notdir $(CURDIR))
34 PROJECT := $(strip $(PROJECT))
35
36 PROJECT_VERSION ?= rolling
37 PROJECT_MOD ?= $(PROJECT)_app
38 PROJECT_ENV ?= []
39
40 # Verbosity.
41
42 V ?= 0
43
44 verbose_0 = @
45 verbose_2 = set -x;
46 verbose = $(verbose_$(V))
47
48 gen_verbose_0 = @echo " GEN   " $@;
49 gen_verbose_2 = set -x;
50 gen_verbose = $(gen_verbose_$(V))
51
52 # Temporary files directory.
53
54 ERLANG_MK_TMP ?= $(CURDIR)/.erlang.mk
55 export ERLANG_MK_TMP
56
57 # "erl" command.
58
59 ERL = erl +A0 -noinput -boot start_clean
60
61 # Platform detection.
62
63 ifeq ($(PLATFORM),)
64 UNAME_S := $(shell uname -s)
65
66 ifeq ($(UNAME_S),Linux)
67 PLATFORM = linux
68 else ifeq ($(UNAME_S),Darwin)
69 PLATFORM = darwin
70 else ifeq ($(UNAME_S),SunOS)
71 PLATFORM = solaris
72 else ifeq ($(UNAME_S),GNU)
73 PLATFORM = gnu
74 else ifeq ($(UNAME_S),FreeBSD)
75 PLATFORM = freebsd
76 else ifeq ($(UNAME_S),NetBSD)
77 PLATFORM = netbsd
78 else ifeq ($(UNAME_S),OpenBSD)
79 PLATFORM = openbsd
80 else ifeq ($(UNAME_S),DragonFly)
81 PLATFORM = dragonfly
82 else ifeq ($(shell uname -o),Msys)
83 PLATFORM = msys2
84 else
85 $(error Unable to detect platform. Please open a ticket with the output of uname -a.)
86 endif
87
88 export PLATFORM
89 endif
90
91 # Core targets.
92
93 all:: deps app rel
94
95 # Noop to avoid a Make warning when there's nothing to do.
96 rel::
97         $(verbose) :
98
99 relup:: deps app
100
101 check:: tests
102
103 clean:: clean-crashdump
104
105 clean-crashdump:
106 ifneq ($(wildcard erl_crash.dump),)
107         $(gen_verbose) rm -f erl_crash.dump
108 endif
109
110 distclean:: clean distclean-tmp
111
112 distclean-tmp:
113         $(gen_verbose) rm -rf $(ERLANG_MK_TMP)
114
115 help::
116         $(verbose) printf "%s\n" \
117                 "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
118                 "Copyright (c) 2013-2016 Loïc Hoguin <essen@ninenines.eu>" \
119                 "" \
120                 "Usage: [V=1] $(MAKE) [target]..." \
121                 "" \
122                 "Core targets:" \
123                 "  all           Run deps, app and rel targets in that order" \
124                 "  app           Compile the project" \
125                 "  deps          Fetch dependencies (if needed) and compile them" \
126                 "  fetch-deps    Fetch dependencies recursively (if needed) without compiling them" \
127                 "  list-deps     List dependencies recursively on stdout" \
128                 "  search q=...  Search for a package in the built-in index" \
129                 "  rel           Build a release for this project, if applicable" \
130                 "  docs          Build the documentation for this project" \
131                 "  install-docs  Install the man pages for this project" \
132                 "  check         Compile and run all tests and analysis for this project" \
133                 "  tests         Run the tests for this project" \
134                 "  clean         Delete temporary and output files from most targets" \
135                 "  distclean     Delete all temporary and output files" \
136                 "  help          Display this help and exit" \
137                 "  erlang-mk     Update erlang.mk to the latest version"
138
139 # Core functions.
140
141 empty :=
142 space := $(empty) $(empty)
143 tab := $(empty) $(empty)
144 comma := ,
145
146 define newline
147
148
149 endef
150
151 define comma_list
152 $(subst $(space),$(comma),$(strip $(1)))
153 endef
154
155 # Adding erlang.mk to make Erlang scripts who call init:get_plain_arguments() happy.
156 define erlang
157 $(ERL) $(2) -pz $(ERLANG_MK_TMP)/rebar/ebin -eval "$(subst $(newline),,$(subst ",\",$(1)))" -- erlang.mk
158 endef
159
160 ifeq ($(PLATFORM),msys2)
161 core_native_path = $(subst \,\\\\,$(shell cygpath -w $1))
162 else
163 core_native_path = $1
164 endif
165
166 core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
167
168 core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
169
170 core_find = $(if $(wildcard $1),$(shell find $(1:%/=%) -type f -name $(subst *,\*,$2)))
171
172 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)))))))))))))))))))))))))))
173
174 core_ls = $(filter-out $(1),$(shell echo $(1)))
175
176 # @todo Use a solution that does not require using perl.
177 core_relpath = $(shell perl -e 'use File::Spec; print File::Spec->abs2rel(@ARGV) . "\n"' $1 $2)
178
179 # Automated update.
180
181 ERLANG_MK_REPO ?= https://github.com/ninenines/erlang.mk
182 ERLANG_MK_COMMIT ?=
183 ERLANG_MK_BUILD_CONFIG ?= build.config
184 ERLANG_MK_BUILD_DIR ?= .erlang.mk.build
185
186 erlang-mk:
187         git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR)
188 ifdef ERLANG_MK_COMMIT
189         cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT)
190 endif
191         if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi
192         $(MAKE) -C $(ERLANG_MK_BUILD_DIR)
193         cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
194         rm -rf $(ERLANG_MK_BUILD_DIR)
195
196 # The erlang.mk package index is bundled in the default erlang.mk build.
197 # Search for the string "copyright" to skip to the rest of the code.
198
199 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
200 # This file is part of erlang.mk and subject to the terms of the ISC License.
201
202 .PHONY: clean-app
203
204 # Configuration.
205
206 ERLC_OPTS ?= -Werror +debug_info +warn_export_vars +warn_shadow_vars \
207         +warn_obsolete_guard # +bin_opt_info +warn_export_all +warn_missing_spec
208 COMPILE_FIRST ?=
209 COMPILE_FIRST_PATHS = $(addprefix src/,$(addsuffix .erl,$(COMPILE_FIRST)))
210 ERLC_EXCLUDE ?=
211 ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))
212
213 ERLC_ASN1_OPTS ?=
214
215 ERLC_MIB_OPTS ?=
216 COMPILE_MIB_FIRST ?=
217 COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))
218
219 # Verbosity.
220
221 app_verbose_0 = @echo " APP   " $(PROJECT);
222 app_verbose_2 = set -x;
223 app_verbose = $(app_verbose_$(V))
224
225 appsrc_verbose_0 = @echo " APP   " $(PROJECT).app.src;
226 appsrc_verbose_2 = set -x;
227 appsrc_verbose = $(appsrc_verbose_$(V))
228
229 makedep_verbose_0 = @echo " DEPEND" $(PROJECT).d;
230 makedep_verbose_2 = set -x;
231 makedep_verbose = $(makedep_verbose_$(V))
232
233 erlc_verbose_0 = @echo " ERLC  " $(filter-out $(patsubst %,%.erl,$(ERLC_EXCLUDE)),\
234         $(filter %.erl %.core,$(?F)));
235 erlc_verbose_2 = set -x;
236 erlc_verbose = $(erlc_verbose_$(V))
237
238 xyrl_verbose_0 = @echo " XYRL  " $(filter %.xrl %.yrl,$(?F));
239 xyrl_verbose_2 = set -x;
240 xyrl_verbose = $(xyrl_verbose_$(V))
241
242 asn1_verbose_0 = @echo " ASN1  " $(filter %.asn1,$(?F));
243 asn1_verbose_2 = set -x;
244 asn1_verbose = $(asn1_verbose_$(V))
245
246 mib_verbose_0 = @echo " MIB   " $(filter %.bin %.mib,$(?F));
247 mib_verbose_2 = set -x;
248 mib_verbose = $(mib_verbose_$(V))
249
250 ifneq ($(wildcard src/),)
251
252 # Targets.
253
254 ifeq ($(wildcard ebin/test),)
255 app:: deps $(PROJECT).d
256         $(verbose) $(MAKE) --no-print-directory app-build
257 else
258 app:: clean deps $(PROJECT).d
259         $(verbose) $(MAKE) --no-print-directory app-build
260 endif
261
262 ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
263 define app_file
264 {application, '$(PROJECT)', [
265         {description, "$(PROJECT_DESCRIPTION)"},
266         {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
267         {id$(comma)$(space)"$(1)"}$(comma))
268         {modules, [$(call comma_list,$(2))]},
269         {registered, []},
270         {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
271         {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
272 ]}.
273 endef
274 else
275 define app_file
276 {application, '$(PROJECT)', [
277         {description, "$(PROJECT_DESCRIPTION)"},
278         {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP),
279         {id$(comma)$(space)"$(1)"}$(comma))
280         {modules, [$(call comma_list,$(2))]},
281         {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
282         {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
283         {mod, {$(PROJECT_MOD), []}},
284         {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
285 ]}.
286 endef
287 endif
288
289 app-build: ebin/$(PROJECT).app
290         $(verbose) :
291
292 # Source files.
293
294 ALL_SRC_FILES := $(sort $(call core_find,src/,*))
295
296 ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
297 CORE_FILES := $(filter %.core,$(ALL_SRC_FILES))
298
299 # ASN.1 files.
300
301 ifneq ($(wildcard asn1/),)
302 ASN1_FILES = $(sort $(call core_find,asn1/,*.asn1))
303 ERL_FILES += $(addprefix src/,$(patsubst %.asn1,%.erl,$(notdir $(ASN1_FILES))))
304
305 define compile_asn1
306         $(verbose) mkdir -p include/
307         $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(ERLC_ASN1_OPTS) $(1)
308         $(verbose) mv asn1/*.erl src/
309         $(verbose) mv asn1/*.hrl include/
310         $(verbose) mv asn1/*.asn1db include/
311 endef
312
313 $(PROJECT).d:: $(ASN1_FILES)
314         $(if $(strip $?),$(call compile_asn1,$?))
315 endif
316
317 # SNMP MIB files.
318
319 ifneq ($(wildcard mibs/),)
320 MIB_FILES = $(sort $(call core_find,mibs/,*.mib))
321
322 $(PROJECT).d:: $(COMPILE_MIB_FIRST_PATHS) $(MIB_FILES)
323         $(verbose) mkdir -p include/ priv/mibs/
324         $(mib_verbose) erlc -v $(ERLC_MIB_OPTS) -o priv/mibs/ -I priv/mibs/ $?
325         $(mib_verbose) erlc -o include/ -- $(addprefix priv/mibs/,$(patsubst %.mib,%.bin,$(notdir $?)))
326 endif
327
328 # Leex and Yecc files.
329
330 XRL_FILES := $(filter %.xrl,$(ALL_SRC_FILES))
331 XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES))))
332 ERL_FILES += $(XRL_ERL_FILES)
333
334 YRL_FILES := $(filter %.yrl,$(ALL_SRC_FILES))
335 YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
336 ERL_FILES += $(YRL_ERL_FILES)
337
338 $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
339         $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
340
341 # Erlang and Core Erlang files.
342
343 define makedep.erl
344         E = ets:new(makedep, [bag]),
345         G = digraph:new([acyclic]),
346         ErlFiles = lists:usort(string:tokens("$(ERL_FILES)", " ")),
347         Modules = [{list_to_atom(filename:basename(F, ".erl")), F} || F <- ErlFiles],
348         Add = fun (Mod, Dep) ->
349                 case lists:keyfind(Dep, 1, Modules) of
350                         false -> ok;
351                         {_, DepFile} ->
352                                 {_, ModFile} = lists:keyfind(Mod, 1, Modules),
353                                 ets:insert(E, {ModFile, DepFile}),
354                                 digraph:add_vertex(G, Mod),
355                                 digraph:add_vertex(G, Dep),
356                                 digraph:add_edge(G, Mod, Dep)
357                 end
358         end,
359         AddHd = fun (F, Mod, DepFile) ->
360                 case file:open(DepFile, [read]) of
361                         {error, enoent} -> ok;
362                         {ok, Fd} ->
363                                 F(F, Fd, Mod),
364                                 {_, ModFile} = lists:keyfind(Mod, 1, Modules),
365                                 ets:insert(E, {ModFile, DepFile})
366                 end
367         end,
368         Attr = fun
369                 (F, Mod, behavior, Dep) -> Add(Mod, Dep);
370                 (F, Mod, behaviour, Dep) -> Add(Mod, Dep);
371                 (F, Mod, compile, {parse_transform, Dep}) -> Add(Mod, Dep);
372                 (F, Mod, compile, Opts) when is_list(Opts) ->
373                         case proplists:get_value(parse_transform, Opts) of
374                                 undefined -> ok;
375                                 Dep -> Add(Mod, Dep)
376                         end;
377                 (F, Mod, include, Hrl) ->
378                         case filelib:is_file("include/" ++ Hrl) of
379                                 true -> AddHd(F, Mod, "include/" ++ Hrl);
380                                 false ->
381                                         case filelib:is_file("src/" ++ Hrl) of
382                                                 true -> AddHd(F, Mod, "src/" ++ Hrl);
383                                                 false -> false
384                                         end
385                         end;
386                 (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
387                 (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl);
388                 (F, Mod, import, {Imp, _}) ->
389                         IsFile =
390                                 case lists:keyfind(Imp, 1, Modules) of
391                                         false -> false;
392                                         {_, FilePath} -> filelib:is_file(FilePath)
393                                 end,
394                         case IsFile of
395                                 false -> ok;
396                                 true -> Add(Mod, Imp)
397                         end;
398                 (_, _, _, _) -> ok
399         end,
400         MakeDepend = fun(F, Fd, Mod) ->
401                 case io:parse_erl_form(Fd, undefined) of
402                         {ok, {attribute, _, Key, Value}, _} ->
403                                 Attr(F, Mod, Key, Value),
404                                 F(F, Fd, Mod);
405                         {eof, _} ->
406                                 file:close(Fd);
407                         _ ->
408                                 F(F, Fd, Mod)
409                 end
410         end,
411         [begin
412                 Mod = list_to_atom(filename:basename(F, ".erl")),
413                 {ok, Fd} = file:open(F, [read]),
414                 MakeDepend(MakeDepend, Fd, Mod)
415         end || F <- ErlFiles],
416         Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),
417         CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],
418         TargetPath = fun(Target) ->
419                 case lists:keyfind(Target, 1, Modules) of
420                         false -> "";
421                         {_, DepFile} ->
422                                 DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),
423                                 string:join(DirSubname ++ [atom_to_list(Target)], "/")
424                 end
425         end,
426         ok = file:write_file("$(1)", [
427                 [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend],
428                 "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"
429         ]),
430         halt()
431 endef
432
433 ifeq ($(if $(NO_MAKEDEP),$(wildcard $(PROJECT).d),),)
434 $(PROJECT).d:: $(ERL_FILES) $(call core_find,include/,*.hrl) $(MAKEFILE_LIST)
435         $(makedep_verbose) $(call erlang,$(call makedep.erl,$@))
436 endif
437
438 ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
439 # Rebuild everything when the Makefile changes.
440 $(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
441         $(verbose) mkdir -p $(ERLANG_MK_TMP)
442         $(verbose) if test -f $@; then \
443                 touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
444                 touch -c $(PROJECT).d; \
445         fi
446         $(verbose) touch $@
447
448 $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
449 ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
450 endif
451
452 include $(wildcard $(PROJECT).d)
453
454 ebin/$(PROJECT).app:: ebin/
455
456 ebin/:
457         $(verbose) mkdir -p ebin/
458
459 define compile_erl
460         $(erlc_verbose) erlc -v $(if $(IS_DEP),$(filter-out -Werror,$(ERLC_OPTS)),$(ERLC_OPTS)) -o ebin/ \
461                 -pa ebin/ -I include/ $(filter-out $(ERLC_EXCLUDE_PATHS),$(COMPILE_FIRST_PATHS) $(1))
462 endef
463
464 ebin/$(PROJECT).app:: $(ERL_FILES) $(CORE_FILES) $(wildcard src/$(PROJECT).app.src)
465         $(eval FILES_TO_COMPILE := $(filter-out src/$(PROJECT).app.src,$?))
466         $(if $(strip $(FILES_TO_COMPILE)),$(call compile_erl,$(FILES_TO_COMPILE)))
467         $(eval GITDESCRIBE := $(shell git describe --dirty --abbrev=7 --tags --always --first-parent 2>/dev/null || true))
468         $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
469                 $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
470 ifeq ($(wildcard src/$(PROJECT).app.src),)
471         $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
472                 > ebin/$(PROJECT).app
473 else
474         $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
475                 echo "Empty modules entry not found in $(PROJECT).app.src. Please consult the erlang.mk README for instructions." >&2; \
476                 exit 1; \
477         fi
478         $(appsrc_verbose) cat src/$(PROJECT).app.src \
479                 | sed "s/{[[:space:]]*modules[[:space:]]*,[[:space:]]*\[\]}/{modules, \[$(call comma_list,$(MODULES))\]}/" \
480                 | sed "s/{id,[[:space:]]*\"git\"}/{id, \"$(subst /,\/,$(GITDESCRIBE))\"}/" \
481                 > ebin/$(PROJECT).app
482 endif
483
484 clean:: clean-app
485
486 clean-app:
487         $(gen_verbose) rm -rf $(PROJECT).d ebin/ priv/mibs/ $(XRL_ERL_FILES) $(YRL_ERL_FILES) \
488                 $(addprefix include/,$(patsubst %.mib,%.hrl,$(notdir $(MIB_FILES)))) \
489                 $(addprefix include/,$(patsubst %.asn1,%.hrl,$(notdir $(ASN1_FILES)))) \
490                 $(addprefix include/,$(patsubst %.asn1,%.asn1db,$(notdir $(ASN1_FILES)))) \
491                 $(addprefix src/,$(patsubst %.asn1,%.erl,$(notdir $(ASN1_FILES))))
492
493 endif
494
495 # Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
496 # Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
497 # This file is part of erlang.mk and subject to the terms of the ISC License.
498
499 .PHONY: docs-deps
500
501 # Configuration.
502
503 ALL_DOC_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(DOC_DEPS))
504
505 # Targets.
506
507 $(foreach dep,$(DOC_DEPS),$(eval $(call dep_target,$(dep))))
508
509 ifneq ($(SKIP_DEPS),)
510 doc-deps:
511 else
512 doc-deps: $(ALL_DOC_DEPS_DIRS)
513         $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
514 endif
515
516 # Copyright (c) 2015-2016, Loïc Hoguin <essen@ninenines.eu>
517 # This file is part of erlang.mk and subject to the terms of the ISC License.
518
519 .PHONY: rebar.config
520
521 # We strip out -Werror because we don't want to fail due to
522 # warnings when used as a dependency.
523
524 compat_prepare_erlc_opts = $(shell echo "$1" | sed 's/, */,/g')
525
526 define compat_convert_erlc_opts
527 $(if $(filter-out -Werror,$1),\
528         $(if $(findstring +,$1),\
529                 $(shell echo $1 | cut -b 2-)))
530 endef
531
532 define compat_erlc_opts_to_list
533 [$(call comma_list,$(foreach o,$(call compat_prepare_erlc_opts,$1),$(call compat_convert_erlc_opts,$o)))]
534 endef
535
536 define compat_rebar_config
537 {deps, [
538 $(call comma_list,$(foreach d,$(DEPS),\
539         $(if $(filter hex,$(call dep_fetch,$d)),\
540                 {$(call dep_name,$d)$(comma)"$(call dep_repo,$d)"},\
541                 {$(call dep_name,$d)$(comma)".*"$(comma){git,"$(call dep_repo,$d)"$(comma)"$(call dep_commit,$d)"}})))
542 ]}.
543 {erl_opts, $(call compat_erlc_opts_to_list,$(ERLC_OPTS))}.
544 endef
545
546 $(eval _compat_rebar_config = $$(compat_rebar_config))
547 $(eval export _compat_rebar_config)
548
549 rebar.config:
550         $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
551
552 # Copyright (c) 2014-2016, Loïc Hoguin <essen@ninenines.eu>
553 # This file is part of erlang.mk and subject to the terms of the ISC License.
554
555 .PHONY: clean-c_src distclean-c_src-env
556
557 # Configuration.
558
559 C_SRC_DIR ?= $(CURDIR)/c_src
560 C_SRC_ENV ?= $(C_SRC_DIR)/env.mk
561 C_SRC_OUTPUT ?= $(CURDIR)/priv/$(PROJECT)
562 C_SRC_TYPE ?= shared
563
564 # System type and C compiler/flags.
565
566 ifeq ($(PLATFORM),msys2)
567         C_SRC_OUTPUT_EXECUTABLE_EXTENSION ?= .exe
568         C_SRC_OUTPUT_SHARED_EXTENSION ?= .dll
569 else
570         C_SRC_OUTPUT_EXECUTABLE_EXTENSION ?=
571         C_SRC_OUTPUT_SHARED_EXTENSION ?= .so
572 endif
573
574 ifeq ($(C_SRC_TYPE),shared)
575         C_SRC_OUTPUT_FILE = $(C_SRC_OUTPUT)$(C_SRC_OUTPUT_SHARED_EXTENSION)
576 else
577         C_SRC_OUTPUT_FILE = $(C_SRC_OUTPUT)$(C_SRC_OUTPUT_EXECUTABLE_EXTENSION)
578 endif
579
580 ifeq ($(PLATFORM),msys2)
581 # We hardcode the compiler used on MSYS2. The default CC=cc does
582 # not produce working code. The "gcc" MSYS2 package also doesn't.
583         CC = /mingw64/bin/gcc
584         export CC
585         CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes
586         CXXFLAGS ?= -O3 -finline-functions -Wall
587 else ifeq ($(PLATFORM),darwin)
588         CC ?= cc
589         CFLAGS ?= -O3 -std=c99 -arch x86_64 -finline-functions -Wall -Wmissing-prototypes
590         CXXFLAGS ?= -O3 -arch x86_64 -finline-functions -Wall
591         LDFLAGS ?= -arch x86_64 -flat_namespace -undefined suppress
592 else ifeq ($(PLATFORM),freebsd)
593         CC ?= cc
594         CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes
595         CXXFLAGS ?= -O3 -finline-functions -Wall
596 else ifeq ($(PLATFORM),linux)
597         CC ?= gcc
598         CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes
599         CXXFLAGS ?= -O3 -finline-functions -Wall
600 endif
601
602 ifneq ($(PLATFORM),msys2)
603         CFLAGS += -fPIC
604         CXXFLAGS += -fPIC
605 endif
606
607 CFLAGS += -I"$(ERTS_INCLUDE_DIR)" -I"$(ERL_INTERFACE_INCLUDE_DIR)"
608 CXXFLAGS += -I"$(ERTS_INCLUDE_DIR)" -I"$(ERL_INTERFACE_INCLUDE_DIR)"
609
610 LDLIBS += -L"$(ERL_INTERFACE_LIB_DIR)" -lerl_interface -lei
611
612 # Verbosity.
613
614 c_verbose_0 = @echo " C     " $(?F);
615 c_verbose = $(c_verbose_$(V))
616
617 cpp_verbose_0 = @echo " CPP   " $(?F);
618 cpp_verbose = $(cpp_verbose_$(V))
619
620 link_verbose_0 = @echo " LD    " $(@F);
621 link_verbose = $(link_verbose_$(V))
622
623 # Targets.
624
625 ifeq ($(wildcard $(C_SRC_DIR)),)
626 else ifneq ($(wildcard $(C_SRC_DIR)/Makefile),)
627 app:: app-c_src
628
629 test-build:: app-c_src
630
631 app-c_src:
632         $(MAKE) -C $(C_SRC_DIR)
633
634 clean::
635         $(MAKE) -C $(C_SRC_DIR) clean
636
637 else
638
639 ifeq ($(SOURCES),)
640 SOURCES := $(sort $(foreach pat,*.c *.C *.cc *.cpp,$(call core_find,$(C_SRC_DIR)/,$(pat))))
641 endif
642 OBJECTS = $(addsuffix .o, $(basename $(SOURCES)))
643
644 COMPILE_C = $(c_verbose) $(CC) $(CFLAGS) $(CPPFLAGS) -c
645 COMPILE_CPP = $(cpp_verbose) $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c
646
647 app:: $(C_SRC_ENV) $(C_SRC_OUTPUT_FILE)
648
649 test-build:: $(C_SRC_ENV) $(C_SRC_OUTPUT_FILE)
650
651 $(C_SRC_OUTPUT_FILE): $(OBJECTS)
652         $(verbose) mkdir -p priv/
653         $(link_verbose) $(CC) $(OBJECTS) \
654                 $(LDFLAGS) $(if $(filter $(C_SRC_TYPE),shared),-shared) $(LDLIBS) \
655                 -o $(C_SRC_OUTPUT_FILE)
656
657 %.o: %.c
658         $(COMPILE_C) $(OUTPUT_OPTION) $<
659
660 %.o: %.cc
661         $(COMPILE_CPP) $(OUTPUT_OPTION) $<
662
663 %.o: %.C
664         $(COMPILE_CPP) $(OUTPUT_OPTION) $<
665
666 %.o: %.cpp
667         $(COMPILE_CPP) $(OUTPUT_OPTION) $<
668
669 clean:: clean-c_src
670
671 clean-c_src:
672         $(gen_verbose) rm -f $(C_SRC_OUTPUT_FILE) $(OBJECTS)
673
674 endif
675
676 ifneq ($(wildcard $(C_SRC_DIR)),)
677 $(C_SRC_ENV):
678         $(verbose) $(ERL) -eval "file:write_file(\"$(call core_native_path,$(C_SRC_ENV))\", \
679                 io_lib:format( \
680                         \"ERTS_INCLUDE_DIR ?= ~s/erts-~s/include/~n\" \
681                         \"ERL_INTERFACE_INCLUDE_DIR ?= ~s~n\" \
682                         \"ERL_INTERFACE_LIB_DIR ?= ~s~n\", \
683                         [code:root_dir(), erlang:system_info(version), \
684                         code:lib_dir(erl_interface, include), \
685                         code:lib_dir(erl_interface, lib)])), \
686                 halt()."
687
688 distclean:: distclean-c_src-env
689
690 distclean-c_src-env:
691         $(gen_verbose) rm -f $(C_SRC_ENV)
692
693 -include $(C_SRC_ENV)
694 endif
695
696 # Templates.
697
698 define bs_c_nif
699 #include "erl_nif.h"
700
701 static int loads = 0;
702
703 static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
704 {
705         /* Initialize private data. */
706         *priv_data = NULL;
707
708         loads++;
709
710         return 0;
711 }
712
713 static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info)
714 {
715         /* Convert the private data to the new version. */
716         *priv_data = *old_priv_data;
717
718         loads++;
719
720         return 0;
721 }
722
723 static void unload(ErlNifEnv* env, void* priv_data)
724 {
725         if (loads == 1) {
726                 /* Destroy the private data. */
727         }
728
729         loads--;
730 }
731
732 static ERL_NIF_TERM hello(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
733 {
734         if (enif_is_atom(env, argv[0])) {
735                 return enif_make_tuple2(env,
736                         enif_make_atom(env, "hello"),
737                         argv[0]);
738         }
739
740         return enif_make_tuple2(env,
741                 enif_make_atom(env, "error"),
742                 enif_make_atom(env, "badarg"));
743 }
744
745 static ErlNifFunc nif_funcs[] = {
746         {"hello", 1, hello}
747 };
748
749 ERL_NIF_INIT($n, nif_funcs, load, NULL, upgrade, unload)
750 endef
751
752 define bs_erl_nif
753 -module($n).
754
755 -export([hello/1]).
756
757 -on_load(on_load/0).
758 on_load() ->
759         PrivDir = case code:priv_dir(?MODULE) of
760                 {error, _} ->
761                         AppPath = filename:dirname(filename:dirname(code:which(?MODULE))),
762                         filename:join(AppPath, "priv");
763                 Path ->
764                         Path
765         end,
766         erlang:load_nif(filename:join(PrivDir, atom_to_list(?MODULE)), 0).
767
768 hello(_) ->
769         erlang:nif_error({not_loaded, ?MODULE}).
770 endef
771
772 new-nif:
773 ifneq ($(wildcard $(C_SRC_DIR)/$n.c),)
774         $(error Error: $(C_SRC_DIR)/$n.c already exists)
775 endif
776 ifneq ($(wildcard src/$n.erl),)
777         $(error Error: src/$n.erl already exists)
778 endif
779 ifdef in
780         $(verbose) $(MAKE) -C $(APPS_DIR)/$(in)/ new-nif n=$n in=
781 else
782         $(verbose) mkdir -p $(C_SRC_DIR) src/
783         $(call render_template,bs_c_nif,$(C_SRC_DIR)/$n.c)
784         $(call render_template,bs_erl_nif,src/$n.erl)
785 endif
786
787 # Copyright (c) 2013-2016, Loïc Hoguin <essen@ninenines.eu>
788 # This file is part of erlang.mk and subject to the terms of the ISC License.
789
790 .PHONY: distclean-edoc edoc
791
792 # Configuration.
793
794 EDOC_OPTS ?=
795
796 # Core targets.
797
798 ifneq ($(wildcard doc/overview.edoc),)
799 docs:: edoc
800 endif
801
802 distclean:: distclean-edoc
803
804 # Plugin-specific targets.
805
806 edoc: distclean-edoc doc-deps
807         $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'
808
809 distclean-edoc:
810         $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info