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, no_core_prepare/1, 38 warnings/1, pre_load_check/1, env_compiler_options/1, 39 bc_options/1, deterministic_include/1, deterministic_paths/1, 40 compile_attribute/1, message_printing/1, other_options/1, 41 transforms/1, erl_compile_api/1 42 ]). 43 44suite() -> [{ct_hooks,[ts_install_cth]}]. 45 46%% To cover the stripping of 'type' and 'spec' in beam_asm. 47-type all_return_type() :: [atom()]. 48-spec all() -> all_return_type(). 49 50all() -> 51 [app_test, appup_test, file_1, forms_2, module_mismatch, outdir, 52 binary, makedep, cond_and_ifdef, listings, listings_big, 53 other_output, kernel_listing, encrypted_abstr, tuple_calls, 54 strict_record, utf8_atoms, utf8_functions, extra_chunks, 55 cover, env, core_pp, core_roundtrip, asm, no_core_prepare, 56 sys_pre_attributes, dialyzer, warnings, pre_load_check, 57 env_compiler_options, custom_debug_info, bc_options, 58 custom_compile_info, deterministic_include, deterministic_paths, 59 compile_attribute, message_printing, other_options, transforms, 60 erl_compile_api]. 61 62groups() -> 63 []. 64 65init_per_suite(Config) -> 66 test_lib:recompile(?MODULE), 67 Config. 68 69end_per_suite(_Config) -> 70 ok. 71 72init_per_group(_GroupName, Config) -> 73 Config. 74 75end_per_group(_GroupName, Config) -> 76 Config. 77 78 79 80%% Test that the Application file has no `basic' errors."; 81app_test(Config) when is_list(Config) -> 82 test_server:app_test(compiler). 83 84%% Test that the Application upgrade file has no `basic' errors."; 85appup_test(Config) when is_list(Config) -> 86 ok = test_server:appup_test(compiler). 87 88%% Tests that we can compile and run a simple Erlang program, 89%% using compile:file/1. 90 91file_1(Config) when is_list(Config) -> 92 process_flag(trap_exit, true), 93 94 DataDir = proplists:get_value(data_dir, Config), 95 PrivDir = proplists:get_value(priv_dir, Config), 96 97 {Simple, Target} = get_files(Config, simple, "file_1"), 98 {ok, Cwd} = file:get_cwd(), 99 ok = file:set_cwd(filename:dirname(Target)), 100 101 {ok,simple} = compile:file(Simple, [slim]), %Smoke test only. 102 {ok,simple} = compile:file(Simple), %Smoke test only. 103 104 compile_and_verify(Simple, Target, []), 105 compile_and_verify(Simple, Target, [debug_info]), 106 compile_and_verify(Simple, Target, [no_postopt]), 107 108 {ok,simple} = compile:file(Simple, [no_line_info]), %Coverage 109 {ok,simple} = compile:file(Simple, [{eprof,beam_z}]), %Coverage 110 111 %% Cover option not in a list (undocumented feature). 112 {ok,simple} = compile:file(Simple, no_postopt), 113 114 %% Test compiling from an .abstr file. 115 {ok,[]} = compile:file(Simple, [dabstr]), 116 {ok,simple} = compile:file(filename:rootname(Target), [from_abstr]), 117 ok = file:delete(filename:rootname(Target) ++ ".abstr"), 118 119 %% Test option 'deterministic'. 120 {ok,simple} = compile:file(Simple, [deterministic]), 121 {module,simple} = c:l(simple), 122 [{version,_}] = simple:module_info(compile), 123 true = code:delete(simple), 124 false = code:purge(simple), 125 126 ok = file:set_cwd(Cwd), 127 true = exists(Target), 128 passed = run(Target, test, []), 129 130 %% Test option 'deterministic' as a compiler attribute. 131 Det = deterministic_module, 132 {DetPath, DetTarget} = get_files(Config, Det, "det_target"), 133 {ok,Det,DetCode} = compile:file(DetPath, [binary]), 134 {module,Det} = code:load_binary(Det, "", DetCode), 135 [{version,_}] = Det:module_info(compile), 136 true = code:delete(Det), 137 false = code:purge(Det), 138 139 %% Cover error handling code. 140 NonExisting = definitely__no__such__module, 141 error = compile:file(NonExisting, [report]), 142 error = compile:file(NonExisting, [from_abstr,report]), 143 error = compile:file(NonExisting, [from_core,report]), 144 error = compile:file(NonExisting, [from_asm,report]), 145 146 error = compile:file(filename:join(DataDir, "bad_core"), [from_core,report]), 147 error = compile:file(filename:join(DataDir, "bad_core_tokens"), [from_core,report]), 148 149 %% Create a directory with the same name as the temp file to cover 150 %% handling of write errors. 151 Simple = filename:join(DataDir, "simple"), 152 SimpleTempFile = filename:join(PrivDir, "simple.bea#"), 153 SimpleOutputFile = filename:join(PrivDir, "simple.beam"), 154 ok = file:make_dir(SimpleTempFile), 155 ok = file:make_dir(SimpleOutputFile), 156 try 157 error = compile:file(Simple, [{outdir,PrivDir}, report]), 158 _ = file:del_dir(SimpleTempFile), 159 error = compile:file(Simple, [{outdir,PrivDir}, report]) 160 after 161 _ = file:del_dir(SimpleTempFile), 162 _ = file:del_dir(SimpleOutputFile) 163 end, 164 165 %% Cleanup. 166 ok = file:delete(Target), 167 ok = file:del_dir(filename:dirname(Target)), 168 ok = file:del_dir(filename:dirname(DetTarget)), 169 170 %% There should not be any messages in the messages. 171 receive 172 Any -> 173 ct:fail({unexpected,Any}) 174 after 10 -> 175 ok 176 end, 177 178 ok. 179 180forms_2(Config) when is_list(Config) -> 181 {Simple, Target} = get_files(Config, simple, "file_1"), 182 ok = file:del_dir(filename:dirname(Target)), 183 184 Src = Simple, 185 AbsSrc = filename:absname(Src), 186 {ok,[],SimpleCode} = compile:file(Simple, [dabstr,binary]), 187 188 {ok,simple,Bin1} = compile:forms(SimpleCode, [binary,{source,Src}]), 189 {ok,simple,_} = compile:forms(SimpleCode, 190 [binary,{error_location,line},{source,Src}]), 191 192 %% Cover option not in a list (undocumented feature). 193 {ok,simple,_} = compile:forms(SimpleCode, no_postopt), 194 195 %% Load and test that the proper source is returned. 196 AbsSrc = forms_load_code(simple, Src, Bin1), 197 198 %% Work in a deleted directory. 199 PrivDir = proplists:get_value(priv_dir, Config), 200 WorkDir = filename:join(PrivDir, ?FUNCTION_NAME), 201 ok = file:make_dir(WorkDir), 202 ok = file:set_cwd(WorkDir), 203 case os:type() of 204 {unix,_} -> os:cmd("rm -rf " ++ WorkDir); 205 _ -> ok 206 end, 207 {ok,simple,Bin2} = compile:forms(SimpleCode), 208 undefined = forms_load_code(simple, "ignore", Bin2), 209 210 {ok,simple,Bin3} = compile:forms(SimpleCode, [{source,Src},report]), 211 case forms_load_code(simple, "ignore", Bin3) of 212 Src -> %Unix. 213 ok; 214 AbsSrc -> %Windows. 215 ok 216 end, 217 218 {ok,simple,Core} = compile:forms(SimpleCode, [to_core0,binary]), 219 forms_compile_and_load(Core, [from_core]), 220 221 {ok,simple,Asm} = compile:forms(SimpleCode, [to_asm,binary]), 222 forms_compile_and_load(Asm, [from_asm]), 223 224 %% The `from_abstr` option is redundant when compiling from forms, 225 %% but it should work. 226 forms_compile_and_load(SimpleCode, [from_abstr]), 227 228 %% Cover the error handling code. 229 error = compile:forms(bad_core, [from_core,report]), 230 error = compile:forms(bad_asm, [from_asm,report]), 231 232 ok. 233 234 235forms_load_code(Mod, Src, Bin) -> 236 {module,Mod} = code:load_binary(Mod, Src, Bin), 237 Info = Mod:module_info(compile), 238 SourceOption = proplists:get_value(source, Info), 239 240 %% Ensure that the options are not polluted with 'source'. 241 [] = proplists:get_value(options, Info), 242 243 %% Cleanup. 244 true = code:delete(simple), 245 false = code:purge(simple), 246 247 SourceOption. 248 249forms_compile_and_load(Code, Opts) -> 250 Mod = simple, 251 {ok,Mod,Bin} = compile:forms(Code, Opts), 252 {module,Mod} = code:load_binary(Mod, "ignore", Bin), 253 _ = Mod:module_info(), 254 true = code:delete(simple), 255 false = code:purge(simple), 256 ok. 257 258module_mismatch(Config) when is_list(Config) -> 259 DataDir = proplists:get_value(data_dir, Config), 260 File = filename:join(DataDir, "wrong_module_name.erl"), 261 {error,[{"wrong_module_name.beam", 262 [{none,compile,{module_name,arne,"wrong_module_name"}}]}], 263 []} = compile:file(File, [return]), 264 error = compile:file(File, [report]), 265 266 {ok,arne,[]} = compile:file(File, 267 [return,no_error_module_mismatch]), 268 269 ok. 270 271%% Tests that the {outdir, Dir} option works. 272 273outdir(Config) when is_list(Config) -> 274 {Simple, Target} = get_files(Config, simple, "outdir"), 275 {ok, simple} = compile:file(Simple, [{outdir, filename:dirname(Target)}]), 276 true = exists(Target), 277 passed = run(Target, test, []), 278 ok = file:delete(Target), 279 ok = file:del_dir(filename:dirname(Target)), 280 ok. 281 282%% Tests that the binary option works. 283 284binary(Config) when is_list(Config) -> 285 {Simple, Target} = get_files(Config, simple, "binary"), 286 {ok, simple, Binary} = compile:file(Simple, [binary]), 287 code:load_binary(simple, Target, Binary), 288 passed = simple:test(), 289 true = code:delete(simple), 290 false = code:purge(simple), 291 ok = file:del_dir(filename:dirname(Target)), 292 ok. 293 294%% Tests that the dependencies-Makefile-related options work. 295 296makedep(Config) when is_list(Config) -> 297 DataDir = proplists:get_value(data_dir, Config), 298 PrivDir = proplists:get_value(priv_dir, Config), 299 300 {Simple,Target} = get_files(Config, simple, "makedep"), 301 SimpleRootname = filename:rootname(Simple), 302 IncludeDir = filename:join(filename:dirname(Simple), "include"), 303 IncludeOptions = [ 304 {d,need_foo}, 305 {d,foo_value,42}, 306 {d,include_generated}, 307 {i,IncludeDir} 308 ], 309 310 %% Basic rule. 311 BasicMf1Name = SimpleRootname ++ "-basic1.mk", 312 {ok,BasicMf1} = file:read_file(BasicMf1Name), 313 {ok,_,Mf1} = compile:file(Simple, [binary,makedep]), 314 BasicMf1 = makedep_canonicalize_result(Mf1, DataDir), 315 316 %% Basic rule with one existing header. 317 BasicMf2Name = SimpleRootname ++ "-basic2.mk", 318 {ok,BasicMf2} = file:read_file(BasicMf2Name), 319 {ok,_,Mf2} = compile:file(Simple, [binary,makedep|IncludeOptions]), 320 BasicMf2 = makedep_canonicalize_result(Mf2, DataDir), 321 322 %% Rule with one existing header and one missing header. 323 MissingMfName = SimpleRootname ++ "-missing.mk", 324 {ok,MissingMf} = file:read_file(MissingMfName), 325 {ok,_,Mf3} = compile:file(Simple, 326 [binary,makedep,makedep_add_missing|IncludeOptions]), 327 MissingMf = makedep_canonicalize_result(Mf3, DataDir), 328 329 %% Rule with modified target. 330 TargetMf1Name = SimpleRootname ++ "-target1.mk", 331 {ok,TargetMf1} = file:read_file(TargetMf1Name), 332 {ok,_,Mf4} = compile:file(Simple, 333 [binary,makedep,{makedep_target,"$target"}|IncludeOptions]), 334 TargetMf1 = makedep_modify_target( 335 makedep_canonicalize_result(Mf4, DataDir), "$$target"), 336 337 %% Rule with quoted modified target. 338 TargetMf2Name = SimpleRootname ++ "-target2.mk", 339 {ok,TargetMf2} = file:read_file(TargetMf2Name), 340 {ok,_,Mf5} = compile:file(Simple, 341 [binary,makedep,{makedep_target,"$target"},makedep_quote_target| 342 IncludeOptions]), 343 TargetMf2 = makedep_modify_target( 344 makedep_canonicalize_result(Mf5, DataDir), "$$target"), 345 346 %% Basic rule written to some file. 347 {ok,_} = compile:file(Simple, 348 [makedep,{makedep_output,Target}|IncludeOptions]), 349 {ok,Mf6} = file:read_file(Target), 350 BasicMf2 = makedep_canonicalize_result(Mf6, DataDir), 351 352 %% Rule with creating phony target. 353 PhonyMfName = SimpleRootname ++ "-phony.mk", 354 {ok,PhonyMf} = file:read_file(PhonyMfName), 355 {ok,_,Mf7} = compile:file(Simple, 356 [binary,makedep,makedep_phony|IncludeOptions]), 357 PhonyMf = makedep_canonicalize_result(Mf7, DataDir), 358 359 %% Basic rule written to the default file. 360 {ok,_} = compile:file(Simple, [makedep|IncludeOptions]), 361 {ok,Mf8} = file:read_file(Target), 362 BasicMf2 = makedep_canonicalize_result(Mf8, DataDir), 363 364 %% Generate dependencies and compile normally at the same time. 365 GeneratedHrl = filename:join(PrivDir, "generated.hrl"), 366 ok = file:write_file(GeneratedHrl, ""), 367 {ok,simple} = compile:file(Simple, [report,makedep_side_effect, 368 {makedep_output,Target}, 369 {i,PrivDir}|IncludeOptions]), 370 {ok,Mf9} = file:read_file(Target), 371 BasicMf3 = iolist_to_binary([string:trim(BasicMf2), " ", filename:join(PrivDir, "generated.hrl"), "\n"]), 372 BasicMf3 = makedep_canonicalize_result(Mf9, DataDir), 373 error = compile:file(Simple, [report,makedep_side_effect, 374 {makedep_output,PrivDir}|IncludeOptions]), 375 376 %% Cover generation of long lines that must be split. 377 CompileModule = filename:join(code:lib_dir(compiler), "src/compile.erl"), 378 {ok,_} = compile:file(CompileModule, [report, 379 makedep,{makedep_output,standard_io}, 380 {i,filename:join(code:lib_dir(stdlib), "include")}]), 381 382 %% Basic rule written to the standard output. 383 {ok,_} = compile:file(Simple, [makedep,{makedep_output,standard_io}|IncludeOptions]), 384 385 %% Test error handling. 386 error = compile:file(Simple, [report,makedep,{makedep_output,DataDir}]), 387 error = compile:file(Simple, [report,makedep,{makedep_output,a_bad_output_device}]), 388 389 ok = file:delete(Target), 390 ok = file:delete(GeneratedHrl), 391 ok = file:del_dir(filename:dirname(Target)), 392 ok. 393 394makedep_canonicalize_result(Mf, DataDir) -> 395 Mf0 = binary_to_list(Mf), 396 %% Replace the Datadir by "$(srcdir)". 397 Mf1 = re:replace(Mf0, DataDir, "$(srcdir)/", 398 [global,multiline,{return,list}]), 399 %% Long lines are splitted, put back everything on one line. 400 Mf2 = re:replace(Mf1, "\\\\\n ", "", [global,multiline,{return,list}]), 401 list_to_binary(Mf2). 402 403makedep_modify_target(Mf, Target) -> 404 Mf0 = binary_to_list(Mf), 405 Mf1 = re:replace(Mf0, Target, "$target", [{return,list}]), 406 list_to_binary(Mf1). 407 408%% Tests that conditional compilation, defining values, including files work. 409 410no_core_prepare(_Config) -> 411 Mod = {c_module,[], 412 {c_literal,[],sample_receive}, 413 [{c_var,[],{discard,0}}], 414 [], 415 [{{c_var,[],{discard,0}}, 416 {c_fun,[],[], 417 {c_case,[], 418 {c_values,[],[]}, 419 [{c_clause,[],[], 420 {c_literal,[],true}, 421 {c_receive,[],[],{c_literal,[],0},{c_literal,[],ok}}}]}}}]}, 422 423 {ok,sample_receive,_,_} = compile:forms(Mod, [from_core,binary,return]), 424 {error,_,_} = compile:forms(Mod, [from_core,binary,return,no_core_prepare]), 425 ok. 426 427cond_and_ifdef(Config) when is_list(Config) -> 428 {Simple, Target} = get_files(Config, simple, "cond_and_ifdef"), 429 IncludeDir = filename:join(filename:dirname(Simple), "include"), 430 Options = [{outdir, filename:dirname(Target)}, 431 {d, need_foo}, {d, foo_value, 42}, 432 {i, IncludeDir}, report], 433 {ok, simple} = compile:file(Simple, Options), 434 true = exists(Target), 435 {hiker, 42} = run(Target, foo, []), 436 ok = file:delete(Target), 437 ok = file:del_dir(filename:dirname(Target)), 438 ok. 439 440listings(Config) when is_list(Config) -> 441 DataDir = proplists:get_value(data_dir, Config), 442 PrivDir = proplists:get_value(priv_dir, Config), 443 ok = do_file_listings(DataDir, PrivDir, [ 444 "simple", 445 "small", 446 "small_maps" 447 ]), 448 449 %% Cover handling of write errors. 450 Simple = filename:join(DataDir, "simple"), 451 SimpleTarget = filename:join(PrivDir, "simple.S"), 452 ok = file:make_dir(SimpleTarget), 453 try 454 error = compile:file(Simple, ['S', {outdir,PrivDir}, report]) 455 after 456 ok = file:del_dir(SimpleTarget) 457 end, 458 459 ok. 460 461do_file_listings(_, _, []) -> ok; 462do_file_listings(DataDir, PrivDir, [File|Files]) -> 463 Simple = filename:join(DataDir, File), 464 TargetDir = filename:join(PrivDir, listings), 465 ok = file:make_dir(TargetDir), 466 467 List = [{'S',".S"}, 468 {'E',".E"}, 469 {'P',".P"}, 470 {dpp, ".pp"}, 471 {dabstr, ".abstr"}, 472 {dexp, ".expand"}, 473 {dcore, ".core"}, 474 {doldinline, ".oldinline"}, 475 {dinline, ".inline"}, 476 {dcore, ".core"}, 477 {dcopt, ".copt"}, 478 {dcbsm, ".core_bsm"}, 479 {dkern, ".kernel"}, 480 {dssa, ".ssa"}, 481 {dbool, ".bool"}, 482 {dssashare, ".ssashare"}, 483 {dssaopt, ".ssaopt"}, 484 {dprecg, ".precodegen"}, 485 {dcg, ".codegen"}, 486 {dblk, ".block"}, 487 {djmp, ".jump"}, 488 {dclean, ".clean"}, 489 {dpeep, ".peep"}, 490 {dopt, ".optimize"}, 491 {diffable, ".S"}], 492 p_listings(List, Simple, TargetDir), 493 494 %% Test options that produce a listing file if 'binary' is not given. 495 do_listing(Simple, TargetDir, to_pp, ".P"), 496 do_listing(Simple, TargetDir, to_exp, ".E"), 497 do_listing(Simple, TargetDir, to_core0, ".core"), 498 Listings = filename:join(PrivDir, listings), 499 ok = file:delete(filename:join(Listings, File ++ ".core")), 500 do_listing(Simple, TargetDir, to_core, ".core"), 501 do_listing(Simple, TargetDir, to_kernel, ".kernel"), 502 do_listing(Simple, TargetDir, to_dis, ".dis"), 503 504 %% Final clean up. 505 lists:foreach(fun(F) -> ok = file:delete(F) end, 506 filelib:wildcard(filename:join(Listings, "*"))), 507 ok = file:del_dir(Listings), 508 509 do_file_listings(DataDir,PrivDir,Files). 510 511listings_big(Config) when is_list(Config) -> 512 {Big,Target} = get_files(Config, big, listings_big), 513 TargetDir = filename:dirname(Target), 514 List = [{'S',".S"}, 515 {'E',".E"}, 516 {'P',".P"}, 517 {dkern, ".kernel"}, 518 {dssa, ".ssa"}, 519 {dssaopt, ".ssaopt"}, 520 {dprecg, ".precodegen"}, 521 {to_dis, ".dis"}], 522 p_listings(List, Big, TargetDir). 523 524p_listings(List, File, BaseDir) -> 525 Run = fun({Option,Extension}) -> 526 Uniq = erlang:unique_integer([positive]), 527 Dir = filename:join(BaseDir, integer_to_list(Uniq)), 528 ok = file:make_dir(Dir), 529 try 530 do_listing(File, Dir, Option, Extension), 531 ok 532 catch 533 Class:Error:Stk -> 534 io:format("~p:~p\n~p\n", [Class,Error,Stk]), 535 error 536 after 537 _ = [ok = file:delete(F) || 538 F <- filelib:wildcard(filename:join(Dir, "*"))], 539 ok = file:del_dir(Dir) 540 end 541 end, 542 test_lib:p_run(Run, List). 543 544other_output(Config) when is_list(Config) -> 545 {Simple,_Target} = get_files(Config, simple, "other_output"), 546 547 io:put_chars("to_pp"), 548 {ok,[],PP} = compile:file(Simple, [to_pp,binary,time]), 549 [] = [E || E <- PP, 550 begin 551 case element(1, E) of 552 attribute -> false; 553 function -> false; 554 eof -> false 555 end 556 end], 557 558 io:put_chars("to_exp (file)"), 559 {ok,[],Expand} = compile:file(Simple, [to_exp,binary,time]), 560 true = is_list(Expand), 561 {attribute,_,module,simple} = lists:keyfind(module, 3, Expand), 562 io:put_chars("to_exp (forms)"), 563 {ok,[],Expand} = compile:forms(PP, [to_exp,binary,time]), 564 565 io:put_chars("to_core (file)"), 566 {ok,simple,Core} = compile:file(Simple, [to_core,binary,time]), 567 c_module = element(1, Core), 568 {ok,_} = core_lint:module(Core), 569 io:put_chars("to_core (forms)"), 570 {ok,simple,Core} = compile:forms(PP, [to_core,binary,time]), 571 572 io:put_chars("to_kernel (file)"), 573 {ok,simple,Kernel} = compile:file(Simple, [to_kernel,binary,time]), 574 k_mdef = element(1, Kernel), 575 io:put_chars("to_kernel (forms)"), 576 {ok,simple,Kernel} = compile:forms(PP, [to_kernel,binary,time]), 577 578 io:put_chars("to_asm (file)"), 579 {ok,simple,Asm} = compile:file(Simple, [to_asm,binary,time]), 580 {simple,_,_,_,_} = Asm, 581 io:put_chars("to_asm (forms)"), 582 {ok,simple,Asm} = compile:forms(PP, [to_asm,binary,time]), 583 584 ok. 585 586%% Smoke test and cover of pretty-printing of Kernel code. 587kernel_listing(_Config) -> 588 TestBeams = get_unique_beam_files(), 589 Abstr = [begin {ok,{Mod,[{abstract_code, 590 {raw_abstract_v1,Abstr}}]}} = 591 beam_lib:chunks(Beam, [abstract_code]), 592 {Mod,Abstr} end || Beam <- TestBeams], 593 test_lib:p_run(fun(F) -> do_kernel_listing(F) end, Abstr). 594 595do_kernel_listing({M,A}) -> 596 try 597 {ok,M,Kern} = compile:forms(A, [to_kernel]), 598 IoList = v3_kernel_pp:format(Kern), 599 case unicode:characters_to_binary(IoList) of 600 Bin when is_binary(Bin) -> 601 ok 602 end 603 catch 604 throw:{error,Error} -> 605 io:format("*** compilation failure '~p' for module ~s\n", 606 [Error,M]), 607 error; 608 Class:Error:Stk -> 609 io:format("~p: ~p ~p\n~p\n", [M,Class,Error,Stk]), 610 error 611 end. 612 613encrypted_abstr(Config) when is_list(Config) -> 614 {Simple,Target} = get_files(Config, simple, "encrypted_abstr"), 615 616 Res = case has_crypto() of 617 false -> 618 %% No crypto. 619 encrypted_abstr_no_crypto(Simple, Target), 620 {comment,"The crypto application is missing or broken"}; 621 true -> 622 %% Simulate not having crypto by removing 623 %% the crypto application from the path. 624 OldPath = code:get_path(), 625 try 626 NewPath = OldPath -- [filename:dirname(code:which(crypto))], 627 (catch crypto:stop()), 628 code:delete(crypto), 629 code:purge(crypto), 630 code:set_path(NewPath), 631 encrypted_abstr_no_crypto(Simple, Target) 632 after 633 code:set_path(OldPath) 634 end, 635 636 %% Now run the tests that require crypto. 637 encrypted_abstr_1(Simple, Target), 638 ok = file:delete(Target), 639 ok = file:del_dir(filename:dirname(Target)) 640 end, 641 642 %% Cleanup. 643 Res. 644 645encrypted_abstr_1(Simple, Target) -> 646 TargetDir = filename:dirname(Target), 647 Key = "ablurf123BX#$;3", 648 install_crypto_key(Key), 649 {ok,simple} = compile:file(Simple, 650 [debug_info,{debug_info_key,Key}, 651 {outdir,TargetDir}]), 652 verify_abstract(Target, erl_abstract_code), 653 654 {ok,simple} = compile:file(Simple, 655 [{debug_info_key,Key}, 656 {outdir,TargetDir}]), 657 verify_abstract(Target, erl_abstract_code), 658 659 {ok,simple} = compile:file(Simple, 660 [debug_info,{debug_info_key,{des3_cbc,Key}}, 661 {outdir,TargetDir}]), 662 verify_abstract(Target, erl_abstract_code), 663 664 {ok,simple} = compile:file(Simple, 665 [{debug_info,{?MODULE,ok}}, 666 {debug_info_key,Key}, 667 {outdir,TargetDir}]), 668 verify_abstract(Target, ?MODULE), 669 670 {ok,{simple,[{compile_info,CInfo}]}} = 671 beam_lib:chunks(Target, [compile_info]), 672 {_,Opts} = lists:keyfind(options, 1, CInfo), 673 {_,'********'} = lists:keyfind(debug_info_key, 1, Opts), 674 675 %% Try some illegal forms of crypto keys. 676 error = compile:file(Simple, 677 [debug_info,{debug_info_key,{blurf,"ss"}},report]), 678 error = compile:file(Simple, 679 [debug_info,{debug_info_key,{blurf,1,"ss"}},report]), 680 error = compile:file(Simple, 681 [debug_info,{debug_info_key,42},report]), 682 683 %% Place the crypto key in .erlang.crypt. 684 beam_lib:clear_crypto_key_fun(), 685 {ok,OldCwd} = file:get_cwd(), 686 ok = file:set_cwd(TargetDir), 687 688 error = compile:file(Simple, [encrypt_debug_info,report]), 689 690 NewKey = "better use another key here", 691 write_crypt_file(["[{debug_info,des3_cbc,simple,\"",NewKey,"\"}].\n"]), 692 {ok,simple} = compile:file(Simple, [encrypt_debug_info,report]), 693 verify_abstract("simple.beam", erl_abstract_code), 694 ok = file:delete(".erlang.crypt"), 695 beam_lib:clear_crypto_key_fun(), 696 {error,beam_lib,{key_missing_or_invalid,"simple.beam",abstract_code}} = 697 beam_lib:chunks("simple.beam", [abstract_code]), 698 ok = file:set_cwd(OldCwd), 699 700 %% Test key compatibility by reading a BEAM file produced before 701 %% the update to the new crypto functions. 702 install_crypto_key("an old key"), 703 KeyCompat = filename:join(filename:dirname(Simple), 704 "key_compatibility"), 705 {ok,{key_compatibility,[Chunk]}} = beam_lib:chunks(KeyCompat, 706 [abstract_code]), 707 {abstract_code,{raw_abstract_v1,_}} = Chunk, 708 709 ok. 710 711 712write_crypt_file(Contents0) -> 713 Contents = list_to_binary([Contents0]), 714 io:format("~s\n", [binary_to_list(Contents)]), 715 ok = file:write_file(".erlang.crypt", Contents). 716 717encrypted_abstr_no_crypto(Simple, Target) -> 718 io:format("simpe: ~p~n", [Simple]), 719 TargetDir = filename:dirname(Target), 720 Key = "ablurf123BX#$;3", 721 error = compile:file(Simple, 722 [debug_info,{debug_info_key,Key}, 723 {outdir,TargetDir},report]), 724 ok. 725 726verify_abstract(Beam, Backend) -> 727 {ok,{simple,[Abst, Dbgi]}} = beam_lib:chunks(Beam, [abstract_code, debug_info]), 728 {abstract_code,{raw_abstract_v1,_}} = Abst, 729 {debug_info,{debug_info_v1,Backend,_}} = Dbgi. 730 731has_crypto() -> 732 try 733 crypto:start(), 734 crypto:stop(), 735 true 736 catch 737 error:_ -> false 738 end. 739 740install_crypto_key(Key) -> 741 F = fun (init) -> ok; 742 ({debug_info,des3_cbc,_,_}) -> Key; 743 (clear) -> ok 744 end, 745 ok = beam_lib:crypto_key_fun(F). 746 747%% Miscellanous tests, mainly to get better coverage. 748debug_info(erlang_v1, Module, ok, _Opts) -> 749 {ok, [Module]}; 750debug_info(erlang_v1, _Module, error, _Opts) -> 751 {error, unknown_format}. 752 753custom_debug_info(Config) when is_list(Config) -> 754 {Simple,_} = get_files(Config, simple, "file_1"), 755 756 {ok,simple,OkBin} = compile:file(Simple, [binary, {debug_info,{?MODULE,ok}}]), %Coverage 757 {ok,{simple,[{abstract_code,{raw_abstract_v1,[simple]}}]}} = 758 beam_lib:chunks(OkBin, [abstract_code]), 759 {ok,{simple,[{debug_info,{debug_info_v1,?MODULE,ok}}]}} = 760 beam_lib:chunks(OkBin, [debug_info]), 761 762 {ok,simple,ErrorBin} = compile:file(Simple, [binary, {debug_info,{?MODULE,error}}]), %Coverage 763 {ok,{simple,[{abstract_code,no_abstract_code}]}} = 764 beam_lib:chunks(ErrorBin, [abstract_code]), 765 {ok,{simple,[{debug_info,{debug_info_v1,?MODULE,error}}]}} = 766 beam_lib:chunks(ErrorBin, [debug_info]). 767 768custom_compile_info(Config) when is_list(Config) -> 769 Anno = erl_anno:new(1), 770 Forms = [{attribute,Anno,module,custom_compile_info}], 771 Opts = [binary,{compile_info,[{another,version}]}], 772 773 {ok,custom_compile_info,Bin} = compile:forms(Forms, Opts), 774 {ok,{custom_compile_info,[{compile_info,CompileInfo}]}} = 775 beam_lib:chunks(Bin, [compile_info]), 776 version = proplists:get_value(another, CompileInfo), 777 CompileOpts = proplists:get_value(options, CompileInfo), 778 undefined = proplists:get_value(compile_info, CompileOpts), 779 780 {ok,custom_compile_info,DetBin} = compile:forms(Forms, [deterministic|Opts]), 781 {ok,{custom_compile_info,[{compile_info,DetInfo}]}} = 782 beam_lib:chunks(DetBin, [compile_info]), 783 version = proplists:get_value(another, DetInfo). 784 785cover(Config) when is_list(Config) -> 786 io:format("~p\n", [compile:options()]), 787 ok. 788 789do_listing(Source, TargetDir, Type, Ext) -> 790 io:format("Source: ~p TargetDir: ~p\n Type: ~p Ext: ~p\n", 791 [Source, TargetDir, Type, Ext]), 792 case compile:file(Source, [Type, time, {outdir, TargetDir}]) of 793 {ok, _} -> ok; 794 Other -> ct:fail({unexpected_result, Other}) 795 end, 796 SourceBase = filename:rootname(filename:basename(Source)), 797 798 Target = filename:join(TargetDir, SourceBase ++ Ext), 799 true = exists(Target). 800 801get_files(Config, Module, OutputName) -> 802 code:delete(Module), 803 code:purge(Module), 804 DataDir = proplists:get_value(data_dir, Config), 805 PrivDir = proplists:get_value(priv_dir, Config), 806 Src = filename:join(DataDir, atom_to_list(Module)), 807 TargetDir = filename:join(PrivDir, OutputName), 808 ok = file:make_dir(TargetDir), 809 File = atom_to_list(Module) ++ code:objfile_extension(), 810 Target = filename:join(TargetDir, File), 811 {Src, Target}. 812 813run(Target, Func, Args) -> 814 Module = list_to_atom(filename:rootname(filename:basename(Target))), 815 {module, Module} = code:load_abs(filename:rootname(Target)), 816 Result = (catch apply(Module, Func, Args)), 817 true = code:delete(Module), 818 false = code:purge(Module), 819 Result. 820 821exists(Name) -> 822 case file:read_file_info(Name) of 823 {ok, _} -> true; 824 {error, _} -> false 825 end. 826 827 828strict_record(Config) when is_list(Config) -> 829 Priv = proplists:get_value(priv_dir, Config), 830 ok = file:set_cwd(proplists:get_value(data_dir, Config)), 831 Opts = [{outdir,Priv},report_errors], 832 M = record_access, 833 834 {ok,M} = c:c(M, [strict_record_tests|Opts]), 835 Turtle = test_strict(), 836 837 {ok,M} = c:c(M, [no_strict_record_tests|Opts]), 838 Turtle = test_sloppy(), 839 840 %% The option first given wins. 841 {ok,M} = c:c(M, [no_strict_record_tests,strict_record_tests|Opts]), 842 Turtle = test_sloppy(), 843 {ok,M} = c:c(M, [strict_record_tests,no_strict_record_tests|Opts]), 844 Turtle = test_strict(), 845 846 %% Default (possibly influenced by ERL_COMPILER_OPTIONS). 847 {ok,M} = c:c(M, [{outdir,Priv},report_errors]), 848 try 849 {1,2} = record_access:test(Turtle), 850 {comment,"Default: no_strict_record_tests"} 851 catch 852 error:{badrecord,tortoise} -> 853 {comment,"Default: strict_record_tests"} 854 end. 855 856test_strict() -> 857 Turtle = record_access:turtle(), 858 try 859 record_access:test(Turtle) 860 catch 861 error:{badrecord,tortoise} -> 862 ok 863 end, 864 Turtle. 865 866test_sloppy() -> 867 Turtle = record_access:turtle(), 868 {1,2} = record_access:test(Turtle), 869 Turtle. 870 871utf8_atoms(Config) when is_list(Config) -> 872 Anno = erl_anno:new(1), 873 Atom = binary_to_atom(<<"こんにちは"/utf8>>, utf8), 874 Forms = [{attribute,Anno,compile,[export_all]}, 875 {function,Anno,atom,0,[{clause,Anno,[],[],[{atom,Anno,Atom}]}]}], 876 877 Utf8AtomForms = [{attribute,Anno,module,utf8_atom}|Forms], 878 {ok,utf8_atom,Utf8AtomBin} = 879 compile:forms(Utf8AtomForms, [binary]), 880 {ok,{utf8_atom,[{atoms,_}]}} = 881 beam_lib:chunks(Utf8AtomBin, [atoms]), 882 code:load_binary(utf8_atom, "compile_SUITE", Utf8AtomBin), 883 Atom = utf8_atom:atom(), 884 885 NoUtf8AtomForms = [{attribute,Anno,module,no_utf8_atom}|Forms], 886 error = compile:forms(NoUtf8AtomForms, [binary, r19]). 887 888utf8_functions(Config) when is_list(Config) -> 889 Anno = erl_anno:new(1), 890 Atom = binary_to_atom(<<"こんにちは"/utf8>>, utf8), 891 Forms = [{attribute,Anno,compile,[export_all]}, 892 {function,Anno,Atom,0,[{clause,Anno,[],[],[{atom,Anno,world}]}]}], 893 894 Utf8FunctionForms = [{attribute,Anno,module,utf8_function}|Forms], 895 {ok,utf8_function,Utf8FunctionBin} = 896 compile:forms(Utf8FunctionForms, [binary]), 897 {ok,{utf8_function,[{atoms,_}]}} = 898 beam_lib:chunks(Utf8FunctionBin, [atoms]), 899 code:load_binary(utf8_function, "compile_SUITE", Utf8FunctionBin), 900 world = utf8_function:Atom(), 901 902 NoUtf8FunctionForms = [{attribute,Anno,module,no_utf8_function}|Forms], 903 error = compile:forms(NoUtf8FunctionForms, [binary, r19]). 904 905extra_chunks(Config) when is_list(Config) -> 906 Anno = erl_anno:new(1), 907 Forms = [{attribute,Anno,module,extra_chunks}], 908 909 {ok,extra_chunks,ExtraChunksBinary} = 910 compile:forms(Forms, [binary, {extra_chunks, [{<<"ExCh">>, <<"Contents">>}]}]), 911 {ok,{extra_chunks,[{"ExCh",<<"Contents">>}]}} = 912 beam_lib:chunks(ExtraChunksBinary, ["ExCh"]). 913 914tuple_calls(Config) when is_list(Config) -> 915 Anno = erl_anno:new(1), 916 Forms = [{attribute,Anno,export,[{size,1},{store,1}]}, 917 {function,Anno,size,1, 918 [{clause,Anno,[{var,Anno,mod}],[], 919 [{call,Anno,{remote,Anno,{var,Anno,mod},{atom,Anno,size}}, 920 []}]}]}, 921 {function,Anno,store,1, 922 [{clause,Anno,[{var,Anno,mod}],[], 923 [{call,Anno,{remote,Anno,{var,Anno,mod},{atom,Anno,store}}, 924 [{atom,Anno,key},{atom,Anno,value}]}]}]}], 925 926 TupleCallsFalse = [{attribute,Anno,module,tuple_calls_false}|Forms], 927 {ok,_,TupleCallsFalseBinary} = compile:forms(TupleCallsFalse, [binary]), 928 code:load_binary(tuple_calls_false, "compile_SUITE.erl", TupleCallsFalseBinary), 929 {'EXIT',{badarg,_}} = (catch tuple_calls_false:store(dict())), 930 {'EXIT',{badarg,_}} = (catch tuple_calls_false:size(dict())), 931 {'EXIT',{badarg,_}} = (catch tuple_calls_false:size(empty_tuple())), 932 933 TupleCallsTrue = [{attribute,Anno,module,tuple_calls_true}|Forms], 934 {ok,_,TupleCallsTrueBinary} = compile:forms(TupleCallsTrue, [binary,tuple_calls]), 935 code:load_binary(tuple_calls_true, "compile_SUITE.erl", TupleCallsTrueBinary), 936 Dict = tuple_calls_true:store(dict()), 937 1 = tuple_calls_true:size(Dict), 938 {'EXIT',{badarg,_}} = (catch tuple_calls_true:size(empty_tuple())), 939 940 ok. 941 942dict() -> 943 dict:new(). 944empty_tuple() -> 945 {}. 946 947env(Config) when is_list(Config) -> 948 {Simple,Target} = get_files(Config, simple, env), 949 {ok,Cwd} = file:get_cwd(), 950 ok = file:set_cwd(filename:dirname(Target)), 951 952 true = os:putenv("ERL_COMPILER_OPTIONS", "binary"), 953 try 954 env_1(Simple, Target) 955 after 956 true = os:putenv("ERL_COMPILER_OPTIONS", "ignore_me"), 957 file:set_cwd(Cwd), 958 file:delete(Target), 959 file:del_dir(filename:dirname(Target)) 960 end, 961 ok. 962 963env_1(Simple, Target) -> 964 %% file 965 {ok,simple,<<_/binary>>} = compile:file(Simple), 966 {ok,simple} = compile:noenv_file(Simple, [debug_info]), 967 {ok,simple} = compile:noenv_file(Simple, debug_info), 968 969 true = exists(Target), 970 {ok,{simple,[{abstract_code,Abstr0}]}} = 971 beam_lib:chunks(Target, [abstract_code]), 972 {raw_abstract_v1,Forms} = Abstr0, 973 974 %% forms 975 true = os:putenv("ERL_COMPILER_OPTIONS", "strong_validation"), 976 {ok,simple} = compile:forms(Forms), 977 {ok,simple,<<"FOR1",_/binary>>} = compile:noenv_forms(Forms, []), 978 979 %% output_generated 980 false = compile:output_generated([]), 981 true = compile:noenv_output_generated([]), 982 983 ok = file:delete(Target), 984 985 %% Cover error handling. 986 true = os:putenv("ERL_COMPILER_OPTIONS", "'unterminated_atom"), 987 {ok,[]} = compile:forms(Forms, [basic_validation]), 988 true = os:putenv("ERL_COMPILER_OPTIONS", ",,,"), 989 {ok,[]} = compile:forms(Forms, [basic_validation]), 990 {ok,simple,<<"FOR1",_/binary>>} = compile:noenv_forms(Forms, no_postopt), 991 992 ok. 993 994%% Test pretty-printing in Core Erlang format and then try to 995%% compile the generated Core Erlang files. 996 997core_pp(Config) when is_list(Config) -> 998 PrivDir = proplists:get_value(priv_dir, Config), 999 Outdir = filename:join(PrivDir, atom_to_list(?FUNCTION_NAME)), 1000 ok = file:make_dir(Outdir), 1001 1002 TestBeams = get_unique_beam_files(), 1003 Abstr = [begin {ok,{Mod,[{abstract_code, 1004 {raw_abstract_v1,Abstr}}]}} = 1005 beam_lib:chunks(Beam, [abstract_code]), 1006 {Mod,Abstr} end || Beam <- TestBeams], 1007 test_lib:p_run(fun(F) -> do_core_pp(F, Outdir) end, Abstr). 1008 1009do_core_pp({M,A}, Outdir) -> 1010 try 1011 do_core_pp_1(M, A, Outdir) 1012 catch 1013 throw:{error,Error} -> 1014 io:format("*** compilation failure '~p' for module ~s\n", 1015 [Error,M]), 1016 error; 1017 Class:Error:Stk -> 1018 io:format("~p: ~p ~p\n~p\n", [M,Class,Error,Stk]), 1019 error 1020 end. 1021 1022do_core_pp_1(M, A, Outdir) -> 1023 {ok,M,Core0} = compile:forms(A, [to_core]), 1024 CoreFile = filename:join(Outdir, atom_to_list(M)++".core"), 1025 CorePP = core_pp:format(Core0), 1026 ok = file:write_file(CoreFile, unicode:characters_to_binary(CorePP)), 1027 1028 %% Parse the .core file and return the result as Core Erlang Terms. 1029 Core = case compile:file(CoreFile, [report_errors,from_core,no_copt,to_core,binary]) of 1030 {ok,M,Core1} -> Core1; 1031 Other -> throw({error,Other}) 1032 end, 1033 ok = file:delete(CoreFile), 1034 1035 %% Compile as usual (including optimizations). 1036 compile_forms(M, Core, [clint,ssalint,from_core,binary]), 1037 1038 %% Don't optimize to test that we are not dependent 1039 %% on the Core Erlang optmimization passes. 1040 %% (Example of a previous bug: The core_parse pass 1041 %% would not turn map literals into #c_literal{} 1042 %% records; if sys_core_fold was run it would fix 1043 %% that; if sys_core_fold was not run v3_kernel would 1044 %% crash.) 1045 compile_forms(M, Core, [clint,ssalint,from_core,no_copt,binary]), 1046 1047 ok. 1048 1049compile_forms(Mod, Forms, Opts) -> 1050 case compile:forms(Forms, [report_errors|Opts]) of 1051 {ok,Mod,_} -> ok; 1052 Other -> throw({error,Other}) 1053 end. 1054 1055%% Pretty-print core and read it back. Should be identical. 1056 1057core_roundtrip(Config) -> 1058 PrivDir = proplists:get_value(priv_dir, Config), 1059 Outdir = filename:join(PrivDir, atom_to_list(?FUNCTION_NAME)), 1060 ok = file:make_dir(Outdir), 1061 1062 TestBeams = get_unique_beam_files(), 1063 test_lib:p_run(fun(F) -> do_core_roundtrip(F, Outdir) end, TestBeams). 1064 1065do_core_roundtrip(Beam, Outdir) -> 1066 try 1067 {ok,{Mod,[{abstract_code,{raw_abstract_v1,Abstr}}]}} = 1068 beam_lib:chunks(Beam, [abstract_code]), 1069 do_core_roundtrip_1(Mod, Abstr, Outdir) 1070 catch 1071 throw:{error,Error} -> 1072 io:format("*** compilation failure '~p' for file ~s\n", 1073 [Error,Beam]), 1074 error; 1075 Class:Error:Stk -> 1076 io:format("~p: ~p ~p\n~p\n", [Beam,Class,Error,Stk]), 1077 error 1078 end. 1079 1080do_core_roundtrip_1(Mod, Abstr, Outdir) -> 1081 {ok,Mod,Core0} = compile:forms(Abstr, [to_core0]), 1082 do_core_roundtrip_2(Mod, Core0, Outdir), 1083 1084 %% Primarily, test that annotations are accepted for all 1085 %% constructs. Secondarily, smoke test cerl_trees:label/1. 1086 {Core1,_} = cerl_trees:label(Core0), 1087 do_core_roundtrip_2(Mod, Core1, Outdir), 1088 1089 %% Run the inliner to force generation of variables 1090 %% with numeric names. 1091 {ok,Mod,Core2} = compile:forms(Abstr, [inline,to_core]), 1092 do_core_roundtrip_2(Mod, Core2, Outdir). 1093 1094do_core_roundtrip_2(M, Core0, Outdir) -> 1095 CoreFile = filename:join(Outdir, atom_to_list(M)++".core"), 1096 CorePP = core_pp:format_all(Core0), 1097 ok = file:write_file(CoreFile, unicode:characters_to_binary(CorePP)), 1098 1099 %% Parse the .core file and return the result as Core Erlang Terms. 1100 Core2 = case compile:file(CoreFile, [report_errors,from_core, 1101 no_copt,to_core,binary]) of 1102 {ok,M,Core1} -> Core1; 1103 Other -> throw({error,Other}) 1104 end, 1105 Core = undo_var_translation(Core2), 1106 ok = file:delete(CoreFile), 1107 1108 case cmp_core(Core0, Core, M) of 1109 true -> ok; 1110 false -> error 1111 end. 1112 1113undo_var_translation(Tree) -> 1114 F = fun(Node) -> 1115 case cerl:is_c_var(Node) of 1116 true -> 1117 Name0 = cerl:var_name(Node), 1118 try atom_to_list(Name0) of 1119 "_X"++Name -> 1120 cerl:update_c_var(Node, list_to_atom(Name)); 1121 "_"++Name -> 1122 cerl:update_c_var(Node, list_to_atom(Name)); 1123 _ -> 1124 Node 1125 catch 1126 error:badarg -> 1127 Node 1128 1129 end; 1130 false -> 1131 Node 1132 end 1133 end, 1134 cerl_trees:map(F, Tree). 1135 1136cmp_core(E, E, _Mod) -> 1137 true; 1138cmp_core(M1, M2, Mod) -> 1139 cmp_core_fs(cerl:module_defs(M1), cerl:module_defs(M2), Mod). 1140 1141cmp_core_fs([F1|T1], [F2|T2], Mod) -> 1142 cmp_core_f(F1, F2, Mod) andalso cmp_core_fs(T1, T2, Mod); 1143cmp_core_fs([], [], _Mod) -> 1144 true; 1145cmp_core_fs(_, _, _Mod) -> 1146 false. 1147 1148cmp_core_f(E, E, _Mod) -> 1149 true; 1150cmp_core_f({Name,F1}, {Name,F2}, Mod) -> 1151 case diff(F1, F2) of 1152 F1 -> 1153 true; 1154 Diff -> 1155 io:format("~p ~p:\n~p\n", [Mod,Name,Diff]), 1156 false 1157 end. 1158 1159diff(E, E) -> 1160 E; 1161diff([H1|T1], [H2|T2]) -> 1162 [diff(H1, H2)|diff(T1, T2)]; 1163diff(T1, T2) when tuple_size(T1) =:= tuple_size(T2) -> 1164 case cerl:is_c_var(T1) andalso cerl:is_c_var(T2) of 1165 true -> 1166 diff_var(T1, T2); 1167 false -> 1168 case cerl:is_c_map(T1) andalso cerl:is_c_map(T2) of 1169 true -> 1170 diff_map(T1, T2); 1171 false -> 1172 diff_tuple(T1, T2) 1173 end 1174 end; 1175diff(E1, E2) -> 1176 {'DIFF',E1,E2}. 1177 1178diff_var(V1, V2) -> 1179 case {cerl:var_name(V1),cerl:var_name(V2)} of 1180 {Same,Same} -> 1181 V1; 1182 {Name1,Name2} -> 1183 %% The inliner uses integers as variable names. Such integers 1184 %% are read back as atoms. 1185 case is_integer(Name1) andalso 1186 list_to_atom(integer_to_list(Name1)) =:= Name2 of 1187 true -> 1188 V1; 1189 _ -> 1190 cerl:update_c_var(V1, {'DIFF',Name1,Name2}) 1191 end 1192 end. 1193 1194%% Annotations for maps are not preserved exactly, but that is not 1195%% a real problem. Workaround by not comparing all annotations when 1196%% comparing maps. 1197 1198diff_map(M, M) -> 1199 M; 1200diff_map(M1, M2) -> 1201 case cerl:get_ann(M1) =:= cerl:get_ann(M2) of 1202 false -> 1203 diff_tuple(M1, M2); 1204 true -> 1205 case remove_compiler_gen(M1) =:= remove_compiler_gen(M2) of 1206 true -> 1207 M1; 1208 false -> 1209 diff_tuple(M1, M2) 1210 end 1211 end. 1212 1213diff_tuple(T1, T2) -> 1214 L = diff(tuple_to_list(T1), tuple_to_list(T2)), 1215 list_to_tuple(L). 1216 1217remove_compiler_gen(M) -> 1218 Arg0 = cerl:map_arg(M), 1219 Arg = cerl:set_ann(Arg0, []), 1220 Es0 = cerl:map_es(M), 1221 Es = [remove_compiler_gen_1(Pair) || Pair <- Es0], 1222 cerl:update_c_map(M, Arg, Es). 1223 1224remove_compiler_gen_1(Pair) -> 1225 Op0 = cerl:map_pair_op(Pair), 1226 Op = cerl:set_ann(Op0, []), 1227 K = map_var(cerl:map_pair_key(Pair)), 1228 V = map_var(cerl:map_pair_val(Pair)), 1229 cerl:update_c_map_pair(Pair, Op, K, V). 1230 1231map_var(Var) -> 1232 case cerl:is_c_var(Var) of 1233 true -> 1234 case cerl:var_name(Var) of 1235 Name when is_atom(Name) -> 1236 L = atom_to_list(Name), 1237 try list_to_integer(L) of 1238 Int -> 1239 cerl:update_c_var(Var, Int) 1240 catch 1241 error:_ -> 1242 Var 1243 end; 1244 _ -> 1245 Var 1246 end; 1247 false -> 1248 Var 1249 end. 1250 1251%% Compile to Beam assembly language (.S) and then try to 1252%% run .S through the compiler again. 1253 1254asm(Config) when is_list(Config) -> 1255 PrivDir = proplists:get_value(priv_dir, Config), 1256 Outdir = filename:join(PrivDir, "asm"), 1257 ok = file:make_dir(Outdir), 1258 1259 TestBeams = get_unique_beam_files(), 1260 Res = test_lib:p_run(fun(F) -> do_asm(F, Outdir) end, TestBeams), 1261 Res. 1262 1263 1264do_asm(Beam, Outdir) -> 1265 {ok,{M,[{abstract_code,{raw_abstract_v1,A}}]}} = 1266 beam_lib:chunks(Beam, [abstract_code]), 1267 try 1268 {ok,M,Asm} = compile:forms(A, ['S']), 1269 AsmFile = filename:join(Outdir, atom_to_list(M)++".S"), 1270 {ok,Fd} = file:open(AsmFile, [write,{encoding,utf8}]), 1271 beam_listing:module(Fd, Asm), 1272 ok = file:close(Fd), 1273 case compile:file(AsmFile, [from_asm,binary,report]) of 1274 {ok,M,_} -> 1275 ok = file:delete(AsmFile); 1276 Other -> 1277 io:format("*** failure '~p' for ~s\n", 1278 [Other,AsmFile]), 1279 error 1280 end 1281 catch Class:Error:Stk -> 1282 io:format("~p: ~p ~p\n~p\n", [M,Class,Error,Stk]), 1283 error 1284 end. 1285 1286sys_pre_attributes(Config) -> 1287 DataDir = proplists:get_value(data_dir, Config), 1288 File = filename:join(DataDir, "attributes.erl"), 1289 Mod = attributes, 1290 CommonOpts = [binary,report,verbose, 1291 {parse_transform,sys_pre_attributes}], 1292 PreOpts = [{attribute,delete,deleted}], 1293 PostOpts = [{attribute,insert,inserted,"value"}], 1294 PrePostOpts = [{attribute,replace,replaced,42}, 1295 {attribute,replace,replace_nonexisting,new}], 1296 {ok,Mod,Code} = compile:file(File, PrePostOpts ++ PreOpts ++ 1297 PostOpts ++ CommonOpts), 1298 code:load_binary(Mod, File, Code), 1299 Attr = Mod:module_info(attributes), 1300 io:format("~p", [Attr]), 1301 {inserted,"value"} = lists:keyfind(inserted, 1, Attr), 1302 {replaced,[42]} = lists:keyfind(replaced, 1, Attr), 1303 {replace_nonexisting,[new]} = lists:keyfind(replace_nonexisting, 1, Attr), 1304 false = lists:keymember(deleted, 1, Attr), 1305 1306 %% Cover more code. 1307 {ok,Mod,_} = compile:file(File, PostOpts ++ CommonOpts), 1308 {ok,Mod,_} = compile:file(File, CommonOpts -- [verbose]), 1309 {ok,Mod,_} = compile:file(File, PreOpts ++ CommonOpts), 1310 {ok,Mod,_} = compile:file(File, 1311 [{attribute,replace,replaced,42}|CommonOpts]), 1312 {ok,Mod,_} = compile:file(File, PrePostOpts ++ PreOpts ++ 1313 PostOpts ++ CommonOpts -- 1314 [report,verbose]), 1315 ok. 1316 1317%% Test the dialyzer option to cover more code. 1318dialyzer(Config) -> 1319 Priv = proplists:get_value(priv_dir, Config), 1320 ok = file:set_cwd(proplists:get_value(data_dir, Config)), 1321 Opts = [{outdir,Priv},report_errors], 1322 M = dialyzer_test, 1323 {ok,M} = c:c(M, [dialyzer|Opts]), 1324 [{a,b,c}] = M:M(), 1325 1326 %% Cover huge line numbers without the 'dialyzer' option. 1327 {ok,M} = c:c(M, Opts), 1328 [{a,b,c}] = M:M(), 1329 ok. 1330 1331 1332%% Test that warnings contain filenames and line numbers. 1333warnings(_Config) -> 1334 Files = get_unique_files(".erl"), 1335 test_lib:p_run(fun do_warnings/1, Files). 1336 1337do_warnings(F) -> 1338 {ok,_,_,Ws} = compile:file(F, [binary,bin_opt_info,recv_opt_info,return]), 1339 do_warnings_1(Ws, F). 1340 1341do_warnings_1([{"no_file",Ws}|_], F) -> 1342 io:format("~s:\nMissing file for warnings: ~p\n", 1343 [F,Ws]), 1344 error; 1345do_warnings_1([{Name,Ws}|T], F) -> 1346 case filename:extension(Name) of 1347 ".erl" -> 1348 do_warnings_2(Ws, T, F); 1349 _ -> 1350 io:format("~s:\nNo .erl extension\n", [F]), 1351 error 1352 end; 1353do_warnings_1([], _) -> ok. 1354 1355do_warnings_2([{Pos,_,_}=W|T], Next, F) -> 1356 case Pos of 1357 Line when is_integer(Line) -> 1358 do_warnings_2(T, Next, F); 1359 {Line,Col} when is_integer(Line), is_integer(Col) -> 1360 do_warnings_2(T, Next, F); 1361 true -> 1362 io:format("~s:\nMissing line number: ~p\n", 1363 [F,W]), 1364 error 1365 end; 1366do_warnings_2([], Next, F) -> 1367 do_warnings_1(Next, F). 1368 1369message_printing(Config) -> 1370 DataDir = proplists:get_value(data_dir, Config), 1371 BadEncFile = filename:join(DataDir, "bad_enc.erl"), 1372 1373 {error,BadEncErrors, []} = compile:file(BadEncFile, [return]), 1374 1375 [":7:15: cannot parse file, giving up\n" 1376 "% 7| \t {ok, \"xyz\n" 1377 "% | \t ^\n\n" 1378 , 1379 ":7:15: cannot translate from UTF-8\n" 1380 "% 7| \t {ok, \"xyz\n" 1381 "% | \t ^\n\n" 1382 ] = messages(BadEncErrors), 1383 1384 UTF8File = filename:join(DataDir, "col_utf8.erl"), 1385 {ok,_,UTF8Errors} = compile:file(UTF8File, [return]), 1386 [":5:23: a term is constructed, but never used\n" 1387 "% 5| B = <<\"xyzåäö\">>,\t<<\"12345\">>,\n" 1388 "% | \t^\n\n" 1389 ] = messages(UTF8Errors), 1390 1391 Latin1File = filename:join(DataDir, "col_lat1.erl"), 1392 {ok,_,Latin1Errors} = compile:file(Latin1File, [return]), 1393 [":6:23: a term is constructed, but never used\n" 1394 "% 6| B = <<\"xyzåäö\">>,\t<<\"12345\">>,\n" 1395 "% | \t^\n\n" 1396 ] = messages(Latin1Errors), 1397 1398 {ok,OldCwd} = file:get_cwd(), 1399 try 1400 ok = file:set_cwd(DataDir), 1401 {ok,cover_messages,_} = compile:file(cover_messages, [report, binary]) 1402 after 1403 file:set_cwd(OldCwd) 1404 end, 1405 1406 ok. 1407 1408messages(Errors) -> 1409 lists:flatmap(fun ({{File,_L},Descs}) -> format_descs(File, Descs); 1410 ({File,Descs}) -> format_descs(File, Descs) 1411 end, 1412 Errors). 1413 1414format_descs(File, Descs) -> 1415 [strip_prefix(File, lists:flatten(Text)) 1416 || {_Where, Text} <- sys_messages:format_messages(File, "", Descs, [])]. 1417 1418strip_prefix(Prefix, String) -> 1419 case string:prefix(String, Prefix) of 1420 nomatch -> String; 1421 Rest -> Rest 1422 end. 1423 1424%% Test that the compile:pre_load/0 function (used by 'erlc') 1425%% pre-loads the modules that are used by a typical compilation. 1426 1427pre_load_check(Config) -> 1428 case test_server:is_cover() of 1429 true -> 1430 {skip,"Cover is running"}; 1431 false -> 1432 try 1433 do_pre_load_check(Config) 1434 after 1435 dbg:stop_clear() 1436 end 1437 end. 1438 1439do_pre_load_check(Config) -> 1440 DataDir = ?config(data_dir, Config), 1441 Simple = filename:join(DataDir, "simple.erl"), 1442 Big = filename:join(DataDir, "big.erl"), 1443 {ok,_} = dbg:tracer(process, {fun pre_load_trace/2,[]}), 1444 dbg:p(self(), call), 1445 dbg:p(new, call), 1446 {ok,_} = dbg:tpl({?MODULE,get_trace_data,0}, []), 1447 {ok,_} = dbg:tp({code,ensure_modules_loaded,1}, []), 1448 1449 %% Compile a simple module using the erl_compile interface 1450 %% to find out the modules that are pre-loaded by 1451 %% compile:pre_load/0. 1452 Opts = #options{specific=[binary]}, 1453 {ok,simple,_} = compile:compile(Simple, "", Opts), 1454 [{code,ensure_modules_loaded,[PreLoaded0]}] = get_trace_data(), 1455 PreLoaded1 = ordsets:from_list(PreLoaded0), 1456 1457 %% Since 'compile' is the function doing the pre-loaded, 1458 %% it is useless to include it in the list. 1459 case ordsets:is_element(compile, PreLoaded1) of 1460 true -> 1461 io:put_chars("The 'compile' module should not be included " 1462 "in the list of modules to be pre-loaded."), 1463 ct:fail(compile); 1464 false -> 1465 [] 1466 end, 1467 PreLoaded = ordsets:add_element(compile, PreLoaded1), 1468 1469 %% Now unload all pre-loaded modules and all modules in 1470 %% compiler application. Then compile a module to find 1471 %% which modules that get loaded. 1472 CompilerMods = compiler_modules(), 1473 Unload = ordsets:union(ordsets:from_list(CompilerMods), PreLoaded), 1474 _ = [begin 1475 code:delete(M), 1476 code:purge(M) 1477 end || M <- Unload], 1478 1479 {ok,_} = dbg:ctp({code,ensure_modules_loaded,1}), 1480 {ok,_} = dbg:tp({code,ensure_loaded,1}, []), 1481 {ok,big,_} = compile:file(Big, [binary]), 1482 WasLoaded0 = get_trace_data(), 1483 WasLoaded1 = [M || {code,ensure_loaded,[M]} <- WasLoaded0], 1484 WasLoaded = ordsets:from_list(WasLoaded1), 1485 1486 %% Check for modules that should have been pre-loaded. 1487 case ordsets:subtract(WasLoaded, PreLoaded) of 1488 [] -> 1489 ok; 1490 [_|_]=NotPreLoaded -> 1491 io:format("The following modules were used " 1492 "but not pre-loaded:\n~p\n", 1493 [NotPreLoaded]), 1494 ct:fail({not_preload,NotPreLoaded}) 1495 end, 1496 1497 %% Check for modules that should not be pre-loaded. 1498 case ordsets:subtract(PreLoaded, WasLoaded) of 1499 [] -> 1500 ok; 1501 [_|_]=NotUsed -> 1502 io:format("The following modules were pre-loaded" 1503 " but not used:\n~p\n", 1504 [NotUsed]), 1505 ct:fail({not_used,NotUsed}) 1506 end, 1507 1508 ok. 1509 1510get_trace_data() -> 1511 %% Apparantely, doing a receive at the beginning of 1512 %% a traced function can cause extra trace messages. 1513 %% To avoid that, don't do the receive in this function. 1514 do_get_trace_data(). 1515 1516do_get_trace_data() -> 1517 receive 1518 {trace_data,Data} -> Data 1519 end. 1520 1521pre_load_trace({trace,Pid,call,{?MODULE,get_trace_data,[]}}, Acc) -> 1522 Pid ! {trace_data,Acc}, 1523 []; 1524pre_load_trace({trace,_,call,MFA}, Acc) -> 1525 [MFA|Acc]. 1526 1527compiler_modules() -> 1528 Wc = filename:join([code:lib_dir(compiler),"ebin","*.beam"]), 1529 Ms = filelib:wildcard(Wc), 1530 FN = filename, 1531 [list_to_atom(FN:rootname(FN:basename(M), ".beam")) || M <- Ms]. 1532 1533%% Test that ERL_COMPILER_OPTIONS are correctly retrieved 1534%% by env_compiler_options/0 1535 1536env_compiler_options(_Config) -> 1537 Cases = [ 1538 {"bin_opt_info", [bin_opt_info]}, 1539 {"recv_opt_info", [recv_opt_info]}, 1540 {"'S'", ['S']}, 1541 {"{source, \"test.erl\"}", [{source, "test.erl"}]}, 1542 {"[{d,macro_one,1},{d,macro_two}]", [{d, macro_one, 1}, {d, macro_two}]}, 1543 {"[warn_export_all, warn_export_vars]", [warn_export_all, warn_export_vars]} 1544 ], 1545 F = fun({Env, Expected}) -> 1546 true = os:putenv("ERL_COMPILER_OPTIONS", Env), 1547 Expected = compile:env_compiler_options() 1548 end, 1549 lists:foreach(F, Cases). 1550 1551%% Test options for compatibility with previous major versions of OTP. 1552 1553bc_options(Config) -> 1554 DataDir = proplists:get_value(data_dir, Config), 1555 1556 L = [{101, small_float, [no_shared_fun_wrappers, 1557 no_get_hd_tl,no_line_info]}, 1558 {125, small_float, [no_shared_fun_wrappers,no_get_hd_tl, 1559 no_line_info, 1560 no_ssa_opt_float]}, 1561 1562 {132, small, [no_init_yregs,no_shared_fun_wrappers, 1563 no_put_tuple2,no_get_hd_tl,no_ssa_opt_record, 1564 no_ssa_opt_float,no_line_info,no_bsm3]}, 1565 1566 {153, small, [r20]}, 1567 {153, small, [r21]}, 1568 1569 {153, big, [r18]}, 1570 {153, big, [r19]}, 1571 {153, small_float, [no_shared_fun_wrappers]}, 1572 1573 {158, small_maps, [r18]}, 1574 {158, small_maps, [r19]}, 1575 {158, small_maps, [r20]}, 1576 {158, small_maps, [r21]}, 1577 1578 {164, small_maps, [no_init_yregs,no_shared_fun_wrappers]}, 1579 {164, small_maps, [r22]}, 1580 {164, big, [r22]}, 1581 1582 {168, small, [r22]}, 1583 1584 {169, big, [no_init_yregs,no_shared_fun_wrappers, 1585 no_put_tuple2,no_get_hd_tl,no_ssa_opt_record, 1586 no_line_info,no_stack_trimming, 1587 no_make_fun3]}, 1588 {169, big, [r23]}, 1589 1590 {169, small_maps, [no_init_yregs]}, 1591 1592 {170, small, [no_shared_fun_wrappers,no_init_yregs]}, 1593 1594 {171, big, [no_init_yregs]}, 1595 {172, big, []} 1596 ], 1597 1598 Test = fun({Expected,Mod,Options}) -> 1599 case highest_opcode(DataDir, Mod, Options) of 1600 Expected -> 1601 ok; 1602 Got -> 1603 io:format("*** module ~p, options ~p => got ~p; expected ~p\n", 1604 [Mod,Options,Got,Expected]), 1605 error 1606 end 1607 end, 1608 test_lib:p_run(Test, L), 1609 ok. 1610 1611highest_opcode(DataDir, Mod, Opt) -> 1612 Src = filename:join(DataDir, atom_to_list(Mod)++".erl"), 1613 {ok,Mod,Beam} = compile:file(Src, [binary|Opt]), 1614 test_lib:highest_opcode(Beam). 1615 1616deterministic_include(Config) when is_list(Config) -> 1617 DataDir = proplists:get_value(data_dir, Config), 1618 Simple = filename:join(DataDir, "simple"), 1619 1620 %% Files without +deterministic should differ if their include paths do, 1621 %% as their debug info will be different. 1622 {ok,_,NonDetA} = compile:file(Simple, [binary, {i,"gurka"}]), 1623 {ok,_,NonDetB} = compile:file(Simple, [binary, {i,"gaffel"}]), 1624 true = NonDetA =/= NonDetB, 1625 1626 %% ... but files with +deterministic shouldn't. 1627 {ok,_,DetC} = compile:file(Simple, [binary, deterministic, {i,"gurka"}]), 1628 {ok,_,DetD} = compile:file(Simple, [binary, deterministic, {i,"gaffel"}]), 1629 true = DetC =:= DetD, 1630 1631 ok. 1632 1633deterministic_paths(Config) when is_list(Config) -> 1634 DataDir = proplists:get_value(data_dir, Config), 1635 1636 %% Files without +deterministic should differ if they were compiled from a 1637 %% different directory. 1638 true = deterministic_paths_1(DataDir, "simple", []), 1639 1640 %% ... but files with +deterministic shouldn't. 1641 false = deterministic_paths_1(DataDir, "simple", [deterministic]), 1642 1643 ok. 1644 1645deterministic_paths_1(DataDir, Name, Opts) -> 1646 Simple = filename:join(DataDir, "simple"), 1647 {ok, Cwd} = file:get_cwd(), 1648 try 1649 {ok,_,A} = compile:file(Simple, [binary | Opts]), 1650 ok = file:set_cwd(DataDir), 1651 {ok,_,B} = compile:file(Name, [binary | Opts]), 1652 A =/= B 1653 after 1654 file:set_cwd(Cwd) 1655 end. 1656 1657%% ERL-1058: -compile(debug_info) had no effect 1658compile_attribute(Config) when is_list(Config) -> 1659 DataDir = proplists:get_value(data_dir, Config), 1660 1661 %% The test module has a -compile([debug_info]). attribute, which means 1662 %% debug information should always be included. 1663 debug_info_attribute(DataDir, "debug_info", [debug_info]), 1664 debug_info_attribute(DataDir, "debug_info", []), 1665 1666 ok. 1667 1668debug_info_attribute(DataDir, Name, Opts) -> 1669 File = filename:join(DataDir, Name), 1670 {ok,_,Bin} = compile:file(File, [binary | Opts]), 1671 {ok, {_, Attrs}} = beam_lib:chunks(Bin, [debug_info]), 1672 1673 [{debug_info,{debug_info_v1,erl_abstract_code, {Forms, _}}}] = Attrs, 1674 [{attribute,{1,1},file,{_,1}}, 1675 {attribute,{1,2},module,debug_info}, 1676 {attribute,{2,2},compile,[debug_info]}, 1677 {eof,_}] = forms_to_terms(Forms), 1678 1679 ok. 1680 1681forms_to_terms(Forms) -> 1682 [erl_parse:anno_to_term(Form) || Form <- Forms]. 1683 1684%% Test compiler options not tested in other test cases. 1685other_options(Config) -> 1686 DataDir = proplists:get_value(data_dir, Config), 1687 1688 %% Smoke test of no_spawn_compiler_process and brief options. 1689 Big = filename:join(DataDir, "big"), 1690 {ok,big,<<_/binary>>} = 1691 compile:file(Big, [binary, no_spawn_compiler_process, brief, report]), 1692 1693 %% Test generating a compressed BEAM file. Also cover the redundant 1694 %% `beam` option and the `no_inline` option. 1695 Small = filename:join(DataDir, "small"), 1696 {ok,small} = compile:file(Small, [report, no_inline, compressed, beam]), 1697 1698 ok. 1699 1700%% Test core transforms and parse transforms. 1701transforms(Config) -> 1702 {ok, Cwd} = file:get_cwd(), 1703 try 1704 do_transforms(Config) 1705 after 1706 file:set_cwd(Cwd) 1707 end. 1708 1709do_transforms(Config) -> 1710 DataDir = proplists:get_value(data_dir, Config), 1711 PrivDir = proplists:get_value(priv_dir, Config), 1712 TargetDir = filename:join(PrivDir, ?FUNCTION_NAME), 1713 ok = file:make_dir(TargetDir), 1714 ok = file:set_cwd(TargetDir), 1715 1716 %% Compile our parse transforms. 1717 LinePt = filename:join(DataDir, "line_pt"), 1718 {ok,line_pt} = compile:file(LinePt, [report, {outdir,TargetDir}]), 1719 ColumnPt = filename:join(DataDir, "column_pt"), 1720 {ok,column_pt} = compile:file(ColumnPt, [report, {outdir,TargetDir}]), 1721 GenericPt = filename:join(DataDir, "generic_pt"), 1722 {ok,generic_pt} = compile:file(GenericPt, [report, {outdir,TargetDir}]), 1723 1724 %% Compile a file using line_pt and verify that column numbers 1725 %% have been stripped. 1726 Big = filename:join(DataDir, "big"), 1727 {[],[_|_]} = compile_partition_warnings(Big, [{parse_transform,line_pt}]), 1728 1729 %% Compile a file using column_pt and verify that column numbers 1730 %% have NOT been stripped. 1731 {[_|_],[]} = compile_partition_warnings(Big, [{parse_transform,column_pt}]), 1732 1733 %% Compile a file using column_pt and error_location=line and verify 1734 %% that column numbers have been stripped. 1735 {[],[_|_]} = compile_partition_warnings(Big, [{error_location,line}, 1736 {parse_transform,column_pt}]), 1737 1738 %% Compile a file using column_pt, line_pt and verify 1739 %% that column numbers have been stripped. 1740 {[],[_|_]} = compile_partition_warnings(Big, [{parse_transform,column_pt}, 1741 {parse_transform,line_pt}]), 1742 1743 %% Compile a file using column_pt that adds columns and error_location=line and 1744 %% verify that column numbers have been stripped. 1745 {[],[_|_]} = compile_partition_warnings(Big, [{error_location,line}, 1746 add_columns, 1747 {parse_transform,column_pt}]), 1748 1749 %% Compile a file using column_pt that adds columns and error_location=line and 1750 %% then call column_pt again to check that columns are stripped in between calls. 1751 %% and then verify that column numbers have been stripped from output. 1752 {[],[_|_]} = compile_partition_warnings(Big, [{error_location,line}, 1753 add_columns, 1754 {parse_transform,column_pt}, 1755 {parse_transform,column_pt}]), 1756 1757 %% Compile a file using column_pt that adds columns and en error and error_location=line and 1758 %% verify that column numbers have been stripped. 1759 {error,[{_What,[{Line,_,_}]}],[_|_]} = 1760 compile_partition_warnings(Big, [{error_location,line}, 1761 add_columns, 1762 add_error, 1763 {parse_transform,column_pt}]), 1764 true = is_integer(Line), 1765 1766 %% Cover transform code implementing the `time` option. 1767 {ok,big,_} = compile:file(Big, [binary, time, report, 1768 {core_transform,generic_pt}, 1769 {parse_transform,generic_pt}]), 1770 1771 %% Test exceptions from a core transform. 1772 Simple = filename:join(DataDir, simple), 1773 error = compile:file(Simple, [report, {core_transform,generic_pt}, {action, crash}]), 1774 {error,_,[]} = compile:file(Simple, [return, {core_transform,generic_pt}, {action, crash}]), 1775 1776 error = compile:file(Simple, [report, {core_transform,generic_pt}, {action, throw}]), 1777 {error,_,[]} = compile:file(Simple, [return, {core_transform,generic_pt}, {action, throw}]), 1778 1779 error = compile:file(Simple, [report, {core_transform,generic_pt}, {action, exit}]), 1780 {error,_,[]} = compile:file(Simple, [return, {core_transform,generic_pt}, {action, exit}]), 1781 1782 %% Test exceptions from a parse transform. 1783 error = compile:file(Simple, [report, {parse_transform,generic_pt}, {action, crash}]), 1784 {error,_,[]} = compile:file(Simple, [return, {parse_transform,generic_pt}, {action, crash}]), 1785 1786 error = compile:file(Simple, [report, {parse_transform,generic_pt}, {action, throw}]), 1787 {error,_,[]} = compile:file(Simple, [return, {parse_transform,generic_pt}, {action, throw}]), 1788 1789 error = compile:file(Simple, [report, {parse_transform,generic_pt}, {action, exit}]), 1790 {error,_,[]} = compile:file(Simple, [return, {parse_transform,generic_pt}, {action, exit}]), 1791 1792 %% Test generating errors and warnings in a parse_transform. 1793 {ok,simple} = compile:file(Simple, [report, {parse_transform,generic_pt}, {action, warning}]), 1794 {ok,simple,[_|_]} = compile:file(Simple, [return, {parse_transform,generic_pt}, {action, warning}]), 1795 error = compile:file(Simple, [report, {parse_transform,generic_pt}, {action, error}]), 1796 {error,[_|_],[_|_]} = compile:file(Simple, [return, {parse_transform,generic_pt}, {action, error}]), 1797 error = compile:file(Simple, [report, {parse_transform,generic_pt}, {action, undefined_error}]), 1798 {error,[_|_],[]} = compile:file(Simple, [return, {parse_transform,generic_pt}, {action, undefined_error}]), 1799 1800 ok. 1801 1802compile_partition_warnings(Source, Opts) -> 1803 case compile:file(Source, [binary, return | Opts]) of 1804 {ok,big,<<_/binary>>,Ws0} -> 1805 [{_SourcePath,Ws}] = Ws0, 1806 1807 %% Return {[ColumnWarning], [LineWarning]}. 1808 lists:partition(fun({{L,C},_,_}) when is_integer(L), is_integer(C) -> true; 1809 ({L,_,_}) when is_integer(L) -> false 1810 end, Ws); 1811 Error -> 1812 Error 1813 end. 1814 1815%% Cover the erl_compile API used by erlc. 1816erl_compile_api(Config) -> 1817 DataDir = proplists:get_value(data_dir, Config), 1818 PrivDir = proplists:get_value(priv_dir, Config), 1819 Simple = filename:join(DataDir, "simple.erl"), 1820 1821 Opts = #options{outdir=PrivDir}, 1822 BinOpts = Opts#options{specific=[binary]}, 1823 1824 ok = compile:compile(Simple, "ignored", Opts), 1825 ok = compile:compile(Simple, "ignored", Opts#options{cwd=PrivDir,includes=[PrivDir]}), 1826 {ok,simple,_} = compile:compile(Simple, "ignored", BinOpts), 1827 1828 ok = compile:compile(Simple, "ignored", Opts#options{specific=[dabstr]}), 1829 ok = compile:compile(Simple, "ignored", Opts#options{specific=[to_core]}), 1830 ok = compile:compile(Simple, "ignored", Opts#options{specific=[to_asm]}), 1831 1832 SimpleAbstr = filename:join(PrivDir, "simple.abstr"), 1833 SimpleCore = filename:join(PrivDir, "simple.core"), 1834 SimpleAsm = filename:join(PrivDir, "simple.S"), 1835 1836 ok = compile:compile_abstr(SimpleAbstr, "ignored", Opts), 1837 ok = compile:compile_core(SimpleCore, "ignored", Opts), 1838 ok = compile:compile_asm(SimpleAsm, "ignored", Opts), 1839 1840 {ok,simple,<<_/binary>>} = compile:compile_abstr(SimpleAbstr, "ignored", BinOpts), 1841 {ok,simple,<<_/binary>>} = compile:compile_core(SimpleCore, "ignored", BinOpts), 1842 {ok,simple,<<_/binary>>} = compile:compile_asm(SimpleAsm, "ignored", BinOpts), 1843 1844 NeedsDefines = filename:join(DataDir, "needs_defines.erl"), 1845 ok = compile:compile(NeedsDefines, "ignored", Opts#options{defines=[compile_this,{'TEST_RESULT',whatever}]}), 1846 1847 ok = file:delete(SimpleAbstr), 1848 ok = file:delete(SimpleCore), 1849 ok = file:delete(SimpleAsm), 1850 ok = file:delete(filename:join(PrivDir, "simple.beam")), 1851 ok = file:delete(filename:join(PrivDir, "needs_defines.beam")), 1852 1853 ok. 1854 1855%%% 1856%%% Utilities. 1857%%% 1858 1859compile_and_verify(Name, Target, Opts) -> 1860 Mod = list_to_atom(filename:basename(Name, ".erl")), 1861 {ok,Mod} = compile:file(Name, Opts), 1862 {ok,{Mod,[{compile_info,CInfo}]}} = 1863 beam_lib:chunks(Target, [compile_info]), 1864 {options,BeamOpts} = lists:keyfind(options, 1, CInfo), 1865 Opts = BeamOpts. 1866 1867get_unique_beam_files() -> 1868 get_unique_files(".beam"). 1869 1870get_unique_files(Ext) -> 1871 Wc = filename:join(filename:dirname(code:which(?MODULE)), "*"++Ext), 1872 [F || F <- filelib:wildcard(Wc), 1873 not is_cloned(F, Ext), not is_lfe_module(F, Ext)]. 1874 1875is_cloned(File, Ext) -> 1876 Mod = list_to_atom(filename:basename(File, Ext)), 1877 test_lib:is_cloned_mod(Mod). 1878 1879is_lfe_module(File, Ext) -> 1880 case filename:basename(File, Ext) of 1881 "lfe_" ++ _ -> true; 1882 _ -> false 1883 end. 1884