34
35:- module(prolog_qlfmake,
36 [ qlf_make/0,
37 qlf_make/1 38 ]). 39:- use_module(library(debug)). 40:- use_module(library(lists)). 41:- use_module(library(ansi_term)). 42:- use_module(library(apply)). 43:- if(exists_source(library(pldoc))). 44:- use_module(library(pldoc)). 45:- use_module(library(prolog_source)). 46:- use_module(library(dcg/high_order)). 47
48:- endif. 49
60
62
75
76qlf_make :-
77 set_prolog_flag(optimise, true),
78 set_prolog_flag(optimise_debug, true),
79 preload(library(apply_macros), []),
80 preload_pldoc,
81 qmake_aggregates,
82 system_lib_files(Files),
83 include(qlf_needs_rebuild, Files, Rebuild),
84 report_work(Files, Rebuild),
85 qcompile_files(Rebuild),
86 size_stats(Files).
87
93
94qlf_make(Spec) :-
95 absolute_file_name(Spec, PlFile,
96 [ file_type(prolog),
97 access(read)
98 ]),
99 ( qlf_needs_rebuild(PlFile)
100 -> qcompile_(PlFile)
101 ; true
102 ).
103
104qcompile_files([]) => true.
105qcompile_files([+H|T]) =>
106 qcompile_(H),
107 qcompile_files(T).
108qcompile_files([H|T]) =>
109 file_dependencies(H, Deps),
110 intersection(Deps, T, Deps1),
111 ( Deps1 == []
112 -> qcompile_(H),
113 qcompile_files(T)
114 ; subtract(T, Deps1, T1),
115 append([Deps1, [+H], T1], Agenda),
116 qcompile_files(Agenda)
117 ).
118
119qcompile_(PlFile) :-
120 progress(PlFile),
121 qcompile(PlFile, [imports([])]).
122
127
128preload_pldoc :-
129 exists_source(library(pldoc)),
130 !,
131 preload(library(pldoc), [doc_collect/1]),
132 doc_collect(false).
133preload_pldoc.
134
140
141preload(Spec, Imports) :-
142 absolute_file_name(Spec, File,
143 [ extensions([pl]),
144 access(read),
145 file_errors(fail)
146 ]),
147 !,
148 qlf_make(File),
149 use_module(File, Imports).
150preload(_, _).
151
157
158qlf_needs_rebuild(PlFile) :-
159 pl_qlf_file(PlFile, QlfFile),
160 ( \+ exists_file(QlfFile)
161 -> true
162 ; '$qlf_versions'(QlfFile, CurrentVersion, _MinLOadVersion, FileVersion,
163 CurrentSignature, FileSignature),
164 ( FileVersion \== CurrentVersion
165 ; FileSignature \== CurrentSignature
166 )
167 -> true
168 ; time_file(QlfFile, QlfTime),
169 '$qlf_sources'(QlfFile, Sources),
170 member(S, Sources),
171 source_changed(S, QlfTime)
172 ).
173
189
190source_changed(Source, _QlfTime) :-
191 arg(1, Source, File),
192 arg(2, Source, Hash),
193 Hash =\= 0,
194 !,
195 \+ '$file_hash'(File, Hash).
196source_changed(Source, QlfTime) :-
197 arg(1, Source, File),
198 time_file(File, STime),
199 STime > QlfTime+1.
200
201pl_qlf_file(PlFile, QlfFile) :-
202 file_name_extension(Base, pl, PlFile),
203 file_name_extension(Base, qlf, QlfFile).
204
208
209size_stats(Files) :-
210 maplist(size_stat, Files, PlSizes, Qlfizes),
211 sum_list(PlSizes, PlSize),
212 sum_list(Qlfizes, Qlfize),
213 length(Files, Count),
214 print_message(informational, qlf_make(size(Count, Qlfize, PlSize))).
215
216size_stat(PlFile, PlSize, QlfSize) :-
217 pl_qlf_file(PlFile, QlfFile),
218 size_file(PlFile, PlSize),
219 size_file(QlfFile, QlfSize).
220
221:- dynamic qlf_part_of/2. 222
223 226
242
243file_dependencies(File, Deps) :-
244 prolog_file_directives(File, Directives, []),
245 phrase(file_deps(Directives), Deps0),
246 convlist(absolute_path(File), Deps0, Deps1),
247 sort(Deps1, Deps).
248
249file_deps([]) ==>
250 [].
251file_deps([H|T]) ==>
252 file_dep(H),
253 file_deps(T).
254
255file_dep((:- Dir)) ==>
256 ( { directive_file(Dir, Files) }
257 -> file_or_files(Files)
258 ; []
259 ).
260file_dep(_) ==>
261 [].
262
263file_or_files(Files), is_list(Files) ==>
264 sequence(file, Files).
265file_or_files(File) ==>
266 file(File).
267
268file(File) -->
269 [File].
270
271directive_file(ensure_loaded(File), File).
272directive_file(consult(File), File).
273directive_file(load_files(File, _), File).
274directive_file(use_module(File), File).
275directive_file(use_module(File, _), File).
276directive_file(autoload(File), File).
277directive_file(autoload(File, _), File).
278directive_file(reexport(File), File).
279directive_file(reexport(File, _), File).
280
281absolute_path(RelativeTo, _:Spec, File) =>
282 absolute_path(RelativeTo, Spec, File).
283absolute_path(_RelativeTo, Spec, File),
284 compound(Spec), compound_name_arity(Spec, _, 1) =>
285 absolute_file_name(Spec, File,
286 [ access(read),
287 file_type(source),
288 file_errors(fail)
289 ]).
290absolute_path(RelativeTo, Spec, File) =>
291 absolute_file_name(Spec, File,
292 [ relative_to(RelativeTo),
293 access(read),
294 file_type(source),
295 file_errors(fail)
296 ]).
297
298
299 302
317
318system_lib_files(LibFiles) :-
319 findall(Dir, system_lib_dir(Dir), Dirs),
320 maplist(dir_files, Dirs, FilesL),
321 append(FilesL, Files0),
322 sort(Files0, Files),
323 exclude(excluded, Files, LibFiles).
324
325system_lib_dir(LibDir) :-
326 working_directory(PWD, PWD),
327 source_alias(Alias),
328 absolute_file_name(Alias, LibDir,
329 [ file_type(directory),
330 solutions(all),
331 file_errors(fail),
332 access(read)
333 ]),
334 sub_atom(LibDir, 0, _, _, PWD).
335
336source_alias(library(.)).
337source_alias(app(.)).
338source_alias(pce('prolog/demo')).
339source_alias(pce('prolog/contrib')).
340
341
346
347dir_files(Dir, Files) :-
348 dir_files_([Dir|DirT], DirT, Files).
349
350dir_files_([], [], []) :- !.
351dir_files_([D|DT], DirT, Files) :-
352 \+ excluded_directory(D),
353 !,
354 dir_files_dirs(D, Files, FileT, DirT, DirT2),
355 dir_files_(DT, DirT2, FileT).
356dir_files_([_|DT], DirT, Files) :-
357 dir_files_(DT, DirT, Files).
358
359dir_files_dirs(Dir, Files, FileT, Dirs, DirT) :-
360 directory_files(Dir, Entries),
361 dir_files_dirs_(Entries, Dir, Files, FileT, Dirs, DirT).
362
363dir_files_dirs_([], _, Files, Files, Dirs, Dirs).
364dir_files_dirs_([H|T], Dir, Files, FileT, Dirs, DirT) :-
365 hidden_entry(H),
366 !,
367 dir_files_dirs_(T, Dir, Files, FileT, Dirs, DirT).
368dir_files_dirs_([H|T], Dir, Files, FileT, Dirs, DirT) :-
369 atomic_list_concat([Dir, /, H], Path),
370 ( exists_file(Path)
371 -> Files = [Path|Files1],
372 dir_files_dirs_(T, Dir, Files1, FileT, Dirs, DirT)
373 ; exists_directory(Path)
374 -> Dirs = [Path|Dirs1],
375 dir_files_dirs_(T, Dir, Files, FileT, Dirs1, DirT)
376 ; dir_files_dirs_(T, Dir, Files, FileT, Dirs, DirT)
377 ).
378
379hidden_entry('.').
380hidden_entry('..').
381
382excluded(File) :-
383 \+ file_name_extension(_, pl, File),
384 !.
385excluded(File) :-
386 file_base_name(File, 'INDEX.pl'),
387 !.
388excluded(File) :-
389 file_base_name(File, 'MKINDEX.pl'),
390 !.
391excluded(File) :-
392 file_base_name(File, 'CLASSINDEX.pl'),
393 !.
394excluded(File) :-
395 qlf_part_of(File, Main),
396 !,
397 report_excluded(excluded(part(Main), File)).
398excluded(File) :-
399 exclude(Spec),
400 same_base(Spec, pl, File),
401 absolute_file_name(Spec, File1,
402 [ extensions([pl]),
403 access(read),
404 solutions(all)
405 ]),
406 File == File1,
407 !,
408 report_excluded(excluded(rule(Spec), File)).
409
410same_base(Spec, Ext, Path) :-
411 spec_base(Spec, Base),
412 file_base_name(Path, File),
413 file_name_extension(Base, Ext, File).
414
415spec_base(Spec, Base) :-
416 compound(Spec),
417 Spec =.. [_,Sub],
418 last_segment(Sub, Base).
419
420last_segment(_/B, L) =>
421 last_segment(B, L).
422last_segment(A, L), atomic(A) =>
423 L = A.
424
425exclude(library(prolog_qlfmake)).
426exclude(library(win_menu)).
427exclude(library(threadutil)).
428exclude(library(check_installation)).
429exclude(library(sty_pldoc)).
430exclude(library(sty_xpce)).
431exclude(library(tabling)).
432exclude(library(theme/dark)).
433exclude(library(http/dcg_basics)).
434exclude(library(http/json)).
435exclude(library(http/json_convert)).
436exclude(library(http/js_grammar)).
437exclude(library(chr/chr_translate_bootstrap1)).
438exclude(library(chr/chr_translate_bootstrap2)).
439exclude(library(trace/pprint)).
440exclude(library(xref/quintus)).
441exclude(library(xref/sicstus)).
442exclude(library(pldoc/hooks)).
443exclude(library(pldoc/doc_changes)).
444exclude(library(pldoc/git_extract_changes)).
445exclude(library(pldoc/changelog_events)).
446
447excluded_directory(Dir) :-
448 exclude_dir(Spec),
449 spec_base(Spec, Base),
450 atom_concat(/, Base, SBase),
451 once(sub_atom(Dir, _, _, _, SBase)),
452 absolute_file_name(Spec, Dir1,
453 [ file_type(directory),
454 access(read),
455 solutions(all)
456 ]),
457 sub_atom(Dir, 0, _, _, Dir1),
458 !,
459 report_excluded(excluded(rule(Spec), Dir)).
460
461exclude_dir(swi(xpce/prolog/lib/compatibility)).
462
463
464 467
472
473qmake_aggregates :-
474 retractall(qlf_part_of(_,_)),
475 forall(aggregate_qlf(Spec),
476 qmake_aggregate(Spec)).
477
478qmake_aggregate(Spec) :-
479 exists_source(Spec),
480 !,
481 qlf_make(Spec),
482 absolute_file_name(Spec, PlFile,
483 [ file_type(prolog),
484 access(read)
485 ]),
486 pl_qlf_file(PlFile, QlfFile),
487 '$qlf_sources'(QlfFile, Sources),
488 forall(member(source(S, _Hash), Sources),
489 assertz(qlf_part_of(S, PlFile))).
490qmake_aggregate(_).
491
492aggregate_qlf(library(pce)).
493aggregate_qlf(library(trace/trace)).
494aggregate_qlf(library(emacs/emacs)).
495
496
497 500
501:- multifile
502 user:file_search_path/2. 503
504user:file_search_path(chr, library(chr)).
505user:file_search_path(pldoc, library(pldoc)).
506user:file_search_path(doc, swi(xpce/prolog/lib/doc)).
507
508
509 512
513report_work(Files, Rebuild) :-
514 length(Files, AllFiles),
515 length(Rebuild, NeedsRebuild),
516 print_message(informational, qlf_make(planning(AllFiles, NeedsRebuild))).
517
518progress(_PlFile) :-
519 current_prolog_flag(verbose, silent),
520 !.
521progress(PlFile) :-
522 stream_property(user_output, tty(true)),
523 current_prolog_flag(color_term, true),
524 \+ debugging(qlf_make),
525 !,
526 ansi_format(comment, '\r~w ...', [PlFile]),
527 format(user_output, '\e[K', []),
528 flush_output(user_output).
529progress(PlFile) :-
530 format(user_output, '~N~w ...', [PlFile]),
531 flush_output(user_output).
532
533report_excluded(Msg) :-
534 debugging(qlf_make),
535 !,
536 print_message(informational, qlf_make(Msg)).
537report_excluded(_).
538
539:- multifile prolog:message//1. 540
541prolog:message(qlf_make(Msg)) -->
542 message(Msg).
543
544message(planning(_AllFiles, 0)) ==>
545 [].
546message(planning(AllFiles, AllFiles)) ==>
547 [ 'Building ~D qlf files'-[AllFiles] ].
548message(planning(AllFiles, NeedsRebuild)) ==>
549 [ '~D qlf files. ~D need to be rebuild'-[AllFiles, NeedsRebuild] ].
550message(size(Count, Qlfize, PlSize)) ==>
551 [ '~D qlf files take ~D bytes. Source ~D bytes'-
552 [Count, Qlfize, PlSize]
553 ].
554message(excluded(Reason, File)) ==>
555 [ 'Excluded ', url(File) ],
556 excl_reason(Reason).
557
558excl_reason(part(_Main)) -->
559 [ ' (part of aggregate QLF)' ].
560excl_reason(rule(_Spec)) -->
561 [ ' (explicit)' ]