Skip to content

Commit ee9ea74

Browse files
committed
Implement resource v2 callback compatibiity
Implement rebar_resource_v2 compatible callbacks in order to make the plugin compatible with all known versions of rebar3, following the guide at https://www.rebar3.org/v3/docs/custom-dep-resources#section-resources-compatible-with-all-versions
1 parent 1468599 commit ee9ea74

1 file changed

Lines changed: 154 additions & 21 deletions

File tree

src/rebar_raw_resource.erl

Lines changed: 154 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,16 @@
3636
% provider
3737
do/1,
3838
format_error/1,
39+
init/2,
40+
% rebar_resource_v2 callbacks
41+
download/4,
42+
lock/2,
43+
make_vsn/2,
44+
needs_update/2,
45+
% pre-rebar3 3.7.0 rebar_resource callbacks
3946
init/1,
40-
% rebar_resource
4147
download/3,
42-
lock/2,
43-
make_vsn/1,
44-
needs_update/2
48+
make_vsn/1
4549
]).
4650

4751
% For development only - you *really* don't want this defined!
@@ -194,10 +198,28 @@
194198
% download/3 is NOT called first if the dependency is already present, and
195199
% it's the only resource call that gets to see the rebar state.
196200
%
201+
% Note: This arity is the pre-rebar3 3.7.0 format of this function.
202+
%
197203
init(State) ->
198204
#mod_data{} = absorb_state(State),
199205
{'ok', rebar_state:add_resource(State, {?RTYPE, ?MODULE})}.
200206

207+
-spec init(Type :: rsrc_type(),
208+
State :: rebar_state()) -> {'ok', rebar_state()}.
209+
%
210+
% Installs the resource handler, the provider itself does nothing.
211+
%
212+
% This gets called repeatedly, for each profile, and in each case we want
213+
% to prime the process environment with any info we may need later, as
214+
% download/4 is NOT called first if the dependency is already present, and
215+
% it's the only resource call that gets to see the rebar state.
216+
%
217+
% Note: This arity is the rebar_resource_v2 format of this function.
218+
%
219+
init(Type, State) ->
220+
#mod_data{} = absorb_state(State),
221+
{'ok', rebar_resource_v2:new(Type, ?MODULE, State)}.
222+
201223
-spec do(State :: rebar_state:t()) -> {'ok', rebar_state()}.
202224
%
203225
% Fulfills the `provider' contract, does nothing ... for now.
@@ -225,14 +247,18 @@ format_error(Error) ->
225247
%% Resource API
226248
%% ===================================================================
227249

228-
-spec download(
229-
Dest :: rsrc_dir(), From :: this_spec(), State :: rebar_state())
230-
-> {'ok', term()} | rebar_err().
250+
-spec download(Dest :: rsrc_dir(),
251+
From :: this_spec(),
252+
State :: rebar_state())
253+
-> {'ok', term()} | rebar_err().
231254
%
232255
% Download the specified resource using its underlying handler.
233256
%
234-
download(Dest, {?RTYPE, Loc,
235-
#mod_ref{res = Res, ref = Ref, opt = Opts}}, State) ->
257+
% Note: This arity is the pre-rebar3 3.7.0 format of this function.
258+
%
259+
download(Dest,
260+
{?RTYPE, Loc, #mod_ref{res = Res, ref = Ref, opt = Opts}},
261+
State) ->
236262
download(Dest, {?RTYPE, {Res, Loc, Ref}, Opts}, State);
237263

238264
download(Dest, {?RTYPE, Spec}, State) ->
@@ -254,13 +280,60 @@ download(Dest, {?RTYPE, Spec, Opts}, State) ->
254280
Err
255281
end.
256282

257-
-spec lock(Path :: rsrc_dir(), Spec :: this_spec())
258-
-> rebar_lock() | no_return().
283+
-spec download(Dest :: rsrc_dir(),
284+
AppInfo :: rebar_app_info:t(),
285+
ResourceState :: term(),
286+
State :: rebar_state())
287+
-> {'ok', term()} | rebar_err().
288+
%
289+
% Download the specified resource using its underlying handler.
290+
%
291+
% Note: This arity is the rebar_resource_v2 format of this function.
292+
%
293+
download(Dest, AppInfo0, ResourceState, RebarState) ->
294+
{Spec, Opts} = case rebar_app_info:source(AppInfo0) of
295+
{?RTYPE, Loc0, #mod_ref{res = Res0, ref = Ref, opt = Opts0}} ->
296+
{{Res0, Loc0, Ref}, Opts0};
297+
{?RTYPE, S, O} ->
298+
{S, O};
299+
{?RTYPE, S} ->
300+
{S, []}
301+
end,
302+
{Res, Loc} = parse_ext_spec(Spec),
303+
#mod_dep{name = Name} = lookup_loc(mod_data(), Loc),
304+
#mod_res{mod = Mod} = lookup_res(mod_data(), Res),
305+
AppInfo = rebar_app_info:source(AppInfo0, Spec),
306+
case Mod:download(Dest, AppInfo, ResourceState, RebarState) of
307+
ok ->
308+
case lists:keytake(vsn, 1, Opts) of
309+
false ->
310+
% because download has not put the downloaded app into
311+
% its final location, configure the app dir to be the
312+
% Dest provided.
313+
TmpAppInfo = rebar_app_info:dir(AppInfo, Dest),
314+
{plain, Vsn} = Mod:make_vsn(TmpAppInfo, []),
315+
ensure_app(Dest, Mod, Name, [{vsn, Vsn} |Opts], ok);
316+
{value, _, _} ->
317+
ensure_app(Dest, Mod, Name, Opts, ok)
318+
end;
319+
Err -> Err
320+
end.
321+
322+
-spec lock(Path :: rsrc_dir(),
323+
Spec :: this_spec())
324+
-> rebar_lock() | no_return();
325+
(AppInfo :: rebar_app_info:t(),
326+
ResourceState :: term())
327+
-> rebar_lock() | no_return().
259328
%
260329
% Pass through to the underlying resource handler.
261330
% Note that the callback doesn't allow an error tuple to be returned, so an
262331
% exception is our only option if we can't look up the mapping.
263332
%
333+
% Note: this function is common to the pre-rebar3 3.7.0 and rebar_resource_v2
334+
% formats, and as such has heads for each.
335+
%
336+
%% pre-rebar3 3.7.0 format
264337
lock(Path, {?RTYPE, Loc, #mod_ref{res = Res, ref = Prev} = Rec}) ->
265338
#mod_res{mod = Mod} = lookup_res(mod_data(), Res),
266339
{Res, Loc, Ref} = Mod:lock(Path, {Res, Loc, Prev}),
@@ -273,15 +346,35 @@ lock(Path, {?RTYPE, Spec, Opts}) ->
273346
{Res, _} = parse_ext_spec(Spec),
274347
#mod_res{mod = Mod} = lookup_res(mod_data(), Res),
275348
{Res, Loc, Ref} = Mod:lock(Path, Spec),
349+
{?RTYPE, Loc, #mod_ref{res = Res, ref = Ref, opt = Opts}};
350+
%% rebar_resource_v2 format
351+
lock(AppInfo, _ResourceState) ->
352+
{Spec, Opts} = case rebar_app_info:source(AppInfo) of
353+
{?RTYPE, Loc0, #mod_ref{res = Res0, ref = Ref0, opt = Opts0}} ->
354+
{{Res0, Loc0, Ref0}, Opts0};
355+
{?RTYPE, S, O} -> {S, O};
356+
{?RTYPE, S} -> {S, []}
357+
end,
358+
{Res, _Loc} = parse_ext_spec(Spec),
359+
#mod_res{mod = Mod} = lookup_res(mod_data(), Res),
360+
{Res, Loc, Ref} = Mod:lock(rebar_app_info:source(AppInfo, Spec), []),
276361
{?RTYPE, Loc, #mod_ref{res = Res, ref = Ref, opt = Opts}}.
277362

278-
-spec needs_update(Path :: rsrc_dir(), Spec :: this_spec())
279-
-> boolean() | no_return().
363+
-spec needs_update(Path :: rsrc_dir(),
364+
SpecOrResourceState :: this_spec())
365+
-> boolean() | no_return();
366+
(AppInfo :: rebar_app_info:t(),
367+
State :: rebar_state())
368+
-> boolean() | no_return().
280369
%
281370
% Pass through to the underlying resource handler.
282371
% Note that the callback doesn't allow an error tuple to be returned, so an
283372
% exception is our only option if we can't look up the mapping.
284373
%
374+
% Note: this function is common to the pre-rebar3 3.7.0 and rebar_resource_v2
375+
% formats, and as such has heads for each.
376+
%
377+
%% pre-rebar3 3.7.0 format
285378
needs_update(Path, {?RTYPE, Loc, #mod_ref{res = Res, ref = Ref}}) ->
286379
#mod_res{mod = Mod} = lookup_res(mod_data(), Res),
287380
Mod:needs_update(Path, {Res, Loc, Ref});
@@ -292,20 +385,55 @@ needs_update(Path, {?RTYPE, Spec, _}) ->
292385
needs_update(Path, {?RTYPE, Spec}) ->
293386
{Res, _} = parse_ext_spec(Spec),
294387
#mod_res{mod = Mod} = lookup_res(mod_data(), Res),
295-
Mod:needs_update(Path, Spec).
388+
Mod:needs_update(Path, Spec);
389+
%% rebar_resource_v2 format
390+
needs_update(AppInfo, State) ->
391+
{Mod, SourceSpec} = case rebar_app_info:source(AppInfo) of
392+
{?RTYPE, Loc, #mod_ref{res = Res, ref = Ref}} ->
393+
#mod_res{mod = M} = lookup_res(mod_data(), Res),
394+
{M, {Res, Loc, Ref}};
395+
{?RTYPE, Spec, _} ->
396+
{Res, _} = parse_ext_spec(Spec),
397+
#mod_res{mod = M} = lookup_res(mod_data(), Res),
398+
{M, Spec};
399+
{?RTYPE, Spec} ->
400+
{Res, _} = parse_ext_spec(Spec),
401+
#mod_res{mod = M} = lookup_res(mod_data(), Res),
402+
{M, Spec}
403+
end,
404+
Mod:needs_update(rebar_app_info:source(AppInfo, SourceSpec), State).
296405

297406
-spec make_vsn(Path :: rsrc_dir())
298407
-> rebar_vsn() | {'error', string()} | no_return().
299408
%
300409
% Pass through to the underlying resource handler.
301410
% The weird error tuple spec comes from the rebar_resource behavior.
411+
% Note: This arity is the pre-rebar3 3.7.0 format of this function.
302412
%
303413
make_vsn(Path) ->
304414
Data = mod_data(),
305415
#mod_dep{res = Res} = lookup_dep(Data, path_name(Path)),
306416
#mod_res{mod = Mod} = lookup_res(Data, Res),
307417
Mod:make_vsn(Path).
308418

419+
-spec make_vsn(Path :: rsrc_dir(),
420+
State :: rebar_state())
421+
-> rebar_vsn() | {'error', string()} | no_return().
422+
%
423+
% Pass through to the underlying resource handler.
424+
% The weird error tuple spec comes from the rebar_resource behavior.
425+
% Note: This arity is the rebar_resource_v2 format of this function.
426+
%
427+
make_vsn(AppInfo0, _ResourceState) ->
428+
Spec = case rebar_app_info:source(AppInfo0) of
429+
{?RTYPE, Loc0, #mod_ref{res = Res0, ref = Ref}} -> {Res0, Loc0, Ref};
430+
{?RTYPE, S, _} -> S;
431+
{?RTYPE, S} -> S
432+
end,
433+
{Res, _Loc} = parse_ext_spec(Spec),
434+
#mod_res{mod = Mod} = lookup_res(mod_data(), Res),
435+
Mod:make_vsn(rebar_app_info:source(AppInfo0, Spec), []).
436+
309437
%% ===================================================================
310438
%% Internal
311439
%% ===================================================================
@@ -348,16 +476,21 @@ format_error(Mod, Func, Class, Data) ->
348476
absorb_state(State) ->
349477
Data1 = mod_data(),
350478
Data2 = absorb_resources(Data1, rebar_state:resources(State)),
351-
Data3 = absorb_deps(Data2, rebar_state:get(State, 'deps', [])),
352-
Data4 = absorb_app_infos(Data3, rebar_state:lock(State)),
479+
Data3 = absorb_app_infos(Data2, rebar_state:lock(State)),
480+
Data4 = absorb_deps(Data3, rebar_state:get(State, 'deps', [])),
353481
% Data5 = absorb_profiles(Data4, rebar_state:current_profiles(State), State),
354482
mod_data(dump_mod_data(Data4)).
355483

356484
-spec absorb_app_infos(Data :: mod_data(), AppInfos :: [tuple()])
357485
-> mod_data() | no_return().
358486
absorb_app_infos(Data, [AppInfo | AppInfos]) ->
487+
% absorb deps from app_info to ensure that all deps are absorbed
488+
% see comments in https://github.com/alertlogic/rebar_raw_resource/pull/1
489+
% for further discussion.
490+
AppInfoDeps = rebar_app_info:get(AppInfo, deps, []),
359491
absorb_app_infos(
360-
absorb_dep(Data,
492+
absorb_dep(
493+
absorb_deps(Data, AppInfoDeps),
361494
rebar_app_info:name(AppInfo), rebar_app_info:source(AppInfo)),
362495
AppInfos);
363496
absorb_app_infos(Data, []) ->
@@ -390,14 +523,14 @@ absorb_profiles(Data, [], _) ->
390523
%
391524
% Allow for whatever may come through to handle future extensions.
392525
%
393-
% rebar v2 resource
526+
% rebar_resource_v2 format
394527
absorb_resources(Data, [Res | Resources]) when ?is_rec_type(Res, resource, 3) ->
395528
absorb_resources(
396529
map_res(Data, #mod_res{
397530
res = term_to_atom(erlang:element(2, Res)),
398531
mod = erlang:element(3, Res)}),
399532
Resources);
400-
% old style rebar resource
533+
% pre-rebar3 3.7.0 format
401534
absorb_resources(Data, [Res | Resources]) when ?is_min_tuple(Res, 2) ->
402535
absorb_resources(
403536
map_res(Data, #mod_res{
@@ -480,9 +613,9 @@ ensure_app(Path, Mod, Name, Opts, Result) ->
480613
" {applications, [kernel, stdlib]}\n"
481614
"]}.\n",
482615
[?MODULE, Name, Desc, Vsn]),
483-
case filelib:ensure_dir(BApp) of
616+
case filelib:ensure_dir(SApp) of
484617
'ok' ->
485-
case file:write_file(BApp, Data) of
618+
case file:write_file(SApp, Data) of
486619
'ok' ->
487620
Result;
488621
Err ->

0 commit comments

Comments
 (0)