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