如何在预处理时区分出ERTS版本?

Rob*_*loi 4 erlang preprocessor

在最近的Erlang R14中,inets的文件httpd.hrl已被移除:

src/httpd.hrl
Run Code Online (Sandbox Code Playgroud)

至:

src/http_server/httpd.hrl
Run Code Online (Sandbox Code Playgroud)

二郎的Web框架,包括使用以下指令几个地方的文件:

-include_lib("inets/src/httpd.hrl").
Run Code Online (Sandbox Code Playgroud)

因为我喜欢Erlang Web用两个版本的Erlang(R13和R14)进行编译,所以理想情况下我需要的是:

-ifdef(OLD_ERTS_VERSION).
-include_lib("inets/src/httpd.hrl").
-else.
-include_lib("inets/src/http_server/httpd.hrl").
-endif.
Run Code Online (Sandbox Code Playgroud)

即使可以通过以下方式检索ERTS版本:

erlang:system_info(version).
Run Code Online (Sandbox Code Playgroud)

这在预处理时确实是不可能的.

如何处理这些情况?解析变换是唯一的方法吗?还有更好的选择吗?

I G*_*ICE 6

不确定你是否喜欢这个hackish技巧,但你可以使用解析变换.

让我们首先定义一个基本的解析转换模块:

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

parse_transform(AST, _Opts) ->
    io:format("~p~n", [AST]).
Run Code Online (Sandbox Code Playgroud)

编译它,然后您可以在要使用它的模块中包含两个标头.这应该给出以下内容:

-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.
Run Code Online (Sandbox Code Playgroud)

如果您使用的是R14B并进行编译,那么您应该将模块的抽象格式看起来像这样:

[{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}},
 ...
Run Code Online (Sandbox Code Playgroud)

这告诉我们的是,我们可以使用两个标头,并且有效的标头将自动包含在内,另一个标头会出错.我们需要做的就是删除{error,...}元组并获得有效的编译.为此,请修复parse_transform模块,使其如下所示:

-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)].
Run Code Online (Sandbox Code Playgroud)

这将删除错误包括,只有它在您想要的精确模块上.其他凌乱的包括应该像往常一样失败.

我没有在所有版本上测试过这个,所以如果它们之间的行为发生了变化,这将无法正常工作.另一方面,如果它保持不变,这个parse_transform将是版本无关的,代价是需要订购模块的编译顺序,这对于Emakefiles和rebar来说非常简单.