mirror of
https://github.com/correl/rebar.git
synced 2024-11-14 19:19:30 +00:00
Add support for target-specific port options
{port_specs, [{".*", "priv/foo.so", ["c_src/foo.c"], [{env, []}]}]}.
This commit is contained in:
parent
2ae73cc2d3
commit
7c418ed2b4
4 changed files with 268 additions and 217 deletions
|
@ -37,14 +37,16 @@
|
||||||
|
|
||||||
%% Port compilation environment variables. See rebar_port_compiler.erl for
|
%% Port compilation environment variables. See rebar_port_compiler.erl for
|
||||||
%% more info. Default is `[]'
|
%% more info. Default is `[]'
|
||||||
{port_env, []}.
|
{port_env, [{"CFLAGS", "$CFLAGS -Ifoo"},
|
||||||
|
{"freebsd", "LDFLAGS", "$LDFLAGS -lfoo"}]}.
|
||||||
|
|
||||||
%% port_specs
|
%% port_specs
|
||||||
%% List of filenames or wildcards to be compiled. May also contain a tuple
|
%% List of filenames or wildcards to be compiled. May also contain a tuple
|
||||||
%% consisting of a regular expression to be applied against the system
|
%% consisting of a regular expression to be applied against the system
|
||||||
%% architecture as a filter.
|
%% architecture as a filter.
|
||||||
{port_specs, [{"priv/so_name.so", ["c_src/*.c"]},
|
{port_specs, [{"priv/so_name.so", ["c_src/*.c"]},
|
||||||
{"linux", "priv/hello_linux", ["c_src/hello_linux.c"]]}.
|
{"linux", "priv/hello_linux", ["c_src/hello_linux.c"]},
|
||||||
|
{"linux", "priv/hello_linux", ["c_src/*.c"], [{env, []}]}}.
|
||||||
|
|
||||||
%% == LFE Compiler ==
|
%% == LFE Compiler ==
|
||||||
|
|
||||||
|
|
|
@ -31,12 +31,17 @@
|
||||||
get_all/2,
|
get_all/2,
|
||||||
set/3,
|
set/3,
|
||||||
set_global/2, get_global/2,
|
set_global/2, get_global/2,
|
||||||
is_verbose/0, get_jobs/0]).
|
is_verbose/0, get_jobs/0,
|
||||||
|
set_env/3, get_env/2]).
|
||||||
|
|
||||||
-include("rebar.hrl").
|
-include("rebar.hrl").
|
||||||
|
|
||||||
-record(config, { dir :: file:filename(),
|
-record(config, { dir :: file:filename(),
|
||||||
opts :: list() }).
|
opts = [] :: list(),
|
||||||
|
envs = [] :: list({module(), env()}) }).
|
||||||
|
|
||||||
|
-type env() :: [env_var()].
|
||||||
|
-type env_var() :: {string(), string()}.
|
||||||
|
|
||||||
%% Types that can be used from other modules -- alphabetically ordered.
|
%% Types that can be used from other modules -- alphabetically ordered.
|
||||||
-export_type([config/0]).
|
-export_type([config/0]).
|
||||||
|
@ -53,8 +58,7 @@ base_config(#config{opts=Opts0}) ->
|
||||||
new(Opts0, ConfName).
|
new(Opts0, ConfName).
|
||||||
|
|
||||||
new() ->
|
new() ->
|
||||||
#config { dir = rebar_utils:get_cwd(),
|
#config{dir = rebar_utils:get_cwd()}.
|
||||||
opts = [] }.
|
|
||||||
|
|
||||||
new(ConfigFile) when is_list(ConfigFile) ->
|
new(ConfigFile) when is_list(ConfigFile) ->
|
||||||
case consult_file(ConfigFile) of
|
case consult_file(ConfigFile) of
|
||||||
|
@ -88,7 +92,7 @@ new(Opts0, ConfName) ->
|
||||||
?ABORT("Failed to load ~s: ~p\n", [ConfigFile, Other])
|
?ABORT("Failed to load ~s: ~p\n", [ConfigFile, Other])
|
||||||
end,
|
end,
|
||||||
|
|
||||||
#config { dir = Dir, opts = Opts }.
|
#config{dir = Dir, opts = Opts}.
|
||||||
|
|
||||||
get(Config, Key, Default) ->
|
get(Config, Key, Default) ->
|
||||||
proplists:get_value(Key, Config#config.opts, Default).
|
proplists:get_value(Key, Config#config.opts, Default).
|
||||||
|
@ -143,6 +147,17 @@ consult_file(File) ->
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
set_env(Config, Mod, Env) ->
|
||||||
|
OldEnvs = Config#config.envs,
|
||||||
|
NewEnvs = case lists:keymember(Mod, 1, OldEnvs) of
|
||||||
|
true -> lists:keyreplace(Mod, 1, OldEnvs, {Mod, Env});
|
||||||
|
false -> [{Mod,Env}|OldEnvs]
|
||||||
|
end,
|
||||||
|
Config#config{envs=NewEnvs}.
|
||||||
|
|
||||||
|
get_env(Config, Mod) ->
|
||||||
|
proplists:get_value(Mod, Config#config.envs, []).
|
||||||
|
|
||||||
%% ===================================================================
|
%% ===================================================================
|
||||||
%% Internal functions
|
%% Internal functions
|
||||||
%% ===================================================================
|
%% ===================================================================
|
||||||
|
|
|
@ -168,7 +168,7 @@ maybe_process_dir0(AppFile, ModuleSet, Config, CurrentCodePath,
|
||||||
CurrentCodePath, ModuleSet)
|
CurrentCodePath, ModuleSet)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
process_dir0(Dir, Command, DirSet, Config, CurrentCodePath,
|
process_dir0(Dir, Command, DirSet, Config0, CurrentCodePath,
|
||||||
{DirModules, ModuleSetFile}) ->
|
{DirModules, ModuleSetFile}) ->
|
||||||
%% Get the list of modules for "any dir". This is a catch-all list
|
%% Get the list of modules for "any dir". This is a catch-all list
|
||||||
%% of modules that are processed in addition to modules associated
|
%% of modules that are processed in addition to modules associated
|
||||||
|
@ -180,21 +180,21 @@ process_dir0(Dir, Command, DirSet, Config, CurrentCodePath,
|
||||||
|
|
||||||
%% Invoke 'preprocess' on the modules -- this yields a list of other
|
%% Invoke 'preprocess' on the modules -- this yields a list of other
|
||||||
%% directories that should be processed _before_ the current one.
|
%% directories that should be processed _before_ the current one.
|
||||||
Predirs = acc_modules(Modules, preprocess, Config, ModuleSetFile),
|
Predirs = acc_modules(Modules, preprocess, Config0, ModuleSetFile),
|
||||||
|
|
||||||
SubdirAssoc = remember_cwd_subdir(Dir, Predirs),
|
SubdirAssoc = remember_cwd_subdir(Dir, Predirs),
|
||||||
|
|
||||||
%% Get the list of plug-in modules from rebar.config. These
|
%% Get the list of plug-in modules from rebar.config. These
|
||||||
%% modules may participate in preprocess and postprocess.
|
%% modules may participate in preprocess and postprocess.
|
||||||
{ok, PluginModules} = plugin_modules(Config, SubdirAssoc),
|
{ok, PluginModules} = plugin_modules(Config0, SubdirAssoc),
|
||||||
|
|
||||||
PluginPredirs = acc_modules(PluginModules, preprocess,
|
PluginPredirs = acc_modules(PluginModules, preprocess,
|
||||||
Config, ModuleSetFile),
|
Config0, ModuleSetFile),
|
||||||
|
|
||||||
AllPredirs = Predirs ++ PluginPredirs,
|
AllPredirs = Predirs ++ PluginPredirs,
|
||||||
|
|
||||||
?DEBUG("Predirs: ~p\n", [AllPredirs]),
|
?DEBUG("Predirs: ~p\n", [AllPredirs]),
|
||||||
DirSet2 = process_each(AllPredirs, Command, Config,
|
DirSet2 = process_each(AllPredirs, Command, Config0,
|
||||||
ModuleSetFile, DirSet),
|
ModuleSetFile, DirSet),
|
||||||
|
|
||||||
%% Make sure the CWD is reset properly; processing the dirs may have
|
%% Make sure the CWD is reset properly; processing the dirs may have
|
||||||
|
@ -202,25 +202,31 @@ process_dir0(Dir, Command, DirSet, Config, CurrentCodePath,
|
||||||
ok = file:set_cwd(Dir),
|
ok = file:set_cwd(Dir),
|
||||||
|
|
||||||
%% Check that this directory is not on the skip list
|
%% Check that this directory is not on the skip list
|
||||||
case is_skip_dir(Dir) of
|
Config = case is_skip_dir(Dir) of
|
||||||
true ->
|
true ->
|
||||||
%% Do not execute the command on the directory, as some
|
%% Do not execute the command on the directory, as some
|
||||||
%% module has requested a skip on it.
|
%% module has requested a skip on it.
|
||||||
?INFO("Skipping ~s in ~s\n", [Command, Dir]);
|
?INFO("Skipping ~s in ~s\n", [Command, Dir]),
|
||||||
|
Config0;
|
||||||
|
|
||||||
false ->
|
false ->
|
||||||
%% Execute any before_command plugins on this directory
|
%% Check for and get command specific environments
|
||||||
execute_pre(Command, PluginModules,
|
{Config1, Env} = setup_envs(Config0, Modules),
|
||||||
Config, ModuleSetFile),
|
|
||||||
|
|
||||||
%% Execute the current command on this directory
|
%% Execute any before_command plugins on this directory
|
||||||
execute(Command, Modules ++ PluginModules,
|
execute_pre(Command, PluginModules,
|
||||||
Config, ModuleSetFile),
|
Config1, ModuleSetFile, Env),
|
||||||
|
|
||||||
%% Execute any after_command plugins on this directory
|
%% Execute the current command on this directory
|
||||||
execute_post(Command, PluginModules,
|
execute(Command, Modules ++ PluginModules,
|
||||||
Config, ModuleSetFile)
|
Config1, ModuleSetFile, Env),
|
||||||
end,
|
|
||||||
|
%% Execute any after_command plugins on this directory
|
||||||
|
execute_post(Command, PluginModules,
|
||||||
|
Config1, ModuleSetFile, Env),
|
||||||
|
|
||||||
|
Config1
|
||||||
|
end,
|
||||||
|
|
||||||
%% Mark the current directory as processed
|
%% Mark the current directory as processed
|
||||||
DirSet3 = sets:add_element(Dir, DirSet2),
|
DirSet3 = sets:add_element(Dir, DirSet2),
|
||||||
|
@ -311,22 +317,22 @@ is_dir_type(rel_dir, Dir) ->
|
||||||
is_dir_type(_, _) ->
|
is_dir_type(_, _) ->
|
||||||
false.
|
false.
|
||||||
|
|
||||||
execute_pre(Command, Modules, Config, ModuleFile) ->
|
execute_pre(Command, Modules, Config, ModuleFile, Env) ->
|
||||||
execute_plugin_hook("pre_", Command, Modules,
|
execute_plugin_hook("pre_", Command, Modules,
|
||||||
Config, ModuleFile).
|
Config, ModuleFile, Env).
|
||||||
|
|
||||||
execute_post(Command, Modules, Config, ModuleFile) ->
|
execute_post(Command, Modules, Config, ModuleFile, Env) ->
|
||||||
execute_plugin_hook("post_", Command, Modules,
|
execute_plugin_hook("post_", Command, Modules,
|
||||||
Config, ModuleFile).
|
Config, ModuleFile, Env).
|
||||||
|
|
||||||
execute_plugin_hook(Hook, Command, Modules, Config, ModuleFile) ->
|
execute_plugin_hook(Hook, Command, Modules, Config, ModuleFile, Env) ->
|
||||||
HookFunction = list_to_atom(Hook ++ atom_to_list(Command)),
|
HookFunction = list_to_atom(Hook ++ atom_to_list(Command)),
|
||||||
execute(HookFunction, Modules, Config, ModuleFile).
|
execute(HookFunction, Modules, Config, ModuleFile, Env).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% Execute a command across all applicable modules
|
%% Execute a command across all applicable modules
|
||||||
%%
|
%%
|
||||||
execute(Command, Modules, Config, ModuleFile) ->
|
execute(Command, Modules, Config, ModuleFile, Env) ->
|
||||||
case select_modules(Modules, Command, []) of
|
case select_modules(Modules, Command, []) of
|
||||||
[] ->
|
[] ->
|
||||||
Cmd = atom_to_list(Command),
|
Cmd = atom_to_list(Command),
|
||||||
|
@ -346,9 +352,6 @@ execute(Command, Modules, Config, ModuleFile) ->
|
||||||
|
|
||||||
increment_operations(),
|
increment_operations(),
|
||||||
|
|
||||||
%% Check for and get command specific environments
|
|
||||||
Env = setup_envs(Config, Modules),
|
|
||||||
|
|
||||||
%% Run the available modules
|
%% Run the available modules
|
||||||
apply_hooks(pre_hooks, Config, Command, Env),
|
apply_hooks(pre_hooks, Config, Command, Env),
|
||||||
case catch(run_modules(TargetModules, Command,
|
case catch(run_modules(TargetModules, Command,
|
||||||
|
@ -443,9 +446,16 @@ apply_hook({Env, {Command, Hook}}) ->
|
||||||
rebar_utils:sh(Hook, [{env, Env}, {abort_on_error, Msg}]).
|
rebar_utils:sh(Hook, [{env, Env}, {abort_on_error, Msg}]).
|
||||||
|
|
||||||
setup_envs(Config, Modules) ->
|
setup_envs(Config, Modules) ->
|
||||||
lists:flatten([M:setup_env(Config) ||
|
lists:foldl(fun(M, {C,E}=T) ->
|
||||||
M <- Modules,
|
case erlang:function_exported(M, setup_env, 1) of
|
||||||
erlang:function_exported(M, setup_env, 1)]).
|
true ->
|
||||||
|
Env = M:setup_env(C),
|
||||||
|
C1 = rebar_config:set_env(C, M, Env),
|
||||||
|
{C1, E++Env};
|
||||||
|
false ->
|
||||||
|
T
|
||||||
|
end
|
||||||
|
end, {Config, []}, Modules).
|
||||||
|
|
||||||
acc_modules(Modules, Command, Config, File) ->
|
acc_modules(Modules, Command, Config, File) ->
|
||||||
acc_modules(select_modules(Modules, Command, []),
|
acc_modules(select_modules(Modules, Command, []),
|
||||||
|
|
|
@ -39,8 +39,9 @@
|
||||||
%% Supported configuration variables:
|
%% Supported configuration variables:
|
||||||
%%
|
%%
|
||||||
%% * port_specs - Erlang list of tuples of the forms
|
%% * port_specs - Erlang list of tuples of the forms
|
||||||
%% {arch_regex(), "priv/foo.so", ["c_src/foo.c"]}
|
%% {ArchRegex, TargetFile, Sources, Options}
|
||||||
%% {"priv/foo", ["c_src/foo.c"]}
|
%% {ArchRegex, TargetFile, Sources}
|
||||||
|
%% {TargetFile, Sources}
|
||||||
%%
|
%%
|
||||||
%% * port_env - Erlang list of key/value pairs which will control
|
%% * port_env - Erlang list of key/value pairs which will control
|
||||||
%% the environment when running the compiler and linker.
|
%% the environment when running the compiler and linker.
|
||||||
|
@ -85,43 +86,55 @@
|
||||||
%% "$CFLAGS -X86Options"}]}
|
%% "$CFLAGS -X86Options"}]}
|
||||||
%%
|
%%
|
||||||
|
|
||||||
|
%% TODO: reconsider keeping both sources and objects once
|
||||||
|
%% support for deprecated options has been remove.
|
||||||
|
%% remove [] as valid value for sources, objects, and opts
|
||||||
|
%% when removing deprecated options.
|
||||||
|
-record(spec, {type::'drv' | 'exe',
|
||||||
|
target::file:filename(),
|
||||||
|
sources = [] :: [file:filename(), ...] | [],
|
||||||
|
objects = [] :: [file:filename(), ...] | [],
|
||||||
|
opts = [] ::list() | []}).
|
||||||
|
|
||||||
compile(Config, AppFile) ->
|
compile(Config, AppFile) ->
|
||||||
rebar_utils:deprecated(port_sources, port_specs, Config, "soon"),
|
rebar_utils:deprecated(port_sources, port_specs, Config, "soon"),
|
||||||
rebar_utils:deprecated(so_name, port_specs, Config, "soon"),
|
rebar_utils:deprecated(so_name, port_specs, Config, "soon"),
|
||||||
rebar_utils:deprecated(so_specs, port_specs, Config, "soon"),
|
rebar_utils:deprecated(so_specs, port_specs, Config, "soon"),
|
||||||
|
|
||||||
SourceFiles = get_sources(Config),
|
%% TODO: remove SpecType and OldSources make get_specs/2
|
||||||
|
%% return list(#spec{}) when removing deprecated options
|
||||||
|
{SpecType, {OldSources, Specs}} = get_specs(Config, AppFile),
|
||||||
|
|
||||||
case SourceFiles of
|
case {SpecType, OldSources, Specs} of
|
||||||
[] ->
|
{old, [], _} ->
|
||||||
ok;
|
ok; % old specs empty
|
||||||
_ ->
|
{new, [], []} ->
|
||||||
Env = setup_env(Config),
|
ok; % port_specs empty
|
||||||
|
|
||||||
|
_ -> % have old/new specs
|
||||||
|
|
||||||
|
SharedEnv = rebar_config:get_env(Config, ?MODULE),
|
||||||
|
|
||||||
%% Compile each of the sources
|
%% Compile each of the sources
|
||||||
{NewBins, ExistingBins} = compile_each(SourceFiles, Config, Env,
|
NewBins = compile_sources(OldSources, Specs, SharedEnv),
|
||||||
[], []),
|
|
||||||
|
|
||||||
%% Construct the target filename and make sure that the
|
%% Make sure that the target directories exist
|
||||||
%% target directory exists
|
|
||||||
Specs = port_specs(Config, AppFile, NewBins ++ ExistingBins),
|
|
||||||
?INFO("Using specs ~p\n", [Specs]),
|
?INFO("Using specs ~p\n", [Specs]),
|
||||||
lists:foreach(fun({_, Target,_}) ->
|
lists:foreach(fun(#spec{target=Target}) ->
|
||||||
ok = filelib:ensure_dir(Target);
|
|
||||||
({Target, _}) ->
|
|
||||||
ok = filelib:ensure_dir(Target)
|
ok = filelib:ensure_dir(Target)
|
||||||
end, Specs),
|
end, Specs),
|
||||||
|
|
||||||
%% Only relink if necessary, given the Target
|
%% Only relink if necessary, given the Target
|
||||||
%% and list of new binaries
|
%% and list of new binaries
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun({Target, Bins}) ->
|
fun(#spec{target=Target, objects=Bins, opts=Opts}) ->
|
||||||
AllBins = [sets:from_list(Bins),
|
AllBins = [sets:from_list(Bins),
|
||||||
sets:from_list(NewBins)],
|
sets:from_list(NewBins)],
|
||||||
Intersection = sets:intersection(AllBins),
|
Intersection = sets:intersection(AllBins),
|
||||||
case needs_link(Target, sets:to_list(Intersection)) of
|
case needs_link(Target, sets:to_list(Intersection)) of
|
||||||
true ->
|
true ->
|
||||||
LinkTemplate = select_link_template(Target),
|
LinkTemplate = select_link_template(Target),
|
||||||
|
Env = proplists:get_value(env, Opts, SharedEnv),
|
||||||
Cmd = expand_command(LinkTemplate, Env,
|
Cmd = expand_command(LinkTemplate, Env,
|
||||||
string:join(Bins, " "),
|
string:join(Bins, " "),
|
||||||
Target),
|
Target),
|
||||||
|
@ -134,113 +147,83 @@ compile(Config, AppFile) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
clean(Config, AppFile) ->
|
clean(Config, AppFile) ->
|
||||||
%% Build a list of sources so as to derive all the bins we generated
|
%% TODO: remove SpecType and OldSources make get_specs/2
|
||||||
Sources = get_sources(Config),
|
%% return list(#spec{}) when removing deprecated options
|
||||||
rebar_file_utils:delete_each([source_to_bin(S) || S <- Sources]),
|
{SpecType, {OldSources, Specs}} = get_specs(Config, AppFile),
|
||||||
|
|
||||||
%% Delete the target file
|
case {SpecType, OldSources, Specs} of
|
||||||
ExtractTarget = fun({_, Target, _}) ->
|
{old, [], _} ->
|
||||||
Target;
|
ok; % old specs empty
|
||||||
({Target, _}) ->
|
{new, [], []} ->
|
||||||
Target
|
ok; % port_specs empty
|
||||||
end,
|
|
||||||
rebar_file_utils:delete_each([ExtractTarget(S)
|
_ -> % have old/new specs
|
||||||
|| S <- port_specs(Config, AppFile,
|
|
||||||
expand_objects(Sources))]).
|
lists:foreach(fun(#spec{target=Target, objects=Objects}) ->
|
||||||
|
rebar_file_utils:delete_each([Target]),
|
||||||
|
rebar_file_utils:delete_each(Objects)
|
||||||
|
end, Specs)
|
||||||
|
end.
|
||||||
|
|
||||||
setup_env(Config) ->
|
setup_env(Config) ->
|
||||||
%% Extract environment values from the config (if specified) and
|
setup_env(Config, []).
|
||||||
%% merge with the default for this operating system. This enables
|
|
||||||
%% max flexibility for users.
|
|
||||||
DefaultEnv = filter_env(default_env(), []),
|
|
||||||
PortEnv = port_env(Config),
|
|
||||||
OverrideEnv = global_defines() ++ filter_env(PortEnv, []),
|
|
||||||
RawEnv = apply_defaults(os_env(), DefaultEnv) ++ OverrideEnv,
|
|
||||||
expand_vars_loop(merge_each_var(RawEnv, [])).
|
|
||||||
|
|
||||||
%% ===================================================================
|
%% ===================================================================
|
||||||
%% Internal functions
|
%% Internal functions
|
||||||
%% ===================================================================
|
%% ===================================================================
|
||||||
|
|
||||||
|
setup_env(Config, ExtraEnv) ->
|
||||||
|
%% Extract environment values from the config (if specified) and
|
||||||
|
%% merge with the default for this operating system. This enables
|
||||||
|
%% max flexibility for users.
|
||||||
|
DefaultEnv = filter_env(default_env(), []),
|
||||||
|
PortEnv = filter_env(port_env(Config), []),
|
||||||
|
OverrideEnv = global_defines() ++ PortEnv ++ filter_env(ExtraEnv, []),
|
||||||
|
RawEnv = apply_defaults(os_env(), DefaultEnv) ++ OverrideEnv,
|
||||||
|
expand_vars_loop(merge_each_var(RawEnv, [])).
|
||||||
|
|
||||||
global_defines() ->
|
global_defines() ->
|
||||||
Defines = rebar_config:get_global(defines, []),
|
Defines = rebar_config:get_global(defines, []),
|
||||||
Flags = string:join(["-D" ++ D || D <- Defines], " "),
|
Flags = string:join(["-D" ++ D || D <- Defines], " "),
|
||||||
[{"ERL_CFLAGS", "$ERL_CFLAGS " ++ Flags}].
|
[{"ERL_CFLAGS", "$ERL_CFLAGS " ++ Flags}].
|
||||||
|
|
||||||
get_sources(Config) ->
|
replace_extension(File, NewExt) ->
|
||||||
case rebar_config:get_list(Config, port_specs, []) of
|
OldExt = filename:extension(File),
|
||||||
[] ->
|
replace_extension(File, OldExt, NewExt).
|
||||||
%% TODO: DEPRECATED: remove
|
|
||||||
expand_sources(rebar_config:get_list(Config, port_sources,
|
|
||||||
["c_src/*.c"]), []);
|
|
||||||
PortSpecs ->
|
|
||||||
expand_port_specs(PortSpecs)
|
|
||||||
end.
|
|
||||||
|
|
||||||
expand_port_specs(Specs) ->
|
replace_extension(File, OldExt, NewExt) ->
|
||||||
lists:flatmap(fun({_, Target, FileSpecs}) ->
|
filename:rootname(File, OldExt) ++ NewExt.
|
||||||
expand_file_specs(Target, FileSpecs);
|
|
||||||
({Target, FileSpecs}) ->
|
|
||||||
expand_file_specs(Target, FileSpecs)
|
|
||||||
end, filter_port_specs(Specs)).
|
|
||||||
|
|
||||||
expand_file_specs(Target, FileSpecs) ->
|
%%
|
||||||
Sources = lists:flatmap(fun filelib:wildcard/1, FileSpecs),
|
%% == compile and link ==
|
||||||
[{Target, Src} || Src <- Sources].
|
%%
|
||||||
|
|
||||||
filter_port_specs(Specs) ->
|
compile_sources([], Specs, SharedEnv) -> % port_spec
|
||||||
lists:filter(fun({ArchRegex, _, _}) ->
|
lists:foldl(
|
||||||
rebar_utils:is_arch(ArchRegex);
|
fun(#spec{sources=Sources, type=Type, opts=Opts}, NewBins) ->
|
||||||
({_, _}) ->
|
Env = proplists:get_value(env, Opts, SharedEnv),
|
||||||
true
|
compile_each(Sources, Type, Env, NewBins)
|
||||||
end, Specs).
|
end, [], Specs);
|
||||||
|
compile_sources(OldSources, _Specs, SharedEnv) -> % deprecated
|
||||||
|
compile_each(OldSources, drv, SharedEnv, []).
|
||||||
|
|
||||||
|
compile_each([], _Type, _Env, NewBins) ->
|
||||||
%% TODO: DEPRECATED: remove
|
lists:reverse(NewBins);
|
||||||
expand_sources([], Acc) ->
|
compile_each([Source | Rest], Type, Env, NewBins) ->
|
||||||
Acc;
|
|
||||||
expand_sources([{ArchRegex, Spec} | Rest], Acc) ->
|
|
||||||
case rebar_utils:is_arch(ArchRegex) of
|
|
||||||
true ->
|
|
||||||
Acc2 = expand_sources(Spec, Acc),
|
|
||||||
expand_sources(Rest, Acc2);
|
|
||||||
false ->
|
|
||||||
expand_sources(Rest, Acc)
|
|
||||||
end;
|
|
||||||
expand_sources([Spec | Rest], Acc) ->
|
|
||||||
Acc2 = filelib:wildcard(Spec) ++ Acc,
|
|
||||||
expand_sources(Rest, Acc2).
|
|
||||||
|
|
||||||
expand_objects(Sources) ->
|
|
||||||
[expand_object(".o", Src) || Src <- Sources].
|
|
||||||
|
|
||||||
expand_object(Ext, {_Target, Source}) ->
|
|
||||||
expand_object(Ext, Source);
|
|
||||||
expand_object(Ext, Source) ->
|
|
||||||
filename:join(filename:dirname(Source), filename:basename(Source) ++ Ext).
|
|
||||||
|
|
||||||
compile_each([], _Config, _Env, NewBins, ExistingBins) ->
|
|
||||||
{lists:reverse(NewBins), lists:reverse(ExistingBins)};
|
|
||||||
compile_each([RawSource | Rest], Config, Env, NewBins, ExistingBins) ->
|
|
||||||
%% TODO: DEPRECATED: remove
|
|
||||||
{Type, Source} = source_type(RawSource),
|
|
||||||
Ext = filename:extension(Source),
|
Ext = filename:extension(Source),
|
||||||
Bin = filename:rootname(Source, Ext) ++ ".o",
|
Bin = replace_extension(Source, Ext, ".o"),
|
||||||
case needs_compile(Source, Bin) of
|
case needs_compile(Source, Bin) of
|
||||||
true ->
|
true ->
|
||||||
?CONSOLE("Compiling ~s\n", [Source]),
|
?CONSOLE("Compiling ~s\n", [Source]),
|
||||||
Template = select_compile_template(Type, compiler(Ext)),
|
Template = select_compile_template(Type, compiler(Ext)),
|
||||||
rebar_utils:sh(expand_command(Template, Env, Source, Bin),
|
rebar_utils:sh(expand_command(Template, Env, Source, Bin),
|
||||||
[{env, Env}]),
|
[{env, Env}]),
|
||||||
compile_each(Rest, Config, Env, [Bin | NewBins], ExistingBins);
|
compile_each(Rest, Type, Env, [Bin | NewBins]);
|
||||||
false ->
|
false ->
|
||||||
?INFO("Skipping ~s\n", [Source]),
|
?INFO("Skipping ~s\n", [Source]),
|
||||||
compile_each(Rest, Config, Env, NewBins, [Bin | ExistingBins])
|
compile_each(Rest, Type, Env, NewBins)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
source_type({Target, Source}) -> {target_type(Target), Source};
|
|
||||||
source_type(Source) -> {drv, Source}.
|
|
||||||
|
|
||||||
needs_compile(Source, Bin) ->
|
needs_compile(Source, Bin) ->
|
||||||
%% TODO: Generate depends using gcc -MM so we can also
|
%% TODO: Generate depends using gcc -MM so we can also
|
||||||
%% check for include changes
|
%% check for include changes
|
||||||
|
@ -259,6 +242,127 @@ needs_link(SoName, NewBins) ->
|
||||||
MaxLastMod >= Other
|
MaxLastMod >= Other
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
%%
|
||||||
|
%% == port_specs ==
|
||||||
|
%%
|
||||||
|
|
||||||
|
get_specs(Config, AppFile) ->
|
||||||
|
case rebar_config:get(Config, port_specs, undefined) of
|
||||||
|
undefined ->
|
||||||
|
%% TODO: DEPRECATED: remove support for non-port_specs syntax
|
||||||
|
{old, old_get_specs(Config, AppFile)};
|
||||||
|
PortSpecs ->
|
||||||
|
{new, get_port_specs(Config, PortSpecs)}
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_port_specs(Config, PortSpecs) ->
|
||||||
|
Filtered = filter_port_specs(PortSpecs),
|
||||||
|
OsType = os:type(),
|
||||||
|
{[], [get_port_spec(Config, OsType, Spec) || Spec <- Filtered]}.
|
||||||
|
|
||||||
|
filter_port_specs(Specs) ->
|
||||||
|
[S || S <- Specs, filter_port_spec(S)].
|
||||||
|
|
||||||
|
filter_port_spec({ArchRegex, _, _, _}) ->
|
||||||
|
rebar_utils:is_arch(ArchRegex);
|
||||||
|
filter_port_spec({ArchRegex, _, _}) ->
|
||||||
|
rebar_utils:is_arch(ArchRegex);
|
||||||
|
filter_port_spec({_, _}) ->
|
||||||
|
true.
|
||||||
|
|
||||||
|
get_port_spec(Config, OsType, {Target, Sources}) ->
|
||||||
|
get_port_spec(Config, OsType, {undefined, Target, Sources, []});
|
||||||
|
get_port_spec(Config, OsType, {Arch, Target, Sources}) ->
|
||||||
|
get_port_spec(Config, OsType, {Arch, Target, Sources, []});
|
||||||
|
get_port_spec(Config, OsType, {_Arch, Target, Sources, Opts}) ->
|
||||||
|
SourceFiles = port_sources(Sources),
|
||||||
|
ObjectFiles = port_objects(SourceFiles),
|
||||||
|
#spec{type=target_type(Target),
|
||||||
|
target=maybe_switch_extension(OsType, Target),
|
||||||
|
sources=SourceFiles,
|
||||||
|
objects=ObjectFiles,
|
||||||
|
opts=port_opts(Config, Opts)}.
|
||||||
|
|
||||||
|
port_sources(Sources) ->
|
||||||
|
lists:flatmap(fun filelib:wildcard/1, Sources).
|
||||||
|
|
||||||
|
port_objects(SourceFiles) ->
|
||||||
|
[replace_extension(O, ".o") || O <- SourceFiles].
|
||||||
|
|
||||||
|
port_opts(Config, Opts) ->
|
||||||
|
[port_opt(Config, O) || O <- Opts].
|
||||||
|
|
||||||
|
port_opt(Config, {env, Env}) ->
|
||||||
|
{env, setup_env(Config, Env)};
|
||||||
|
port_opt(_Config, Opt) ->
|
||||||
|
Opt.
|
||||||
|
|
||||||
|
maybe_switch_extension({win32, nt}, Target) ->
|
||||||
|
switch_to_dll_or_exe(Target);
|
||||||
|
maybe_switch_extension(_OsType, Target) ->
|
||||||
|
Target.
|
||||||
|
|
||||||
|
switch_to_dll_or_exe(Target) ->
|
||||||
|
case filename:extension(Target) of
|
||||||
|
".so" -> filename:rootname(Target, ".so") ++ ".dll";
|
||||||
|
[] -> Target ++ ".exe";
|
||||||
|
Other -> Other
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% TODO: DEPRECATED: remove support for non-port_specs syntax [old_*()]
|
||||||
|
old_get_specs(Config, AppFile) ->
|
||||||
|
OsType = os:type(),
|
||||||
|
SourceFiles = old_get_sources(Config),
|
||||||
|
Specs =
|
||||||
|
case rebar_config:get(Config, so_specs, undefined) of
|
||||||
|
undefined ->
|
||||||
|
Objects = port_objects(SourceFiles),
|
||||||
|
%% New form of so_specs is not provided. See if the old form
|
||||||
|
%% of {so_name} is available instead
|
||||||
|
Dir = "priv",
|
||||||
|
SoName = case rebar_config:get(Config, so_name, undefined) of
|
||||||
|
undefined ->
|
||||||
|
%% Ok, neither old nor new form is
|
||||||
|
%% available. Use the app name and
|
||||||
|
%% generate a sensible default.
|
||||||
|
AppName = rebar_app_utils:app_name(AppFile),
|
||||||
|
DrvName = ?FMT("~s_drv.so", [AppName]),
|
||||||
|
filename:join([Dir, DrvName]);
|
||||||
|
AName ->
|
||||||
|
%% Old form is available -- use it
|
||||||
|
filename:join(Dir, AName)
|
||||||
|
end,
|
||||||
|
[old_get_so_spec({SoName, Objects}, OsType)];
|
||||||
|
SoSpecs ->
|
||||||
|
[old_get_so_spec(S, OsType) || S <- SoSpecs]
|
||||||
|
end,
|
||||||
|
{SourceFiles, Specs}.
|
||||||
|
|
||||||
|
old_get_sources(Config) ->
|
||||||
|
RawSources = rebar_config:get_list(Config, port_sources,
|
||||||
|
["c_src/*.c"]),
|
||||||
|
FilteredSources = old_filter_port_sources(RawSources),
|
||||||
|
old_expand_sources(FilteredSources).
|
||||||
|
|
||||||
|
old_filter_port_sources(PortSources) ->
|
||||||
|
[S || S <- PortSources, old_is_arch_port_sources(S)].
|
||||||
|
|
||||||
|
old_is_arch_port_sources({Arch, _Sources}) -> rebar_utils:is_arch(Arch);
|
||||||
|
old_is_arch_port_sources(_Sources) -> true.
|
||||||
|
|
||||||
|
old_expand_sources(Sources) ->
|
||||||
|
lists:flatmap(fun filelib:wildcard/1, Sources).
|
||||||
|
|
||||||
|
old_get_so_spec({Target, Objects}, OsType) ->
|
||||||
|
#spec{type=drv,
|
||||||
|
target=maybe_switch_extension(OsType, Target),
|
||||||
|
sources=[],
|
||||||
|
objects=Objects,
|
||||||
|
opts=[]}.
|
||||||
|
|
||||||
|
%%
|
||||||
|
%% == port_env ==
|
||||||
|
%%
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% Choose a compiler variable, based on a provided extension
|
%% Choose a compiler variable, based on a provided extension
|
||||||
|
@ -365,7 +469,6 @@ expand_keys_in_value([Key | Rest], Value, Vars) ->
|
||||||
end,
|
end,
|
||||||
expand_keys_in_value(Rest, NewValue, Vars).
|
expand_keys_in_value(Rest, NewValue, Vars).
|
||||||
|
|
||||||
|
|
||||||
expand_command(TmplName, Env, InFiles, OutFile) ->
|
expand_command(TmplName, Env, InFiles, OutFile) ->
|
||||||
Cmd0 = proplists:get_value(TmplName, Env),
|
Cmd0 = proplists:get_value(TmplName, Env),
|
||||||
Cmd1 = rebar_utils:expand_env_variable(Cmd0, "PORT_IN_FILES", InFiles),
|
Cmd1 = rebar_utils:expand_env_variable(Cmd0, "PORT_IN_FILES", InFiles),
|
||||||
|
@ -426,7 +529,6 @@ filter_env([{ArchRegex, Key, Value} | Rest], Acc) ->
|
||||||
filter_env([{Key, Value} | Rest], Acc) ->
|
filter_env([{Key, Value} | Rest], Acc) ->
|
||||||
filter_env(Rest, [{Key, Value} | Acc]).
|
filter_env(Rest, [{Key, Value} | Acc]).
|
||||||
|
|
||||||
|
|
||||||
erts_dir() ->
|
erts_dir() ->
|
||||||
lists:concat([code:root_dir(), "/erts-", erlang:system_info(version)]).
|
lists:concat([code:root_dir(), "/erts-", erlang:system_info(version)]).
|
||||||
|
|
||||||
|
@ -520,81 +622,3 @@ default_env() ->
|
||||||
{"darwin11.*-32", "CXXFLAGS", "-m32 $CXXFLAGS"},
|
{"darwin11.*-32", "CXXFLAGS", "-m32 $CXXFLAGS"},
|
||||||
{"darwin11.*-32", "LDFLAGS", "-arch i386 $LDFLAGS"}
|
{"darwin11.*-32", "LDFLAGS", "-arch i386 $LDFLAGS"}
|
||||||
].
|
].
|
||||||
|
|
||||||
source_to_bin({_Target, Source}) ->
|
|
||||||
source_to_bin(Source);
|
|
||||||
source_to_bin(Source) ->
|
|
||||||
Ext = filename:extension(Source),
|
|
||||||
filename:rootname(Source, Ext) ++ ".o".
|
|
||||||
|
|
||||||
port_specs(Config, AppFile, Bins) ->
|
|
||||||
Specs = make_port_specs(Config, AppFile, Bins),
|
|
||||||
case os:type() of
|
|
||||||
{win32, nt} ->
|
|
||||||
[switch_to_dll_or_exe(Spec) || Spec <- Specs];
|
|
||||||
_ ->
|
|
||||||
Specs
|
|
||||||
end.
|
|
||||||
|
|
||||||
switch_to_dll_or_exe(Orig = {Name, Spec}) ->
|
|
||||||
case filename:extension(Name) of
|
|
||||||
".so" ->
|
|
||||||
{filename:rootname(Name, ".so") ++ ".dll", Spec};
|
|
||||||
[] ->
|
|
||||||
{Name ++ ".exe", Spec};
|
|
||||||
_ ->
|
|
||||||
%% Not a .so; leave it
|
|
||||||
Orig
|
|
||||||
end.
|
|
||||||
|
|
||||||
make_port_specs(Config, AppFile, Bins) ->
|
|
||||||
case rebar_config:get(Config, port_specs, undefined) of
|
|
||||||
undefined ->
|
|
||||||
%% TODO: DEPRECATED: remove
|
|
||||||
make_so_specs(Config, AppFile, Bins);
|
|
||||||
PortSpecs ->
|
|
||||||
%% filter based on ArchRegex
|
|
||||||
Specs0 = lists:filter(fun({ArchRegex, _Target, _Sources}) ->
|
|
||||||
rebar_utils:is_arch(ArchRegex);
|
|
||||||
(_) ->
|
|
||||||
true
|
|
||||||
end, PortSpecs),
|
|
||||||
%% TODO: DEPRECATED: remove support for non-port_specs syntax
|
|
||||||
|
|
||||||
|
|
||||||
%% drop ArchRegex from specs
|
|
||||||
lists:map(fun({_, Target, RawSources}) ->
|
|
||||||
{Target, sources_to_bins(RawSources)};
|
|
||||||
({Target, RawSources}) ->
|
|
||||||
{Target, sources_to_bins(RawSources)}
|
|
||||||
end, Specs0)
|
|
||||||
end.
|
|
||||||
|
|
||||||
sources_to_bins(RawSources) ->
|
|
||||||
Sources = lists:flatmap(fun filelib:wildcard/1, RawSources),
|
|
||||||
lists:map(fun source_to_bin/1, Sources).
|
|
||||||
|
|
||||||
%% DEPRECATED
|
|
||||||
make_so_specs(Config, AppFile, Bins) ->
|
|
||||||
case rebar_config:get(Config, so_specs, undefined) of
|
|
||||||
undefined ->
|
|
||||||
%% New form of so_specs is not provided. See if the old form
|
|
||||||
%% of {so_name} is available instead
|
|
||||||
Dir = "priv",
|
|
||||||
SoName = case rebar_config:get(Config, so_name, undefined) of
|
|
||||||
undefined ->
|
|
||||||
%% Ok, neither old nor new form is available. Use
|
|
||||||
%% the app name and generate a sensible default.
|
|
||||||
AppName = rebar_app_utils:app_name(AppFile),
|
|
||||||
filename:join(Dir,
|
|
||||||
lists:concat([AppName, "_drv.so"]));
|
|
||||||
|
|
||||||
AName ->
|
|
||||||
%% Old form is available -- use it
|
|
||||||
filename:join(Dir, AName)
|
|
||||||
end,
|
|
||||||
[{SoName, Bins}];
|
|
||||||
|
|
||||||
SoSpecs ->
|
|
||||||
SoSpecs
|
|
||||||
end.
|
|
||||||
|
|
Loading…
Reference in a new issue