diff options
Diffstat (limited to 'lib/compiler/test/compile_SUITE.erl')
-rw-r--r-- | lib/compiler/test/compile_SUITE.erl | 722 |
1 files changed, 722 insertions, 0 deletions
diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl new file mode 100644 index 0000000000..7c3990a855 --- /dev/null +++ b/lib/compiler/test/compile_SUITE.erl @@ -0,0 +1,722 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(compile_SUITE). + +%% Tests compile:file/1 and compile:file/2 with various options. + +-include("test_server.hrl"). + +-export([all/1, + app_test/1, + file_1/1, module_mismatch/1, big_file/1, outdir/1, + binary/1, cond_and_ifdef/1, listings/1, listings_big/1, + other_output/1, package_forms/1, encrypted_abstr/1, + bad_record_use/1, bad_record_use1/1, bad_record_use2/1, strict_record/1, + missing_testheap/1, cover/1, env/1, core/1, asm/1]). + +-export([init/3]). + + +%% To cover the stripping of 'type' and 'spec' in beam_asm. +-type all_return_type() :: [atom()]. +-spec all('suite' | [_]) -> all_return_type(). + +all(suite) -> + test_lib:recompile(?MODULE), + [app_test, + file_1, module_mismatch, big_file, outdir, binary, + cond_and_ifdef, listings, listings_big, + other_output, package_forms, + encrypted_abstr, + bad_record_use, strict_record, + missing_testheap, cover, env, core, asm]. + + +%% Test that the Application file has no `basic' errors."; +app_test(Config) when is_list(Config) -> + ?line ?t:app_test(compiler). + +%% Tests that we can compile and run a simple Erlang program, +%% using compile:file/1. + +file_1(Config) when is_list(Config) -> + ?line Dog = test_server:timetrap(test_server:minutes(5)), + ?line {Simple, Target} = files(Config, "file_1"), + ?line {ok, Cwd} = file:get_cwd(), + ?line ok = file:set_cwd(filename:dirname(Target)), + ?line {ok,simple} = compile:file(Simple), %Smoke test only. + ?line {ok,simple} = compile:file(Simple, [slim]), %Smoke test only. + ?line {ok,simple} = compile:file(Simple, [native,report]), %Smoke test. + ?line {ok,simple} = compile:file(Target, [native,from_beam]), %Smoke test. + ?line {ok,simple} = compile:file(Simple, [debug_info]), + ?line ok = file:set_cwd(Cwd), + ?line true = exists(Target), + ?line passed = run(Target, test, []), + + %% Cleanup. + ?line ok = file:delete(Target), + ?line ok = file:del_dir(filename:dirname(Target)), + ?line test_server:timetrap_cancel(Dog), + ok. + +module_mismatch(Config) when is_list(Config) -> + ?line DataDir = ?config(data_dir, Config), + ?line File = filename:join(DataDir, "wrong_module_name.erl"), + ?line {error,[{"wrong_module_name.beam", + [{compile,{module_name,arne,"wrong_module_name"}}]}], + []} = compile:file(File, [return]), + ?line error = compile:file(File, [report]), + + ?line {ok,arne,[]} = compile:file(File, + [return,no_error_module_mismatch]), + + ok. + +big_file(Config) when is_list(Config) -> + ?line Dog = test_server:timetrap(test_server:minutes(5)), + ?line DataDir = ?config(data_dir, Config), + ?line PrivDir = ?config(priv_dir, Config), + ?line Big = filename:join(DataDir, "big.erl"), + ?line Target = filename:join(PrivDir, "big.beam"), + ?line ok = file:set_cwd(PrivDir), + ?line {ok,big} = compile:file(Big, []), + ?line {ok,big} = compile:file(Big, [r9,debug_info]), + ?line {ok,big} = compile:file(Big, [no_postopt]), + ?line true = exists(Target), + + %% Cleanup. + ?line ok = file:delete(Target), + ?line test_server:timetrap_cancel(Dog), + ok. + +%% Tests that the {outdir, Dir} option works. + +outdir(Config) when is_list(Config) -> + ?line Dog = test_server:timetrap(test_server:seconds(60)), + ?line {Simple, Target} = files(Config, "outdir"), + ?line {ok, simple} = compile:file(Simple, [{outdir, filename:dirname(Target)}]), + ?line true = exists(Target), + ?line passed = run(Target, test, []), + ?line ok = file:delete(Target), + ?line ok = file:del_dir(filename:dirname(Target)), + ?line test_server:timetrap_cancel(Dog), + ok. + +%% Tests that the binary option works. + +binary(Config) when is_list(Config) -> + ?line Dog = test_server:timetrap(test_server:seconds(60)), + ?line {Simple, Target} = files(Config, "binary"), + ?line {ok, simple, Binary} = compile:file(Simple, [binary]), + ?line code:load_binary(simple, Target, Binary), + ?line passed = simple:test(), + ?line true = code:delete(simple), + ?line false = code:purge(simple), + ?line ok = file:del_dir(filename:dirname(Target)), + ?line test_server:timetrap_cancel(Dog), + ok. + +%% Tests that conditional compilation, defining values, including files work. + +cond_and_ifdef(Config) when is_list(Config) -> + ?line Dog = test_server:timetrap(test_server:seconds(60)), + ?line {Simple, Target} = files(Config, "cond_and_ifdef"), + ?line IncludeDir = filename:join(filename:dirname(Simple), "include"), + ?line Options = [{outdir, filename:dirname(Target)}, + {d, need_foo}, {d, foo_value, 42}, + {i, IncludeDir}, report], + ?line {ok, simple} = compile:file(Simple, Options), + ?line true = exists(Target), + ?line {hiker, 42} = run(Target, foo, []), + ?line ok = file:delete(Target), + ?line ok = file:del_dir(filename:dirname(Target)), + ?line test_server:timetrap_cancel(Dog), + ok. + +listings(Config) when is_list(Config) -> + ?line Dog = test_server:timetrap(test_server:minutes(8)), + ?line DataDir = ?config(data_dir, Config), + ?line PrivDir = ?config(priv_dir, Config), + ?line Simple = filename:join(DataDir, simple), + ?line TargetDir = filename:join(PrivDir, listings), + ?line ok = file:make_dir(TargetDir), + + %% Test all dedicated listing options. + ?line do_listing(Simple, TargetDir, 'S'), + ?line do_listing(Simple, TargetDir, 'E'), + ?line do_listing(Simple, TargetDir, 'P'), + ?line do_listing(Simple, TargetDir, dpp, ".pp"), + ?line do_listing(Simple, TargetDir, dabstr, ".abstr"), + ?line do_listing(Simple, TargetDir, dexp, ".expand"), + ?line do_listing(Simple, TargetDir, dcore, ".core"), + ?line do_listing(Simple, TargetDir, doldinline, ".oldinline"), + ?line do_listing(Simple, TargetDir, dinline, ".inline"), + ?line do_listing(Simple, TargetDir, dcore, ".core"), + ?line do_listing(Simple, TargetDir, dcopt, ".copt"), + ?line do_listing(Simple, TargetDir, dsetel, ".dsetel"), + ?line do_listing(Simple, TargetDir, dkern, ".kernel"), + ?line do_listing(Simple, TargetDir, dlife, ".life"), + ?line do_listing(Simple, TargetDir, dcg, ".codegen"), + ?line do_listing(Simple, TargetDir, dblk, ".block"), + ?line do_listing(Simple, TargetDir, dbool, ".bool"), + ?line do_listing(Simple, TargetDir, dtype, ".type"), + ?line do_listing(Simple, TargetDir, ddead, ".dead"), + ?line do_listing(Simple, TargetDir, djmp, ".jump"), + ?line do_listing(Simple, TargetDir, dclean, ".clean"), + ?line do_listing(Simple, TargetDir, dpeep, ".peep"), + ?line do_listing(Simple, TargetDir, dopt, ".optimize"), + + %% First clean up. + ?line Listings = filename:join(PrivDir, listings), + ?line lists:foreach(fun(F) -> ok = file:delete(F) end, + filelib:wildcard(filename:join(Listings, "*"))), + + %% Test options that produce a listing file if 'binary' is not given. + ?line do_listing(Simple, TargetDir, to_pp, ".P"), + ?line do_listing(Simple, TargetDir, to_exp, ".E"), + ?line do_listing(Simple, TargetDir, to_core0, ".core"), + ?line ok = file:delete(filename:join(Listings, "simple.core")), + ?line do_listing(Simple, TargetDir, to_core, ".core"), + ?line do_listing(Simple, TargetDir, to_kernel, ".kernel"), + + %% Final clean up. + ?line lists:foreach(fun(F) -> ok = file:delete(F) end, + filelib:wildcard(filename:join(Listings, "*"))), + ?line ok = file:del_dir(Listings), + ?line test_server:timetrap_cancel(Dog), + ok. + +listings_big(Config) when is_list(Config) -> + ?line Dog = test_server:timetrap(test_server:minutes(10)), + ?line DataDir = ?config(data_dir, Config), + ?line PrivDir = ?config(priv_dir, Config), + ?line Big = filename:join(DataDir, big), + ?line TargetDir = filename:join(PrivDir, listings_big), + ?line ok = file:make_dir(TargetDir), + ?line do_listing(Big, TargetDir, 'S'), + ?line do_listing(Big, TargetDir, 'E'), + ?line do_listing(Big, TargetDir, 'P'), + ?line do_listing(Big, TargetDir, dkern, ".kernel"), + + ?line Target = filename:join(TargetDir, big), + ?line {ok,big} = compile:file(Target, [asm,{outdir,TargetDir}]), + + %% Cleanup. + ?line ok = file:delete(Target ++ ".beam"), + ?line lists:foreach(fun(F) -> ok = file:delete(F) end, + filelib:wildcard(filename:join(TargetDir, "*"))), + ?line ok = file:del_dir(TargetDir), + ?line test_server:timetrap_cancel(Dog), + ok. + +other_output(Config) when is_list(Config) -> + ?line Dog = test_server:timetrap(test_server:minutes(8)), + ?line DataDir = ?config(data_dir, Config), + ?line PrivDir = ?config(priv_dir, Config), + ?line Simple = filename:join(DataDir, simple), + ?line TargetDir = filename:join(PrivDir, other_output), + ?line ok = file:make_dir(TargetDir), + + io:put_chars("to_pp"), + ?line {ok,[],PP} = compile:file(Simple, [to_pp,binary,time]), + ?line [] = [E || E <- PP, + begin + case element(1, E) of + attribute -> false; + function -> false; + eof -> false + end + end], + + io:put_chars("to_exp (file)"), + ?line {ok,simple,Expand} = compile:file(Simple, [to_exp,binary,time]), + ?line case Expand of + {simple,Exports,Forms} when is_list(Exports), is_list(Forms) -> ok + end, + io:put_chars("to_exp (forms)"), + ?line {ok,simple,Expand} = compile:forms(PP, [to_exp,binary,time]), + + io:put_chars("to_core (file)"), + ?line {ok,simple,Core} = compile:file(Simple, [to_core,binary,time]), + ?line c_module = element(1, Core), + ?line {ok,_} = core_lint:module(Core), + io:put_chars("to_core (forms)"), + ?line {ok,simple,Core} = compile:forms(PP, [to_core,binary,time]), + + io:put_chars("to_kernel (file)"), + ?line {ok,simple,Kernel} = compile:file(Simple, [to_kernel,binary,time]), + ?line k_mdef = element(1, Kernel), + io:put_chars("to_kernel (forms)"), + ?line {ok,simple,Kernel} = compile:forms(PP, [to_kernel,binary,time]), + + io:put_chars("to_asm (file)"), + ?line {ok,simple,Asm} = compile:file(Simple, [to_asm,binary,time]), + ?line {simple,_,_,_,_} = Asm, + io:put_chars("to_asm (forms)"), + ?line {ok,simple,Asm} = compile:forms(PP, [to_asm,binary,time]), + + ?line test_server:timetrap_cancel(Dog), + ok. + +package_forms(Config) when is_list(Config) -> + Fs = [{attribute,1,file,{"./p.erl",1}}, + {attribute,1,module,[p,p]}, + {attribute,3,compile,export_all}, + {attribute,1,file, + {"/clearcase/otp/erts/lib/stdlib/include/qlc.hrl",1}}, + {attribute,6,file,{"./p.erl",6}}, + {function,7,q,0, + [{clause,7,[],[], + [{call,8, + {remote,8,{atom,8,qlc},{atom,8,q}}, + [{tuple,-8, + [{atom,-8,qlc_lc}, + {'fun',-8, + {clauses, + [{clause,-8,[],[], + [{tuple,-8, + [{atom,-8,simple_v1}, + {atom,-8,'X'}, + {'fun',-8,{clauses,[{clause,-8,[],[],[{nil,8}]}]}}, + {integer,-8,8}]}]}]}}, + {atom,-8,undefined}]}]}]}]}, + {eof,9}], + {ok,'p.p',_} = compile:forms(Fs, ['S',report]), + ok. + +encrypted_abstr(Config) when is_list(Config) -> + ?line Dog = test_server:timetrap(test_server:minutes(10)), + ?line {Simple,Target} = files(Config, "encrypted_abstr"), + + Res = case has_crypto() of + no -> + %% No crypto. + ?line encrypted_abstr_no_crypto(Simple, Target), + {comment,"The crypto application is missing or broken"}; + yes -> + %% Simulate not having crypto by removing + %% the crypto application from the path. + ?line OldPath = code:get_path(), + try + ?line NewPath = OldPath -- [filename:dirname(code:which(crypto))], + ?line (catch crypto:stop()), + ?line code:delete(crypto), + ?line code:purge(crypto), + ?line code:set_path(NewPath), + ?line encrypted_abstr_no_crypto(Simple, Target) + after + code:set_path(OldPath) + end, + + %% Now run the tests that require crypto. + ?line encrypted_abstr_1(Simple, Target), + ?line ok = file:delete(Target), + ?line ok = file:del_dir(filename:dirname(Target)) + end, + + %% Cleanup. + ?line test_server:timetrap_cancel(Dog), + Res. + +encrypted_abstr_1(Simple, Target) -> + ?line TargetDir = filename:dirname(Target), + ?line Key = "ablurf123BX#$;3", + ?line install_crypto_key(Key), + ?line {ok,simple} = compile:file(Simple, + [debug_info,{debug_info_key,Key}, + {outdir,TargetDir}]), + ?line verify_abstract(Target), + + ?line {ok,simple} = compile:file(Simple, + [{debug_info_key,Key}, + {outdir,TargetDir}]), + ?line verify_abstract(Target), + + ?line {ok,simple} = compile:file(Simple, + [debug_info,{debug_info_key,{des3_cbc,Key}}, + {outdir,TargetDir}]), + ?line verify_abstract(Target), + + ?line {ok,{simple,[{compile_info,CInfo}]}} = + beam_lib:chunks(Target, [compile_info]), + ?line {value,{_,Opts}} = lists:keysearch(options, 1, CInfo), + ?line {value,{_,'********'}} = lists:keysearch(debug_info_key, 1, Opts), + + %% Try some illegal forms of crypto keys. + ?line error = compile:file(Simple, + [debug_info,{debug_info_key,{blurf,"ss"}},report]), + ?line error = compile:file(Simple, + [debug_info,{debug_info_key,{blurf,1,"ss"}},report]), + ?line error = compile:file(Simple, + [debug_info,{debug_info_key,42},report]), + + %% Place the crypto key in .erlang.crypt. + ?line beam_lib:clear_crypto_key_fun(), + ?line {ok,OldCwd} = file:get_cwd(), + ?line ok = file:set_cwd(TargetDir), + + ?line error = compile:file(Simple, [encrypt_debug_info,report]), + + ?line NewKey = "better use another key here", + ?line write_crypt_file(["[{debug_info,des3_cbc,simple,\"",NewKey,"\"}].\n"]), + ?line {ok,simple} = compile:file(Simple, [encrypt_debug_info,report]), + ?line verify_abstract("simple.beam"), + ?line ok = file:delete(".erlang.crypt"), + ?line beam_lib:clear_crypto_key_fun(), + ?line {error,beam_lib,{key_missing_or_invalid,"simple.beam",abstract_code}} = + beam_lib:chunks("simple.beam", [abstract_code]), + ?line ok = file:set_cwd(OldCwd), + ok. + + +write_crypt_file(Contents0) -> + Contents = list_to_binary([Contents0]), + io:format("~s\n", [binary_to_list(Contents)]), + ok = file:write_file(".erlang.crypt", Contents). + +encrypted_abstr_no_crypto(Simple, Target) -> + ?line TargetDir = filename:dirname(Target), + ?line Key = "ablurf123BX#$;3", + ?line error = compile:file(Simple, + [debug_info,{debug_info_key,Key}, + {outdir,TargetDir},report]), + ok. + +verify_abstract(Target) -> + {ok,{simple,[Chunk]}} = beam_lib:chunks(Target, [abstract_code]), + {abstract_code,{raw_abstract_v1,_}} = Chunk. + +has_crypto() -> + try + crypto:start(), + crypto:info(), + crypto:stop(), + yes + catch + error:_ -> no + end. + +install_crypto_key(Key) -> + F = fun (init) -> ok; + ({debug_info,des3_cbc,_,_}) -> Key; + (clear) -> ok + end, + ok = beam_lib:crypto_key_fun(F). + +%% Miscellanous tests, mainly to get better coverage. +cover(Config) when is_list(Config) -> + ?line io:format("~p\n", [compile:options()]), + ok. + +do_listing(Source, TargetDir, Type) -> + do_listing(Source, TargetDir, Type, "." ++ atom_to_list(Type)). + +do_listing(Source, TargetDir, Type, Ext) -> + io:format("Source: ~p TargetDir: ~p\n Type: ~p Ext: ~p\n", + [Source, TargetDir, Type, Ext]), + case compile:file(Source, [Type, time, {outdir, TargetDir}]) of + {ok, _} -> ok; + Other -> test_server:fail({unexpected_result, Other}) + end, + SourceBase = filename:rootname(filename:basename(Source)), + + Target = filename:join(TargetDir, SourceBase ++ Ext), + true = exists(Target). + +files(Config, Name) -> + ?line code:delete(simple), + ?line code:purge(simple), + ?line DataDir = ?config(data_dir, Config), + ?line PrivDir = ?config(priv_dir, Config), + ?line Simple = filename:join(DataDir, "simple"), + ?line TargetDir = filename:join(PrivDir, Name), + ?line ok = file:make_dir(TargetDir), + ?line Target = filename:join(TargetDir, "simple"++code:objfile_extension()), + {Simple, Target}. + + +run(Target, Func, Args) -> + ?line Module = list_to_atom(filename:rootname(filename:basename(Target))), + ?line {module, Module} = code:load_abs(filename:rootname(Target)), + ?line Result = (catch apply(Module, Func, Args)), + ?line true = code:delete(Module), + ?line false = code:purge(Module), + Result. + +exists(Name) -> + case file:read_file_info(Name) of + {ok, _} -> true; + {error, _} -> false + end. + +bad_record_use(suite) -> [bad_record_use1, bad_record_use2]. + +%% Tests that the compiler does not accept +%% bad use of records. +bad_record_use1(Config) when is_list(Config) -> + ?line {ok, Cwd} = file:get_cwd(), + ?line file:set_cwd(?config(data_dir, Config)), + ?line true=exists("bad_record_use.erl"), + ?line Ret=c:c(bad_record_use), + ?line file:set_cwd(Cwd), + ?line error=Ret, + ok. + +%% Tests that the compiler does not accept +%% bad use of records. +bad_record_use2(Config) when is_list(Config) -> + ?line {ok, Cwd} = file:get_cwd(), + ?line file:set_cwd(?config(data_dir, Config)), + ?line true=exists("bad_record_use2.erl"), + ?line Ret=c:c(bad_record_use), + ?line file:set_cwd(Cwd), + ?line error=Ret, + ok. + +strict_record(Config) when is_list(Config) -> + ?line Priv = ?config(priv_dir, Config), + ?line file:set_cwd(?config(data_dir, Config)), + ?line Opts = [{outdir,Priv},report_errors], + M = record_access, + + ?line {ok,M} = c:c(M, [strict_record_tests|Opts]), + ?line Turtle = test_strict(), + + ?line {ok,M} = c:c(M, [no_strict_record_tests|Opts]), + ?line Turtle = test_sloppy(), + + %% The option first given wins. + ?line {ok,M} = c:c(M, [no_strict_record_tests,strict_record_tests|Opts]), + ?line Turtle = test_sloppy(), + ?line {ok,M} = c:c(M, [strict_record_tests,no_strict_record_tests|Opts]), + ?line Turtle = test_strict(), + + %% Default (possibly influenced by ERL_COMPILER_OPTIONS). + ?line {ok,M} = c:c(M, [{outdir,Priv},report_errors]), + ?line try + {1,2} = record_access:test(Turtle), + {comment,"Default: no_strict_record_tests"} + catch + error:{badrecord,tortoise} -> + {comment,"Default: strict_record_tests"} + end. + +test_strict() -> + Turtle = record_access:turtle(), + ?line try + record_access:test(Turtle) + catch + error:{badrecord,tortoise} -> + ok + end, + Turtle. + +test_sloppy() -> + Turtle = record_access:turtle(), + {1,2} = record_access:test(Turtle), + Turtle. + +missing_testheap(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + Opts = [{outdir,PrivDir}], + OldPath = code:get_path(), + try + code:add_patha(PrivDir), + c:c(filename:join(DataDir, "missing_testheap1"), Opts), + c:c(filename:join(DataDir, "missing_testheap2"), Opts), + ?line ok = test(fun() -> + missing_testheap1:f({a,self()},{state,true,b}) + end, {a,b}), + ?line ok = test(fun() -> + missing_testheap2:f({a,self()},16#80000000) end, + bigger) + after + code:set_path(OldPath), + file:delete(filename:join(PrivDir, "missing_testheap1.beam")), + file:delete(filename:join(PrivDir, "missing_testheap2.beam")) + end, + ok. + +test(Fun, Result) -> + test(500, Fun, Result, []). + +test(0, _, _, _) -> + ok; +test(Iter, Fun, Result, Filler) -> + spawn(?MODULE, init, [self(), Fun, list_to_tuple(Filler)]), + receive + {result, Result} -> + test(Iter-1, Fun, Result, [0|Filler]); + {result, Other} -> + io:format("Expected ~p; got ~p~n", [Result, Other]), + test_server:fail() + end. + +init(ReplyTo, Fun, _Filler) -> + ReplyTo ! {result, Fun()}. + +env(Config) when is_list(Config) -> + ?line {Simple,Target} = files(Config, "file_1"), + ?line {ok,Cwd} = file:get_cwd(), + ?line ok = file:set_cwd(filename:dirname(Target)), + + true = os:putenv("ERL_COMPILER_OPTIONS", "binary"), + try + env_1(Simple, Target) + after + true = os:putenv("ERL_COMPILER_OPTIONS", "ignore_me"), + file:set_cwd(Cwd), + file:delete(Target), + file:del_dir(filename:dirname(Target)) + end, + ok. + +env_1(Simple, Target) -> + %% file + ?line {ok,simple,<<_/binary>>} = compile:file(Simple), + ?line {ok,simple} = compile:noenv_file(Simple, [debug_info]), + ?line true = exists(Target), + ?line {ok,{simple,[{abstract_code,Abstr0}]}} = + beam_lib:chunks(Target, [abstract_code]), + ?line {raw_abstract_v1,Forms} = Abstr0, + + %% forms + ?line true = os:putenv("ERL_COMPILER_OPTIONS", "strong_validation"), + ?line {ok,simple} = compile:forms(Forms), + ?line {ok,simple,<<"FOR1",_/binary>>} = compile:noenv_forms(Forms, []), + + %% output_generated + ?line false = compile:output_generated([]), + ?line true = compile:noenv_output_generated([]), + + ?line ok = file:delete(Target), + + ok. + +%% Test pretty-printing in Core Erlang format and then try to +%% compile the generated Core Erlang files. + +core(Config) when is_list(Config) -> + ?line Dog = test_server:timetrap(test_server:minutes(5)), + ?line PrivDir = ?config(priv_dir, Config), + ?line Outdir = filename:join(PrivDir, "core"), + ?line ok = file:make_dir(Outdir), + + ?line Wc = filename:join(filename:dirname(code:which(?MODULE)), "*.beam"), + ?line TestBeams = filelib:wildcard(Wc), + ?line Abstr = [begin {ok,{Mod,[{abstract_code, + {raw_abstract_v1,Abstr}}]}} = + beam_lib:chunks(Beam, [abstract_code]), + {Mod,Abstr} end || Beam <- TestBeams], + ?line Res = p_run(fun(F) -> do_core(F, Outdir) end, Abstr), + ?line test_server:timetrap_cancel(Dog), + Res. + + +do_core({M,A}, Outdir) -> + try + {ok,M,Core} = compile:forms(A, [to_core,report]), + CoreFile = filename:join(Outdir, atom_to_list(M)++".core"), + CorePP = core_pp:format(Core), + ok = file:write_file(CoreFile, CorePP), + case compile:file(CoreFile, [clint,from_core,binary]) of + {ok,M,_} -> + ok = file:delete(CoreFile); + Other -> + io:format("*** core_lint failure '~p' for ~s\n", + [Other,CoreFile]), + error + end + catch Class:Error -> + io:format("~p: ~p ~p\n~p\n", + [M,Class,Error,erlang:get_stacktrace()]), + error + end. + +%% Compile to Beam assembly language (.S) and the try to +%% run .S throught the compiler again. + +asm(Config) when is_list(Config) -> + ?line Dog = test_server:timetrap(test_server:minutes(20)), + ?line PrivDir = ?config(priv_dir, Config), + ?line Outdir = filename:join(PrivDir, "asm"), + ?line ok = file:make_dir(Outdir), + + ?line Wc = filename:join(filename:dirname(code:which(?MODULE)), "*.beam"), + ?line TestBeams = filelib:wildcard(Wc), + ?line Res = p_run(fun(F) -> do_asm(F, Outdir) end, TestBeams), + ?line test_server:timetrap_cancel(Dog), + Res. + + +do_asm(Beam, Outdir) -> + {ok,{M,[{abstract_code,{raw_abstract_v1,A}}]}} = + beam_lib:chunks(Beam, [abstract_code]), + try + {ok,M,Asm} = compile:forms(A, ['S']), + AsmFile = filename:join(Outdir, atom_to_list(M)++".S"), + {ok,Fd} = file:open(AsmFile, [write]), + beam_listing:module(Fd, Asm), + ok = file:close(Fd), + case compile:file(AsmFile, [from_asm,no_postopt,binary,report]) of + {ok,M,_} -> + ok = file:delete(AsmFile); + Other -> + io:format("*** failure '~p' for ~s\n", + [Other,AsmFile]), + error + end + catch Class:Error -> + io:format("~p: ~p ~p\n~p\n", + [M,Class,Error,erlang:get_stacktrace()]), + error + end. + +%% p_run(fun() -> ok|error, List) -> ok +%% Will fail the test case if there were any errors. + +p_run(Test, List) -> + N = erlang:system_info(schedulers) + 1, + p_run_loop(Test, List, N, [], 0, 0). + +p_run_loop(_, [], _, [], Errors, Ws) -> + case Errors of + 0 -> + case Ws of + 0 -> ok; + 1 -> {comment,"1 core_lint failure"}; + N -> {comment,integer_to_list(N)++" core_lint failures"} + end; + N -> ?t:fail({N,errors}) + end; +p_run_loop(Test, [H|T], N, Refs, Errors, Ws) when length(Refs) < N -> + {_,Ref} = erlang:spawn_monitor(fun() -> exit(Test(H)) end), + p_run_loop(Test, T, N, [Ref|Refs], Errors, Ws); +p_run_loop(Test, List, N, Refs0, Errors0, Ws0) -> + receive + {'DOWN',Ref,process,_,Res} -> + {Errors,Ws} = case Res of + ok -> {Errors0,Ws0}; + error -> {Errors0+1,Ws0}; + warning -> {Errors0,Ws0+1} + end, + Refs = Refs0 -- [Ref], + p_run_loop(Test, List, N, Refs, Errors, Ws) + end. |