1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1997-2020. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20-module(compile_SUITE). 21 22%% Tests compile:file/1 and compile:file/2 with various options. 23 24-include_lib("common_test/include/ct.hrl"). 25-include_lib("stdlib/include/erl_compile.hrl"). 26 27-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 28 init_per_group/2,end_per_group/2, 29 app_test/1,appup_test/1, 30 debug_info/4, custom_debug_info/1, custom_compile_info/1, 31 file_1/1, forms_2/1, module_mismatch/1, outdir/1, 32 binary/1, makedep/1, cond_and_ifdef/1, listings/1, listings_big/1, 33 other_output/1, kernel_listing/1, encrypted_abstr/1, 34 strict_record/1, utf8_atoms/1, utf8_functions/1, extra_chunks/1, 35 cover/1, env/1, core_pp/1, tuple_calls/1, 36 core_roundtrip/1, asm/1, 37 sys_pre_attributes/1, dialyzer/1, 38 warnings/1, pre_load_check/1, env_compiler_options/1, 39 bc_options/1, deterministic_include/1, deterministic_paths/1 40 ]). 41 42suite() -> [{ct_hooks,[ts_install_cth]}]. 43 44%% To cover the stripping of 'type' and 'spec' in beam_asm. 45-type all_return_type() :: [atom()]. 46-spec all() -> all_return_type(). 47 48all() -> 49 [app_test, appup_test, file_1, forms_2, module_mismatch, outdir, 50 binary, makedep, cond_and_ifdef, listings, listings_big, 51 other_output, kernel_listing, encrypted_abstr, tuple_calls, 52 strict_record, utf8_atoms, utf8_functions, extra_chunks, 53 cover, env, core_pp, core_roundtrip, asm, 54 sys_pre_attributes, dialyzer, warnings, pre_load_check, 55 env_compiler_options, custom_debug_info, bc_options, 56 custom_compile_info, deterministic_include, deterministic_paths]. 57 58groups() -> 59 []. 60 61init_per_suite(Config) -> 62 test_lib:recompile(?MODULE), 63 Config. 64 65end_per_suite(_Config) -> 66 ok. 67 68init_per_group(_GroupName, Config) -> 69 Config. 70 71end_per_group(_GroupName, Config) -> 72 Config. 73 74 75 76%% Test that the Application file has no `basic' errors."; 77app_test(Config) when is_list(Config) -> 78 test_server:app_test(compiler). 79 80%% Test that the Application upgrade file has no `basic' errors."; 81appup_test(Config) when is_list(Config) -> 82 ok = test_server:appup_test(compiler). 83 84%% Tests that we can compile and run a simple Erlang program, 85%% using compile:file/1. 86 87file_1(Config) when is_list(Config) -> 88 process_flag(trap_exit, true), 89 90 {Simple, Target} = get_files(Config, simple, "file_1"), 91 {ok, Cwd} = file:get_cwd(), 92 ok = file:set_cwd(filename:dirname(Target)), 93 94 %% Native from BEAM without compilation info. 95 {ok,simple} = compile:file(Simple, [slim]), %Smoke test only. 96 {ok,simple} = compile:file(Target, [native,from_beam]), %Smoke test. 97 98 %% Native from BEAM with compilation info. 99 {ok,simple} = compile:file(Simple), %Smoke test only. 100 {ok,simple} = compile:file(Target, [native,from_beam]), %Smoke test. 101 102 {ok,simple} = compile:file(Simple, [native,report]), %Smoke test. 103 104 compile_and_verify(Simple, Target, []), 105 compile_and_verify(Simple, Target, [native]), 106 compile_and_verify(Simple, Target, [debug_info]), 107 compile_and_verify(Simple, Target, [no_postopt]), 108 {ok,simple} = compile:file(Simple, [no_line_info]), %Coverage 109 110 {ok,simple} = compile:file(Simple, [{eprof,beam_z}]), %Coverage 111 112 113 %% Test option 'deterministic'. 114 {ok,simple} = compile:file(Simple, [deterministic]), 115 {module,simple} = c:l(simple), 116 [{version,_}] = simple:module_info(compile), 117 true = code:delete(simple), 118 false = code:purge(simple), 119 120 ok = file:set_cwd(Cwd), 121 true = exists(Target), 122 passed = run(Target, test, []), 123 124 %% Test option 'deterministic' as a compiler attribute. 125 Det = deterministic_module, 126 {DetPath, DetTarget} = get_files(Config, Det, "det_target"), 127 {ok,Det,DetCode} = compile:file(DetPath, [binary]), 128 {module,Det} = code:load_binary(Det, "", DetCode), 129 [{version,_}] = Det:module_info(compile), 130 true = code:delete(Det), 131 false = code:purge(Det), 132 133 %% Cleanup. 134 ok = file:delete(Target), 135 ok = file:del_dir(filename:dirname(Target)), 136 ok = file:del_dir(filename:dirname(DetTarget)), 137 138 %% There should not be any messages in the messages. 139 receive 140 Any -> 141 ct:fail({unexpected,Any}) 142 after 10 -> 143 ok 144 end, 145 146 ok. 147 148forms_2(Config) when is_list(Config) -> 149 Src = "/foo/bar", 150 AbsSrc = filename:absname(Src), 151 Anno = erl_anno:new(1), 152 SimpleCode = [{attribute,Anno,module,simple}], 153 {ok,simple,Bin1} = compile:forms(SimpleCode, [binary,{source,Src}]), 154 155 %% Load and test that the proper source is returned. 156 AbsSrc = forms_load_code(simple, Src, Bin1), 157 158 %% Work in a deleted directory. 159 PrivDir = proplists:get_value(priv_dir, Config), 160 WorkDir = filename:join(PrivDir, ?FUNCTION_NAME), 161 ok = file:make_dir(WorkDir), 162 ok = file:set_cwd(WorkDir), 163 case os:type() of 164 {unix,_} -> os:cmd("rm -rf " ++ WorkDir); 165 _ -> ok 166 end, 167 {ok,simple,Bin2} = compile:forms(SimpleCode), 168 undefined = forms_load_code(simple, "ignore", Bin2), 169 170 {ok,simple,Bin3} = compile:forms(SimpleCode, [{source,Src},report]), 171 case forms_load_code(simple, "ignore", Bin3) of 172 Src -> %Unix. 173 ok; 174 AbsSrc -> %Windows. 175 ok 176 end, 177 178 {ok,simple,Core} = compile:forms(SimpleCode, [to_core0,binary]), 179 forms_compile_and_load(Core, [from_core]), 180 forms_compile_and_load(Core, [from_core,native]), 181 182 {ok,simple,Asm} = compile:forms(SimpleCode, [to_asm,binary]), 183 forms_compile_and_load(Asm, [from_asm]), 184 forms_compile_and_load(Asm, [from_asm,native]), 185 186 {ok,simple,Beam} = compile:forms(SimpleCode, []), 187 forms_compile_and_load(Beam, [from_beam]), 188 forms_compile_and_load(Beam, [from_beam,native]), 189 190 %% Cover the error handling code. 191 error = compile:forms(bad_core, [from_core,report]), 192 error = compile:forms(bad_asm, [from_asm,report]), 193 error = compile:forms(<<"bad_beam">>, [from_beam,report]), 194 error = compile:forms(<<"bad_beam">>, [from_beam,native,report]), 195 196 ok. 197 198 199forms_load_code(Mod, Src, Bin) -> 200 {module,Mod} = code:load_binary(Mod, Src, Bin), 201 Info = Mod:module_info(compile), 202 SourceOption = proplists:get_value(source, Info), 203 204 %% Ensure that the options are not polluted with 'source'. 205 [] = proplists:get_value(options, Info), 206 207 %% Cleanup. 208 true = code:delete(simple), 209 false = code:purge(simple), 210 211 SourceOption. 212 213forms_compile_and_load(Code, Opts) -> 214 Mod = simple, 215 {ok,Mod,Bin} = compile:forms(Code, Opts), 216 {module,Mod} = code:load_binary(Mod, "ignore", Bin), 217 _ = Mod:module_info(), 218 true = code:delete(simple), 219 false = code:purge(simple), 220 ok. 221 222module_mismatch(Config) when is_list(Config) -> 223 DataDir = proplists:get_value(data_dir, Config), 224 File = filename:join(DataDir, "wrong_module_name.erl"), 225 {error,[{"wrong_module_name.beam", 226 [{none,compile,{module_name,arne,"wrong_module_name"}}]}], 227 []} = compile:file(File, [return]), 228 error = compile:file(File, [report]), 229 230 {ok,arne,[]} = compile:file(File, 231 [return,no_error_module_mismatch]), 232 233 ok. 234 235%% Tests that the {outdir, Dir} option works. 236 237outdir(Config) when is_list(Config) -> 238 {Simple, Target} = get_files(Config, simple, "outdir"), 239 {ok, simple} = compile:file(Simple, [{outdir, filename:dirname(Target)}]), 240 true = exists(Target), 241 passed = run(Target, test, []), 242 ok = file:delete(Target), 243 ok = file:del_dir(filename:dirname(Target)), 244 ok. 245 246%% Tests that the binary option works. 247 248binary(Config) when is_list(Config) -> 249 {Simple, Target} = get_files(Config, simple, "binary"), 250 {ok, simple, Binary} = compile:file(Simple, [binary]), 251 code:load_binary(simple, Target, Binary), 252 passed = simple:test(), 253 true = code:delete(simple), 254 false = code:purge(simple), 255 ok = file:del_dir(filename:dirname(Target)), 256 ok. 257 258%% Tests that the dependencies-Makefile-related options work. 259 260makedep(Config) when is_list(Config) -> 261 {Simple,Target} = get_files(Config, simple, "makedep"), 262 DataDir = proplists:get_value(data_dir, Config), 263 SimpleRootname = filename:rootname(Simple), 264 IncludeDir = filename:join(filename:dirname(Simple), "include"), 265 IncludeOptions = [ 266 {d,need_foo}, 267 {d,foo_value,42}, 268 {d,include_generated}, 269 {i,IncludeDir} 270 ], 271 %% Basic rule. 272 BasicMf1Name = SimpleRootname ++ "-basic1.mk", 273 {ok,BasicMf1} = file:read_file(BasicMf1Name), 274 {ok,_,Mf1} = compile:file(Simple, [binary,makedep]), 275 BasicMf1 = makedep_canonicalize_result(Mf1, DataDir), 276 %% Basic rule with one existing header. 277 BasicMf2Name = SimpleRootname ++ "-basic2.mk", 278 {ok,BasicMf2} = file:read_file(BasicMf2Name), 279 {ok,_,Mf2} = compile:file(Simple, [binary,makedep|IncludeOptions]), 280 BasicMf2 = makedep_canonicalize_result(Mf2, DataDir), 281 %% Rule with one existing header and one missing header. 282 MissingMfName = SimpleRootname ++ "-missing.mk", 283 {ok,MissingMf} = file:read_file(MissingMfName), 284 {ok,_,Mf3} = compile:file(Simple, 285 [binary,makedep,makedep_add_missing|IncludeOptions]), 286 MissingMf = makedep_canonicalize_result(Mf3, DataDir), 287 %% Rule with modified target. 288 TargetMf1Name = SimpleRootname ++ "-target1.mk", 289 {ok,TargetMf1} = file:read_file(TargetMf1Name), 290 {ok,_,Mf4} = compile:file(Simple, 291 [binary,makedep,{makedep_target,"$target"}|IncludeOptions]), 292 TargetMf1 = makedep_modify_target( 293 makedep_canonicalize_result(Mf4, DataDir), "$$target"), 294 %% Rule with quoted modified target. 295 TargetMf2Name = SimpleRootname ++ "-target2.mk", 296 {ok,TargetMf2} = file:read_file(TargetMf2Name), 297 {ok,_,Mf5} = compile:file(Simple, 298 [binary,makedep,{makedep_target,"$target"},makedep_quote_target| 299 IncludeOptions]), 300 TargetMf2 = makedep_modify_target( 301 makedep_canonicalize_result(Mf5, DataDir), "$$target"), 302 %% Basic rule written to some file. 303 {ok,_} = compile:file(Simple, 304 [makedep,{makedep_output,Target}|IncludeOptions]), 305 {ok,Mf6} = file:read_file(Target), 306 BasicMf2 = makedep_canonicalize_result(Mf6, DataDir), 307 %% Rule with creating phony target. 308 PhonyMfName = SimpleRootname ++ "-phony.mk", 309 {ok,PhonyMf} = file:read_file(PhonyMfName), 310 {ok,_,Mf7} = compile:file(Simple, 311 [binary,makedep,makedep_phony|IncludeOptions]), 312 PhonyMf = makedep_canonicalize_result(Mf7, DataDir), 313 314 ok = file:delete(Target), 315 ok = file:del_dir(filename:dirname(Target)), 316 ok. 317 318makedep_canonicalize_result(Mf, DataDir) -> 319 Mf0 = binary_to_list(Mf), 320 %% Replace the Datadir by "$(srcdir)". 321 Mf1 = re:replace(Mf0, DataDir, "$(srcdir)/", 322 [global,multiline,{return,list}]), 323 %% Long lines are splitted, put back everything on one line. 324 Mf2 = re:replace(Mf1, "\\\\\n ", "", [global,multiline,{return,list}]), 325 list_to_binary(Mf2). 326 327makedep_modify_target(Mf, Target) -> 328 Mf0 = binary_to_list(Mf), 329 Mf1 = re:replace(Mf0, Target, "$target", [{return,list}]), 330 list_to_binary(Mf1). 331 332%% Tests that conditional compilation, defining values, including files work. 333 334cond_and_ifdef(Config) when is_list(Config) -> 335 {Simple, Target} = get_files(Config, simple, "cond_and_ifdef"), 336 IncludeDir = filename:join(filename:dirname(Simple), "include"), 337 Options = [{outdir, filename:dirname(Target)}, 338 {d, need_foo}, {d, foo_value, 42}, 339 {i, IncludeDir}, report], 340 {ok, simple} = compile:file(Simple, Options), 341 true = exists(Target), 342 {hiker, 42} = run(Target, foo, []), 343 ok = file:delete(Target), 344 ok = file:del_dir(filename:dirname(Target)), 345 ok. 346 347listings(Config) when is_list(Config) -> 348 DataDir = proplists:get_value(data_dir, Config), 349 PrivDir = proplists:get_value(priv_dir, Config), 350 ok = do_file_listings(DataDir, PrivDir, [ 351 "simple", 352 "small", 353 "small_maps" 354 ]), 355 ok. 356 357do_file_listings(_, _, []) -> ok; 358do_file_listings(DataDir, PrivDir, [File|Files]) -> 359 Simple = filename:join(DataDir, File), 360 TargetDir = filename:join(PrivDir, listings), 361 ok = file:make_dir(TargetDir), 362 363 List = [{'S',".S"}, 364 {'E',".E"}, 365 {'P',".P"}, 366 {dpp, ".pp"}, 367 {dabstr, ".abstr"}, 368 {dexp, ".expand"}, 369 {dcore, ".core"}, 370 {doldinline, ".oldinline"}, 371 {dinline, ".inline"}, 372 {dcore, ".core"}, 373 {dcopt, ".copt"}, 374 {dcbsm, ".core_bsm"}, 375 {dkern, ".kernel"}, 376 {dssa, ".ssa"}, 377 {dssaopt, ".ssaopt"}, 378 {dprecg, ".precodegen"}, 379 {dcg, ".codegen"}, 380 {dblk, ".block"}, 381 {dexcept, ".except"}, 382 {djmp, ".jump"}, 383 {dclean, ".clean"}, 384 {dpeep, ".peep"}, 385 {dopt, ".optimize"}, 386 {diffable, ".S"}], 387 p_listings(List, Simple, TargetDir), 388 389 %% Test options that produce a listing file if 'binary' is not given. 390 do_listing(Simple, TargetDir, to_pp, ".P"), 391 do_listing(Simple, TargetDir, to_exp, ".E"), 392 do_listing(Simple, TargetDir, to_core0, ".core"), 393 Listings = filename:join(PrivDir, listings), 394 ok = file:delete(filename:join(Listings, File ++ ".core")), 395 do_listing(Simple, TargetDir, to_core, ".core"), 396 do_listing(Simple, TargetDir, to_kernel, ".kernel"), 397 do_listing(Simple, TargetDir, to_dis, ".dis"), 398 399 %% Final clean up. 400 lists:foreach(fun(F) -> ok = file:delete(F) end, 401 filelib:wildcard(filename:join(Listings, "*"))), 402 ok = file:del_dir(Listings), 403 404 do_file_listings(DataDir,PrivDir,Files). 405 406listings_big(Config) when is_list(Config) -> 407 {Big,Target} = get_files(Config, big, listings_big), 408 TargetDir = filename:dirname(Target), 409 List = [{'S',".S"}, 410 {'E',".E"}, 411 {'P',".P"}, 412 {dkern, ".kernel"}, 413 {dssa, ".ssa"}, 414 {dssaopt, ".ssaopt"}, 415 {dprecg, ".precodegen"}, 416 {to_dis, ".dis"}], 417 p_listings(List, Big, TargetDir). 418 419p_listings(List, File, BaseDir) -> 420 Run = fun({Option,Extension}) -> 421 Uniq = erlang:unique_integer([positive]), 422 Dir = filename:join(BaseDir, integer_to_list(Uniq)), 423 ok = file:make_dir(Dir), 424 try 425 do_listing(File, Dir, Option, Extension), 426 ok 427 catch 428 Class:Error:Stk -> 429 io:format("~p:~p\n~p\n", [Class,Error,Stk]), 430 error 431 after 432 _ = [ok = file:delete(F) || 433 F <- filelib:wildcard(filename:join(Dir, "*"))], 434 ok = file:del_dir(Dir) 435 end 436 end, 437 test_lib:p_run(Run, List). 438 439other_output(Config) when is_list(Config) -> 440 {Simple,_Target} = get_files(Config, simple, "other_output"), 441 442 io:put_chars("to_pp"), 443 {ok,[],PP} = compile:file(Simple, [to_pp,binary,time]), 444 [] = [E || E <- PP, 445 begin 446 case element(1, E) of 447 attribute -> false; 448 function -> false; 449 eof -> false 450 end 451 end], 452 453 io:put_chars("to_exp (file)"), 454 {ok,[],Expand} = compile:file(Simple, [to_exp,binary,time]), 455 true = is_list(Expand), 456 {attribute,_,module,simple} = lists:keyfind(module, 3, Expand), 457 io:put_chars("to_exp (forms)"), 458 {ok,[],Expand} = compile:forms(PP, [to_exp,binary,time]), 459 460 io:put_chars("to_core (file)"), 461 {ok,simple,Core} = compile:file(Simple, [to_core,binary,time]), 462 c_module = element(1, Core), 463 {ok,_} = core_lint:module(Core), 464 io:put_chars("to_core (forms)"), 465 {ok,simple,Core} = compile:forms(PP, [to_core,binary,time]), 466 467 io:put_chars("to_kernel (file)"), 468 {ok,simple,Kernel} = compile:file(Simple, [to_kernel,binary,time]), 469 k_mdef = element(1, Kernel), 470 io:put_chars("to_kernel (forms)"), 471 {ok,simple,Kernel} = compile:forms(PP, [to_kernel,binary,time]), 472 473 io:put_chars("to_asm (file)"), 474 {ok,simple,Asm} = compile:file(Simple, [to_asm,binary,time]), 475 {simple,_,_,_,_} = Asm, 476 io:put_chars("to_asm (forms)"), 477 {ok,simple,Asm} = compile:forms(PP, [to_asm,binary,time]), 478 479 ok. 480 481%% Smoke test and cover of pretty-printing of Kernel code. 482kernel_listing(_Config) -> 483 TestBeams = get_unique_beam_files(), 484 Abstr = [begin {ok,{Mod,[{abstract_code, 485 {raw_abstract_v1,Abstr}}]}} = 486 beam_lib:chunks(Beam, [abstract_code]), 487 {Mod,Abstr} end || Beam <- TestBeams], 488 test_lib:p_run(fun(F) -> do_kernel_listing(F) end, Abstr). 489 490do_kernel_listing({M,A}) -> 491 try 492 {ok,M,Kern} = compile:forms(A, [to_kernel]), 493 IoList = v3_kernel_pp:format(Kern), 494 case unicode:characters_to_binary(IoList) of 495 Bin when is_binary(Bin) -> 496 ok 497 end 498 catch 499 throw:{error,Error} -> 500 io:format("*** compilation failure '~p' for module ~s\n", 501 [Error,M]), 502 error; 503 Class:Error:Stk -> 504 io:format("~p: ~p ~p\n~p\n", [M,Class,Error,Stk]), 505 error 506 end. 507 508encrypted_abstr(Config) when is_list(Config) -> 509 {Simple,Target} = get_files(Config, simple, "encrypted_abstr"), 510 511 Res = case has_crypto() of 512 false -> 513 %% No crypto. 514 encrypted_abstr_no_crypto(Simple, Target), 515 {comment,"The crypto application is missing or broken"}; 516 true -> 517 %% Simulate not having crypto by removing 518 %% the crypto application from the path. 519 OldPath = code:get_path(), 520 try 521 NewPath = OldPath -- [filename:dirname(code:which(crypto))], 522 (catch crypto:stop()), 523 code:delete(crypto), 524 code:purge(crypto), 525 code:set_path(NewPath), 526 encrypted_abstr_no_crypto(Simple, Target) 527 after 528 code:set_path(OldPath) 529 end, 530 531 %% Now run the tests that require crypto. 532 encrypted_abstr_1(Simple, Target), 533 ok = file:delete(Target), 534 ok = file:del_dir(filename:dirname(Target)) 535 end, 536 537 %% Cleanup. 538 Res. 539 540encrypted_abstr_1(Simple, Target) -> 541 TargetDir = filename:dirname(Target), 542 Key = "ablurf123BX#$;3", 543 install_crypto_key(Key), 544 {ok,simple} = compile:file(Simple, 545 [debug_info,{debug_info_key,Key}, 546 {outdir,TargetDir}]), 547 verify_abstract(Target, erl_abstract_code), 548 549 {ok,simple} = compile:file(Simple, 550 [{debug_info_key,Key}, 551 {outdir,TargetDir}]), 552 verify_abstract(Target, erl_abstract_code), 553 554 {ok,simple} = compile:file(Simple, 555 [debug_info,{debug_info_key,{des3_cbc,Key}}, 556 {outdir,TargetDir}]), 557 verify_abstract(Target, erl_abstract_code), 558 559 {ok,simple} = compile:file(Simple, 560 [{debug_info,{?MODULE,ok}}, 561 {debug_info_key,Key}, 562 {outdir,TargetDir}]), 563 verify_abstract(Target, ?MODULE), 564 565 {ok,{simple,[{compile_info,CInfo}]}} = 566 beam_lib:chunks(Target, [compile_info]), 567 {_,Opts} = lists:keyfind(options, 1, CInfo), 568 {_,'********'} = lists:keyfind(debug_info_key, 1, Opts), 569 570 %% Try some illegal forms of crypto keys. 571 error = compile:file(Simple, 572 [debug_info,{debug_info_key,{blurf,"ss"}},report]), 573 error = compile:file(Simple, 574 [debug_info,{debug_info_key,{blurf,1,"ss"}},report]), 575 error = compile:file(Simple, 576 [debug_info,{debug_info_key,42},report]), 577 578 %% Place the crypto key in .erlang.crypt. 579 beam_lib:clear_crypto_key_fun(), 580 {ok,OldCwd} = file:get_cwd(), 581 ok = file:set_cwd(TargetDir), 582 583 error = compile:file(Simple, [encrypt_debug_info,report]), 584 585 NewKey = "better use another key here", 586 write_crypt_file(["[{debug_info,des3_cbc,simple,\"",NewKey,"\"}].\n"]), 587 {ok,simple} = compile:file(Simple, [encrypt_debug_info,report]), 588 verify_abstract("simple.beam", erl_abstract_code), 589 ok = file:delete(".erlang.crypt"), 590 beam_lib:clear_crypto_key_fun(), 591 {error,beam_lib,{key_missing_or_invalid,"simple.beam",abstract_code}} = 592 beam_lib:chunks("simple.beam", [abstract_code]), 593 ok = file:set_cwd(OldCwd), 594 595 %% Test key compatibility by reading a BEAM file produced before 596 %% the update to the new crypto functions. 597 install_crypto_key("an old key"), 598 KeyCompat = filename:join(filename:dirname(Simple), 599 "key_compatibility"), 600 {ok,{key_compatibility,[Chunk]}} = beam_lib:chunks(KeyCompat, 601 [abstract_code]), 602 {abstract_code,{raw_abstract_v1,_}} = Chunk, 603 604 ok. 605 606 607write_crypt_file(Contents0) -> 608 Contents = list_to_binary([Contents0]), 609 io:format("~s\n", [binary_to_list(Contents)]), 610 ok = file:write_file(".erlang.crypt", Contents). 611 612encrypted_abstr_no_crypto(Simple, Target) -> 613 io:format("simpe: ~p~n", [Simple]), 614 TargetDir = filename:dirname(Target), 615 Key = "ablurf123BX#$;3", 616 error = compile:file(Simple, 617 [debug_info,{debug_info_key,Key}, 618 {outdir,TargetDir},report]), 619 ok. 620 621verify_abstract(Beam, Backend) -> 622 {ok,{simple,[Abst, Dbgi]}} = beam_lib:chunks(Beam, [abstract_code, debug_info]), 623 {abstract_code,{raw_abstract_v1,_}} = Abst, 624 {debug_info,{debug_info_v1,Backend,_}} = Dbgi. 625 626has_crypto() -> 627 try 628 crypto:start(), 629 crypto:stop(), 630 true 631 catch 632 error:_ -> false 633 end. 634 635install_crypto_key(Key) -> 636 F = fun (init) -> ok; 637 ({debug_info,des3_cbc,_,_}) -> Key; 638 (clear) -> ok 639 end, 640 ok = beam_lib:crypto_key_fun(F). 641 642%% Miscellanous tests, mainly to get better coverage. 643debug_info(erlang_v1, Module, ok, _Opts) -> 644 {ok, [Module]}; 645debug_info(erlang_v1, _Module, error, _Opts) -> 646 {error, unknown_format}. 647 648custom_debug_info(Config) when is_list(Config) -> 649 {Simple,_} = get_files(Config, simple, "file_1"), 650 651 {ok,simple,OkBin} = compile:file(Simple, [binary, {debug_info,{?MODULE,ok}}]), %Coverage 652 {ok,{simple,[{abstract_code,{raw_abstract_v1,[simple]}}]}} = 653 beam_lib:chunks(OkBin, [abstract_code]), 654 {ok,{simple,[{debug_info,{debug_info_v1,?MODULE,ok}}]}} = 655 beam_lib:chunks(OkBin, [debug_info]), 656 657 {ok,simple,ErrorBin} = compile:file(Simple, [binary, {debug_info,{?MODULE,error}}]), %Coverage 658 {ok,{simple,[{abstract_code,no_abstract_code}]}} = 659 beam_lib:chunks(ErrorBin, [abstract_code]), 660 {ok,{simple,[{debug_info,{debug_info_v1,?MODULE,error}}]}} = 661 beam_lib:chunks(ErrorBin, [debug_info]). 662 663custom_compile_info(Config) when is_list(Config) -> 664 Anno = erl_anno:new(1), 665 Forms = [{attribute,Anno,module,custom_compile_info}], 666 Opts = [binary,{compile_info,[{another,version}]}], 667 668 {ok,custom_compile_info,Bin} = compile:forms(Forms, Opts), 669 {ok,{custom_compile_info,[{compile_info,CompileInfo}]}} = 670 beam_lib:chunks(Bin, [compile_info]), 671 version = proplists:get_value(another, CompileInfo), 672 CompileOpts = proplists:get_value(options, CompileInfo), 673 undefined = proplists:get_value(compile_info, CompileOpts), 674 675 {ok,custom_compile_info,DetBin} = compile:forms(Forms, [deterministic|Opts]), 676 {ok,{custom_compile_info,[{compile_info,DetInfo}]}} = 677 beam_lib:chunks(DetBin, [compile_info]), 678 version = proplists:get_value(another, DetInfo). 679 680cover(Config) when is_list(Config) -> 681 io:format("~p\n", [compile:options()]), 682 ok. 683 684do_listing(Source, TargetDir, Type, Ext) -> 685 io:format("Source: ~p TargetDir: ~p\n Type: ~p Ext: ~p\n", 686 [Source, TargetDir, Type, Ext]), 687 case compile:file(Source, [Type, time, {outdir, TargetDir}]) of 688 {ok, _} -> ok; 689 Other -> ct:fail({unexpected_result, Other}) 690 end, 691 SourceBase = filename:rootname(filename:basename(Source)), 692 693 Target = filename:join(TargetDir, SourceBase ++ Ext), 694 true = exists(Target). 695 696get_files(Config, Module, OutputName) -> 697 code:delete(Module), 698 code:purge(Module), 699 DataDir = proplists:get_value(data_dir, Config), 700 PrivDir = proplists:get_value(priv_dir, Config), 701 Src = filename:join(DataDir, atom_to_list(Module)), 702 TargetDir = filename:join(PrivDir, OutputName), 703 ok = file:make_dir(TargetDir), 704 File = atom_to_list(Module) ++ code:objfile_extension(), 705 Target = filename:join(TargetDir, File), 706 {Src, Target}. 707 708run(Target, Func, Args) -> 709 Module = list_to_atom(filename:rootname(filename:basename(Target))), 710 {module, Module} = code:load_abs(filename:rootname(Target)), 711 Result = (catch apply(Module, Func, Args)), 712 true = code:delete(Module), 713 false = code:purge(Module), 714 Result. 715 716exists(Name) -> 717 case file:read_file_info(Name) of 718 {ok, _} -> true; 719 {error, _} -> false 720 end. 721 722 723strict_record(Config) when is_list(Config) -> 724 Priv = proplists:get_value(priv_dir, Config), 725 ok = file:set_cwd(proplists:get_value(data_dir, Config)), 726 Opts = [{outdir,Priv},report_errors], 727 M = record_access, 728 729 {ok,M} = c:c(M, [strict_record_tests|Opts]), 730 Turtle = test_strict(), 731 732 {ok,M} = c:c(M, [no_strict_record_tests|Opts]), 733 Turtle = test_sloppy(), 734 735 %% The option first given wins. 736 {ok,M} = c:c(M, [no_strict_record_tests,strict_record_tests|Opts]), 737 Turtle = test_sloppy(), 738 {ok,M} = c:c(M, [strict_record_tests,no_strict_record_tests|Opts]), 739 Turtle = test_strict(), 740 741 %% Default (possibly influenced by ERL_COMPILER_OPTIONS). 742 {ok,M} = c:c(M, [{outdir,Priv},report_errors]), 743 try 744 {1,2} = record_access:test(Turtle), 745 {comment,"Default: no_strict_record_tests"} 746 catch 747 error:{badrecord,tortoise} -> 748 {comment,"Default: strict_record_tests"} 749 end. 750 751test_strict() -> 752 Turtle = record_access:turtle(), 753 try 754 record_access:test(Turtle) 755 catch 756 error:{badrecord,tortoise} -> 757 ok 758 end, 759 Turtle. 760 761test_sloppy() -> 762 Turtle = record_access:turtle(), 763 {1,2} = record_access:test(Turtle), 764 Turtle. 765 766utf8_atoms(Config) when is_list(Config) -> 767 Anno = erl_anno:new(1), 768 Atom = binary_to_atom(<<"こんにちは"/utf8>>, utf8), 769 Forms = [{attribute,Anno,compile,[export_all]}, 770 {function,Anno,atom,0,[{clause,Anno,[],[],[{atom,Anno,Atom}]}]}], 771 772 Utf8AtomForms = [{attribute,Anno,module,utf8_atom}|Forms], 773 {ok,utf8_atom,Utf8AtomBin} = 774 compile:forms(Utf8AtomForms, [binary]), 775 {ok,{utf8_atom,[{atoms,_}]}} = 776 beam_lib:chunks(Utf8AtomBin, [atoms]), 777 code:load_binary(utf8_atom, "compile_SUITE", Utf8AtomBin), 778 Atom = utf8_atom:atom(), 779 780 NoUtf8AtomForms = [{attribute,Anno,module,no_utf8_atom}|Forms], 781 error = compile:forms(NoUtf8AtomForms, [binary, r19]). 782 783utf8_functions(Config) when is_list(Config) -> 784 Anno = erl_anno:new(1), 785 Atom = binary_to_atom(<<"こんにちは"/utf8>>, utf8), 786 Forms = [{attribute,Anno,compile,[export_all]}, 787 {function,Anno,Atom,0,[{clause,Anno,[],[],[{atom,Anno,world}]}]}], 788 789 Utf8FunctionForms = [{attribute,Anno,module,utf8_function}|Forms], 790 {ok,utf8_function,Utf8FunctionBin} = 791 compile:forms(Utf8FunctionForms, [binary]), 792 {ok,{utf8_function,[{atoms,_}]}} = 793 beam_lib:chunks(Utf8FunctionBin, [atoms]), 794 code:load_binary(utf8_function, "compile_SUITE", Utf8FunctionBin), 795 world = utf8_function:Atom(), 796 797 NoUtf8FunctionForms = [{attribute,Anno,module,no_utf8_function}|Forms], 798 error = compile:forms(NoUtf8FunctionForms, [binary, r19]). 799 800extra_chunks(Config) when is_list(Config) -> 801 Anno = erl_anno:new(1), 802 Forms = [{attribute,Anno,module,extra_chunks}], 803 804 {ok,extra_chunks,ExtraChunksBinary} = 805 compile:forms(Forms, [binary, {extra_chunks, [{<<"ExCh">>, <<"Contents">>}]}]), 806 {ok,{extra_chunks,[{"ExCh",<<"Contents">>}]}} = 807 beam_lib:chunks(ExtraChunksBinary, ["ExCh"]). 808 809tuple_calls(Config) when is_list(Config) -> 810 Anno = erl_anno:new(1), 811 Forms = [{attribute,Anno,export,[{size,1},{store,1}]}, 812 {function,Anno,size,1, 813 [{clause,Anno,[{var,[],mod}],[], 814 [{call,[],{remote,[],{var,[],mod},{atom,[],size}},[]}]}]}, 815 {function,Anno,store,1, 816 [{clause,Anno,[{var,[],mod}],[], 817 [{call,[],{remote,[],{var,[],mod},{atom,[],store}},[{atom,[],key},{atom,[],value}]}]}]}], 818 819 TupleCallsFalse = [{attribute,Anno,module,tuple_calls_false}|Forms], 820 {ok,_,TupleCallsFalseBinary} = compile:forms(TupleCallsFalse, [binary]), 821 code:load_binary(tuple_calls_false, "compile_SUITE.erl", TupleCallsFalseBinary), 822 {'EXIT',{badarg,_}} = (catch tuple_calls_false:store(dict())), 823 {'EXIT',{badarg,_}} = (catch tuple_calls_false:size(dict())), 824 {'EXIT',{badarg,_}} = (catch tuple_calls_false:size(empty_tuple())), 825 826 TupleCallsTrue = [{attribute,Anno,module,tuple_calls_true}|Forms], 827 {ok,_,TupleCallsTrueBinary} = compile:forms(TupleCallsTrue, [binary,tuple_calls]), 828 code:load_binary(tuple_calls_true, "compile_SUITE.erl", TupleCallsTrueBinary), 829 Dict = tuple_calls_true:store(dict()), 830 1 = tuple_calls_true:size(Dict), 831 {'EXIT',{badarg,_}} = (catch tuple_calls_true:size(empty_tuple())), 832 833 ok. 834 835dict() -> 836 dict:new(). 837empty_tuple() -> 838 {}. 839 840env(Config) when is_list(Config) -> 841 {Simple,Target} = get_files(Config, simple, env), 842 {ok,Cwd} = file:get_cwd(), 843 ok = file:set_cwd(filename:dirname(Target)), 844 845 true = os:putenv("ERL_COMPILER_OPTIONS", "binary"), 846 try 847 env_1(Simple, Target) 848 after 849 true = os:putenv("ERL_COMPILER_OPTIONS", "ignore_me"), 850 file:set_cwd(Cwd), 851 file:delete(Target), 852 file:del_dir(filename:dirname(Target)) 853 end, 854 ok. 855 856env_1(Simple, Target) -> 857 %% file 858 {ok,simple,<<_/binary>>} = compile:file(Simple), 859 {ok,simple} = compile:noenv_file(Simple, [debug_info]), 860 true = exists(Target), 861 {ok,{simple,[{abstract_code,Abstr0}]}} = 862 beam_lib:chunks(Target, [abstract_code]), 863 {raw_abstract_v1,Forms} = Abstr0, 864 865 %% forms 866 true = os:putenv("ERL_COMPILER_OPTIONS", "strong_validation"), 867 {ok,simple} = compile:forms(Forms), 868 {ok,simple,<<"FOR1",_/binary>>} = compile:noenv_forms(Forms, []), 869 870 %% output_generated 871 false = compile:output_generated([]), 872 true = compile:noenv_output_generated([]), 873 874 ok = file:delete(Target), 875 876 ok. 877 878%% Test pretty-printing in Core Erlang format and then try to 879%% compile the generated Core Erlang files. 880 881core_pp(Config) when is_list(Config) -> 882 PrivDir = proplists:get_value(priv_dir, Config), 883 Outdir = filename:join(PrivDir, atom_to_list(?FUNCTION_NAME)), 884 ok = file:make_dir(Outdir), 885 886 TestBeams = get_unique_beam_files(), 887 Abstr = [begin {ok,{Mod,[{abstract_code, 888 {raw_abstract_v1,Abstr}}]}} = 889 beam_lib:chunks(Beam, [abstract_code]), 890 {Mod,Abstr} end || Beam <- TestBeams], 891 test_lib:p_run(fun(F) -> do_core_pp(F, Outdir) end, Abstr). 892 893do_core_pp({M,A}, Outdir) -> 894 try 895 do_core_pp_1(M, A, Outdir) 896 catch 897 throw:{error,Error} -> 898 io:format("*** compilation failure '~p' for module ~s\n", 899 [Error,M]), 900 error; 901 Class:Error:Stk -> 902 io:format("~p: ~p ~p\n~p\n", [M,Class,Error,Stk]), 903 error 904 end. 905 906do_core_pp_1(M, A, Outdir) -> 907 {ok,M,Core0} = compile:forms(A, [to_core]), 908 CoreFile = filename:join(Outdir, atom_to_list(M)++".core"), 909 CorePP = core_pp:format(Core0), 910 ok = file:write_file(CoreFile, unicode:characters_to_binary(CorePP)), 911 912 %% Parse the .core file and return the result as Core Erlang Terms. 913 Core = case compile:file(CoreFile, [report_errors,from_core,no_copt,to_core,binary]) of 914 {ok,M,Core1} -> Core1; 915 Other -> throw({error,Other}) 916 end, 917 ok = file:delete(CoreFile), 918 919 %% Compile as usual (including optimizations). 920 compile_forms(M, Core, [clint,ssalint,from_core,binary]), 921 922 %% Don't optimize to test that we are not dependent 923 %% on the Core Erlang optmimization passes. 924 %% (Example of a previous bug: The core_parse pass 925 %% would not turn map literals into #c_literal{} 926 %% records; if sys_core_fold was run it would fix 927 %% that; if sys_core_fold was not run v3_kernel would 928 %% crash.) 929 compile_forms(M, Core, [clint,ssalint,from_core,no_copt,binary]), 930 931 ok. 932 933compile_forms(Mod, Forms, Opts) -> 934 case compile:forms(Forms, [report_errors|Opts]) of 935 {ok,Mod,_} -> ok; 936 Other -> throw({error,Other}) 937 end. 938 939%% Pretty-print core and read it back. Should be identical. 940 941core_roundtrip(Config) -> 942 PrivDir = proplists:get_value(priv_dir, Config), 943 Outdir = filename:join(PrivDir, atom_to_list(?FUNCTION_NAME)), 944 ok = file:make_dir(Outdir), 945 946 TestBeams = get_unique_beam_files(), 947 test_lib:p_run(fun(F) -> do_core_roundtrip(F, Outdir) end, TestBeams). 948 949do_core_roundtrip(Beam, Outdir) -> 950 try 951 {ok,{Mod,[{abstract_code,{raw_abstract_v1,Abstr}}]}} = 952 beam_lib:chunks(Beam, [abstract_code]), 953 do_core_roundtrip_1(Mod, Abstr, Outdir) 954 catch 955 throw:{error,Error} -> 956 io:format("*** compilation failure '~p' for file ~s\n", 957 [Error,Beam]), 958 error; 959 Class:Error:Stk -> 960 io:format("~p: ~p ~p\n~p\n", [Beam,Class,Error,Stk]), 961 error 962 end. 963 964do_core_roundtrip_1(Mod, Abstr, Outdir) -> 965 {ok,Mod,Core0} = compile:forms(Abstr, [to_core0]), 966 do_core_roundtrip_2(Mod, Core0, Outdir), 967 968 %% Primarily, test that annotations are accepted for all 969 %% constructs. Secondarily, smoke test cerl_trees:label/1. 970 {Core1,_} = cerl_trees:label(Core0), 971 do_core_roundtrip_2(Mod, Core1, Outdir), 972 973 %% Run the inliner to force generation of variables 974 %% with numeric names. 975 {ok,Mod,Core2} = compile:forms(Abstr, [inline,to_core]), 976 do_core_roundtrip_2(Mod, Core2, Outdir). 977 978do_core_roundtrip_2(M, Core0, Outdir) -> 979 CoreFile = filename:join(Outdir, atom_to_list(M)++".core"), 980 CorePP = core_pp:format_all(Core0), 981 ok = file:write_file(CoreFile, unicode:characters_to_binary(CorePP)), 982 983 %% Parse the .core file and return the result as Core Erlang Terms. 984 Core2 = case compile:file(CoreFile, [report_errors,from_core, 985 no_copt,to_core,binary]) of 986 {ok,M,Core1} -> Core1; 987 Other -> throw({error,Other}) 988 end, 989 Core = undo_var_translation(Core2), 990 ok = file:delete(CoreFile), 991 992 case cmp_core(Core0, Core, M) of 993 true -> ok; 994 false -> error 995 end. 996 997undo_var_translation(Tree) -> 998 F = fun(Node) -> 999 case cerl:is_c_var(Node) of 1000 true -> 1001 Name0 = cerl:var_name(Node), 1002 try atom_to_list(Name0) of 1003 "_X"++Name -> 1004 cerl:update_c_var(Node, list_to_atom(Name)); 1005 "_"++Name -> 1006 cerl:update_c_var(Node, list_to_atom(Name)); 1007 _ -> 1008 Node 1009 catch 1010 error:badarg -> 1011 Node 1012 1013 end; 1014 false -> 1015 Node 1016 end 1017 end, 1018 cerl_trees:map(F, Tree). 1019 1020cmp_core(E, E, _Mod) -> 1021 true; 1022cmp_core(M1, M2, Mod) -> 1023 cmp_core_fs(cerl:module_defs(M1), cerl:module_defs(M2), Mod). 1024 1025cmp_core_fs([F1|T1], [F2|T2], Mod) -> 1026 cmp_core_f(F1, F2, Mod) andalso cmp_core_fs(T1, T2, Mod); 1027cmp_core_fs([], [], _Mod) -> 1028 true; 1029cmp_core_fs(_, _, _Mod) -> 1030 false. 1031 1032cmp_core_f(E, E, _Mod) -> 1033 true; 1034cmp_core_f({Name,F1}, {Name,F2}, Mod) -> 1035 case diff(F1, F2) of 1036 F1 -> 1037 true; 1038 Diff -> 1039 io:format("~p ~p:\n~p\n", [Mod,Name,Diff]), 1040 false 1041 end. 1042 1043diff(E, E) -> 1044 E; 1045diff([H1|T1], [H2|T2]) -> 1046 [diff(H1, H2)|diff(T1, T2)]; 1047diff(T1, T2) when tuple_size(T1) =:= tuple_size(T2) -> 1048 case cerl:is_c_var(T1) andalso cerl:is_c_var(T2) of 1049 true -> 1050 diff_var(T1, T2); 1051 false -> 1052 case cerl:is_c_map(T1) andalso cerl:is_c_map(T2) of 1053 true -> 1054 diff_map(T1, T2); 1055 false -> 1056 diff_tuple(T1, T2) 1057 end 1058 end; 1059diff(E1, E2) -> 1060 {'DIFF',E1,E2}. 1061 1062diff_var(V1, V2) -> 1063 case {cerl:var_name(V1),cerl:var_name(V2)} of 1064 {Same,Same} -> 1065 V1; 1066 {Name1,Name2} -> 1067 %% The inliner uses integers as variable names. Such integers 1068 %% are read back as atoms. 1069 case is_integer(Name1) andalso 1070 list_to_atom(integer_to_list(Name1)) =:= Name2 of 1071 true -> 1072 V1; 1073 _ -> 1074 cerl:update_c_var(V1, {'DIFF',Name1,Name2}) 1075 end 1076 end. 1077 1078%% Annotations for maps are not preserved exactly, but that is not 1079%% a real problem. Workaround by not comparing all annotations when 1080%% comparing maps. 1081 1082diff_map(M, M) -> 1083 M; 1084diff_map(M1, M2) -> 1085 case cerl:get_ann(M1) =:= cerl:get_ann(M2) of 1086 false -> 1087 diff_tuple(M1, M2); 1088 true -> 1089 case remove_compiler_gen(M1) =:= remove_compiler_gen(M2) of 1090 true -> 1091 M1; 1092 false -> 1093 diff_tuple(M1, M2) 1094 end 1095 end. 1096 1097diff_tuple(T1, T2) -> 1098 L = diff(tuple_to_list(T1), tuple_to_list(T2)), 1099 list_to_tuple(L). 1100 1101remove_compiler_gen(M) -> 1102 Arg0 = cerl:map_arg(M), 1103 Arg = cerl:set_ann(Arg0, []), 1104 Es0 = cerl:map_es(M), 1105 Es = [remove_compiler_gen_1(Pair) || Pair <- Es0], 1106 cerl:update_c_map(M, Arg, Es). 1107 1108remove_compiler_gen_1(Pair) -> 1109 Op0 = cerl:map_pair_op(Pair), 1110 Op = cerl:set_ann(Op0, []), 1111 K = map_var(cerl:map_pair_key(Pair)), 1112 V = map_var(cerl:map_pair_val(Pair)), 1113 cerl:update_c_map_pair(Pair, Op, K, V). 1114 1115map_var(Var) -> 1116 case cerl:is_c_var(Var) of 1117 true -> 1118 case cerl:var_name(Var) of 1119 Name when is_atom(Name) -> 1120 L = atom_to_list(Name), 1121 try list_to_integer(L) of 1122 Int -> 1123 cerl:update_c_var(Var, Int) 1124 catch 1125 error:_ -> 1126 Var 1127 end; 1128 _ -> 1129 Var 1130 end; 1131 false -> 1132 Var 1133 end. 1134 1135%% Compile to Beam assembly language (.S) and then try to 1136%% run .S through the compiler again. 1137 1138asm(Config) when is_list(Config) -> 1139 PrivDir = proplists:get_value(priv_dir, Config), 1140 Outdir = filename:join(PrivDir, "asm"), 1141 ok = file:make_dir(Outdir), 1142 1143 TestBeams = get_unique_beam_files(), 1144 Res = test_lib:p_run(fun(F) -> do_asm(F, Outdir) end, TestBeams), 1145 Res. 1146 1147 1148do_asm(Beam, Outdir) -> 1149 {ok,{M,[{abstract_code,{raw_abstract_v1,A}}]}} = 1150 beam_lib:chunks(Beam, [abstract_code]), 1151 try 1152 {ok,M,Asm} = compile:forms(A, ['S']), 1153 AsmFile = filename:join(Outdir, atom_to_list(M)++".S"), 1154 {ok,Fd} = file:open(AsmFile, [write,{encoding,utf8}]), 1155 beam_listing:module(Fd, Asm), 1156 ok = file:close(Fd), 1157 case compile:file(AsmFile, [from_asm,binary,report]) of 1158 {ok,M,_} -> 1159 ok = file:delete(AsmFile); 1160 Other -> 1161 io:format("*** failure '~p' for ~s\n", 1162 [Other,AsmFile]), 1163 error 1164 end 1165 catch Class:Error:Stk -> 1166 io:format("~p: ~p ~p\n~p\n", [M,Class,Error,Stk]), 1167 error 1168 end. 1169 1170sys_pre_attributes(Config) -> 1171 DataDir = proplists:get_value(data_dir, Config), 1172 File = filename:join(DataDir, "attributes.erl"), 1173 Mod = attributes, 1174 CommonOpts = [binary,report,verbose, 1175 {parse_transform,sys_pre_attributes}], 1176 PreOpts = [{attribute,delete,deleted}], 1177 PostOpts = [{attribute,insert,inserted,"value"}], 1178 PrePostOpts = [{attribute,replace,replaced,42}, 1179 {attribute,replace,replace_nonexisting,new}], 1180 {ok,Mod,Code} = compile:file(File, PrePostOpts ++ PreOpts ++ 1181 PostOpts ++ CommonOpts), 1182 code:load_binary(Mod, File, Code), 1183 Attr = Mod:module_info(attributes), 1184 io:format("~p", [Attr]), 1185 {inserted,"value"} = lists:keyfind(inserted, 1, Attr), 1186 {replaced,[42]} = lists:keyfind(replaced, 1, Attr), 1187 {replace_nonexisting,[new]} = lists:keyfind(replace_nonexisting, 1, Attr), 1188 false = lists:keymember(deleted, 1, Attr), 1189 1190 %% Cover more code. 1191 {ok,Mod,_} = compile:file(File, PostOpts ++ CommonOpts), 1192 {ok,Mod,_} = compile:file(File, CommonOpts -- [verbose]), 1193 {ok,Mod,_} = compile:file(File, PreOpts ++ CommonOpts), 1194 {ok,Mod,_} = compile:file(File, 1195 [{attribute,replace,replaced,42}|CommonOpts]), 1196 {ok,Mod,_} = compile:file(File, PrePostOpts ++ PreOpts ++ 1197 PostOpts ++ CommonOpts -- 1198 [report,verbose]), 1199 ok. 1200 1201%% Test the dialyzer option to cover more code. 1202dialyzer(Config) -> 1203 Priv = proplists:get_value(priv_dir, Config), 1204 ok = file:set_cwd(proplists:get_value(data_dir, Config)), 1205 Opts = [{outdir,Priv},report_errors], 1206 M = dialyzer_test, 1207 {ok,M} = c:c(M, [dialyzer|Opts]), 1208 [{a,b,c}] = M:M(), 1209 1210 %% Cover huge line numbers without the 'dialyzer' option. 1211 {ok,M} = c:c(M, Opts), 1212 [{a,b,c}] = M:M(), 1213 ok. 1214 1215 1216%% Test that warnings contain filenames and line numbers. 1217warnings(_Config) -> 1218 Files = get_unique_files(".erl"), 1219 test_lib:p_run(fun do_warnings/1, Files). 1220 1221do_warnings(F) -> 1222 {ok,_,_,Ws} = compile:file(F, [binary,bin_opt_info,return]), 1223 do_warnings_1(Ws, F). 1224 1225do_warnings_1([{"no_file",Ws}|_], F) -> 1226 io:format("~s:\nMissing file for warnings: ~p\n", 1227 [F,Ws]), 1228 error; 1229do_warnings_1([{Name,Ws}|T], F) -> 1230 case filename:extension(Name) of 1231 ".erl" -> 1232 do_warnings_2(Ws, T, F); 1233 _ -> 1234 io:format("~s:\nNo .erl extension\n", [F]), 1235 error 1236 end; 1237do_warnings_1([], _) -> ok. 1238 1239do_warnings_2([{Int,_,_}=W|T], Next, F) -> 1240 if 1241 is_integer(Int) -> 1242 do_warnings_2(T, Next, F); 1243 true -> 1244 io:format("~s:\nMissing line number: ~p\n", 1245 [F,W]), 1246 error 1247 end; 1248do_warnings_2([], Next, F) -> 1249 do_warnings_1(Next, F). 1250 1251 1252%% Test that the compile:pre_load/0 function (used by 'erlc') 1253%% pre-loads the modules that are used by a typical compilation. 1254 1255pre_load_check(Config) -> 1256 case {test_server:is_cover(),code:module_info(native)} of 1257 {true,_} -> 1258 {skip,"Cover is running"}; 1259 {false,true} -> 1260 %% Tracing won't work. 1261 {skip,"'code' is native-compiled"}; 1262 {false,false} -> 1263 try 1264 do_pre_load_check(Config) 1265 after 1266 dbg:stop_clear() 1267 end 1268 end. 1269 1270do_pre_load_check(Config) -> 1271 DataDir = ?config(data_dir, Config), 1272 Simple = filename:join(DataDir, "simple.erl"), 1273 Big = filename:join(DataDir, "big.erl"), 1274 {ok,_} = dbg:tracer(process, {fun pre_load_trace/2,[]}), 1275 dbg:p(self(), call), 1276 dbg:p(new, call), 1277 {ok,_} = dbg:tpl({?MODULE,get_trace_data,0}, []), 1278 {ok,_} = dbg:tp({code,ensure_modules_loaded,1}, []), 1279 1280 %% Compile a simple module using the erl_compile interface 1281 %% to find out the modules that are pre-loaded by 1282 %% compile:pre_load/0. 1283 Opts = #options{specific=[binary]}, 1284 {ok,simple,_} = compile:compile(Simple, "", Opts), 1285 [{code,ensure_modules_loaded,[PreLoaded0]}] = get_trace_data(), 1286 PreLoaded1 = ordsets:from_list(PreLoaded0), 1287 1288 %% Since 'compile' is the function doing the pre-loaded, 1289 %% it is useless to include it in the list. 1290 case ordsets:is_element(compile, PreLoaded1) of 1291 true -> 1292 io:put_chars("The 'compile' module should not be included " 1293 "in the list of modules to be pre-loaded."), 1294 ?t:fail(compile); 1295 false -> 1296 [] 1297 end, 1298 PreLoaded = ordsets:add_element(compile, PreLoaded1), 1299 1300 %% Now unload all pre-loaded modules and all modules in 1301 %% compiler application. Then compile a module to find 1302 %% which modules that get loaded. 1303 CompilerMods = compiler_modules(), 1304 Unload = ordsets:union(ordsets:from_list(CompilerMods), PreLoaded), 1305 _ = [begin 1306 code:delete(M), 1307 code:purge(M) 1308 end || M <- Unload], 1309 1310 {ok,_} = dbg:ctp({code,ensure_modules_loaded,1}), 1311 {ok,_} = dbg:tp({code,ensure_loaded,1}, []), 1312 {ok,big,_} = compile:file(Big, [binary]), 1313 WasLoaded0 = get_trace_data(), 1314 WasLoaded1 = [M || {code,ensure_loaded,[M]} <- WasLoaded0], 1315 WasLoaded = ordsets:from_list(WasLoaded1), 1316 1317 %% Check for modules that should have been pre-loaded. 1318 case ordsets:subtract(WasLoaded, PreLoaded) of 1319 [] -> 1320 ok; 1321 [_|_]=NotPreLoaded -> 1322 io:format("The following modules were used " 1323 "but not pre-loaded:\n~p\n", 1324 [NotPreLoaded]), 1325 ?t:fail({not_preload,NotPreLoaded}) 1326 end, 1327 1328 %% Check for modules that should not be pre-loaded. 1329 case ordsets:subtract(PreLoaded, WasLoaded) of 1330 [] -> 1331 ok; 1332 [_|_]=NotUsed -> 1333 io:format("The following modules were pre-loaded" 1334 " but not used:\n~p\n", 1335 [NotUsed]), 1336 ?t:fail({not_used,NotUsed}) 1337 end, 1338 1339 ok. 1340 1341get_trace_data() -> 1342 %% Apparantely, doing a receive at the beginning of 1343 %% a traced function can cause extra trace messages. 1344 %% To avoid that, don't do the receive in this function. 1345 do_get_trace_data(). 1346 1347do_get_trace_data() -> 1348 receive 1349 {trace_data,Data} -> Data 1350 end. 1351 1352pre_load_trace({trace,Pid,call,{?MODULE,get_trace_data,[]}}, Acc) -> 1353 Pid ! {trace_data,Acc}, 1354 []; 1355pre_load_trace({trace,_,call,MFA}, Acc) -> 1356 [MFA|Acc]. 1357 1358compiler_modules() -> 1359 Wc = filename:join([code:lib_dir(compiler),"ebin","*.beam"]), 1360 Ms = filelib:wildcard(Wc), 1361 FN = filename, 1362 [list_to_atom(FN:rootname(FN:basename(M), ".beam")) || M <- Ms]. 1363 1364%% Test that ERL_COMPILER_OPTIONS are correctly retrieved 1365%% by env_compiler_options/0 1366 1367env_compiler_options(_Config) -> 1368 Cases = [ 1369 {"bin_opt_info", [bin_opt_info]}, 1370 {"'S'", ['S']}, 1371 {"{source, \"test.erl\"}", [{source, "test.erl"}]}, 1372 {"[{d,macro_one,1},{d,macro_two}]", [{d, macro_one, 1}, {d, macro_two}]}, 1373 {"[warn_export_all, warn_export_vars]", [warn_export_all, warn_export_vars]} 1374 ], 1375 F = fun({Env, Expected}) -> 1376 true = os:putenv("ERL_COMPILER_OPTIONS", Env), 1377 Expected = compile:env_compiler_options() 1378 end, 1379 lists:foreach(F, Cases). 1380 1381%% Test options for compatibility with previous major versions of OTP. 1382 1383bc_options(Config) -> 1384 DataDir = proplists:get_value(data_dir, Config), 1385 1386 L = [{101, small_float, [no_get_hd_tl,no_line_info]}, 1387 {103, big, [no_put_tuple2,no_get_hd_tl,no_ssa_opt_record, 1388 no_line_info,no_stack_trimming]}, 1389 {125, small_float, [no_get_hd_tl,no_line_info,no_ssa_opt_float]}, 1390 1391 {132, small, [no_put_tuple2,no_get_hd_tl,no_ssa_opt_record, 1392 no_ssa_opt_float,no_line_info,no_bsm3]}, 1393 1394 {153, small, [r20]}, 1395 {153, small, [r21]}, 1396 1397 {136, big, [no_put_tuple2,no_get_hd_tl, 1398 no_ssa_opt_record,no_line_info]}, 1399 1400 {153, big, [no_put_tuple2,no_get_hd_tl, no_ssa_opt_record]}, 1401 {153, big, [r16]}, 1402 {153, big, [r17]}, 1403 {153, big, [r18]}, 1404 {153, big, [r19]}, 1405 {153, small_float, [r16]}, 1406 {153, small_float, []}, 1407 1408 {158, small_maps, [r17]}, 1409 {158, small_maps, [r18]}, 1410 {158, small_maps, [r19]}, 1411 {158, small_maps, [r20]}, 1412 {158, small_maps, [r21]}, 1413 1414 {164, small_maps, []}, 1415 {164, big, []} 1416 ], 1417 1418 Test = fun({Expected,Mod,Options}) -> 1419 case highest_opcode(DataDir, Mod, Options) of 1420 Expected -> 1421 ok; 1422 Got -> 1423 io:format("*** module ~p, options ~p => got ~p; expected ~p\n", 1424 [Mod,Options,Got,Expected]), 1425 error 1426 end 1427 end, 1428 test_lib:p_run(Test, L), 1429 ok. 1430 1431highest_opcode(DataDir, Mod, Opt) -> 1432 Src = filename:join(DataDir, atom_to_list(Mod)++".erl"), 1433 {ok,Mod,Beam} = compile:file(Src, [binary|Opt]), 1434 test_lib:highest_opcode(Beam). 1435 1436deterministic_include(Config) when is_list(Config) -> 1437 DataDir = proplists:get_value(data_dir, Config), 1438 Simple = filename:join(DataDir, "simple"), 1439 1440 %% Files without +deterministic should differ if their include paths do, 1441 %% as their debug info will be different. 1442 {ok,_,NonDetA} = compile:file(Simple, [binary, {i,"gurka"}]), 1443 {ok,_,NonDetB} = compile:file(Simple, [binary, {i,"gaffel"}]), 1444 true = NonDetA =/= NonDetB, 1445 1446 %% ... but files with +deterministic shouldn't. 1447 {ok,_,DetC} = compile:file(Simple, [binary, deterministic, {i,"gurka"}]), 1448 {ok,_,DetD} = compile:file(Simple, [binary, deterministic, {i,"gaffel"}]), 1449 true = DetC =:= DetD, 1450 1451 ok. 1452 1453deterministic_paths(Config) when is_list(Config) -> 1454 DataDir = proplists:get_value(data_dir, Config), 1455 1456 %% Files without +deterministic should differ if they were compiled from a 1457 %% different directory. 1458 true = deterministic_paths_1(DataDir, "simple", []), 1459 1460 %% ... but files with +deterministic shouldn't. 1461 false = deterministic_paths_1(DataDir, "simple", [deterministic]), 1462 1463 ok. 1464 1465deterministic_paths_1(DataDir, Name, Opts) -> 1466 Simple = filename:join(DataDir, "simple"), 1467 {ok, Cwd} = file:get_cwd(), 1468 try 1469 {ok,_,A} = compile:file(Simple, [binary | Opts]), 1470 ok = file:set_cwd(DataDir), 1471 {ok,_,B} = compile:file(Name, [binary | Opts]), 1472 A =/= B 1473 after 1474 file:set_cwd(Cwd) 1475 end. 1476 1477%%% 1478%%% Utilities. 1479%%% 1480 1481compile_and_verify(Name, Target, Opts) -> 1482 Mod = list_to_atom(filename:basename(Name, ".erl")), 1483 {ok,Mod} = compile:file(Name, Opts), 1484 {ok,{Mod,[{compile_info,CInfo}]}} = 1485 beam_lib:chunks(Target, [compile_info]), 1486 {options,BeamOpts} = lists:keyfind(options, 1, CInfo), 1487 Opts = BeamOpts. 1488 1489get_unique_beam_files() -> 1490 get_unique_files(".beam"). 1491 1492get_unique_files(Ext) -> 1493 Wc = filename:join(filename:dirname(code:which(?MODULE)), "*"++Ext), 1494 [F || F <- filelib:wildcard(Wc), 1495 not is_cloned(F, Ext), not is_lfe_module(F, Ext)]. 1496 1497is_cloned(File, Ext) -> 1498 Mod = list_to_atom(filename:basename(File, Ext)), 1499 test_lib:is_cloned_mod(Mod). 1500 1501is_lfe_module(File, Ext) -> 1502 case filename:basename(File, Ext) of 1503 "lfe_" ++ _ -> true; 1504 _ -> false 1505 end. 1506