1%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
2%% ex: ts=4 sw=4 et
3%% -------------------------------------------------------------------
4%%
5%% rebar: Erlang Build Tools
6%%
7%% Copyright (c) 2009, 2010 Dave Smith (dizzyd@dizzyd.com)
8%%
9%% Permission is hereby granted, free of charge, to any person obtaining a copy
10%% of this software and associated documentation files (the "Software"), to deal
11%% in the Software without restriction, including without limitation the rights
12%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13%% copies of the Software, and to permit persons to whom the Software is
14%% furnished to do so, subject to the following conditions:
15%%
16%% The above copyright notice and this permission notice shall be included in
17%% all copies or substantial portions of the Software.
18%%
19%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25%% THE SOFTWARE.
26%% -------------------------------------------------------------------
27-module(rebar_dia_compiler).
28
29-export([compile/2,
30         clean/2]).
31
32%% for internal use only
33-export([info/2]).
34
35-include("rebar.hrl").
36
37%% ===================================================================
38%% Public API
39%% ===================================================================
40
41-spec compile(rebar_config:config(), file:filename()) -> 'ok'.
42compile(Config, _AppFile) ->
43    DiaOpts = rebar_config:get(Config, dia_opts, []),
44    IncludeEbin = proplists:get_value(include, DiaOpts, []),
45    DiaFiles = filelib:wildcard("dia/*.dia"),
46    code:add_pathsz(["ebin" | IncludeEbin]),
47    FileSequence = case rebar_config:get(Config, dia_first_files, []) of
48        [] ->
49            DiaFiles;
50        CompileFirst ->
51            CompileFirst ++
52            [F || F <- DiaFiles, not lists:member(F, CompileFirst)]
53    end,
54    rebar_base_compiler:run(Config, FileSequence,
55                            "dia", ".dia", "src", ".erl",
56                            fun compile_dia/3).
57
58-spec clean(rebar_config:config(), file:filename()) -> 'ok'.
59clean(Config, _AppFile) ->
60    DiaOpts = rebar_config:get(Config, dia_opts, []),
61    IncludeEbin = proplists:get_value(include, DiaOpts, []),
62    code:add_pathsz(["ebin" | IncludeEbin]),
63    GeneratedFiles = dia_generated_files("dia", "src", "include"),
64    ok = rebar_file_utils:delete_each(GeneratedFiles),
65    ok.
66
67%% ===================================================================
68%% Internal functions
69%% ===================================================================
70
71info(help, compile) ->
72    info_help("Build Diameter (*.dia) sources");
73info(help, clean) ->
74    info_help("Delete generated Diameter files").
75
76info_help(Description) ->
77    ?CONSOLE(
78       "~s.~n"
79       "~n"
80       "Valid rebar.config options:~n"
81       "  {dia_opts, []} (options from diameter_make:codec/2 supported with~n"
82       "                  exception of inherits)~n"
83       "  {dia_first_files, []} (files in sequence to compile first)~n",
84       [Description]).
85
86-spec compile_dia(file:filename(), file:filename(),
87                   rebar_config:config()) -> ok.
88compile_dia(Source, Target, Config) ->
89    ok = filelib:ensure_dir(Target),
90    ok = filelib:ensure_dir(filename:join("include", "dummy.hrl")),
91    ok = filelib:ensure_dir(filename:join("ebin", "dummy.beam")),
92    true = code:add_path(filename:absname("ebin")),
93    Opts = [{outdir, "src"}] ++ rebar_config:get(Config, dia_opts, []),
94    case diameter_dict_util:parse({path, Source}, []) of
95        {ok, Spec} ->
96            FileName = dia_filename(Source, Spec),
97            _ = diameter_codegen:from_dict(FileName, Spec, Opts, erl),
98            _ = diameter_codegen:from_dict(FileName, Spec, Opts, hrl),
99            HrlFile = filename:join("src", FileName ++ ".hrl"),
100            ErlFile = filename:join("src", FileName ++ ".erl"),
101            ErlCOpts = [{outdir, "ebin"}] ++
102                        rebar_config:get(Config, erl_opts, []),
103            _ = compile:file(ErlFile, ErlCOpts),
104            case filelib:is_regular(HrlFile) of
105                true ->
106                    ok = rebar_file_utils:mv(HrlFile, "include");
107                false ->
108                    ok
109            end;
110        {error, Reason} ->
111            ?ABORT(
112                "Compiling ~s failed: ~s~n",
113                [Source, diameter_dict_util:format_error(Reason)]
114            )
115    end.
116
117dia_generated_files(DiaDir, SrcDir, IncDir) ->
118    F = fun(File, Acc) ->
119            case catch diameter_dict_util:parse({path, File}, []) of
120                {ok, Spec} ->
121                    FileName = dia_filename(File, Spec),
122                    [
123                        filename:join([IncDir, FileName ++ ".hrl"]) |
124                        filelib:wildcard(
125                            filename:join([SrcDir, FileName ++ ".*"])
126                        )
127                    ] ++ Acc;
128                _ ->
129                    Acc
130            end
131    end,
132    lists:foldl(F, [], filelib:wildcard(filename:join([DiaDir, "*.dia"]))).
133
134dia_filename(File, Spec) ->
135    case proplists:get_value(name, Spec) of
136        undefined ->
137            filename:rootname(filename:basename(File));
138        Name ->
139            Name
140    end.
141