├── .gitignore ├── README.md └── src ├── rebar_elixir_compiler.erl ├── rebar_elixir_plugin.app.src └── rebar_exunit.erl /.gitignore: -------------------------------------------------------------------------------- 1 | ebin 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## rebar_elixir_plugin 2 | 3 | This plugin allows you to compile Elixir files in your Erlang projects using Rebar. 4 | 5 | ## Installation 6 | 7 | To install this plugin, you need to add it as a dependency to your rebar application: 8 | 9 | {deps, [{ rebar_elixir_plugin, ".*", 10 | {git, "git://github.com/yrashk/rebar_elixir_plugin"}}]}. 11 | 12 | %% Let rebar know about the new plugins 13 | {plugins, [rebar_elixir_compiler, rebar_exunit] }. 14 | 15 | This plugin depends on Elixir too, so you need to add it as a rebar dependency: 16 | 17 | {deps, [{ elixir, "0.9.*", 18 | {git, "git://github.com/elixir-lang/elixir"}}]}. 19 | 20 | Elixir is structured similarly to Erlang's OTP. It is divided into applications that are placed inside the `lib` directory, as seen on its [source code repository](https://github.com/elixir-lang/elixir). We need to explicitly tell rebar so: 21 | 22 | {lib_dirs, [ 23 | "deps/elixir/lib" 24 | ]}. 25 | 26 | This should be all. 27 | 28 | ## Available configuration 29 | 30 | * `ex_first_files` - first elixir files to be configured; 31 | * `src_dirs` - where to find elixir source; 32 | * `ex_opts` - elixir compilation options: `ignore_module_conflict` (default to true), `docs` (default to false) and `debug_info` (default to false); 33 | 34 | ## License 35 | 36 | Copyright 2012 Yurii Rashkovskii 37 | 38 | Permission is hereby granted, free of charge, to any person obtaining 39 | a copy of this software and associated documentation files (the 40 | "Software"), to deal in the Software without restriction, including 41 | without limitation the rights to use, copy, modify, merge, publish, 42 | distribute, sublicense, and/or sell copies of the Software, and to 43 | permit persons to whom the Software is furnished to do so, subject to 44 | the following conditions: 45 | 46 | The above copyright notice and this permission notice shall be 47 | included in all copies or substantial portions of the Software. 48 | 49 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 50 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 51 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 52 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 53 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 54 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 55 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 56 | -------------------------------------------------------------------------------- /src/rebar_elixir_compiler.erl: -------------------------------------------------------------------------------- 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_elixir_compiler). 28 | 29 | -export([pre_compile/2, 30 | clean/2, 31 | pre_eunit/2]). 32 | 33 | -export([dotex_compile/2, 34 | dotex_compile/3]). 35 | 36 | %% =================================================================== 37 | %% Public API 38 | %% =================================================================== 39 | 40 | %% Supported configuration variables: 41 | %% 42 | %% * ex_first_files - First elixir files to compile 43 | %% * elixir_opts - Erlang list of elixir compiler options 44 | %% 45 | %% For example, {elixir_opts, [{ignore_module_conflict, false}]} 46 | %% 47 | 48 | -spec pre_compile(Config::rebar_config:config(), AppFile::file:filename()) -> 'ok'. 49 | pre_compile(Config, _AppFile) -> 50 | dotex_compile(Config, "ebin"). 51 | 52 | -spec clean(Config::rebar_config:config(), AppFile::file:filename()) -> 'ok'. 53 | clean(_Config, _AppFile) -> 54 | BeamFiles = rebar_utils:find_files("ebin", "^.*\\.beam\$"), 55 | rebar_file_utils:delete_each(BeamFiles), 56 | lists:foreach(fun(Dir) -> delete_dir(Dir, dirs(Dir)) end, dirs("ebin")), 57 | ok. 58 | 59 | -spec pre_eunit(Config::rebar_config:config(), AppFile::file:filename()) -> 'ok'. 60 | pre_eunit(Config, _AppFIle) -> 61 | dotex_compile(Config, ".eunit"). 62 | 63 | %% =================================================================== 64 | %% .ex Compilation API 65 | %% =================================================================== 66 | 67 | -spec dotex_compile(Config::rebar_config:config(), 68 | OutDir::file:filename()) -> 'ok'. 69 | dotex_compile(Config, OutDir) -> 70 | dotex_compile(Config, OutDir, []). 71 | 72 | dotex_compile(Config, OutDir, MoreSources) -> 73 | App = application:load(elixir), 74 | Loaded = (App == ok orelse App == {error, {already_loaded, elixir}}) and 75 | (code:ensure_loaded(elixir) == {module, elixir}), 76 | case Loaded of 77 | true -> 78 | case lists:member({ensure_all_started,1}, 79 | application:module_info(exports)) of 80 | true -> 81 | {ok,_} = application:ensure_all_started(elixir); 82 | _ -> 83 | ok = application:start(elixir) 84 | end, 85 | FirstExs = rebar_config:get_local(Config, ex_first_files, []), 86 | ExOpts = ex_opts(Config), 87 | %% Support the src_dirs option allowing multiple directories to 88 | %% contain elixir source. This might be used, for example, should 89 | %% eunit tests be separated from the core application source. 90 | SrcDirs = src_dirs(proplists:append_values(src_dirs, ExOpts)), 91 | RestExs = [Source || Source <- gather_src(SrcDirs, []) ++ MoreSources, 92 | not lists:member(Source, FirstExs)], 93 | 94 | %% Make sure that ebin/ exists and is on the path 95 | filelib:ensure_dir(filename:join(OutDir, ".")), 96 | CurrPath = code:get_path(), 97 | code:add_path(filename:absname(OutDir)), 98 | 99 | EbinDate = begin 100 | {ok, Files} = file:list_dir(OutDir), 101 | Dates = [ filelib:last_modified(filename:join([OutDir, F])) || F <- Files, case F of "Elixir." ++ _ -> true; _ -> false end ], 102 | case Dates of 103 | [] -> 0; 104 | _ -> 105 | lists:max(Dates) 106 | end 107 | end, 108 | 109 | compile(FirstExs, ExOpts, OutDir, EbinDate), 110 | compile(RestExs, ExOpts, OutDir, EbinDate), 111 | 112 | true = code:set_path(CurrPath), 113 | ok; 114 | false -> 115 | rebar_log:log(error, "No Elixir compiler found~n", []) 116 | end. 117 | 118 | 119 | %% =================================================================== 120 | %% Internal functions 121 | %% =================================================================== 122 | compile(Exs, ExOpts, OutDir, EbinDate) -> 123 | case is_newer(Exs, EbinDate) of 124 | true -> 125 | 'Elixir.Code':compiler_options(orddict:from_list(ExOpts)), 126 | Files = [ list_to_binary(F) || F <- Exs], 127 | 'Elixir.Kernel.ParallelCompiler': 128 | files_to_path(Files, 129 | list_to_binary(OutDir), 130 | [{each_file, fun(F) -> 131 | io:format("Compiled ~s~n",[F]) 132 | end}]), 133 | file:change_time(OutDir, erlang:localtime()), 134 | ok; 135 | false -> ok 136 | end. 137 | 138 | is_newer(Files, Time) -> 139 | lists:any(fun(FileTime) -> 140 | FileTime >= Time 141 | end, [ filelib:last_modified(File) || File <- Files ]). 142 | 143 | ex_opts(Config) -> 144 | orddict:from_list(rebar_config:get_local(Config, ex_opts, [{ignore_module_conflict, true}])). 145 | 146 | gather_src([], Srcs) -> 147 | Srcs; 148 | gather_src([Dir|Rest], Srcs) -> 149 | gather_src(Rest, Srcs ++ rebar_utils:find_files(Dir, ".*\\.ex\$")). 150 | 151 | -spec src_dirs(SrcDirs::[string()]) -> [file:filename(), ...]. 152 | src_dirs([]) -> 153 | ["src","lib"]; 154 | src_dirs(SrcDirs) -> 155 | SrcDirs. 156 | 157 | -spec dirs(Dir::file:filename()) -> [file:filename()]. 158 | dirs(Dir) -> 159 | [F || F <- filelib:wildcard(filename:join([Dir, "*"])), filelib:is_dir(F)]. 160 | 161 | -spec delete_dir(Dir::file:filename(), 162 | Subdirs::[string()]) -> 'ok' | {'error', atom()}. 163 | delete_dir(Dir, []) -> 164 | file:del_dir(Dir); 165 | delete_dir(Dir, Subdirs) -> 166 | lists:foreach(fun(D) -> delete_dir(D, dirs(D)) end, Subdirs), 167 | file:del_dir(Dir). 168 | -------------------------------------------------------------------------------- /src/rebar_elixir_plugin.app.src: -------------------------------------------------------------------------------- 1 | {application, rebar_elixir_plugin, 2 | [ 3 | {description, "rebar plugin for compiling elixir source code"}, 4 | {vsn, git}, 5 | {registered, []}, 6 | {applications, [ 7 | kernel, 8 | stdlib 9 | ]}, 10 | {env, []} 11 | ]}. -------------------------------------------------------------------------------- /src/rebar_exunit.erl: -------------------------------------------------------------------------------- 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 | %% The following Global options are supported: 28 | %% 31 | %% ------------------------------------------------------------------- 32 | -module(rebar_exunit). 33 | 34 | -export([exunit/2]). 35 | 36 | %% =================================================================== 37 | %% Public API 38 | %% =================================================================== 39 | 40 | exunit(Config, _AppFile) -> 41 | CodePath = code:get_path(), 42 | true = code:add_pathz(ebin_dir()), 43 | App = application:load(elixir), 44 | Loaded = (App == ok orelse App == {error, {already_loaded, elixir}}), 45 | case Loaded of 46 | true -> 47 | application:start(elixir), 48 | Suite = 49 | %check if rebar_config exports get_global/3 -- changed between rebar 2.0.0 and 2.1.0 50 | case lists:keymember(3,2,proplists:lookup_all(get_global,rebar_config:module_info(exports))) of 51 | true -> 52 | rebar_config:get_global(Config,suite, undefined); 53 | false -> 54 | rebar_config:get_global(suite, undefined) 55 | end, 56 | TestExs = 57 | case Suite of 58 | undefined -> rebar_utils:find_files("test", ".*\\.exs\$"); 59 | Suite -> [Suite] 60 | end, 61 | perform_exunit(Config, TestExs); 62 | false -> 63 | rebar_log:log(info, "ExUnit not found") 64 | end, 65 | true = code:set_path(CodePath), 66 | ok. 67 | 68 | %% =================================================================== 69 | %% Internal functions 70 | %% =================================================================== 71 | 72 | ebin_dir() -> 73 | filename:join(rebar_utils:get_cwd(), "ebin"). 74 | 75 | perform_exunit(_Config, Files) -> 76 | case whereis(exunit_server) of 77 | undefined -> 'Elixir.ExUnit':start([]); 78 | _ -> ok 79 | end, 80 | [ 'Elixir.Code':require_file(list_to_binary(File)) || File <- Files ], 81 | Result = 'Elixir.ExUnit':run(), 82 | case maps:get(failures, Result) of 83 | 0 -> ok; 84 | F -> rebar_utils:abort("~p tests failed.~n", [F]) 85 | end. 86 | --------------------------------------------------------------------------------