Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to distinguish ERTS versions at pre-processing time?

In the recent Erlang R14, inets' file httpd.hrl has been moved from:

src/httpd.hrl

to:

src/http_server/httpd.hrl

The Erlang Web framework includes the file in several places using the following directive:

-include_lib("inets/src/httpd.hrl").

Since I'd love the Erlang Web to compile with both versions of Erlang (R13 and R14), what I'd need ideally is:

-ifdef(OLD_ERTS_VERSION).
-include_lib("inets/src/httpd.hrl").
-else.
-include_lib("inets/src/http_server/httpd.hrl").
-endif.

Even if it possible to retrieve the ERTS version via:

erlang:system_info(version).

That's indeed not possible at pre-processing time.

How to deal with these situations? Is the parse transform the only way to go? Are there better alternatives?

like image 234
Roberto Aloi Avatar asked Feb 26 '23 18:02

Roberto Aloi


1 Answers

Not sure if you'll like this hackish trick, but you could use a parse transform.

Let's first define a basic parse transform module:

-module(erts_v).
-export([parse_transform/2]).

parse_transform(AST, _Opts) ->
    io:format("~p~n", [AST]).

Compile it, then you can include both headers in the module you want this to work for. This should give the following:

-module(test).
-compile({parse_transform, erts_v}).
-include_lib("inets/src/httpd.hrl").
-include_lib("inets/src/http_server/httpd.hrl").
-export([fake_fun/1]).

fake_fun(A) -> A.

If you're on R14B and compile it, you should have the abstract format of the module looking like this:

[{attribute,1,file,{"./test.erl",1}},
 {attribute,1,module,test},
 {error,{3,epp,{include,lib,"inets/src/httpd.hrl"}}},
 {attribute,1,file,
     {"/usr/local/lib/erlang/lib/inets-5.5/src/http_server/httpd.hrl",1}},
 {attribute,1,file,
     {"/usr/local/lib/erlang/lib/kernel-2.14.1/include/file.hrl",1}},
 {attribute,24,record,
     {file_info,
         [{record_field,25,{atom,25,size}},
          {record_field,26,{atom,26,type}},
 ...

What this tells us is that we can use both headers, and the valid one will automatically be included while the other will error out. All we need to do is remove the {error,...} tuple and get a working compilation. To do this, fix the parse_transform module so it looks like this:

-module(erts_v).
-export([parse_transform/2]).

parse_transform(AST, _Opts) ->
    walk_ast(AST).

walk_ast([{error,{_,epp,{include,lib,"inets/src/httpd.hrl"}}}|AST]) ->
    AST;
walk_ast([{error,{_,epp,{include,lib,"inets/src/http_server/httpd.hrl"}}}|AST]) ->
    AST;
walk_ast([H|T]) ->
    [H|walk_ast(T)].

This will then remove the error include, only if it's on the precise module you wanted. Other messy includes should fail as usual.

I haven't tested this on all versions, so if the behaviour changed between them, this won't work. On the other hand, if it stayed the same, this parse_transform will be version independent, at the cost of needing to order the compiling order of your modules, which is simple enough with Emakefiles and rebar.

like image 185
I GIVE TERRIBLE ADVICE Avatar answered Feb 28 '23 07:02

I GIVE TERRIBLE ADVICE