2009-11-25 22:23:42 +00:00
|
|
|
%% -------------------------------------------------------------------
|
|
|
|
%%
|
|
|
|
%% rebar: Erlang Build Tools
|
|
|
|
%%
|
|
|
|
%% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com)
|
|
|
|
%%
|
|
|
|
%% 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:
|
|
|
|
%%
|
|
|
|
%% The above copyright notice and this permission notice shall be included in
|
|
|
|
%% all copies or substantial portions of the Software.
|
|
|
|
%%
|
|
|
|
%% 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.
|
|
|
|
%% -------------------------------------------------------------------
|
2009-11-26 04:00:22 +00:00
|
|
|
-module(rebar_erlc_compiler).
|
2009-11-25 22:23:42 +00:00
|
|
|
|
|
|
|
-export([compile/2,
|
|
|
|
clean/2]).
|
|
|
|
|
2009-12-05 22:18:09 +00:00
|
|
|
%% make available for rebar_eunit until there is a better option
|
2009-12-21 17:48:36 +00:00
|
|
|
-export([do_compile/8, compile_opts/2, list_hrls/2]).
|
2009-12-05 22:18:09 +00:00
|
|
|
|
2009-11-25 22:23:42 +00:00
|
|
|
-include("rebar.hrl").
|
|
|
|
|
|
|
|
%% ===================================================================
|
|
|
|
%% Public API
|
|
|
|
%% ===================================================================
|
|
|
|
|
2009-11-30 14:00:48 +00:00
|
|
|
compile(Config, _AppFile) ->
|
2009-11-25 23:03:14 +00:00
|
|
|
do_compile(Config, "src/*.erl", "ebin", ".erl", ".beam",
|
2009-12-21 17:15:21 +00:00
|
|
|
fun list_hrls/2, fun compile_erl/2,
|
2009-11-26 04:55:47 +00:00
|
|
|
rebar_config:get_list(Config, erl_first_files, [])),
|
2009-11-25 23:03:14 +00:00
|
|
|
do_compile(Config, "mibs/*.mib", "priv/mibs", ".mib", ".bin",
|
2009-12-21 17:15:21 +00:00
|
|
|
undefined, fun compile_mib/2,
|
2009-11-26 04:55:47 +00:00
|
|
|
rebar_config:get_list(Config, mib_first_files, [])).
|
2009-11-25 22:23:42 +00:00
|
|
|
|
2009-11-30 14:00:48 +00:00
|
|
|
clean(_Config, _AppFile) ->
|
2009-11-26 03:24:51 +00:00
|
|
|
%% TODO: This would be more portable if it used Erlang to traverse
|
|
|
|
%% the dir structure and delete each file; however it would also
|
|
|
|
%% much slower.
|
|
|
|
[] = os:cmd("rm -f ebin/*.beam priv/mibs/*.bin"),
|
2009-11-25 23:03:14 +00:00
|
|
|
ok.
|
2009-11-25 22:23:42 +00:00
|
|
|
|
|
|
|
|
2009-11-26 03:24:51 +00:00
|
|
|
|
2009-11-25 22:23:42 +00:00
|
|
|
%% ===================================================================
|
|
|
|
%% Internal functions
|
|
|
|
%% ===================================================================
|
2009-11-25 23:03:14 +00:00
|
|
|
|
2009-12-21 17:15:21 +00:00
|
|
|
do_compile(Config, SrcWildcard, OutDir, InExt, OutExt,
|
|
|
|
IncludeFn, CompileFn, FirstFiles) ->
|
2009-11-25 23:03:14 +00:00
|
|
|
case filelib:wildcard(SrcWildcard) of
|
|
|
|
[] ->
|
|
|
|
ok;
|
2009-11-26 04:55:47 +00:00
|
|
|
FoundFiles when is_list(FoundFiles) ->
|
|
|
|
%% Ensure that the FirstFiles are compiled first; drop them from the
|
2009-12-02 03:34:40 +00:00
|
|
|
%% FoundFiles and compile them in sequence
|
|
|
|
FirstTargets = [{Fs, target_file(Fs, OutDir, InExt, OutExt)} || Fs <- FirstFiles],
|
|
|
|
RestTargets = [{Fs, target_file(Fs, OutDir, InExt, OutExt)} ||
|
|
|
|
Fs <- drop_each(FirstFiles, FoundFiles)],
|
|
|
|
|
2009-12-30 12:13:39 +00:00
|
|
|
%% Make sure target directory exists
|
2009-12-02 03:34:40 +00:00
|
|
|
ok = filelib:ensure_dir(target_file(hd(FoundFiles), OutDir, InExt, OutExt)),
|
2009-11-29 23:44:30 +00:00
|
|
|
|
2009-12-02 03:34:40 +00:00
|
|
|
%% Compile first targets in sequence
|
2009-12-21 17:15:21 +00:00
|
|
|
compile_each(FirstTargets, Config, IncludeFn, CompileFn),
|
2009-12-30 12:13:39 +00:00
|
|
|
|
2009-12-02 03:34:40 +00:00
|
|
|
%% Spin up workers
|
|
|
|
Self = self(),
|
|
|
|
Pids = [spawn_monitor(fun() -> compile_worker(Self) end) || _I <- lists:seq(1,3)],
|
|
|
|
|
|
|
|
%% Process rest of targets
|
2009-12-21 17:15:21 +00:00
|
|
|
compile_queue(Pids, RestTargets, Config, IncludeFn, CompileFn)
|
2009-11-25 23:03:14 +00:00
|
|
|
end.
|
|
|
|
|
2009-11-26 04:55:47 +00:00
|
|
|
drop_each([], List) ->
|
|
|
|
List;
|
|
|
|
drop_each([Member | Rest], List) ->
|
|
|
|
drop_each(Rest, lists:delete(Member, List)).
|
2009-11-25 23:03:14 +00:00
|
|
|
|
2009-12-21 17:15:21 +00:00
|
|
|
compile_each([], _Config, _IncludeFn, _CompileFn) ->
|
2009-11-25 23:03:14 +00:00
|
|
|
ok;
|
2009-12-21 17:15:21 +00:00
|
|
|
compile_each([{Src, Target} | Rest], Config, IncludeFn, CompileFn) ->
|
|
|
|
case needs_compile(Src, Target, IncludeFn, Config) of
|
2009-11-25 23:03:14 +00:00
|
|
|
true ->
|
|
|
|
?CONSOLE("Compiling ~s\n", [Src]),
|
|
|
|
CompileFn(Src, Config);
|
|
|
|
false ->
|
2009-11-29 23:44:30 +00:00
|
|
|
?INFO("Skipping ~s\n", [Src]),
|
2009-11-25 23:03:14 +00:00
|
|
|
ok
|
|
|
|
end,
|
2009-12-21 17:15:21 +00:00
|
|
|
compile_each(Rest, Config, IncludeFn, CompileFn).
|
2009-12-30 12:13:39 +00:00
|
|
|
|
2009-12-21 17:15:21 +00:00
|
|
|
needs_compile(Src, Target, IncludeFn, Config) ->
|
|
|
|
TargetLM = filelib:last_modified(Target),
|
|
|
|
case TargetLM < filelib:last_modified(Src) of
|
|
|
|
true ->
|
|
|
|
true;
|
|
|
|
false ->
|
|
|
|
if is_function(IncludeFn) ->
|
|
|
|
lists:any(fun(I) ->
|
|
|
|
TargetLM < filelib:last_modified(I)
|
|
|
|
end,
|
|
|
|
IncludeFn(Src, Config));
|
|
|
|
true ->
|
|
|
|
false
|
|
|
|
end
|
|
|
|
end.
|
2009-11-25 23:03:14 +00:00
|
|
|
|
|
|
|
target_file(F, TargetDir, InExt, OutExt) ->
|
|
|
|
filename:join([TargetDir, filename:basename(F, InExt) ++ OutExt]).
|
|
|
|
|
2009-11-29 23:44:30 +00:00
|
|
|
compile_opts(Config, Key) ->
|
|
|
|
rebar_config:get_list(Config, Key, []).
|
2009-11-25 23:03:14 +00:00
|
|
|
|
2009-12-21 17:15:21 +00:00
|
|
|
list_hrls(Src, Config) ->
|
|
|
|
case epp:open(Src, include_path(Src, Config)) of
|
2009-12-30 12:13:39 +00:00
|
|
|
{ok, Epp} ->
|
2009-12-21 17:15:21 +00:00
|
|
|
%% check include for erlang files
|
|
|
|
extract_includes(Epp, Src);
|
|
|
|
_ ->
|
|
|
|
false
|
|
|
|
end.
|
|
|
|
|
|
|
|
include_path(Src, Config) ->
|
|
|
|
[filename:dirname(Src)|compile_opts(Config, i)].
|
|
|
|
|
|
|
|
extract_includes(Epp, Src) ->
|
|
|
|
case epp:parse_erl_form(Epp) of
|
|
|
|
{ok, {attribute, 1, file, {Src, 1}}} ->
|
|
|
|
extract_includes(Epp, Src);
|
|
|
|
{ok, {attribute, 1, file, {IncFile, 1}}} ->
|
|
|
|
[IncFile|extract_includes(Epp, Src)];
|
|
|
|
{ok, _} ->
|
|
|
|
extract_includes(Epp, Src);
|
|
|
|
{eof, _} ->
|
|
|
|
epp:close(Epp),
|
|
|
|
[];
|
|
|
|
{error, _Error} ->
|
|
|
|
extract_includes(Epp, Src)
|
|
|
|
end.
|
|
|
|
|
2009-11-25 23:03:14 +00:00
|
|
|
compile_erl(Source, Config) ->
|
2009-12-14 14:27:47 +00:00
|
|
|
Opts = [{i, "include"}, {outdir, "ebin"}, report, return] ++ compile_opts(Config, erl_opts),
|
2009-11-29 23:44:30 +00:00
|
|
|
case compile:file(Source, Opts) of
|
2009-12-14 14:27:47 +00:00
|
|
|
{ok, _, []} ->
|
2009-11-25 23:03:14 +00:00
|
|
|
ok;
|
2009-12-14 14:27:47 +00:00
|
|
|
{ok, _, _Warnings} ->
|
|
|
|
%% We got at least one warning -- if fail_on_warning is in options, fail
|
|
|
|
case lists:member(fail_on_warning, Opts) of
|
|
|
|
true ->
|
|
|
|
?FAIL;
|
|
|
|
false ->
|
|
|
|
ok
|
|
|
|
end;
|
|
|
|
_ ->
|
2009-11-25 23:03:14 +00:00
|
|
|
?FAIL
|
|
|
|
end.
|
|
|
|
|
|
|
|
compile_mib(Source, Config) ->
|
2009-11-29 23:44:30 +00:00
|
|
|
Opts = [{outdir, "priv/mibs"}, {i, ["priv/mibs"]}] ++ compile_opts(Config, mib_opts),
|
|
|
|
case snmpc:compile(Source, Opts) of
|
2009-11-25 23:03:14 +00:00
|
|
|
{ok, _} ->
|
|
|
|
ok;
|
|
|
|
{error, compilation_failed} ->
|
|
|
|
?FAIL
|
|
|
|
end.
|
2009-12-02 03:34:40 +00:00
|
|
|
|
2009-12-21 17:15:21 +00:00
|
|
|
compile_queue([], [], _Config, _IncludeFn, _CompileFn) ->
|
2009-12-02 03:34:40 +00:00
|
|
|
ok;
|
2009-12-21 17:15:21 +00:00
|
|
|
compile_queue(Pids, Targets, Config, IncludeFn, CompileFn) ->
|
2009-12-02 03:34:40 +00:00
|
|
|
receive
|
|
|
|
{next, Worker} ->
|
|
|
|
case Targets of
|
|
|
|
[] ->
|
|
|
|
Worker ! empty,
|
2009-12-21 17:15:21 +00:00
|
|
|
compile_queue(Pids, Targets, Config, IncludeFn, CompileFn);
|
2009-12-02 03:34:40 +00:00
|
|
|
[{Src, Target} | Rest] ->
|
2009-12-21 17:15:21 +00:00
|
|
|
Worker ! {compile, Src, Target, Config, IncludeFn, CompileFn},
|
|
|
|
compile_queue(Pids, Rest, Config, IncludeFn, CompileFn)
|
2009-12-02 03:34:40 +00:00
|
|
|
end;
|
2009-12-14 14:27:47 +00:00
|
|
|
|
|
|
|
{fail, Error} ->
|
|
|
|
?DEBUG("Worker compilation failed: ~p\n", [Error]),
|
|
|
|
?FAIL;
|
2009-12-30 12:13:39 +00:00
|
|
|
|
2009-12-02 03:34:40 +00:00
|
|
|
{compiled, Source} ->
|
|
|
|
?CONSOLE("Compiled ~s\n", [Source]),
|
2009-12-21 17:15:21 +00:00
|
|
|
compile_queue(Pids, Targets, Config, IncludeFn, CompileFn);
|
2009-12-30 12:13:39 +00:00
|
|
|
|
2009-12-02 03:34:40 +00:00
|
|
|
{'DOWN', Mref, _, Pid, normal} ->
|
|
|
|
?DEBUG("Worker exited cleanly\n", []),
|
|
|
|
Pids2 = lists:delete({Pid, Mref}, Pids),
|
2009-12-21 17:15:21 +00:00
|
|
|
compile_queue(Pids2, Targets, Config, IncludeFn, CompileFn);
|
2009-12-30 12:13:39 +00:00
|
|
|
|
2009-12-14 13:58:22 +00:00
|
|
|
{'DOWN', _Mref, _, _Pid, Info} ->
|
2009-12-02 03:34:40 +00:00
|
|
|
?DEBUG("Worker failed: ~p\n", [Info]),
|
|
|
|
?FAIL
|
|
|
|
end.
|
|
|
|
|
|
|
|
compile_worker(QueuePid) ->
|
|
|
|
QueuePid ! {next, self()},
|
|
|
|
receive
|
2009-12-21 17:15:21 +00:00
|
|
|
{compile, Src, Target, Config, IncludeFn, CompileFn} ->
|
|
|
|
case needs_compile(Src, Target, IncludeFn, Config) of
|
2009-12-02 03:34:40 +00:00
|
|
|
true ->
|
2009-12-14 14:27:47 +00:00
|
|
|
case catch(CompileFn(Src, Config)) of
|
|
|
|
ok ->
|
|
|
|
QueuePid ! {compiled, Src},
|
|
|
|
compile_worker(QueuePid);
|
|
|
|
Error ->
|
|
|
|
QueuePid ! {fail, Error},
|
|
|
|
ok
|
|
|
|
end;
|
2009-12-02 03:34:40 +00:00
|
|
|
false ->
|
|
|
|
?INFO("Skipping ~s\n", [Src]),
|
2009-12-14 14:27:47 +00:00
|
|
|
compile_worker(QueuePid)
|
|
|
|
end;
|
|
|
|
|
2009-12-02 03:34:40 +00:00
|
|
|
empty ->
|
|
|
|
ok
|
|
|
|
end.
|