简体   繁体   中英

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?

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.

If you are using makefiles, you can do something like

ERTS_VER=$(shell erl +V 2>&1 | egrep -o '[0-9]+.[0-9]+.[0-9]+')

than match the string and define macro in erlc arguments or in Emakefile. There is no other way, AFAIK.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM