#!/usr/bin/env escript %%% This script converts an arbitrary binary file to an Erlang module with a %%% single exported 'bin/0' function that returns the original binary. %%% %%% See the end of the file for how I figured out the correct terms. main(["+debug_info" | Files]) -> io:format("Embedding binaries with debug_info...\n"), lists:foreach(fun(X) -> embed_file_in_beam(X, [debug_info]) end, Files); main(Files) -> io:format("Embedding binaries...\n"), lists:foreach(fun(X) -> embed_file_in_beam(X, []) end, Files). embed_file_in_beam(InputPath, CompilerOptions) -> % Read the source file. {ok, Bytes} = file:read_file(InputPath), % Work out what we're going to call the output. ModuleName = get_module_name(filename:basename(InputPath)), file:make_dir("ebin"), OutputPath = filename:join("ebin", ModuleName ++ ".beam"), io:format(" ~s -> ~s (~s)\n", [InputPath, ModuleName, OutputPath]), % We'll export a function Mod:bin/0. Mod = list_to_atom(ModuleName), Fun = 'bin', % An Erlang module is a list of forms; we need three: Forms = [ module_form(Mod), % -module(foo). export_form(Fun), % -export([bin/0]). function_form(Fun, Bytes) % bin() -> <<...>>. ], % Compile the forms into a binary. {ok, Mod, Bin} = compile:forms(Forms, CompilerOptions), % The binary _is_ the beam file, so write it out. ok = file:write_file(OutputPath, Bin). % Given, e.g., "foo_bar.baz", return "foo_bar_baz". get_module_name(Filename) -> re:replace(Filename, "\\.", "_", [global, {return, list}]). module_form(Mod) -> {attribute, 1, module, Mod}. export_form(Fun) -> {attribute, 1, export, [{Fun, 0}]}. function_form(Fun, Binary) -> % a function, {function, 1, Fun, 0, % with one clause, [{clause, 1, [], [], % which has one expression, a binary, [{bin, 1, [ % made up of the stuff we first thought of. {bin_element, 1, {integer, 1, Byte}, default, default} || <> <= Binary ]}]}]}. %%% How it works: %%% %%% See http://stackoverflow.com/a/2160696/8446, but... %%% %%% {ok, MTs, _} = erl_scan:string("-module(foo)."). %%% {ok, ETs, _} = erl_scan:string("-export([bin/0])."). %%% % bin() -> <<"Hello">>. %%% {ok, FTs, _} = erl_scan:string("bin() -> <<72, 101, 108, 108, 111>>."). %%% %%% {ok, MF} = erl_parse:parse_form(MTs). %%% {ok, EF} = erl_parse:parse_form(ETs). %%% {ok, FF} = erl_parse:parse_form(FTs). %%% %%% Forms = [MF, EF, FF]. %%% %%% The above returns a readable AST, suitable for compile:forms/2. %%% %%% Debug Information %%% %%% By passing +debug_info to embed_binaries, you can persuade it to generate %%% debug information. This can be verified by: %%% %%% beam_lib:chunks("foo.beam", [abstract_code]). %%% % Without +debug_info: %%% % {ok, {foo, [{abstract_code, no_abstract_code}]}} %%% % With +debug_info: %%% % {ok, {foo, [{abstract_code, {raw_abstract_v1, [{attribute, .... %% vi: ft=erlang