2
(* Manager object for FPDoc GUI, by DoDi
3
Holds configuration and packages.
5
Packages (shall) contain extended descriptions for:
6
- default OSTarget (FPCDocs: Unix/Linux)
8
- directories: project(file), InputDir, DescrDir[by language?]
9
- FPCVersion, LazVersion: variations of inputs
10
- Skeleton and Output options, depending on DocType/Level and Format.
11
Units can be described in multiple XML docs, so that it's possible to
12
have specific parts depending on Laz/FPC version, OSTarget, Language, Widgetset.
14
This version is decoupled from the fpdoc classes, introduces the classes
15
TFPDocManager for all packages
16
TDocPackage for a single package
17
TFPDocHelper for fpdoc projects
20
(* Currently registered writers:
21
TFPDocWriter in 'dwriter.pp'
22
template: TTemplateWriter(TFPDocWriter) in 'dw_tmpl.pp'
23
man: TMANWriter(TFPDocWriter) in 'dw_man.pp' --> <pkg>.man /unit.
24
dxml: TDXMLWriter(TFPDocWriter) in 'dw_dxml.pp'
25
xml: TXMLWriter(TFPDocWriter) in 'dw_xml.pp'
26
html: THTMLWriter(TFPDocWriter) in 'dw_html.pp'
27
htm: THTMWriter(THTMLWriter)
28
chm: TCHMHTMLWriter(THTMLWriter)
29
TLinearWriter in 'dwlinear.pp'
30
template: TTemplateWriter(TLinearWriter) in 'dw_lintmpl.pp'
31
ipf: TIPFNewWriter(TLinearWriter) in 'dw_ipflin.pas'
32
latex: TLaTeXWriter(TLinearWriter) in 'dw_latex.pp'
33
rtf: TRTFWriter(TLinearWriter) in 'dw_linrtf.pp'
34
txt: TTXTWriter(TLinearWriter) in 'dw_txt.pp'
36
TLinearWriter based writers create an single output file for a package:
38
TFPDocWriter based writers create an file for every module:
39
<path>/pkg /unit.<ext>
44
{$DEFINE EasyImports} //EasyImports.patch applied?
50
umakeskel, ConfigFile, fpdocproj, dw_HTML;
57
(* TDocPackage describes a package documentation project.
65
FDescriptions: TStrings;
77
function GetAltDir: string;
78
procedure SetAllDirs(AValue: boolean);
79
procedure SetAltDir(AValue: string);
80
procedure SetCompOpts(AValue: string);
81
procedure SetDescrDir(AValue: string);
82
procedure SetDescriptions(AValue: TStrings);
83
procedure SetIncludePath(AValue: string);
84
procedure SetInputDir(AValue: string);
85
procedure SetLazPkg(AValue: string);
86
procedure SetLoaded(AValue: boolean);
87
procedure SetName(AValue: string);
88
procedure SetProjectDir(AValue: string);
89
procedure SetProjectFile(AValue: string);
90
procedure SetRequires(AValue: TStrings);
91
procedure SetUnitPath(AValue: string);
92
procedure SetUnits(AValue: TStrings);
95
procedure ReadConfig; virtual;
97
constructor Create; virtual;
98
destructor Destroy; override;
99
function IniFileName: string;
100
function CreateProject(APrj: TFPDocHelper; const AFile: string): boolean; virtual; //new package project
101
function ImportProject(APrj: TFPDocHelper; APkg: TFPDocPackage; const AFile: string): boolean;
102
procedure UpdateConfig;
103
procedure EnumUnits(AList: TStrings); virtual;
104
function DescrFileName(const AUnit: string): string;
105
property Name: string read FName write SetName;
106
property Loaded: boolean read FLoaded write SetLoaded;
107
property ProjectFile: string read FProjectFile write SetProjectFile; //xml?
109
procedure AddUnit(const AFile: string);
110
property AllDirs: boolean read FAllDirs write SetAllDirs;
111
property CompOpts: string read FCompOpts write SetCompOpts;
112
property LazPkg: string read FLazPkg write SetLazPkg; //LPK name?
113
property ProjectDir: string read FProjectDir write SetProjectDir;
114
property DescrDir: string read FDescrDir write SetDescrDir;
115
property Descriptions: TStrings read FDescriptions write SetDescriptions;
116
property AltDir: string read GetAltDir write SetAltDir;
117
property InputDir: string read FInputDir write SetInputDir;
118
property SrcDirs: TStrings read FSrcDirs;
119
property Units: TStrings read FUnits write SetUnits;
120
property Requires: TStrings read FRequires write SetRequires; //only string?
121
property IncludePath: string read FIncludePath write SetIncludePath; //-Fi
122
property UnitPath: string read FUnitPath write SetUnitPath; //-Fu
127
TFCLDocPackage = class(TDocPackage)
129
procedure ReadConfig; override;
131
function CreateProject(APrj: TFPDocHelper; const AFile: string): boolean; override;
136
//holds temporary project
138
TFPDocHelper = class(TFPDocMaker)
141
procedure SetProjectDir(AValue: string);
143
InputList, DescrList: TStringList; //still required?
145
constructor Create(AOwner: TComponent); override;
146
destructor Destroy; override;
147
function BeginTest(APkg: TDocPackage): boolean;
148
function BeginTest(ADir: string): boolean;
150
function CmdToPrj(const AFileName: string): boolean;
151
function TestRun(APkg: TDocPackage; AUnit: string): boolean;
152
function Update(APkg: TDocPackage; const AUnit: string): boolean;
153
function MakeDocs(APkg: TDocPackage; const AUnit: string; AOutput: string): boolean;
154
property ProjectDir: string read FProjectDir write SetProjectDir;
157
TLogHandler = Procedure (Sender : TObject; Const Msg : String) of object;
161
(* Holds configuration and package projects.
163
TFPDocManager = class(TComponent)
165
FExcludedUnits: boolean;
170
FNoParseUnits: TStringList;
171
FOnChange: TNotifyEvent;
173
FOptions: TCmdOptions;
174
FPackage: TDocPackage;
177
FProfiles: string; //CSV list of profile names
179
UpdateCount: integer;
180
function GetNoParseUnits: TStrings;
181
procedure SetExcludedUnits(AValue: boolean);
182
procedure SetFpcDir(AValue: string);
183
procedure SetFPDocDir(AValue: string);
184
procedure SetLazarusDir(AValue: string);
185
procedure SetNoParseUnits(AValue: TStrings);
186
procedure SetOnChange(AValue: TNotifyEvent);
187
procedure SetPackage(AValue: TDocPackage);
188
procedure SetProfile(AValue: string);
189
procedure SetRootDir(AValue: string);
191
Helper: TFPDocHelper; //temporary
193
function BeginTest(const ADir: string): boolean;
195
function RegisterPackage(APkg: TDocPackage): integer;
196
Procedure DoLog(Const Msg : String);
198
Config: TConfigFile; //extend class
199
constructor Create(AOwner: TComponent); override;
200
destructor Destroy; override;
201
procedure BeginUpdate;
203
function LoadConfig(const ADir: string; Force: boolean = False): boolean;
204
function SaveConfig: boolean;
205
procedure AddProfile(const AName: string);
206
function AddProject(const APkg, AFile: string): boolean; //from config
207
function CreateProject(const AFileName: string; APkg: TDocPackage): boolean;
208
function AddPackage(AName: string): TDocPackage;
209
function IsExtended(const APkg: string): string;
210
function ImportLpk(const AFile: string): TDocPackage;
211
procedure ImportProject(APkg: TFPDocPackage; const AFile: string);
212
function ImportCmd(const AFile: string): boolean;
213
procedure UpdatePackage(const AName: string);
214
function UpdateFCL(enabled: boolean): boolean;
216
function MakeDoc(APkg: TDocPackage; const AUnit, AOutput: string): boolean;
217
function TestRun(APkg: TDocPackage; AUnit: string): boolean;
218
function Update(APkg: TDocPackage; const AUnit: string): boolean;
220
property ExcludeUnits: boolean read FExcludedUnits write SetExcludedUnits;
221
property NoParseUnits: TStrings read GetNoParseUnits write SetNoParseUnits;
222
property FpcDir: string read FFpcDir write SetFpcDir;
223
property FpcDocDir: string read FFPDocDir write SetFPDocDir;
224
property LazarusDir: string read FLazarusDir write SetLazarusDir;
225
property RootDir: string read FRootDir write SetRootDir;
226
property Options: TCmdOptions read FOptions;
227
property Profile: string read FProfile write SetProfile;
228
property Profiles: string read FProfiles;
229
property Packages: TStrings read FPackages;
230
property Package: TDocPackage read FPackage write SetPackage;
231
property Modified: boolean read FModified; //app
232
property OnChange: TNotifyEvent read FOnChange write SetOnChange;
233
Property OnLog : TLogHandler Read FOnLog Write FOnLog;
237
Manager: TFPDocManager = nil; //init by application
239
function FixPath(const s: string): string;
240
procedure ListDirs(const ARoot: string; AList: TStrings);
241
procedure ListUnits(const AMask: string; AList: TStrings);
242
function MatchUnits(const ADir: string; AList: TStrings): integer;
250
ConfigName = 'docmgr.ini';
251
SecProjects = 'projects';
255
function FixPath(const s: string): string;
259
if DirectorySeparator = '/' then
263
Result := StringReplace(s, c, DirectorySeparator, [rfReplaceAll]);
266
procedure ListDirs(const ARoot: string; AList: TStrings);
271
if FindFirst (ARoot+'/*',faDirectory,Info)=0 then begin
273
if not ((Info.Attr and faDirectory) = faDirectory) then
277
and (AList.IndexOf(s) < 0) then //exclude dupes
278
AList.Add(s); //name only, allow to create relative refs
279
until FindNext(info)<>0;
284
procedure ListUnits(const AMask: string; AList: TStrings);
289
if FindFirst (AMask,faArchive,Info)=0 then begin
292
if s[1] <> '.' then begin
293
f := ChangeFileExt(s, ''); //unit name only
294
if Manager.ExcludeUnits and (Manager.NoParseUnits.IndexOf(f) >= 0) then
295
continue; //excluded unit!
298
until FindNext(info)<>0;
303
function MatchUnits(const ADir: string; AList: TStrings): integer;
309
if FindFirst(ADir+DirectorySeparator+'*',faArchive,Info)=0 then begin
312
ext := ExtractFileExt(s);
313
if (ext = '.pas') or (ext = '.pp') then begin
314
ext := ChangeFileExt(s, '');
315
Result := AList.IndexOf(ext); //ChangeFileExt(s, '.xml'));
316
if Result >= 0 then begin
317
//AList.Delete(Result); //don't search any more
321
Until FindNext(info)<>0;
328
procedure TFCLDocPackage.ReadConfig;
330
inherited ReadConfig;
331
if FSrcDirs = nil then
332
FSrcDirs := TStringList.Create;
333
Config.ReadSection('SrcDirs', FSrcDirs);
336
function TFCLDocPackage.CreateProject(APrj: TFPDocHelper; const AFile: string
341
dirs, descs: TStringList;
344
(* Add Lazarus FCL, and explicit or added or all FCL dirs
346
if APrj.Package <> nil then
347
exit(True); //already configured
348
Result:=inherited CreateProject(APrj, AFile);
349
descs := TStringList.Create;
351
if AltDir <> '' then begin
352
(* Add inputs for all descrs found in AltDir.
353
For *MakeSkel* add all units in the selected(!) fcl packages to inputs.
354
How to distinguish both modes?
356
s := Manager.LazarusDir + 'docs' + DirectorySeparator + 'xml' + DirectorySeparator + 'fcl';
357
//APrj.ParseFPDocOption(Format('--descr-dir="%s"', [s])); //todo: add includes
358
//APrj.AddDirToFileList(descs, s, '*.xml');
359
ListUnits(s+ DirectorySeparator+ '*.xml', descs); //exclude NoParseUnits
360
descs.Sorted := True;
363
dirs := TStringList.Create; //use prepared list?
364
s := Manager.FFpcDir + 'packages' + DirectorySeparator;
366
//now match all files in the source dirs
367
for i := dirs.Count - 1 downto 0 do begin
368
d := s + dirs[i] + DirectorySeparator + 'src';
369
if not DirectoryExists(d) then continue; //can this happen?
370
(* skip explicitly excluded packages, and exclude selected units.
373
and assigned(FSrcDirs)
374
and (SrcDirs.IndexOfName(dirs[i]) >= 0)
375
and (SrcDirs.Values[dirs[i]] <= '0');
377
//Manager.DoLog('Skipping directory ' + dirs[i]);
380
or (assigned(FSrcDirs)
381
and (SrcDirs.IndexOfName(dirs[i]) >= 0)
382
and (SrcDirs.Values[dirs[i]] > '0'))
383
or (MatchUnits(d, descs) >= 0); //!!! descs now is empty!
386
Manager.DoLog('Adding directory ' + dirs[i]);
387
APrj.ParseFPDocOption(Format('--input-dir="%s"', [d])); //todo: add includes?
391
//exclude explicit units - only for FCL?
392
if Manager.ExcludeUnits and (Manager.NoParseUnits.Count > 0) then begin
394
for i := APrj.InputList.Count - 1 downto 0 do begin
395
s := ExtractUnitName(APrj.InputList, i);
396
if Manager.NoParseUnits.IndexOf(s) >= 0 then //case?
397
APrj.InputList.Delete(i);
400
//re-create project? The normal project was already created by inherited!
401
if AFile <> '' then begin
402
f := ChangeFileExt(AFile, '_ext.xml'); //preserve unmodified project?
403
APrj.CreateProjectFile(f);
404
end; // else APrj.CreateProjectFile(Manager.RootDir + 'fcl_ext.xml'); //preserve unmodified project?
412
procedure TDocPackage.SetDescrDir(AValue: string);
414
if FDescrDir=AValue then Exit;
418
procedure TDocPackage.SetCompOpts(AValue: string);
420
(* collect all compiler options
422
if FCompOpts=AValue then Exit;
423
if AValue = '' then exit;
424
if FCompOpts = '' then
427
FCompOpts:= FCompOpts + ' ' + AValue;
430
procedure TDocPackage.SetAltDir(AValue: string);
432
AValue:=FixPath(AValue);
433
if FAltDir=AValue then Exit;
435
//we must signal config updated
436
Config.WriteString(SecDoc, 'AltDir', AltDir);
439
procedure TDocPackage.SetAllDirs(AValue: boolean);
441
if FAllDirs=AValue then Exit;
445
function TDocPackage.GetAltDir: string;
454
procedure TDocPackage.SetDescriptions(AValue: TStrings);
455
(* Shall we allow for multiple descriptions? (general + OS specific!?)
458
if FDescriptions=AValue then Exit;
459
if AValue = nil then exit; //clear?
460
if AValue.Count = 0 then exit;
461
FDescriptions.Assign(AValue);
464
(* Requires[] only contain package names.
465
Internal use: Get/Set CommaText
467
procedure TDocPackage.SetRequires(AValue: TStrings);
474
FRequires.Clear; //assume full replace
475
for i := 0 to AValue.Count - 1 do begin
476
s := AValue[i]; //<name.xct>,<prefix>
477
FRequires.Add(ExtractImportName(s)); // + '=' + s);
482
if FRequires=AValue then Exit;
483
if AValue = nil then exit;
484
if AValue.Count = 0 then exit;
488
procedure TDocPackage.SetUnits(AValue: TStrings);
495
FUnits.Clear; //assume full replace
496
for i := 0 to AValue.Count - 1 do begin
497
s := AValue[i]; //filespec
498
FUnits.Add(ExtractUnitName(AValue, i) + '=' + s);
503
if FUnits=AValue then Exit;
504
if AValue = nil then exit;
505
if AValue.Count = 0 then exit;
506
//import formatted: <unit>=<descr file> (multiple???)
507
if Pos('=', AValue[0]) > 0 then
508
FUnits.Assign(AValue) //clears previous content
509
else //if AValue.Count > 0 then
513
procedure TDocPackage.SetIncludePath(AValue: string);
515
if FIncludePath=AValue then Exit;
516
FIncludePath:=AValue;
519
procedure TDocPackage.SetInputDir(AValue: string);
521
if FInputDir=AValue then Exit;
525
procedure TDocPackage.SetLazPkg(AValue: string);
527
if FLazPkg=AValue then Exit;
528
if AValue = '' then exit;
530
FProjectDir := ExtractFilePath(AValue);
534
procedure TDocPackage.SetLoaded(AValue: boolean);
536
if FLoaded=AValue then Exit;
540
if Manager.RegisterPackage(self) < 0 then //now definitely loaded
543
UpdateConfig; //create INI file when loaded
546
procedure TDocPackage.SetName(AValue: string);
548
if FName=AValue then Exit;
553
procedure TDocPackage.SetProjectDir(AValue: string);
555
if FProjectDir=AValue then Exit;
559
procedure TDocPackage.SetProjectFile(AValue: string);
561
if FProjectFile=AValue then Exit;
562
FProjectFile:=AValue;
564
if FProjectFile = '' then
566
ProjectDir:=ExtractFilePath(FProjectFile);
567
if ExtractFileExt(FProjectFile) <> '.xml' then
568
; //really change here???
569
//import requires fpdocproject - must be created by Manager!
572
procedure TDocPackage.SetUnitPath(AValue: string);
574
if FUnitPath=AValue then Exit;
579
constructor TDocPackage.Create;
581
FUnits := TStringList.Create;
582
FDescriptions := TStringList.Create;
583
FRequires := TStringList.Create;
584
//Config requires valid Name -> in SetName
587
destructor TDocPackage.Destroy;
591
FreeAndNil(FDescriptions);
592
FreeAndNil(FRequires);
593
FreeAndNil(FSrcDirs);
597
(* Create new(?) project.
598
Usage: after LoadLpk, in general for configured project (user options!)
599
(more options to come)
601
function TDocPackage.CreateProject(APrj: TFPDocHelper; const AFile: string): boolean;
608
Result := APrj.Package <> nil; //already configured?
611
Result := ProjectDir <> '';
613
exit; //dir must be known
615
APrj.ParseFPDocOption('--package=' + Name); //selects or creates the pkg
616
pkg := APrj.SelectedPackage;
618
//todo: common options? OS options?
619
for i := 0 to Units.Count - 1 do begin
620
s := Units.ValueFromIndex[i];
621
if CompOpts <> '' then
622
s := s + ' ' + CompOpts;
623
//add further options?
626
//add Descriptions - either explicit or implicit
627
if (DescrDir <> '') and (Descriptions.Count = 0) then begin
628
//first check for existing directory
629
if not DirectoryExists(DescrDir) then begin
630
MkDir(DescrDir); //exclude \?
631
end else if Descriptions.Count = 0 then begin
632
APrj.ParseFPDocOption('--descr-dir=' + DescrDir); //adds all XML files
635
APrj.DescrDir := DescrDir; //needed by Update
636
for i := 0 to Descriptions.Count - 1 do begin
637
s := Descriptions[i];
638
if Pos('=', s) > 0 then
639
pkg.Descriptions.Add(Descriptions.ValueFromIndex[i])
641
pkg.Descriptions.Add(s);
644
if AltDir <> '' then begin
646
s := Manager.LazarusDir + AltDir;
648
if ForceDirectories(s) then begin
649
//exclude NoParse units
650
//APrj.ParseFPDocOption(Format('--descr-dir="%s"', [s]));
651
lst := TStringList.Create;
652
ListUnits(AltDir + '*.xml', lst); //unit names only
653
(* add the unit names from lst to the pkg/project
655
pkg := APrj.SelectedPackage;
656
for i := 0 to lst.Count - 1 do begin
657
s := AltDir + lst[i] + '.xml';
658
pkg.Descriptions.Add(s);
664
for i := 0 to Requires.Count - 1 do begin
667
imp := Manager.RootDir + s;
669
imp := Manager.RootDir + s + '.xct,../' + s + '/'; //valid for HTML, not for CHM!
671
APrj.ParseFPDocOption('--import=' + imp);
674
APrj.Options.Assign(Manager.Options);
675
//debug, looks good here!?
676
if APrj.Options.Backend = '' then
677
Manager.DoLog('No format, should be ' + Manager.Options.Backend);
678
pkg.Output := Manager.RootDir + Name; //???
679
pkg.ContentFile := Manager.RootDir + Name + '.xct';
680
//now create project file
681
if AFile <> '' then begin
682
if ExtractFileExt(AFile) <> '.xml' then
683
FProjectFile := ExtractFilePath(AFile) + Name + '_prj.xml'
685
FProjectFile := AFile;
686
APrj.CreateProjectFile(ProjectFile);
688
Result := True; //assume okay
691
(* Init from TFPDocPackage, into which AFile has been loaded.
693
function TDocPackage.ImportProject(APrj: TFPDocHelper; APkg: TFPDocPackage; const AFile: string): boolean;
702
s := UnitFile(APkg.Inputs, 0);
704
FUnitPath := ExtractFilePath(s);
705
s := UnitFile(APkg.Descriptions, 0);
707
FDescrDir := ExtractFilePath(s);
708
//project file - empty if not applicable (multi-package project?!)
709
if (AFile <> '') and (APrj.Packages.Count = 1) then
710
ProjectFile := AFile //only if immediately applicable!
712
ProjectDir := ExtractFilePath(AFile);
714
Units := APkg.Inputs;
715
Descriptions := APkg.Descriptions;
716
Requires := APkg.Imports;
724
procedure TDocPackage.ReadConfig;
731
Config := TConfigFile.Create(IniFileName);
733
s := Config.ReadString(SecDoc, 'projectdir', '');
735
FreeAndNil(Config); //must create and fill later!
736
exit; //project directory MUST be known
738
ProjectFile := Config.ReadString(SecDoc, 'projectfile', '');
739
FInputDir := Config.ReadString(SecDoc, 'inputdir', '');
740
FCompOpts := Config.ReadString(SecDoc, 'options', '');
741
FDescrDir := Config.ReadString(SecDoc, 'descrdir', '');
742
FAltDir := Config.ReadString(SecDoc, 'AltDir', '');
743
FAllDirs:= Config.ReadBool(SecDoc, 'AllDirs', False);
744
Requires.CommaText := Config.ReadString(SecDoc, 'requires', '');
746
Config.ReadSection('units', Units);
747
Config.ReadSection('descrs', Descriptions);
753
(* Initialize the package, write global config (+local?)
755
procedure TDocPackage.UpdateConfig;
757
//create ini file, if not already created
759
Config := TConfigFile.Create(IniFileName); //in document RootDir
760
//general information
761
Config.WriteString(SecDoc, 'projectdir', ProjectDir);
762
Config.WriteString(SecDoc, 'projectfile', ProjectFile);
763
Config.WriteString(SecDoc, 'inputdir', InputDir);
764
Config.WriteString(SecDoc, 'options', CompOpts);
765
Config.WriteString(SecDoc, 'descrdir', DescrDir);
766
Config.WriteString(SecDoc, 'AltDir', FAltDir);
767
Config.WriteBool(SecDoc, 'AllDirs', AllDirs);
768
Config.WriteString(SecDoc, 'requires', Requires.CommaText);
770
Config.WriteSectionValues('units', Units);
771
Config.WriteSectionValues('descrs', Descriptions);
772
Config.WriteSectionValues('SrcDirs', SrcDirs);
778
procedure TDocPackage.EnumUnits(AList: TStrings);
782
//override to add further units (from AltDir...)
783
for i := 0 to Units.Count - 1 do
784
AList.Add(Units.Names[i]);
787
function TDocPackage.DescrFileName(const AUnit: string): string;
789
(* [ProjectDir +] DescrDir + AUnit + .xml
792
if (Result = '') or (Result[1] = '.') then
793
Result := ProjectDir + Result;
794
Result := Result + DirectorySeparator + AUnit + '.xml';
797
function TDocPackage.IniFileName: string;
799
Result := Manager.RootDir + Name + '.ini';
802
procedure TDocPackage.AddUnit(const AFile: string);
806
s := ExtractUnitName(AFile);
808
Manager.DoLog('No unit: ' + AFile)
810
Units.Add(s + '=' + AFile);
815
constructor TFPDocManager.Create(AOwner: TComponent);
819
inherited Create(AOwner);
820
lst := TStringList.Create;
821
lst.OwnsObjects := True;
823
FOptions := TCmdOptions.Create;
824
FNoParseUnits := TStringList.Create;
827
destructor TFPDocManager.Destroy;
831
//FPackages.Clear; //destructor seems NOT to clear/destroy owned object!?
832
FreeAndNil(FPackages);
833
FreeAndNil(FOptions);
834
FreeAndNil(FNoParseUnits);
838
procedure TFPDocManager.SetFPDocDir(AValue: string);
840
if FFPDocDir=AValue then Exit;
842
Config.WriteString(SecGen, 'FpcDocDir', FpcDocDir);
845
procedure TFPDocManager.SetFpcDir(AValue: string);
847
if FFpcDir=AValue then Exit;
849
Config.WriteString(SecGen, 'FpcDir', FpcDir);
852
procedure TFPDocManager.SetExcludedUnits(AValue: boolean);
854
if FExcludedUnits=AValue then Exit;
855
FExcludedUnits:=AValue;
856
Config.WriteBool(SecGen, 'ExcludeUnits', AValue);
859
procedure TFPDocManager.UpdatePackage(const AName: string);
865
if LazarusDir = '' then exit;
866
s := {LazarusDir +} 'docs/xml/'+AName;
867
if not DirectoryExists(FixPath(LazarusDir + s)) then
869
i := Packages.IndexOfName(AName);
872
pkg := Packages.Objects[i] as TDocPackage;
873
pkg.AltDir := s; //add descriptors when configuring the project/helper
876
function TFPDocManager.UpdateFCL(enabled: boolean): boolean;
880
(* Adding to the FCL requires valid FPC and Lazarus directories (caller checks).
881
Then laz/docs/xml/fcl/ is added to fpc descr-dirs.
882
The related units have to be added as input-dirs.
883
Scan fpc/packages/ for candidates.
886
pkg := AddPackage('fcl') as TFCLDocPackage;
890
pkg.AltDir := 'docs/xml/fcl'
896
procedure TFPDocManager.SetLazarusDir(AValue: string);
898
if FLazarusDir=AValue then Exit;
900
Config.WriteString(SecGen, 'LazarusDir', FLazarusDir);
901
//update RTL and FCL - if exist and Dir exists
902
UpdatePackage('rtl');
903
UpdatePackage('fcl');
906
function TFPDocManager.GetNoParseUnits: TStrings;
908
Result := FNoParseUnits;
911
procedure TFPDocManager.SetNoParseUnits(AValue: TStrings);
913
FNoParseUnits.Assign(AValue);
914
FNoParseUnits.Sorted := True;
915
Config.WriteSection('NoParseUnits', NoParseUnits);
918
procedure TFPDocManager.SetOnChange(AValue: TNotifyEvent);
920
if FOnChange=AValue then Exit;
924
procedure TFPDocManager.SetPackage(AValue: TDocPackage);
926
if FPackage=AValue then Exit;
930
procedure TFPDocManager.SetProfile(AValue: string);
932
if AValue = '' then exit;
933
if FProfile=AValue then Exit;
934
if Options.Modified then
935
Options.SaveConfig(Config, FProfile);
937
if not Config.SectionExists(AValue) then begin
938
FProfiles := FProfiles + ',' + AValue;
939
Config.WriteString(SecGen, 'Profiles', FProfiles);
941
Config.WriteString(SecGen, 'Profile', FProfile);
942
Options.LoadConfig(Config, Profile);
945
(* Try load config from new dir - this may fail on the first run.
947
procedure TFPDocManager.SetRootDir(AValue: string);
951
s := IncludeTrailingPathDelimiter(AValue);
952
if FRootDir=s then Exit; //prevent recursion
954
//load config - not here!
957
procedure TFPDocManager.Changed;
959
if not Modified or (UpdateCount > 0) then
960
exit; //should not be called directly
962
if Assigned(OnChange) then
966
function TFPDocManager.BeginTest(const ADir: string): boolean;
968
Helper.Free; //should have been done
969
Helper := TFPDocHelper.Create(nil);
970
Helper.OnLog := OnLog;
971
Result := Helper.BeginTest(ADir);
973
Helper.CmdOptions := Options; //set reference AND propagate!?
976
procedure TFPDocManager.EndTest;
978
SetCurrentDir(ExtractFileDir(RootDir));
982
procedure TFPDocManager.BeginUpdate;
987
procedure TFPDocManager.EndUpdate;
990
if UpdateCount <= 0 then begin
998
Init RootDir (only when config found?)
999
Try load packages from their INI files
1001
function TFPDocManager.LoadConfig(const ADir: string; Force: boolean): boolean;
1006
s := IncludeTrailingPathDelimiter(ADir);
1007
cf := s + ConfigName;
1008
Result := FileExists(cf);
1009
if not Result and not Force then
1011
RootDir:=s; //recurse if RootDir changed
1012
//sanity check: only one config file!
1013
if assigned(Config) then begin
1014
if (Config.FileName = cf) then
1015
exit(false) //nothing new?
1020
Config := TConfigFile.Create(cf);
1021
//Config.CacheUpdates := True;
1023
exit; //nothing to read
1025
FFpcDir := Config.ReadString(SecGen, 'FpcDir', '');
1026
FFPDocDir := Config.ReadString(SecGen, 'FpcDocDir', '');
1027
FLazarusDir:=Config.ReadString(SecGen, 'LazarusDir', '');
1029
Config.ReadSection(SecProjects, FPackages); //<prj>=<file>
1030
//read detailed package information - possibly multiple packages per project!
1031
BeginUpdate; //turn of app notification!
1032
for i := 0 to Packages.Count - 1 do begin
1033
//read package config (=project file name?)
1034
s := Packages.Names[i];
1035
pf := Packages.ValueFromIndex[i];
1036
if pf <> '' then begin
1037
AddProject(s, pf); //add and load project file, don't update config!
1038
FModified := True; //force app notification
1041
//more? (preferences?)
1042
FProfiles:=Config.ReadString(SecGen, 'Profiles', 'default');
1043
FProfile := Config.ReadString(SecGen,'Profile', 'default');
1044
Options.LoadConfig(Config, Profile);
1045
FExcludedUnits := Config.ReadBool(SecGen, 'ExcludeUnits', True);
1046
Config.ReadSection('NoParseUnits', NoParseUnits);
1047
//done, nothing modified
1051
function TFPDocManager.SaveConfig: boolean;
1053
//Options? assume saved by application?
1054
if Options.Modified then begin
1055
Options.SaveConfig(Config, Profile);
1058
Result := True; //for now
1061
procedure TFPDocManager.AddProfile(const AName: string);
1063
//add and select - obsolete!
1067
(* Add a DocPackage to Packages and INI.
1068
Return package Index.
1069
For exclusive use by Package.SetLoaded!
1071
function TFPDocManager.RegisterPackage(APkg: TDocPackage): integer;
1073
Result := Packages.IndexOfName(APkg.Name);
1074
if Result < 0 then begin
1076
Result := Packages.AddObject(APkg.Name + '=' + APkg.ProjectFile, APkg);
1077
end else if Packages.Objects[Result] = nil then
1078
Packages.Objects[Result] := APkg;
1079
if APkg.Loaded then begin
1080
//check/create project file?
1081
if APkg.ProjectFile = '' then begin
1082
if APkg.ProjectDir = '' then begin
1083
DoLog('Missing project directory for package ' + APkg.Name);
1086
APkg.ProjectFile := APkg.ProjectDir + APkg.Name; //to be fixed by pkg
1088
if (ExtractFileExt(APkg.ProjectFile) <> '.xml') then begin
1089
//create project file
1090
APkg.ProjectFile := ChangeFileExt(APkg.ProjectFile, '_prj.xml');
1091
CreateProject(APkg.ProjectFile, APkg);
1092
//update Packages[] string
1093
Packages[Result] := APkg.Name + '=' + APkg.ProjectFile;
1095
Config.WriteString(SecProjects, APkg.Name, APkg.ProjectFile);
1100
(* Load FPDoc (XML) project file.
1104
function TFPDocManager.AddProject(const APkg, AFile: string): boolean;
1110
pkg := AddPackage(APkg);
1112
exit(True); //assume registered!?
1113
//check project file
1114
if ExtractFileExt(AFile) <> '.xml' then begin
1115
DoLog('Not a project file: ' + AFile);
1118
if not FileExists(AFile) then begin
1119
DoLog('Missing project file: ' + AFile);
1125
//load the project file into Helper
1126
Helper.LoadProjectFile(AFile);
1127
if Helper.Packages.Count = 1 then begin
1128
Helper.Package := Helper.Packages[0]; //in LoadProject?
1129
Result := pkg.ImportProject(Helper, Helper.Package, AFile);
1133
for i := 0 to Helper.Packages.Count - 1 do begin
1134
Helper.Package := Helper.Packages[i];
1135
pkg := AddPackage(Helper.Package.Name);
1137
continue; //already initialized
1138
pkg.ImportProject(Helper, Helper.Package, '');
1145
(* Ask DocPackage to create an projectfile.
1146
Overwrite if exists???
1147
AFileName is any file in the project directory, required for CD!
1148
!!! prevent recursive calls, destroying Helper !!!
1150
function TFPDocManager.CreateProject(const AFileName: string; APkg: TDocPackage
1153
if Helper = nil then begin
1154
BeginTest(AFileName); //CD into project dir
1156
Result := APkg.CreateProject(Helper, AFileName);
1161
//assume that Helper IS for APkg
1162
Result := APkg.CreateProject(Helper, AFileName);
1166
(* Return the named package, create if not found.
1169
function TFPDocManager.AddPackage(AName: string): TDocPackage;
1173
AName := LowerCase(AName);
1174
i := FPackages.IndexOfName(AName);
1178
Result := FPackages.Objects[i] as TDocPackage;
1179
if Result = nil then begin
1181
if AName = 'fcl' then
1182
Result := TFCLDocPackage.Create
1186
Result := TDocPackage.Create;
1187
Result.Name := AName; //triggers load config --> register
1188
i := FPackages.IndexOfName(AName); //already registered?
1191
//we MUST create an entry
1192
Packages.AddObject(AName + '=' + Result.ProjectFile, Result);
1196
function TFPDocManager.IsExtended(const APkg: string): string;
1201
pkg := AddPackage(APkg);
1205
Result := pkg.AltDir;
1211
function TFPDocManager.ImportLpk(const AFile: string): TDocPackage;
1214
//import the LPK file into? Here: TDocPackage, could be FPDocProject?
1215
Result := uLpk.ImportLpk(AFile);
1216
if Result = nil then
1217
DoLog('Import failed on ' + AFile)
1219
Result.Loaded := True; //import and write config file
1224
(* Add the project, just created from cmdline or projectfile
1226
procedure TFPDocManager.ImportProject(APkg: TFPDocPackage; const AFile: string);
1230
pkg := AddPackage(APkg.Name);
1231
pkg.ImportProject(Helper, APkg, AFile);
1233
Config.WriteString(SecProjects, pkg.Name, AFile);
1239
function TFPDocManager.ImportCmd(const AFile: string): boolean;
1244
BeginTest(AFile); //directory!!!
1246
Result := Helper.CmdToPrj(AFile);
1249
pkg := AddPackage(Helper.SelectedPackage.Name); //create [and register]
1250
pkg.Loaded := False; //force reload
1251
if not pkg.Loaded then begin
1252
Result := pkg.ImportProject(Helper, Helper.Package, AFile);
1261
function TFPDocManager.MakeDoc(APkg: TDocPackage; const AUnit, AOutput: string): boolean;
1263
Result := assigned(APkg)
1264
and BeginTest(APkg.ProjectDir)
1265
and APkg.CreateProject(Helper, ''); //only configure, don't create file
1269
Helper.ParseFPDocOption(Format('--output="%s"', [AOutput]));
1270
if Options.Backend = 'chm' then begin
1271
Helper.ParseFPDocOption('--auto-toc');
1272
Helper.ParseFPDocOption('--auto-index');
1274
Helper.ParseFPDocOption('--make-searchable'); //always?
1276
Helper.CreateUnitDocumentation(AUnit, False);
1282
function TFPDocManager.TestRun(APkg: TDocPackage; AUnit: string): boolean;
1284
BeginTest(APkg.ProjectFile);
1287
Result := Helper.TestRun(APkg, AUnit);
1289
on E: EParserError do
1290
DoLog(Format('%s(%d,%d): %s',[e.Filename, e.Row, e.Column, e.Message]));
1299
function TFPDocManager.Update(APkg: TDocPackage; const AUnit: string): boolean;
1301
Result := assigned(APkg)
1302
and BeginTest(APkg.ProjectFile);
1306
Result := APkg.CreateProject(Helper, ''); //only configure, don't create file
1309
Result := Helper.Update(APkg, AUnit);
1315
procedure TFPDocManager.DoLog(const Msg: String);
1317
if Assigned(FOnLog) then
1323
constructor TFPDocHelper.Create(AOwner: TComponent);
1325
inherited Create(AOwner);
1326
InputList := TStringList.Create;
1327
DescrList := TStringList.Create;
1330
destructor TFPDocHelper.Destroy;
1332
FreeAndNil(InputList);
1333
FreeAndNil(DescrList);
1337
(* Prepare MakeSkel on temporary FPDocPackage
1339
function TFPDocHelper.BeginTest(APkg: TDocPackage): boolean;
1341
if not assigned(APkg) then
1343
Result := BeginTest(APkg.ProjectFile); //directory would be sufficient!
1346
APkg.CreateProject(self, ''); //create project file?
1347
Package := Packages.FindPackage(APkg.Name);
1350
Result := assigned(Package);
1353
procedure TFPDocHelper.EndTest;
1358
function TFPDocHelper.BeginTest(ADir: string): boolean;
1360
Result := ADir <> '';
1364
if ExtractFileExt(ADir) <> '' then //todo: better check for directory!?
1365
ADir := ExtractFileDir(ADir);
1367
SetCurrentDir(ProjectDir);
1370
(* Create a project from an FPDoc commandline.
1371
Do NOT create an project file!(?)
1373
function TFPDocHelper.CmdToPrj(const AFileName: string): boolean;
1378
Result := False; //in case of errors
1379
//read the commandline
1380
InputList.LoadFromFile(AFileName);
1381
for i := 0 to InputList.Count - 1 do begin
1383
w := GetNextWord(l);
1384
if w = 'fpdoc' then begin //contains!?
1385
Result := True; //so far
1386
break; //fpdoc command found
1393
while l <> '' do begin
1394
w := GetNextWord(l);
1395
ParseFPDocOption(w);
1400
function TFPDocHelper.MakeDocs(APkg: TDocPackage; const AUnit: string;
1401
AOutput: string): boolean;
1403
Result := BeginTest(APkg); //configure and select package
1407
ParseFPDocOption(Format('--output="%s"', [AOutput]));
1408
CreateDocumentation(Package, False);
1414
function TFPDocHelper.TestRun(APkg: TDocPackage; AUnit: string): boolean;
1416
(* more detailed error handling?
1417
Must CD to the project file directory!?
1419
Result := BeginTest(APkg);
1423
//override options for test
1424
ParseFPDocOption('--format=html');
1425
ParseFPDocOption('-v');
1426
ParseFPDocOption('-n');
1428
CreateUnitDocumentation(AUnit, True);
1434
(* MakeSkel functionality - create skeleton or update file
1435
using temporary Project
1437
function TFPDocHelper.Update(APkg: TDocPackage; const AUnit: string): boolean;
1439
function DocumentUnit(const AUnit: string): boolean;
1441
OutName, msg: string;
1443
if Manager.NoParseUnits.IndexOf(AUnit) >= 0 then begin
1444
DoLog('NoParse ' + AUnit);
1448
InputList.Add(UnitSpec(AUnit));
1450
OutName := AUnit + '.xml';
1451
if DescrDir <> '' then
1452
OutName := IncludeTrailingBackslash(DescrDir) + OutName;
1453
CmdOptions.UpdateMode := FileExists(OutName);
1454
if CmdOptions.UpdateMode then begin
1455
DescrList.Add(OutName);
1456
OutName:=Manager.RootDir + 'upd.' + AUnit + '.xml';
1457
DoLog('Update ' + OutName);
1459
DoLog('Create ' + OutName);
1461
msg := DocumentPackage(APkg.Name, OutName, InputList, DescrList);
1465
else if CmdOptions.UpdateMode then begin
1474
Result := BeginTest(APkg);
1477
if AUnit <> '' then begin
1478
Result := DocumentUnit(AUnit);
1480
for i := 0 to Package.Inputs.Count - 1 do begin
1481
u := ExtractUnitName(Package.Inputs, i);
1488
procedure TFPDocHelper.SetProjectDir(AValue: string);
1490
if FProjectDir=AValue then Exit;
1491
FProjectDir:=AValue;