| 1 | %%% Copyright (C) Dominic Williams |
|---|
| 2 | %%% All rights reserved. |
|---|
| 3 | %%% See file COPYING. |
|---|
| 4 | |
|---|
| 5 | -module (compiler). |
|---|
| 6 | -export ([init/2]). |
|---|
| 7 | -export ([init/3]). |
|---|
| 8 | -export ([reset_includes/2]). |
|---|
| 9 | |
|---|
| 10 | init (Notify, Dirs) -> |
|---|
| 11 | init (Notify, Dirs, []). |
|---|
| 12 | |
|---|
| 13 | init (Notify, Dirs, Options) -> |
|---|
| 14 | Watchers = lists: foldl (fun watcher/2, [], Dirs), |
|---|
| 15 | Includes = [{i, D} || D <- Dirs], |
|---|
| 16 | State = {dict: new (), dict: new (), [], [], Includes ++ Options}, |
|---|
| 17 | loop (Notify, Watchers, State). |
|---|
| 18 | |
|---|
| 19 | watcher (Dir, Acc) -> |
|---|
| 20 | Args = [Dir, notify_me ()], |
|---|
| 21 | Watcher = spawn_link (directory_watcher, init_recursive, Args), |
|---|
| 22 | [Watcher | Acc]. |
|---|
| 23 | |
|---|
| 24 | loop (Notify, Watchers, State) -> |
|---|
| 25 | receive |
|---|
| 26 | check -> |
|---|
| 27 | [W ! check || W <- Watchers], |
|---|
| 28 | {Modules, Includes, Binaries, Removed, Options} = State, |
|---|
| 29 | Received = receive_files (Modules, Includes), |
|---|
| 30 | {New_modules, New_includes} = Received, |
|---|
| 31 | New_options = reset_includes (New_includes, Options), |
|---|
| 32 | New_state = {New_modules, New_includes, Binaries, |
|---|
| 33 | Removed, New_options}, |
|---|
| 34 | Last_state = |
|---|
| 35 | if |
|---|
| 36 | New_modules /= Modules -> |
|---|
| 37 | compile (Notify, New_state); |
|---|
| 38 | true -> |
|---|
| 39 | New_state |
|---|
| 40 | end, |
|---|
| 41 | loop (Notify, Watchers, Last_state); |
|---|
| 42 | {Pid, stop} -> |
|---|
| 43 | Pid ! {self (), bye} |
|---|
| 44 | end. |
|---|
| 45 | |
|---|
| 46 | receive_files (Modules, Includes) -> |
|---|
| 47 | %% Includes = sets: from_list ([D || {i, D} <- Options]), |
|---|
| 48 | receive |
|---|
| 49 | {watcher, {{file, ".erl"}, File, Event}} -> |
|---|
| 50 | receive_files (dict: store (File, Event, Modules), Includes); |
|---|
| 51 | {watcher, {{file, ".hrl"}, File, Event}} -> |
|---|
| 52 | receive_files (Modules, dict: store (File, Event, Includes)); |
|---|
| 53 | {watcher, _} -> |
|---|
| 54 | receive_files (Modules, Includes) |
|---|
| 55 | after 500 -> |
|---|
| 56 | {Modules, Includes} |
|---|
| 57 | end. |
|---|
| 58 | |
|---|
| 59 | compile (Notify, State) -> |
|---|
| 60 | {Modules, _, _, _, _} = State, |
|---|
| 61 | Notify (totals (Modules)), |
|---|
| 62 | Process = process_fun (Notify), |
|---|
| 63 | Processed = dict: fold (Process, State, Modules), |
|---|
| 64 | notify_end (Notify, Processed). |
|---|
| 65 | |
|---|
| 66 | reset_includes (Dict, Options) -> |
|---|
| 67 | dict: fold (fun reset_includes/3, Options, Dict). |
|---|
| 68 | |
|---|
| 69 | reset_includes (File, found, Options) -> |
|---|
| 70 | Is_include = fun ({i, _}) -> true; (_) -> false end, |
|---|
| 71 | {Includes, Other} = lists: partition (Is_include, Options), |
|---|
| 72 | Dirs = sets: from_list ([D || {i, D} <- Includes]), |
|---|
| 73 | Dir = filename: dirname (File), |
|---|
| 74 | New_dirs = sets: add_element (Dir, Dirs), |
|---|
| 75 | New_includes = [{i, D} || D <- sets: to_list (New_dirs)], |
|---|
| 76 | Other ++ New_includes; |
|---|
| 77 | reset_includes (_, _, Options) -> |
|---|
| 78 | Options. |
|---|
| 79 | |
|---|
| 80 | process_fun (Notify) -> |
|---|
| 81 | fun (File, Event, Acc) when Event == found; Event == changed -> |
|---|
| 82 | {Modules, Includes, Binaries, Removed, Options} = Acc, |
|---|
| 83 | Result = modules: compile (File, Options), |
|---|
| 84 | notify_result (Result, Notify), |
|---|
| 85 | New_modules = dict: store (File, Result, Modules), |
|---|
| 86 | Notify (totals (New_modules)), |
|---|
| 87 | New_binaries = binaries (Result, Binaries), |
|---|
| 88 | {New_modules, Includes, New_binaries, Removed, Options}; |
|---|
| 89 | (File, Event, Acc) when Event == lost -> |
|---|
| 90 | {Modules, Includes, Binaries, Removed, Options} = Acc, |
|---|
| 91 | New_modules = dict: erase (File, Modules), |
|---|
| 92 | New_removed = [modules: module_name (File) | Removed], |
|---|
| 93 | {New_modules, Includes, Binaries, New_removed, Options}; |
|---|
| 94 | (_, _, Acc) -> |
|---|
| 95 | Acc |
|---|
| 96 | end. |
|---|
| 97 | |
|---|
| 98 | notify_me () -> |
|---|
| 99 | Self = self (), |
|---|
| 100 | fun (E) -> Self ! {watcher, E} end. |
|---|
| 101 | |
|---|
| 102 | totals (Modules) -> |
|---|
| 103 | dict: fold (fun count/3, {0, 0, 0}, Modules). |
|---|
| 104 | |
|---|
| 105 | count (_, lost, Acc) -> |
|---|
| 106 | Acc; |
|---|
| 107 | count (_, {ok, _, _, _}, {Total, Compiled, Successful}) -> |
|---|
| 108 | {Total + 1, Compiled + 1, Successful + 1}; |
|---|
| 109 | count (_, {error, _, _}, {Total, Compiled, Successful}) -> |
|---|
| 110 | {Total + 1, Compiled + 1, Successful}; |
|---|
| 111 | count (_, _, {Total, Compiled, Successful}) -> |
|---|
| 112 | {Total + 1, Compiled, Successful}. |
|---|
| 113 | |
|---|
| 114 | notify_result ({ok, _, _, []}, _) -> |
|---|
| 115 | pass; |
|---|
| 116 | notify_result ({ok, _, _, Warnings}, Notify) -> |
|---|
| 117 | Notify ({[], Warnings}); |
|---|
| 118 | notify_result ({error, Errors, Warnings},Notify) -> |
|---|
| 119 | Notify ({Errors, Warnings}). |
|---|
| 120 | |
|---|
| 121 | binaries ({ok, _, B, _}, Bs) -> |
|---|
| 122 | [B | Bs]; |
|---|
| 123 | binaries ({error, _, _}, Bs) -> |
|---|
| 124 | Bs. |
|---|
| 125 | |
|---|
| 126 | notify_end (Notify, State) -> |
|---|
| 127 | {Modules, _, _, _, _} = State, |
|---|
| 128 | notify_end (Notify, totals (Modules), State). |
|---|
| 129 | |
|---|
| 130 | notify_end (Notify, {N, N, N}, {Modules, Includes, Binaries, Removed, Options}) -> |
|---|
| 131 | Notify ({{binaries, Binaries}, {removed, Removed}}), |
|---|
| 132 | {Modules, Includes, [], [], Options}; |
|---|
| 133 | notify_end (_, _, State) -> |
|---|
| 134 | State. |
|---|