diff --git a/src/rebar_port_compiler.erl b/src/rebar_port_compiler.erl index dace112..1a72d16 100644 --- a/src/rebar_port_compiler.erl +++ b/src/rebar_port_compiler.erl @@ -39,6 +39,9 @@ %% %% * port_sources - Erlang list of files and/or wildcard strings to be compiled %% +%% * so_specs - Erlang list of tuples of the form {"priv/so_name.so", ["c_src/object_file_name.o"]} useful for +%% building multiple *.so files. +%% %% * port_envs - Erlang list of key/value pairs which will control the environment when %% running the compiler and linker. By default, the following variables %% are defined: @@ -92,19 +95,21 @@ compile(Config, AppFile) -> {NewBins, ExistingBins} = compile_each(Sources, Config, Env, [], []), %% Construct the driver name and make sure priv/ exists - SoName = so_name(Config, AppFile), - ok = filelib:ensure_dir(SoName), + SoSpecs = so_specs(Config, AppFile, NewBins ++ ExistingBins), + ?INFO("Using specs ~p\n", [SoSpecs]), + lists:foreach(fun({SoName,_}) -> ok = filelib:ensure_dir(SoName) end, SoSpecs), %% Only relink if necessary, given the SoName and list of new binaries - case needs_link(SoName, NewBins) of - true -> - AllBins = string:join(NewBins ++ ExistingBins, " "), + lists:foreach(fun({SoName,Bins}) -> + case needs_link(SoName, sets:to_list(sets:intersection([sets:from_list(Bins),sets:from_list(NewBins)]))) of + true -> rebar_utils:sh_failfast(?FMT("$CC ~s $LDFLAGS $DRV_LDFLAGS -o ~s", - [AllBins, SoName]), Env); - false -> + [string:join(Bins, " "), SoName]), Env); + false -> ?INFO("Skipping relink of ~s\n", [SoName]), ok - end + end + end, SoSpecs) end. clean(Config, AppFile) -> @@ -113,7 +118,7 @@ clean(Config, AppFile) -> rebar_file_utils:delete_each([source_to_bin(S) || S <- Sources]), %% Delete the .so file - rebar_file_utils:delete_each([so_name(Config, AppFile)]), + rebar_file_utils:delete_each(lists:map(fun({SoName,_}) -> SoName end, so_specs(Config, AppFile, expand_objects(Sources)))), %% Run the cleanup script, if it exists run_cleanup_hook(Config). @@ -130,6 +135,11 @@ expand_sources([], Acc) -> expand_sources([Spec | Rest], Acc) -> Acc2 = filelib:wildcard(Spec) ++ Acc, expand_sources(Rest, Acc2). + +expand_objects(Sources) -> + lists:map(fun(File) -> + filename:join([filename:dirname(File),filename:basename(File) ++ ".o"]) + end, Sources). run_precompile_hook(Config, Env) -> case rebar_config:get(Config, port_pre_script, undefined) of @@ -319,21 +329,19 @@ source_to_bin(Source) -> Ext = filename:extension(Source), filename:rootname(Source, Ext) ++ ".o". -so_name(Config, AppFile) -> +so_specs(Config, AppFile, Bins) -> %% Check config to see if a custom so_name has been specified - PortName = case rebar_config:get(Config, so_name, undefined) of + ?INFO("config ~p\n", [Config]), + case rebar_config:get(Config, so_specs, undefined) of undefined -> %% Get the app name, which we'll use to %% generate the linked port driver name case rebar_app_utils:load_app_file(AppFile) of {ok, AppName, _} -> - lists:concat([AppName, "_drv.so"]); + SoName = ?FMT("priv/~s", [lists:concat([AppName, "_drv.so"])]), + [{SoName, Bins}]; error -> ?FAIL end; - Soname -> - Soname - end, - - %% Construct the driver name - ?FMT("priv/~s", [PortName]). + SoSpecs -> SoSpecs + end.