Created
March 26, 2016 09:30
-
-
Save rlipscombe/770ce8fc75add11e16f1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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} | |
|| <<Byte:8>> <= 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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment