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

时间:2010-11-23 11:36:58

标签: erlang preprocessor

在最近的Erlang R14中,inets的文件httpd.hrl已从以下位置移除:

src/httpd.hrl

为:

src/http_server/httpd.hrl

Erlang Web框架使用以下指令在几个地方包含该文件:

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

由于我喜欢使用两个版本的Erlang(R13和R14)编译Erlang Web,理想情况下我需要的是:

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

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

erlang:system_info(version).

在预处理时间确实不可能。

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

2 个答案:

答案 0 :(得分:6)

不确定你是否喜欢这种hackish技巧,但你可以使用解析转换。

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

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

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

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

-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.

如果您使用的是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}},
 ...

这告诉我们的是,我们可以使用两个标头,并且有效的标头将自动包含在另一个标头错误输出中。我们需要做的就是删除{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)].

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

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

答案 1 :(得分:2)

如果您使用的是makefile,则可以执行类似

的操作

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

匹配字符串并在erlc参数或Emakefile中定义宏。 没有其他办法,AFAIK。