Как отличить версии ERTS во время предварительной обработки? - PullRequest
4 голосов
/ 23 ноября 2010

В последнем Erlang R14 файл inets httpd.hrl был перемещен из:

src/httpd.hrl

до:

src/http_server/httpd.hrl

Платформа Erlang Web включает файл в нескольких местах, используя следующую директиву:

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

Так как я хотел бы, чтобы 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.

Даже если возможно получить версию ERTS через:

erlang:system_info(version).

Это действительно невозможно во время предварительной обработки.

Как справиться с этими ситуациями? Является ли синтаксический анализ единственным способом? Есть ли лучшие альтернативы?

Ответы [ 2 ]

6 голосов
/ 23 ноября 2010

Не уверен, что вам понравится этот хакерский трюк, но вы можете использовать преобразование синтаксического анализа.

Давайте сначала определим модуль базового преобразования:

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

Это затем удалит ошибку include, только если он находится на том модуле, который вы хотели. Другие беспорядочные включения должны потерпеть неудачу как обычно.

Я не проверял это на всех версиях, поэтому, если поведение между ними изменилось, это не сработает. С другой стороны, если он остался прежним, это parse_transform будет независимым от версии, за счет необходимости упорядочить порядок компиляции ваших модулей, что достаточно просто с Emakefiles и rebar.

2 голосов
/ 23 ноября 2010

Если вы используете make-файлы, вы можете сделать что-то вроде

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

, чем соответствует строка и определение макроса в аргументах erlc или в Emakefile.Другого пути нет, AFAIK.

...