Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 336 lines (276 sloc) 9.643 kb
d69e4d8 @klacke ""
authored
1 %%%----------------------------------------------------------------------
2 %%% File : yaws_compile.erl
3 %%% Author : Claes Wikstrom <klacke@hyber.org>
4 %%% Purpose :
5 %%% Created : 20 Feb 2002 by Claes Wikstrom <klacke@hyber.org>
6 %%%----------------------------------------------------------------------
7
8 -module(yaws_compile).
9 -author('klacke@hyber.org').
10
11 -compile(export_all).
12 -include("yaws.hrl").
13
14
15
16 %% tada !!
17 %% returns a CodeSpec which is:
18 %% a list {data, NumChars} |
19 %% {mod, LineNo, YawsFile, NumSkipChars, Mod, Func} |
20 %% {error, NumSkipChars, E}}
21
22 % each erlang fragment inside <erl> .... </erl> is compiled into
23 % its own module
24
25
26 -record(comp, {
27 gc, %% global conf
28 sc, %% server conf
29 startline = 0,
30 modnum = 1,
31 infile,
32 infd,
33 outfile,
34 outfd}).
35
36
37
38 comp_opts(GC) ->
6c9c37f @klacke ""
authored
39 ?Debug("I=~p~n", [GC#gconf.include_dir]),
d69e4d8 @klacke ""
authored
40 I = lists:map(fun(Dir) -> {i, Dir} end, GC#gconf.include_dir),
41 YawsDir = {i, "/home/klacke/yaws/include"},
42 I2 = [YawsDir | I],
43 Opts = [binary, report_errors | I2],
44 ?Debug("Compile opts = ~p~n", [Opts]),
45 Opts.
46
47
48 compile_file(File, GC, SC) ->
49 case file:open(File, [read]) of
50 {ok, Fd} ->
51 Spec = compile_file(#comp{infile = File,
52 infd = Fd, gc = GC, sc = SC},
53 1,
2129adc @klacke ""
authored
54 io:get_line(Fd, ''), init, 0, []),
55 Spec;
56 _Err ->
d69e4d8 @klacke ""
authored
57 yaws:elog("can't open ~s~n", [File]),
58 exit(normal)
59 end.
60
2129adc @klacke ""
authored
61 compile_file(C, _LineNo, eof, _Mode, NumChars, Ack) ->
d69e4d8 @klacke ""
authored
62 file:close(C#comp.infd),
63 {ok, lists:reverse([{data, NumChars} |Ack])};
64
65
39c94ab @klacke ""
authored
66 %% skip initial space if first thing is <erl> otherwise not
d69e4d8 @klacke ""
authored
67 compile_file(C, LineNo, Chars, init, NumChars, Ack) ->
68 case Chars -- [$\s, $\t, $\n, $\r] of
69 [] ->
961926f @klacke ""
authored
70 ?Debug("SKIP ~p~n", [Chars]),
71 L=length(Chars),
72 compile_file(C, LineNo+1, line(C), init, NumChars-L, Ack);
39c94ab @klacke ""
authored
73 "<erl>" ++ _ -> %% first chunk is erl, skip whistespace
74 compile_file(C, LineNo, Chars, html, NumChars, Ack);
d69e4d8 @klacke ""
authored
75 _ ->
39c94ab @klacke ""
authored
76 %% first chunk is html, keep whitespace
77 Fd=C#comp.infd,
78 file:position(Fd, bof),
79 compile_file(C,1,io:get_line(Fd,''),html,0,[])
d69e4d8 @klacke ""
authored
80 end;
81
2129adc @klacke ""
authored
82 compile_file(C, LineNo, Chars = "<erl>" ++ _Tail, html, NumChars, Ack) ->
d69e4d8 @klacke ""
authored
83 ?Debug("start erl:~p",[LineNo]),
84 C2 = new_out_file(LineNo, C, C#comp.gc),
85 C3 = C2#comp{startline = LineNo},
86 L = length(Chars),
87 if
88 NumChars > 0 ->
89 compile_file(C3, LineNo+1, line(C) , erl,L,
90 [{data, NumChars} | Ack]);
91 true -> %% just ignore zero byte data segments
961926f @klacke ""
authored
92 compile_file(C3, LineNo+1, line(C) , erl, L + (-NumChars), Ack) %hack
d69e4d8 @klacke ""
authored
93 end;
94
2129adc @klacke ""
authored
95 compile_file(C, LineNo, Chars = "</erl>" ++ _Tail, erl, NumChars, Ack) ->
d69e4d8 @klacke ""
authored
96 ?Debug("stop erl:~p",[LineNo]),
97 file:close(C#comp.outfd),
98 NumChars2 = NumChars + length(Chars),
99 case proc_compile_file(C#comp.outfile, comp_opts(C#comp.gc)) of
100 {ok, ModuleName, Binary} ->
101 case code:load_binary(ModuleName, C#comp.outfile, Binary) of
102 {module, ModuleName} ->
103 C2 = C#comp{modnum = C#comp.modnum+1},
104 L2 = check_exported(C, LineNo,NumChars2, ModuleName),
105 compile_file(C2, LineNo+1, line(C),html,0,L2++Ack);
106 Err ->
107 A2 = gen_err(C, LineNo, NumChars2,
108 ?F("Cannot load module ~p: ~p",
109 [ModuleName, Err])),
110 compile_file(C, LineNo+1, line(C),
111 html, 0, [A2|Ack])
112 end;
2129adc @klacke ""
authored
113 {error, Errors, _Warnings} ->
d69e4d8 @klacke ""
authored
114 %% FIXME remove outfile here ... keep while debuging
115 A2 = comp_err(C, LineNo, NumChars2, Errors),
116 compile_file(C, LineNo+1, line(C), html, 0, [A2|Ack]);
117 {error, Str} ->
118 %% this is boring but does actually happen
119 %% in order to get proper user errors here we need to catch i/o
120 %% or hack compiler/parser
121 yaws:elog("Dynamic compile error in file ~s, line~w",
122 [C#comp.infile, LineNo]),
123 A2 = {error, NumChars2, ?F("<pre> Dynamic compile error in file "
124 " ~s line ~w~n~s </pre>",
125 [C#comp.infile, LineNo, Str])},
126 compile_file(C, LineNo+1, line(C), html, 0, [A2|Ack])
127 end;
128
2129adc @klacke ""
authored
129 compile_file(C, LineNo, Chars = "<pre>" ++ _Tail, html, NumChars, Ack) ->
d69e4d8 @klacke ""
authored
130 ?Debug("start pre:~p",[LineNo]),
131 compile_file(C, LineNo+1, line(C) , pre, NumChars + length(Chars), Ack);
132
2129adc @klacke ""
authored
133 compile_file(C, LineNo, Chars = "</pre>" ++ _Tail, pre, NumChars, Ack) ->
d69e4d8 @klacke ""
authored
134 ?Debug("stop pre:~p",[LineNo]),
135 compile_file(C, LineNo+1, line(C) , html, NumChars + length(Chars), Ack);
136
137 compile_file(C, LineNo, Chars, erl, NumChars, Ack) ->
0d623c1 @klacke ""
authored
138 case has_tag(Chars, "</erl>") of
139 {ok, Skipped, Chars2} ->
140 compile_file(C, LineNo, Chars2, erl, NumChars + Skipped, Ack);
141 false ->
142 io:format(C#comp.outfd, "~s", [Chars]),
143 compile_file(C, LineNo+1, line(C), erl, NumChars +
144 length(Chars), Ack)
145 end;
146
d69e4d8 @klacke ""
authored
147
148 compile_file(C, LineNo, Chars, html, NumChars, Ack) ->
0d623c1 @klacke ""
authored
149 case has_tag(Chars, "<erl>") of
150 {ok, Skipped, Chars2} ->
151 compile_file(C, LineNo, Chars2, html, NumChars+Skipped, Ack);
152 false ->
153 compile_file(C, LineNo+1, line(C), html, NumChars +
154 length(Chars), Ack)
155 end;
d69e4d8 @klacke ""
authored
156
157 compile_file(C, LineNo, Chars, pre, NumChars, Ack) ->
158 compile_file(C, LineNo+1, line(C), pre, NumChars + length(Chars), Ack).
159
160
161
0d623c1 @klacke ""
authored
162 has_tag(L, Str) ->
163 has_tag(L, Str, 0).
164 has_tag([H|T], Tag, Num) ->
165 case yaws:is_space(H) of
166 true ->
167 has_tag(T, Tag, Num+1);
168 false ->
169 case lists:prefix(Tag, [H|T]) of
170 true ->
171 {ok, Num, [H|T]};
172 false ->
173 false
174 end
175 end;
176 has_tag(_,_,_) ->
177 false.
d69e4d8 @klacke ""
authored
178
179 check_exported(C, LineNo, NumChars, Mod) when C#comp.modnum == 1->
180 case {is_exported(some_headers, 1, Mod),
181 is_exported(all_headers, 1, Mod),
182 is_exported(out, 1, Mod)} of
183 {true, true, _} ->
184 [gen_err(C, LineNo, NumChars,
185 ?F("Cannot have both some and the all "
186 "headers",[]))];
187
188 %% someheaders
189 {true, false, true} ->
190 [{mod, C#comp.startline, C#comp.infile,
191 NumChars,Mod,some_headers},
192 {mod, C#comp.startline, C#comp.infile,
193 NumChars,Mod,out}];
194 {true, false, false} ->
195 [{mod, C#comp.startline, C#comp.infile,
196 NumChars,Mod,some_headers}];
197
198 %% allheaders
199 {false, true, true} ->
200 [{mod, C#comp.startline, C#comp.infile,
201 NumChars,Mod,all_headers},
202 {mod, C#comp.startline, C#comp.infile,
203 NumChars,Mod,out}];
204
205 {false, true, false} ->
206 [{mod, C#comp.startline, C#comp.infile,
207 NumChars,Mod,all_headers}];
208
209 {false, false, true} ->
210 [{mod, C#comp.startline, C#comp.infile,
211 NumChars,Mod,out}];
212 {false, false, false} ->
213 ?Debug("XX ~p~n", [C]),
214 [gen_err(C, LineNo, NumChars,
3b9e4a1 @klacke new ret vals from out/1
authored
215 "compile error out/1 "
216 "is not defined ")]
d69e4d8 @klacke ""
authored
217
218
219 end;
220
221 check_exported(C, LineNo, NumChars, Mod) ->
222 case is_exported(out, 1, Mod) of
223 true ->
224 [{mod, C#comp.startline, C#comp.infile,
225 NumChars,Mod,out}];
226 false ->
227 ?Debug("XX ~p~n", [C]),
228 [gen_err(C, LineNo, NumChars,
229 "out/1 is not defined ")]
230 end.
231
232 line(C) ->
233 io:get_line(C#comp.infd, '').
234
235 is_exported(Fun, A, Mod) ->
236 case (catch Mod:module_info()) of
237 List when list(List) ->
238 case lists:keysearch(exports, 1, List) of
239 {value, {exports, Exp}} ->
240 lists:member({Fun, A}, Exp);
241 _ ->
242 false
243 end;
244 _ ->
245 false
246 end.
247
248
249 %% this will generate 9 lines
250 new_out_file(Line, C, GC) ->
251 Mnum = gen_server:call(yaws_server, mnum),
252 Module = [$m | integer_to_list(Mnum)],
253 OutFile = "/tmp/yaws/" ++ Module ++ ".erl",
254 ?Debug("Writing outout file~s~n", [OutFile]),
255 {ok, Out} = file:open(OutFile, [write]),
256 ok = io:format(Out, "-module(~s).~n-compile(export_all).~n~n", [Module]),
257 io:format(Out, "%%~n%% code at line ~w from file ~s~n%%~n",
258 [Line, C#comp.infile]),
259
260 io:format(Out, "-import(yaws_api, [f/2, fl/1, parse_post_data/2]). ~n~n", []),
261 io:format(Out, '-include("~s/include/yaws_api.hrl").~n',
262 [GC#gconf.yaws_dir]),
263 C#comp{outfd = Out,
264 outfile = OutFile}.
265
266
2129adc @klacke ""
authored
267 gen_err(C, _LineNo, NumChars, Err) ->
d69e4d8 @klacke ""
authored
268 S = io_lib:format("<p> Error in File ~s Erlang code beginning "
269 "at line ~w~n"
270 "Error is: ~p~n", [C#comp.infile, C#comp.startline,
271 Err]),
272 yaws:elog("~s~n", [S]),
273 {error, NumChars, S}.
274
275
2129adc @klacke ""
authored
276 comp_err(C, _LineNo, NumChars, Err) ->
d69e4d8 @klacke ""
authored
277 case Err of
2129adc @klacke ""
authored
278 [{_FileName, [ErrInfo|_]} |_] ->
d69e4d8 @klacke ""
authored
279 {Line0, Mod, E}=ErrInfo,
280 Line = Line0 + C#comp.startline - 9,
2129adc @klacke ""
authored
281 ?Debug("XX ~p~n", [{_LineNo, Line0}]),
d69e4d8 @klacke ""
authored
282 Str = io_lib:format("~s:~w: ~s\n",
283 [C#comp.infile, Line,
284 apply(Mod, format_error, [E])]),
285 HtmlStr = ?F("~n<pre>~nDynamic compile error: ~s~n</pre>~n",
286 [Str]),
287 yaws:elog("Dynamic compiler err ~s", [Str]),
288 {error, NumChars, HtmlStr};
2129adc @klacke ""
authored
289 _Other ->
d69e4d8 @klacke ""
authored
290 yaws:elog("Dynamic compile error", []),
291 {error, NumChars, ?F("<pre> Compile error - "
292 "Other err ~p</pre>~n", [Err])}
293 end.
294
295
296 %% due to compiler not producing proper error
297 %% we NEED to catch all io produced by the compiler
298
299 proc_compile_file(F, Opts) ->
300 G = group_leader(),
301 group_leader(self(), self()),
302 P = proc_lib:spawn(?MODULE, compiler_proc, [self(), F, Opts]),
303 Res = get_compiler_data(P, []),
304 group_leader(G, self()),
305 Res.
306
307 compiler_proc(Top, F, Opts) ->
308 R = (catch compile:file(F, Opts)),
309 Top ! {self(), result, R}.
310
311
312 get_compiler_data(P, Ack) ->
313 receive
314 {P, result, {ok, Mod, Bin}} ->
315 {ok, Mod, Bin};
316 {io_request, P1, P2, {put_chars, M, F, A}} ->
317 P1 ! {io_reply, P2, ok},
318 Str = apply(M, F, A),
319 get_compiler_data(P, [Str|Ack]);
320 {P, result, {error, Errors, Warnings}} ->
321 {error, Errors, Warnings};
322 {P, result, error} ->
323 S = lists:map(
324 fun(S) -> S ++ "\n" end, lists:reverse(Ack)),
325 {error, S}
326 end.
327
328
329
330
331
332
333
334
335
Something went wrong with that request. Please try again.