1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1996-2017. 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%% Purpose : Basic make facility 21 22%% Compares date stamps of .erl and Object files - recompiles when 23%% necessary. 24%% Files to be checked are contained in a file 'Emakefile' 25%% If Emakefile is missing the current directory is used. 26-module(make). 27 28-export([all_or_nothing/0,all/0,all/1,files/1,files/2]). 29 30-include_lib("kernel/include/file.hrl"). 31 32-define(MakeOpts,[noexec,load,netload,noload,emake]). 33 34all_or_nothing() -> 35 case all() of 36 up_to_date -> 37 up_to_date; 38 error -> 39 halt(1) 40 end. 41 42all() -> 43 all([]). 44 45all(Options) -> 46 run_emake(undefined, Options). 47 48files(Fs) -> 49 files(Fs, []). 50 51files(Fs0, Options) -> 52 Fs = [filename:rootname(F,".erl") || F <- Fs0], 53 run_emake(Fs, Options). 54 55run_emake(Mods, Options) -> 56 {MakeOpts,CompileOpts} = sort_options(Options,[],[]), 57 Emake = get_emake(Options), 58 case normalize_emake(Emake, Mods, CompileOpts) of 59 Files when is_list(Files) -> 60 do_make_files(Files,MakeOpts); 61 error -> 62 error 63 end. 64 65do_make_files(Fs, Opts) -> 66 process(Fs, lists:member(noexec, Opts), load_opt(Opts)). 67 68sort_options([{emake, _}=H|T],Make,Comp) -> 69 sort_options(T,[H|Make],Comp); 70 71sort_options([H|T],Make,Comp) -> 72 case lists:member(H,?MakeOpts) of 73 true -> 74 sort_options(T,[H|Make],Comp); 75 false -> 76 sort_options(T,Make,[H|Comp]) 77 end; 78sort_options([],Make,Comp) -> 79 {Make,lists:reverse(Comp)}. 80 81%%% Reads the given Emakefile and returns a list of tuples: {Mods,Opts} 82%%% Mods is a list of module names (strings) 83%%% Opts is a list of options to be used when compiling Mods 84%%% 85%%% Emakefile can contain elements like this: 86%%% Mod. 87%%% {Mod,Opts}. 88%%% Mod is a module name which might include '*' as wildcard 89%%% or a list of such module names 90%%% 91%%% These elements are converted to [{ModList,OptList},...] 92%%% ModList is a list of modulenames (strings) 93 94normalize_emake(EmakeRaw, Mods, Opts) -> 95 case EmakeRaw of 96 {ok, Emake} when Mods =:= undefined -> 97 transform(Emake,Opts,[],[]); 98 {ok, Emake} when is_list(Mods) -> 99 ModsOpts = transform(Emake,Opts,[],[]), 100 ModStrings = [coerce_2_list(M) || M <- Mods], 101 get_opts_from_emakefile(ModsOpts,ModStrings,Opts,[]); 102 {error,enoent} when Mods =:= undefined -> 103 %% No Emakefile found - return all modules in current 104 %% directory and the options given at command line 105 CwdMods = [filename:rootname(F) || F <- filelib:wildcard("*.erl")], 106 [{CwdMods, Opts}]; 107 {error,enoent} when is_list(Mods) -> 108 [{Mods, Opts}]; 109 {error, Error} -> 110 io:format("make: Trouble reading 'Emakefile':~n~tp~n",[Error]), 111 error 112 end. 113 114get_emake(Opts) -> 115 case proplists:get_value(emake, Opts, false) of 116 false -> 117 file:consult('Emakefile'); 118 OptsEmake -> 119 {ok, OptsEmake} 120 end. 121 122transform([{Mod,ModOpts}|Emake],Opts,Files,Already) -> 123 case expand(Mod,Already) of 124 [] -> 125 transform(Emake,Opts,Files,Already); 126 Mods -> 127 transform(Emake,Opts,[{Mods,ModOpts++Opts}|Files],Mods++Already) 128 end; 129transform([Mod|Emake],Opts,Files,Already) -> 130 case expand(Mod,Already) of 131 [] -> 132 transform(Emake,Opts,Files,Already); 133 Mods -> 134 transform(Emake,Opts,[{Mods,Opts}|Files],Mods++Already) 135 end; 136transform([],_Opts,Files,_Already) -> 137 lists:reverse(Files). 138 139expand(Mod,Already) when is_atom(Mod) -> 140 expand(atom_to_list(Mod),Already); 141expand(Mods,Already) when is_list(Mods), not is_integer(hd(Mods)) -> 142 lists:concat([expand(Mod,Already) || Mod <- Mods]); 143expand(Mod,Already) -> 144 case lists:member($*,Mod) of 145 true -> 146 Fun = fun(F,Acc) -> 147 M = filename:rootname(F), 148 case lists:member(M,Already) of 149 true -> Acc; 150 false -> [M|Acc] 151 end 152 end, 153 lists:foldl(Fun, [], filelib:wildcard(Mod++".erl")); 154 false -> 155 Mod2 = filename:rootname(Mod, ".erl"), 156 case lists:member(Mod2,Already) of 157 true -> []; 158 false -> [Mod2] 159 end 160 end. 161 162%%% Reads the given Emake to see if there are any specific compile 163%%% options given for the modules. 164 165get_opts_from_emakefile([{MakefileMods,O}|Rest],Mods,Opts,Result) -> 166 case members(Mods,MakefileMods,[],Mods) of 167 {[],_} -> 168 get_opts_from_emakefile(Rest,Mods,Opts,Result); 169 {I,RestOfMods} -> 170 get_opts_from_emakefile(Rest,RestOfMods,Opts,[{I,O}|Result]) 171 end; 172get_opts_from_emakefile([],[],_Opts,Result) -> 173 Result; 174get_opts_from_emakefile([],RestOfMods,Opts,Result) -> 175 [{RestOfMods,Opts}|Result]. 176 177members([H|T],MakefileMods,I,Rest) -> 178 case lists:member(H,MakefileMods) of 179 true -> 180 members(T,MakefileMods,[H|I],lists:delete(H,Rest)); 181 false -> 182 members(T,MakefileMods,I,Rest) 183 end; 184members([],_MakefileMods,I,Rest) -> 185 {I,Rest}. 186 187 188%% Any flags that are not recognixed as make flags are passed directly 189%% to the compiler. 190%% So for example make:all([load,debug_info]) will make everything 191%% with the debug_info flag and load it. 192 193load_opt(Opts) -> 194 case lists:member(netload,Opts) of 195 true -> 196 netload; 197 false -> 198 case lists:member(load,Opts) of 199 true -> 200 load; 201 _ -> 202 noload 203 end 204 end. 205 206 207process([{[],_Opts}|Rest], NoExec, Load) -> 208 process(Rest, NoExec, Load); 209process([{[H|T],Opts}|Rest], NoExec, Load) -> 210 case recompilep(coerce_2_list(H), NoExec, Load, Opts) of 211 error -> 212 error; 213 _ -> 214 process([{T,Opts}|Rest], NoExec, Load) 215 end; 216process([], _NoExec, _Load) -> 217 up_to_date. 218 219recompilep(File, NoExec, Load, Opts) -> 220 ObjName = lists:append(filename:basename(File), 221 code:objfile_extension()), 222 ObjFile = case lists:keysearch(outdir,1,Opts) of 223 {value,{outdir,OutDir}} -> 224 filename:join(coerce_2_list(OutDir),ObjName); 225 false -> 226 ObjName 227 end, 228 case exists(ObjFile) of 229 true -> 230 recompilep1(File, NoExec, Load, Opts, ObjFile); 231 false -> 232 recompile(File, NoExec, Load, Opts) 233 end. 234 235recompilep1(File, NoExec, Load, Opts, ObjFile) -> 236 {ok, Erl} = file:read_file_info(lists:append(File, ".erl")), 237 {ok, Obj} = file:read_file_info(ObjFile), 238 recompilep1(Erl, Obj, File, NoExec, Load, Opts). 239 240recompilep1(#file_info{mtime=Te}, 241 #file_info{mtime=To}, File, NoExec, Load, Opts) when Te>To -> 242 recompile(File, NoExec, Load, Opts); 243recompilep1(_Erl, #file_info{mtime=To}, File, NoExec, Load, Opts) -> 244 recompile2(To, File, NoExec, Load, Opts). 245 246%% recompile2(ObjMTime, File, NoExec, Load, Opts) 247%% Check if file is of a later date than include files. 248recompile2(ObjMTime, File, NoExec, Load, Opts) -> 249 IncludePath = include_opt(Opts), 250 case check_includes(lists:append(File, ".erl"), IncludePath, ObjMTime) of 251 true -> 252 recompile(File, NoExec, Load, Opts); 253 false -> 254 false 255 end. 256 257include_opt([{i,Path}|Rest]) -> 258 [Path|include_opt(Rest)]; 259include_opt([_First|Rest]) -> 260 include_opt(Rest); 261include_opt([]) -> 262 []. 263 264%% recompile(File, NoExec, Load, Opts) 265%% Actually recompile and load the file, depending on the flags. 266%% Where load can be netload | load | noload 267 268recompile(File, true, _Load, _Opts) -> 269 io:format("Out of date: ~ts\n",[File]); 270recompile(File, false, Load, Opts) -> 271 io:format("Recompile: ~ts\n",[File]), 272 case compile:file(File, [report_errors, report_warnings |Opts]) of 273 Ok when is_tuple(Ok), element(1,Ok)==ok -> 274 maybe_load(element(2,Ok), Load, Opts); 275 _Error -> 276 error 277 end. 278 279maybe_load(_Mod, noload, _Opts) -> 280 ok; 281maybe_load(Mod, Load, Opts) -> 282 %% We have compiled File with options Opts. Find out where the 283 %% output file went to, and load it. 284 case compile:output_generated(Opts) of 285 true -> 286 Dir = proplists:get_value(outdir,Opts,"."), 287 do_load(Dir, Mod, Load); 288 false -> 289 io:format("** Warning: No object file created - nothing loaded **~n"), 290 ok 291 end. 292 293do_load(Dir, Mod, load) -> 294 code:purge(Mod), 295 case code:load_abs(filename:join(Dir, Mod),Mod) of 296 {module,Mod} -> 297 {ok,Mod}; 298 Other -> 299 Other 300 end; 301do_load(Dir, Mod, netload) -> 302 Obj = atom_to_list(Mod) ++ code:objfile_extension(), 303 Fname = filename:join(Dir, Obj), 304 case file:read_file(Fname) of 305 {ok,Bin} -> 306 rpc:eval_everywhere(code,load_binary,[Mod,Fname,Bin]), 307 {ok,Mod}; 308 Other -> 309 Other 310 end. 311 312exists(File) -> 313 case file:read_file_info(File) of 314 {ok, _} -> 315 true; 316 _ -> 317 false 318 end. 319 320coerce_2_list(X) when is_atom(X) -> 321 atom_to_list(X); 322coerce_2_list(X) -> 323 X. 324 325%%% If an include file is found with a modification 326%%% time larger than the modification time of the object 327%%% file, return true. Otherwise return false. 328check_includes(File, IncludePath, ObjMTime) -> 329 {ok,Cwd} = file:get_cwd(), 330 Path = [Cwd,filename:dirname(File)|IncludePath], 331 case epp:open(File, Path, []) of 332 {ok, Epp} -> 333 check_includes2(Epp, File, ObjMTime); 334 _Error -> 335 false 336 end. 337 338check_includes2(Epp, File, ObjMTime) -> 339 A1 = erl_anno:new(1), 340 case epp:parse_erl_form(Epp) of 341 {ok, {attribute, A1, file, {File, A1}}} -> 342 check_includes2(Epp, File, ObjMTime); 343 {ok, {attribute, A1, file, {IncFile, A1}}} -> 344 case file:read_file_info(IncFile) of 345 {ok, #file_info{mtime=MTime}} when MTime>ObjMTime -> 346 epp:close(Epp), 347 true; 348 _ -> 349 check_includes2(Epp, File, ObjMTime) 350 end; 351 {ok, _} -> 352 check_includes2(Epp, File, ObjMTime); 353 {eof, _} -> 354 epp:close(Epp), 355 false; 356 {error, _Error} -> 357 check_includes2(Epp, File, ObjMTime); 358 {warning, _Warning} -> 359 check_includes2(Epp, File, ObjMTime) 360 end. 361