summaryrefslogtreecommitdiff
path: root/src/rebar_base_compiler.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/rebar_base_compiler.erl')
-rw-r--r--src/rebar_base_compiler.erl108
1 files changed, 108 insertions, 0 deletions
diff --git a/src/rebar_base_compiler.erl b/src/rebar_base_compiler.erl
index 5d54057..02d567c 100644
--- a/src/rebar_base_compiler.erl
+++ b/src/rebar_base_compiler.erl
@@ -36,21 +36,65 @@
format_error_source/2]).
-define(DEFAULT_COMPILER_SOURCE_FORMAT, relative).
+-type desc() :: term().
+-type loc() :: {line(), col()} | line().
+-type line() :: integer().
+-type col() :: integer().
+-type err_or_warn() :: {module(), desc()} | {loc(), module(), desc()}.
+
+-type compile_fn_ret() :: ok | {ok, [string()]} | skipped | term().
+-type compile_fn() :: fun((file:filename(), [{_,_}] | rebar_dict()) -> compile_fn_ret()).
+-type compile_fn3() :: fun((file:filename(), file:filename(), [{_,_}] | rebar_dict())
+ -> compile_fn_ret()).
+-type error_tuple() :: {error, [string()], [string()]}.
+-export_type([compile_fn/0, compile_fn_ret/0, error_tuple/0]).
%% ===================================================================
%% Public API
%% ===================================================================
+%% @doc Runs a compile job, applying `compile_fn()' to all files,
+%% starting with `First' files, and then `RestFiles'.
+-spec run(rebar_dict() | [{_,_}] , [First], [Next], compile_fn()) ->
+ compile_fn_ret() when
+ First :: file:filename(),
+ Next :: file:filename().
run(Config, FirstFiles, RestFiles, CompileFn) ->
%% Compile the first files in sequence
compile_each(FirstFiles++RestFiles, Config, CompileFn).
+%% @doc Runs a compile job, applying `compile_fn3()' to all files,
+%% starting with `First' files, and then the other content of `SourceDir'.
+%% Files looked for are those ending in `SourceExt'. Results of the
+%% compilation are put in `TargetDir' with the base file names
+%% postfixed with `SourceExt'.
+-spec run(rebar_dict() | [{_,_}] , [First], SourceDir, SourceExt,
+ TargetDir, TargetExt, compile_fn3()) -> compile_fn_ret() when
+ First :: file:filename(),
+ SourceDir :: file:filename(),
+ TargetDir :: file:filename(),
+ SourceExt :: string(),
+ TargetExt :: string().
run(Config, FirstFiles, SourceDir, SourceExt, TargetDir, TargetExt,
Compile3Fn) ->
run(Config, FirstFiles, SourceDir, SourceExt, TargetDir, TargetExt,
Compile3Fn, [check_last_mod]).
+%% @doc Runs a compile job, applying `compile_fn3()' to all files,
+%% starting with `First' files, and then the other content of `SourceDir'.
+%% Files looked for are those ending in `SourceExt'. Results of the
+%% compilation are put in `TargetDir' with the base file names
+%% postfixed with `SourceExt'.
+%% Additional compile options can be passed in the last argument as
+%% a proplist.
+-spec run(rebar_dict() | [{_,_}] , [First], SourceDir, SourceExt,
+ TargetDir, TargetExt, compile_fn3(), [term()]) -> compile_fn_ret() when
+ First :: file:filename(),
+ SourceDir :: file:filename(),
+ TargetDir :: file:filename(),
+ SourceExt :: string(),
+ TargetExt :: string().
run(Config, FirstFiles, SourceDir, SourceExt, TargetDir, TargetExt,
Compile3Fn, Opts) ->
%% Convert simple extension to proper regex
@@ -73,13 +117,26 @@ run(Config, FirstFiles, SourceDir, SourceExt, TargetDir, TargetExt,
simple_compile_wrapper(S, Target, Compile3Fn, C, CheckLastMod)
end).
+%% @doc Format good compiler results with warnings to work with
+%% module internals. Assumes that warnings are not treated as errors.
+-spec ok_tuple(file:filename(), [string()]) -> {ok, [string()]}.
ok_tuple(Source, Ws) ->
{ok, format_warnings(Source, Ws)}.
+%% @doc format error and warning strings for a given source file
+%% according to user preferences.
+-spec error_tuple(file:filename(), [Err], [Warn], rebar_dict() | [{_,_}]) ->
+ error_tuple() when
+ Err :: string(),
+ Warn :: string().
error_tuple(Source, Es, Ws, Opts) ->
{error, format_errors(Source, Es),
format_warnings(Source, Ws, Opts)}.
+%% @doc from a given path, and based on the user-provided options,
+%% format the file path according to the preferences.
+-spec format_error_source(file:filename(), rebar_dict() | [{_,_}]) ->
+ file:filename().
format_error_source(Path, Opts) ->
Type = case rebar_opts:get(Opts, compiler_source_format,
?DEFAULT_COMPILER_SOURCE_FORMAT) of
@@ -98,6 +155,8 @@ format_error_source(Path, Opts) ->
rebar_dir:make_relative_path(resolve_linked_source(Path), Cwd)
end.
+%% @private takes a filename and canonicalizes its path if it is a link.
+-spec resolve_linked_source(file:filename()) -> file:filename().
resolve_linked_source(Src) ->
{Dir, Base} = rebar_file_utils:split_dirname(Src),
filename:join(rebar_file_utils:resolve_link(Dir), Base).
@@ -106,6 +165,11 @@ resolve_linked_source(Src) ->
%% Internal functions
%% ===================================================================
+%% @private if a check for last modifications is required, do the verification
+%% and possibly skip the compile job.
+-spec simple_compile_wrapper(Source, Target, compile_fn3(), [{_,_}] | rebar_dict(), boolean()) -> compile_fn_ret() when
+ Source :: file:filename(),
+ Target :: file:filename().
simple_compile_wrapper(Source, Target, Compile3Fn, Config, false) ->
Compile3Fn(Source, Target, Config);
simple_compile_wrapper(Source, Target, Compile3Fn, Config, true) ->
@@ -116,18 +180,42 @@ simple_compile_wrapper(Source, Target, Compile3Fn, Config, true) ->
skipped
end.
+%% @private take a basic source set of file fragments and a target location,
+%% create a file path and name for a compile artifact.
+-spec target_file(SourceFile, SourceDir, SourceExt, TargetDir, TargetExt) -> File when
+ SourceFile :: file:filename(),
+ SourceDir :: file:filename(),
+ TargetDir :: file:filename(),
+ SourceExt :: string(),
+ TargetExt :: string(),
+ File :: file:filename().
target_file(SourceFile, SourceDir, SourceExt, TargetDir, TargetExt) ->
BaseFile = remove_common_path(SourceFile, SourceDir),
filename:join([TargetDir, filename:basename(BaseFile, SourceExt) ++ TargetExt]).
+%% @private removes the common prefix between two file paths.
+%% The remainder of the first file path passed will have its ending returned
+%% when either path starts diverging.
+-spec remove_common_path(file:filename(), file:filename()) -> file:filename().
remove_common_path(Fname, Path) ->
remove_common_path1(filename:split(Fname), filename:split(Path)).
+%% @private given two lists of file fragments, discard the identical
+%% prefixed sections, and return the final bit of the first operand
+%% as a filename.
+-spec remove_common_path1([string()], [string()]) -> file:filename().
remove_common_path1([Part | RestFilename], [Part | RestPath]) ->
remove_common_path1(RestFilename, RestPath);
remove_common_path1(FilenameParts, _) ->
filename:join(FilenameParts).
+%% @private runs the compile function `CompileFn' on every file
+%% passed internally, along with the related project configuration.
+%% If any errors are encountered, they're reported to stdout.
+-spec compile_each([file:filename()], Config, CompileFn) -> Ret | no_return() when
+ Config :: [{_,_}] | rebar_dict(),
+ CompileFn :: compile_fn(),
+ Ret :: compile_fn_ret().
compile_each([], _Config, _CompileFn) ->
ok;
compile_each([Source | Rest], Config, CompileFn) ->
@@ -148,12 +236,19 @@ compile_each([Source | Rest], Config, CompileFn) ->
end,
compile_each(Rest, Config, CompileFn).
+%% @private Formats and returns errors ready to be output.
+-spec format_errors(string(), [err_or_warn()]) -> [string()].
format_errors(Source, Errors) ->
format_errors(Source, "", Errors).
+%% @private Formats and returns warning strings ready to be output.
+-spec format_warnings(string(), [err_or_warn()]) -> [string()].
format_warnings(Source, Warnings) ->
format_warnings(Source, Warnings, []).
+%% @private Formats and returns warnings; chooses the distinct format they
+%% may have based on whether `warnings_as_errors' option is on.
+-spec format_warnings(string(), [err_or_warn()], rebar_dict() | [{_,_}]) -> [string()].
format_warnings(Source, Warnings, Opts) ->
%% `Opts' can be passed in both as a list or a dictionary depending
%% on whether the first call to rebar_erlc_compiler was done with
@@ -167,6 +262,11 @@ format_warnings(Source, Warnings, Opts) ->
end,
format_errors(Source, Prefix, Warnings).
+%% @private output compiler errors if they're judged to be reportable.
+-spec maybe_report(Reportable | term()) -> ok when
+ Reportable :: {{error, error_tuple()}, Source} | error_tuple() | ErrProps,
+ ErrProps :: [{error, string()} | Source, ...],
+ Source :: {source, string()}.
maybe_report({{error, {error, _Es, _Ws}=ErrorsAndWarnings}, {source, _}}) ->
maybe_report(ErrorsAndWarnings);
maybe_report([{error, E}, {source, S}]) ->
@@ -177,15 +277,23 @@ maybe_report({error, Es, Ws}) ->
maybe_report(_) ->
ok.
+%% @private Outputs a bunch of strings, including a newline
+-spec report([string()]) -> ok.
report(Messages) ->
lists:foreach(fun(Msg) -> io:format("~s~n", [Msg]) end, Messages).
+%% private format compiler errors into proper outputtable strings
+-spec format_errors(_, Extra, [err_or_warn()]) -> [string()] when
+ Extra :: string().
format_errors(_MainSource, Extra, Errors) ->
[begin
[format_error(Source, Extra, Desc) || Desc <- Descs]
end
|| {Source, Descs} <- Errors].
+%% @private format compiler errors into proper outputtable strings
+-spec format_error(file:filename(), Extra, err_or_warn()) -> string() when
+ Extra :: string().
format_error(Source, Extra, {{Line, Column}, Mod, Desc}) ->
ErrorDesc = Mod:format_error(Desc),
?FMT("~s:~w:~w: ~s~s~n", [Source, Line, Column, Extra, ErrorDesc]);