mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-05 07:23:53 +02:00
1496 lines
41 KiB
ObjectPascal
1496 lines
41 KiB
ObjectPascal
unit uManager;
|
|
(* Manager object for FPDoc GUI, by DoDi
|
|
Holds configuration and packages.
|
|
|
|
Packages (shall) contain extended descriptions for:
|
|
- default OSTarget (FPCDocs: Unix/Linux)
|
|
- inputs: by OSTarget
|
|
- directories: project(file), InputDir, DescrDir[by language?]
|
|
- FPCVersion, LazVersion: variations of inputs
|
|
- Skeleton and Output options, depending on DocType/Level and Format.
|
|
Units can be described in multiple XML docs, so that it's possible to
|
|
have specific parts depending on Laz/FPC version, OSTarget, Language, Widgetset.
|
|
|
|
This version is decoupled from the fpdoc classes, introduces the classes
|
|
TFPDocManager for all packages
|
|
TDocPackage for a single package
|
|
TFPDocHelper for fpdoc projects
|
|
*)
|
|
|
|
(* Currently registered writers:
|
|
TFPDocWriter in 'dwriter.pp'
|
|
template: TTemplateWriter(TFPDocWriter) in 'dw_tmpl.pp'
|
|
man: TMANWriter(TFPDocWriter) in 'dw_man.pp' --> <pkg>.man /unit.
|
|
dxml: TDXMLWriter(TFPDocWriter) in 'dw_dxml.pp'
|
|
xml: TXMLWriter(TFPDocWriter) in 'dw_xml.pp'
|
|
html: THTMLWriter(TFPDocWriter) in 'dw_html.pp'
|
|
htm: THTMWriter(THTMLWriter)
|
|
chm: TCHMHTMLWriter(THTMLWriter)
|
|
TLinearWriter in 'dwlinear.pp'
|
|
template: TTemplateWriter(TLinearWriter) in 'dw_lintmpl.pp'
|
|
ipf: TIPFNewWriter(TLinearWriter) in 'dw_ipflin.pas'
|
|
latex: TLaTeXWriter(TLinearWriter) in 'dw_latex.pp'
|
|
rtf: TRTFWriter(TLinearWriter) in 'dw_linrtf.pp'
|
|
txt: TTXTWriter(TLinearWriter) in 'dw_txt.pp'
|
|
|
|
TLinearWriter based writers create an single output file for a package:
|
|
<path>/pkg .<ext>
|
|
TFPDocWriter based writers create an file for every module:
|
|
<path>/pkg /unit.<ext>
|
|
|
|
*)
|
|
{$mode objfpc}{$H+}
|
|
|
|
{$DEFINE EasyImports} //EasyImports.patch applied?
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils,
|
|
umakeskel, ConfigFile, fpdocproj, dw_HTML;
|
|
|
|
type
|
|
TFPDocHelper = class;
|
|
|
|
{ TDocPackage }
|
|
|
|
(* TDocPackage describes a package documentation project.
|
|
*)
|
|
TDocPackage = class
|
|
private
|
|
FAllDirs: boolean;
|
|
FAltDir: string;
|
|
FCompOpts: string;
|
|
FDescrDir: string;
|
|
FDescriptions: TStrings;
|
|
FIncludePath: string;
|
|
FInputDir: string;
|
|
FLazPkg: string;
|
|
FLoaded: boolean;
|
|
FName: string;
|
|
FProjectDir: string;
|
|
FProjectFile: string;
|
|
FSrcDirs: TStrings;
|
|
FRequires: TStrings;
|
|
FUnitPath: string;
|
|
FUnits: TStrings;
|
|
function GetAltDir: string;
|
|
procedure SetAllDirs(AValue: boolean);
|
|
procedure SetAltDir(AValue: string);
|
|
procedure SetCompOpts(AValue: string);
|
|
procedure SetDescrDir(AValue: string);
|
|
procedure SetDescriptions(AValue: TStrings);
|
|
procedure SetIncludePath(AValue: string);
|
|
procedure SetInputDir(AValue: string);
|
|
procedure SetLazPkg(AValue: string);
|
|
procedure SetLoaded(AValue: boolean);
|
|
procedure SetName(AValue: string);
|
|
procedure SetProjectDir(AValue: string);
|
|
procedure SetProjectFile(AValue: string);
|
|
procedure SetRequires(AValue: TStrings);
|
|
procedure SetUnitPath(AValue: string);
|
|
procedure SetUnits(AValue: TStrings);
|
|
protected
|
|
Config: TConfigFile;
|
|
procedure ReadConfig; virtual;
|
|
public
|
|
constructor Create; virtual;
|
|
destructor Destroy; override;
|
|
function IniFileName: string;
|
|
function CreateProject(APrj: TFPDocHelper; const AFile: string): boolean; virtual; //new package project
|
|
function ImportProject(APrj: TFPDocHelper; APkg: TFPDocPackage; const AFile: string): boolean;
|
|
procedure UpdateConfig;
|
|
procedure EnumUnits(AList: TStrings); virtual;
|
|
function DescrFileName(const AUnit: string): string;
|
|
property Name: string read FName write SetName;
|
|
property Loaded: boolean read FLoaded write SetLoaded;
|
|
property ProjectFile: string read FProjectFile write SetProjectFile; //xml?
|
|
//from LazPkg
|
|
procedure AddUnit(const AFile: string);
|
|
property AllDirs: boolean read FAllDirs write SetAllDirs;
|
|
property CompOpts: string read FCompOpts write SetCompOpts;
|
|
property LazPkg: string read FLazPkg write SetLazPkg; //LPK name?
|
|
property ProjectDir: string read FProjectDir write SetProjectDir;
|
|
property DescrDir: string read FDescrDir write SetDescrDir;
|
|
property Descriptions: TStrings read FDescriptions write SetDescriptions;
|
|
property AltDir: string read GetAltDir write SetAltDir;
|
|
property InputDir: string read FInputDir write SetInputDir;
|
|
property SrcDirs: TStrings read FSrcDirs;
|
|
property Units: TStrings read FUnits write SetUnits;
|
|
property Requires: TStrings read FRequires write SetRequires; //only string?
|
|
property IncludePath: string read FIncludePath write SetIncludePath; //-Fi
|
|
property UnitPath: string read FUnitPath write SetUnitPath; //-Fu
|
|
end;
|
|
|
|
{ TFCLDocPackage }
|
|
|
|
TFCLDocPackage = class(TDocPackage)
|
|
protected
|
|
procedure ReadConfig; override;
|
|
public
|
|
function CreateProject(APrj: TFPDocHelper; const AFile: string): boolean; override;
|
|
end;
|
|
|
|
{ TFPDocHelper }
|
|
|
|
//holds temporary project
|
|
|
|
TFPDocHelper = class(TFPDocMaker)
|
|
private
|
|
FProjectDir: string;
|
|
procedure SetProjectDir(AValue: string);
|
|
public
|
|
InputList, DescrList: TStringList; //still required?
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
function BeginTest(APkg: TDocPackage): boolean;
|
|
function BeginTest(ADir: string): boolean;
|
|
procedure EndTest;
|
|
function CmdToPrj(const AFileName: string): boolean;
|
|
function TestRun(APkg: TDocPackage; AUnit: string): boolean;
|
|
function Update(APkg: TDocPackage; const AUnit: string): boolean;
|
|
function MakeDocs(APkg: TDocPackage; const AUnit: string; AOutput: string): boolean;
|
|
property ProjectDir: string read FProjectDir write SetProjectDir;
|
|
end;
|
|
|
|
TLogHandler = Procedure (Sender : TObject; Const Msg : String) of object;
|
|
|
|
{ TFPDocManager }
|
|
|
|
(* Holds configuration and package projects.
|
|
*)
|
|
TFPDocManager = class(TComponent)
|
|
private
|
|
FExcludedUnits: boolean;
|
|
FFpcDir: string;
|
|
FFPDocDir: string;
|
|
FLazarusDir: string;
|
|
FModified: boolean;
|
|
FNoParseUnits: TStringList;
|
|
FOnChange: TNotifyEvent;
|
|
FOnLog: TLogHandler;
|
|
FOptions: TCmdOptions;
|
|
FPackage: TDocPackage;
|
|
FPackages: TStrings;
|
|
FProfile: string;
|
|
FProfiles: string; //CSV list of profile names
|
|
FRootDir: string;
|
|
UpdateCount: integer;
|
|
function GetNoParseUnits: TStrings;
|
|
procedure SetExcludedUnits(AValue: boolean);
|
|
procedure SetFpcDir(AValue: string);
|
|
procedure SetFPDocDir(AValue: string);
|
|
procedure SetLazarusDir(AValue: string);
|
|
procedure SetNoParseUnits(AValue: TStrings);
|
|
procedure SetOnChange(AValue: TNotifyEvent);
|
|
procedure SetPackage(AValue: TDocPackage);
|
|
procedure SetProfile(AValue: string);
|
|
procedure SetRootDir(AValue: string);
|
|
protected
|
|
Helper: TFPDocHelper; //temporary
|
|
procedure Changed;
|
|
function BeginTest(const ADir: string): boolean;
|
|
procedure EndTest;
|
|
function RegisterPackage(APkg: TDocPackage): integer;
|
|
Procedure DoLog(Const Msg : String);
|
|
public
|
|
Config: TConfigFile; //extend class
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
procedure BeginUpdate;
|
|
procedure EndUpdate;
|
|
function LoadConfig(const ADir: string; Force: boolean = False): boolean;
|
|
function SaveConfig: boolean;
|
|
procedure AddProfile(const AName: string);
|
|
function AddProject(const APkg, AFile: string): boolean; //from config
|
|
function CreateProject(const AFileName: string; APkg: TDocPackage): boolean;
|
|
function AddPackage(AName: string): TDocPackage;
|
|
function IsExtended(const APkg: string): string;
|
|
function ImportLpk(const AFile: string): TDocPackage;
|
|
procedure ImportProject(APkg: TFPDocPackage; const AFile: string);
|
|
function ImportCmd(const AFile: string): boolean;
|
|
procedure UpdatePackage(const AName: string);
|
|
function UpdateFCL(enabled: boolean): boolean;
|
|
//actions
|
|
function MakeDoc(APkg: TDocPackage; const AUnit, AOutput: string): boolean;
|
|
function TestRun(APkg: TDocPackage; AUnit: string): boolean;
|
|
function Update(APkg: TDocPackage; const AUnit: string): boolean;
|
|
public //published?
|
|
property ExcludeUnits: boolean read FExcludedUnits write SetExcludedUnits;
|
|
property NoParseUnits: TStrings read GetNoParseUnits write SetNoParseUnits;
|
|
property FpcDir: string read FFpcDir write SetFpcDir;
|
|
property FpcDocDir: string read FFPDocDir write SetFPDocDir;
|
|
property LazarusDir: string read FLazarusDir write SetLazarusDir;
|
|
property RootDir: string read FRootDir write SetRootDir;
|
|
property Options: TCmdOptions read FOptions;
|
|
property Profile: string read FProfile write SetProfile;
|
|
property Profiles: string read FProfiles;
|
|
property Packages: TStrings read FPackages;
|
|
property Package: TDocPackage read FPackage write SetPackage;
|
|
property Modified: boolean read FModified; //app
|
|
property OnChange: TNotifyEvent read FOnChange write SetOnChange;
|
|
Property OnLog : TLogHandler Read FOnLog Write FOnLog;
|
|
end;
|
|
|
|
var
|
|
Manager: TFPDocManager = nil; //init by application
|
|
|
|
function FixPath(const s: string): string;
|
|
procedure ListDirs(const ARoot: string; AList: TStrings);
|
|
procedure ListUnits(const AMask: string; AList: TStrings);
|
|
function MatchUnits(const ADir: string; AList: TStrings): integer;
|
|
|
|
implementation
|
|
|
|
uses
|
|
uLpk, PParser;
|
|
|
|
const
|
|
ConfigName = 'docmgr.ini';
|
|
SecProjects = 'projects';
|
|
SecGen = 'dirs';
|
|
SecDoc = 'project';
|
|
|
|
function FixPath(const s: string): string;
|
|
var
|
|
c: string;
|
|
begin
|
|
if DirectorySeparator = '/' then
|
|
c := '\'
|
|
else
|
|
c := '/';
|
|
Result := StringReplace(s, c, DirectorySeparator, [rfReplaceAll]);
|
|
end;
|
|
|
|
procedure ListDirs(const ARoot: string; AList: TStrings);
|
|
var
|
|
Info : TSearchRec;
|
|
s: string;
|
|
begin
|
|
if FindFirst (ARoot+'/*',faDirectory,Info)=0 then begin
|
|
repeat
|
|
if not ((Info.Attr and faDirectory) = faDirectory) then
|
|
continue;
|
|
s := Info.Name;
|
|
if (s[1] <> '.')
|
|
and (AList.IndexOf(s) < 0) then //exclude dupes
|
|
AList.Add(s); //name only, allow to create relative refs
|
|
until FindNext(info)<>0;
|
|
end;
|
|
FindClose(Info);
|
|
end;
|
|
|
|
procedure ListUnits(const AMask: string; AList: TStrings);
|
|
var
|
|
Info : TSearchRec;
|
|
s, f: string;
|
|
begin
|
|
if FindFirst (AMask,faArchive,Info)=0 then begin
|
|
repeat
|
|
s := Info.Name;
|
|
if s[1] <> '.' then begin
|
|
f := ChangeFileExt(s, ''); //unit name only
|
|
if Manager.ExcludeUnits and (Manager.NoParseUnits.IndexOf(f) >= 0) then
|
|
continue; //excluded unit!
|
|
AList.Add(f);
|
|
end;
|
|
until FindNext(info)<>0;
|
|
end;
|
|
FindClose(Info);
|
|
end;
|
|
|
|
function MatchUnits(const ADir: string; AList: TStrings): integer;
|
|
var
|
|
Info : TSearchRec;
|
|
s, ext: string;
|
|
begin
|
|
Result := -1;
|
|
if FindFirst(ADir+DirectorySeparator+'*',faArchive,Info)=0 then begin
|
|
repeat
|
|
s := Info.Name;
|
|
ext := ExtractFileExt(s);
|
|
if (ext = '.pas') or (ext = '.pp') then begin
|
|
ext := ChangeFileExt(s, '');
|
|
Result := AList.IndexOf(ext); //ChangeFileExt(s, '.xml'));
|
|
if Result >= 0 then begin
|
|
//AList.Delete(Result); //don't search any more
|
|
break;
|
|
end;
|
|
end;
|
|
Until FindNext(info)<>0;
|
|
end;
|
|
FindClose(Info);
|
|
end;
|
|
|
|
{ TFCLDocPackage }
|
|
|
|
procedure TFCLDocPackage.ReadConfig;
|
|
begin
|
|
inherited ReadConfig;
|
|
if FSrcDirs = nil then
|
|
FSrcDirs := TStringList.Create;
|
|
Config.ReadSection('SrcDirs', FSrcDirs);
|
|
end;
|
|
|
|
function TFCLDocPackage.CreateProject(APrj: TFPDocHelper; const AFile: string
|
|
): boolean;
|
|
var
|
|
i: integer;
|
|
s, d, f: string;
|
|
dirs, descs: TStringList;
|
|
incl, excl: boolean;
|
|
begin
|
|
(* Add Lazarus FCL, and explicit or added or all FCL dirs
|
|
*)
|
|
if APrj.Package <> nil then
|
|
exit(True); //already configured
|
|
Result:=inherited CreateProject(APrj, AFile);
|
|
descs := TStringList.Create;
|
|
//add lazdir
|
|
if AltDir <> '' then begin
|
|
(* Add inputs for all descrs found in AltDir.
|
|
For *MakeSkel* add all units in the selected(!) fcl packages to inputs.
|
|
How to distinguish both modes?
|
|
*)
|
|
s := Manager.LazarusDir + 'docs' + DirectorySeparator + 'xml' + DirectorySeparator + 'fcl';
|
|
//APrj.ParseFPDocOption(Format('--descr-dir="%s"', [s])); //todo: add includes
|
|
//APrj.AddDirToFileList(descs, s, '*.xml');
|
|
ListUnits(s+ DirectorySeparator+ '*.xml', descs); //exclude NoParseUnits
|
|
descs.Sorted := True;
|
|
end;
|
|
//scan fcl dirs
|
|
dirs := TStringList.Create; //use prepared list?
|
|
s := Manager.FFpcDir + 'packages' + DirectorySeparator;
|
|
ListDirs(s, dirs);
|
|
//now match all files in the source dirs
|
|
for i := dirs.Count - 1 downto 0 do begin
|
|
d := s + dirs[i] + DirectorySeparator + 'src';
|
|
if not DirectoryExists(d) then continue; //can this happen?
|
|
(* skip explicitly excluded packages, and exclude selected units.
|
|
*)
|
|
excl := not FAllDirs
|
|
and assigned(FSrcDirs)
|
|
and (SrcDirs.IndexOfName(dirs[i]) >= 0)
|
|
and (SrcDirs.Values[dirs[i]] <= '0');
|
|
if excl then begin
|
|
//Manager.DoLog('Skipping directory ' + dirs[i]);
|
|
end else begin
|
|
incl := FAllDirs
|
|
or (assigned(FSrcDirs)
|
|
and (SrcDirs.IndexOfName(dirs[i]) >= 0)
|
|
and (SrcDirs.Values[dirs[i]] > '0'))
|
|
or (MatchUnits(d, descs) >= 0); //!!! descs now is empty!
|
|
if incl then begin
|
|
//add dir
|
|
Manager.DoLog('Adding directory ' + dirs[i]);
|
|
APrj.ParseFPDocOption(Format('--input-dir="%s"', [d])); //todo: add includes?
|
|
end;
|
|
end;
|
|
end;
|
|
//exclude explicit units - only for FCL?
|
|
if Manager.ExcludeUnits and (Manager.NoParseUnits.Count > 0) then begin
|
|
//exclude inputs
|
|
for i := APrj.InputList.Count - 1 downto 0 do begin
|
|
s := ExtractUnitName(APrj.InputList, i);
|
|
if Manager.NoParseUnits.IndexOf(s) >= 0 then //case?
|
|
APrj.InputList.Delete(i);
|
|
end;
|
|
end;
|
|
//re-create project? The normal project was already created by inherited!
|
|
if AFile <> '' then begin
|
|
f := ChangeFileExt(AFile, '_ext.xml'); //preserve unmodified project?
|
|
APrj.CreateProjectFile(f);
|
|
end; // else APrj.CreateProjectFile(Manager.RootDir + 'fcl_ext.xml'); //preserve unmodified project?
|
|
//finally
|
|
dirs.Free;
|
|
descs.Free;
|
|
end;
|
|
|
|
{ TDocPackage }
|
|
|
|
procedure TDocPackage.SetDescrDir(AValue: string);
|
|
begin
|
|
if FDescrDir=AValue then Exit;
|
|
FDescrDir:=AValue;
|
|
end;
|
|
|
|
procedure TDocPackage.SetCompOpts(AValue: string);
|
|
begin
|
|
(* collect all compiler options
|
|
*)
|
|
if FCompOpts=AValue then Exit;
|
|
if AValue = '' then exit;
|
|
if FCompOpts = '' then
|
|
FCompOpts:=AValue
|
|
else
|
|
FCompOpts:= FCompOpts + ' ' + AValue;
|
|
end;
|
|
|
|
procedure TDocPackage.SetAltDir(AValue: string);
|
|
begin
|
|
AValue:=FixPath(AValue);
|
|
if FAltDir=AValue then Exit;
|
|
FAltDir:=AValue;
|
|
//we must signal config updated
|
|
Config.WriteString(SecDoc, 'AltDir', AltDir);
|
|
end;
|
|
|
|
procedure TDocPackage.SetAllDirs(AValue: boolean);
|
|
begin
|
|
if FAllDirs=AValue then Exit;
|
|
FAllDirs:=AValue;
|
|
end;
|
|
|
|
function TDocPackage.GetAltDir: string;
|
|
begin
|
|
{$IFDEF FCLadds}
|
|
Result := FAltDir;
|
|
{$ELSE}
|
|
Result := '';
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TDocPackage.SetDescriptions(AValue: TStrings);
|
|
(* Shall we allow for multiple descriptions? (general + OS specific!?)
|
|
*)
|
|
begin
|
|
if FDescriptions=AValue then Exit;
|
|
if AValue = nil then exit; //clear?
|
|
if AValue.Count = 0 then exit;
|
|
FDescriptions.Assign(AValue);
|
|
end;
|
|
|
|
(* Requires[] only contain package names.
|
|
Internal use: Get/Set CommaText
|
|
*)
|
|
procedure TDocPackage.SetRequires(AValue: TStrings);
|
|
|
|
procedure Import;
|
|
var
|
|
i: integer;
|
|
s: string;
|
|
begin
|
|
FRequires.Clear; //assume full replace
|
|
for i := 0 to AValue.Count - 1 do begin
|
|
s := AValue[i]; //<name.xct>,<prefix>
|
|
FRequires.Add(ExtractImportName(s)); // + '=' + s);
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
if FRequires=AValue then Exit;
|
|
if AValue = nil then exit;
|
|
if AValue.Count = 0 then exit;
|
|
Import;
|
|
end;
|
|
|
|
procedure TDocPackage.SetUnits(AValue: TStrings);
|
|
|
|
procedure Import;
|
|
var
|
|
i: integer;
|
|
s: string;
|
|
begin
|
|
FUnits.Clear; //assume full replace
|
|
for i := 0 to AValue.Count - 1 do begin
|
|
s := AValue[i]; //filespec
|
|
FUnits.Add(ExtractUnitName(AValue, i) + '=' + s);
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
if FUnits=AValue then Exit;
|
|
if AValue = nil then exit;
|
|
if AValue.Count = 0 then exit;
|
|
//import formatted: <unit>=<descr file> (multiple???)
|
|
if Pos('=', AValue[0]) > 0 then
|
|
FUnits.Assign(AValue) //clears previous content
|
|
else //if AValue.Count > 0 then
|
|
Import;
|
|
end;
|
|
|
|
procedure TDocPackage.SetIncludePath(AValue: string);
|
|
begin
|
|
if FIncludePath=AValue then Exit;
|
|
FIncludePath:=AValue;
|
|
end;
|
|
|
|
procedure TDocPackage.SetInputDir(AValue: string);
|
|
begin
|
|
if FInputDir=AValue then Exit;
|
|
FInputDir:=AValue;
|
|
end;
|
|
|
|
procedure TDocPackage.SetLazPkg(AValue: string);
|
|
begin
|
|
if FLazPkg=AValue then Exit;
|
|
if AValue = '' then exit;
|
|
FLazPkg:=AValue;
|
|
FProjectDir := ExtractFilePath(AValue);
|
|
//todo: import
|
|
end;
|
|
|
|
procedure TDocPackage.SetLoaded(AValue: boolean);
|
|
begin
|
|
if FLoaded=AValue then Exit;
|
|
FLoaded:=AValue;
|
|
if not FLoaded then
|
|
exit; //???
|
|
if Manager.RegisterPackage(self) < 0 then //now definitely loaded
|
|
exit; //really exit?
|
|
if Config = nil then
|
|
UpdateConfig; //create INI file when loaded
|
|
end;
|
|
|
|
procedure TDocPackage.SetName(AValue: string);
|
|
begin
|
|
if FName=AValue then Exit;
|
|
FName:=AValue;
|
|
ReadConfig;
|
|
end;
|
|
|
|
procedure TDocPackage.SetProjectDir(AValue: string);
|
|
begin
|
|
if FProjectDir=AValue then Exit;
|
|
FProjectDir:=AValue;
|
|
end;
|
|
|
|
procedure TDocPackage.SetProjectFile(AValue: string);
|
|
begin
|
|
if FProjectFile=AValue then Exit;
|
|
FProjectFile:=AValue;
|
|
//really do more?
|
|
if FProjectFile = '' then
|
|
exit;
|
|
ProjectDir:=ExtractFilePath(FProjectFile);
|
|
if ExtractFileExt(FProjectFile) <> '.xml' then
|
|
; //really change here???
|
|
//import requires fpdocproject - must be created by Manager!
|
|
end;
|
|
|
|
procedure TDocPackage.SetUnitPath(AValue: string);
|
|
begin
|
|
if FUnitPath=AValue then Exit;
|
|
FUnitPath:=AValue;
|
|
//save to config?
|
|
end;
|
|
|
|
constructor TDocPackage.Create;
|
|
begin
|
|
FUnits := TStringList.Create;
|
|
FDescriptions := TStringList.Create;
|
|
FRequires := TStringList.Create;
|
|
//Config requires valid Name -> in SetName
|
|
end;
|
|
|
|
destructor TDocPackage.Destroy;
|
|
begin
|
|
FreeAndNil(Config);
|
|
FreeAndNil(FUnits);
|
|
FreeAndNil(FDescriptions);
|
|
FreeAndNil(FRequires);
|
|
FreeAndNil(FSrcDirs);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
(* Create new(?) project.
|
|
Usage: after LoadLpk, in general for configured project (user options!)
|
|
(more options to come)
|
|
*)
|
|
function TDocPackage.CreateProject(APrj: TFPDocHelper; const AFile: string): boolean;
|
|
var
|
|
s, imp: string;
|
|
pkg: TFPDocPackage;
|
|
i: integer;
|
|
lst: TStringList;
|
|
begin
|
|
Result := APrj.Package <> nil; //already configured?
|
|
if Result then
|
|
exit;
|
|
Result := ProjectDir <> '';
|
|
if not Result then
|
|
exit; //dir must be known
|
|
//create pkg
|
|
APrj.ParseFPDocOption('--package=' + Name); //selects or creates the pkg
|
|
pkg := APrj.SelectedPackage;
|
|
//add Inputs
|
|
//todo: common options? OS options?
|
|
for i := 0 to Units.Count - 1 do begin
|
|
s := Units.ValueFromIndex[i];
|
|
if CompOpts <> '' then
|
|
s := s + ' ' + CompOpts;
|
|
//add further options?
|
|
pkg.Inputs.Add(s);
|
|
end;
|
|
//add Descriptions - either explicit or implicit
|
|
if (DescrDir <> '') and (Descriptions.Count = 0) then begin
|
|
//first check for existing directory
|
|
if not DirectoryExists(DescrDir) then begin
|
|
MkDir(DescrDir); //exclude \?
|
|
end else if Descriptions.Count = 0 then begin
|
|
APrj.ParseFPDocOption('--descr-dir=' + DescrDir); //adds all XML files
|
|
end;
|
|
end else begin
|
|
APrj.DescrDir := DescrDir; //needed by Update
|
|
for i := 0 to Descriptions.Count - 1 do begin
|
|
s := Descriptions[i];
|
|
if Pos('=', s) > 0 then
|
|
pkg.Descriptions.Add(Descriptions.ValueFromIndex[i])
|
|
else
|
|
pkg.Descriptions.Add(s);
|
|
end;
|
|
end;
|
|
if AltDir <> '' then begin
|
|
//add descr files
|
|
s := Manager.LazarusDir + AltDir;
|
|
s := FixPath(s);
|
|
if ForceDirectories(s) then begin
|
|
//exclude NoParse units
|
|
//APrj.ParseFPDocOption(Format('--descr-dir="%s"', [s]));
|
|
lst := TStringList.Create;
|
|
ListUnits(AltDir + '*.xml', lst); //unit names only
|
|
(* add the unit names from lst to the pkg/project
|
|
*)
|
|
pkg := APrj.SelectedPackage;
|
|
for i := 0 to lst.Count - 1 do begin
|
|
s := AltDir + lst[i] + '.xml';
|
|
pkg.Descriptions.Add(s);
|
|
end;
|
|
end;
|
|
//add source files!?
|
|
end;
|
|
//add Imports
|
|
for i := 0 to Requires.Count - 1 do begin
|
|
s := Requires[i];
|
|
{$IFDEF EasyImports}
|
|
imp := Manager.RootDir + s;
|
|
{$ELSE}
|
|
imp := Manager.RootDir + s + '.xct,../' + s + '/'; //valid for HTML, not for CHM!
|
|
{$ENDIF}
|
|
APrj.ParseFPDocOption('--import=' + imp);
|
|
end;
|
|
//add options
|
|
APrj.Options.Assign(Manager.Options);
|
|
//debug, looks good here!?
|
|
if APrj.Options.Backend = '' then
|
|
Manager.DoLog('No format, should be ' + Manager.Options.Backend);
|
|
pkg.Output := Manager.RootDir + Name; //???
|
|
pkg.ContentFile := Manager.RootDir + Name + '.xct';
|
|
//now create project file
|
|
if AFile <> '' then begin
|
|
if ExtractFileExt(AFile) <> '.xml' then
|
|
FProjectFile := ExtractFilePath(AFile) + Name + '_prj.xml'
|
|
else
|
|
FProjectFile := AFile;
|
|
APrj.CreateProjectFile(ProjectFile);
|
|
end;
|
|
Result := True; //assume okay
|
|
end;
|
|
|
|
(* Init from TFPDocPackage, into which AFile has been loaded.
|
|
*)
|
|
function TDocPackage.ImportProject(APrj: TFPDocHelper; APkg: TFPDocPackage; const AFile: string): boolean;
|
|
var
|
|
s: string;
|
|
begin
|
|
//check loaded
|
|
Result := Loaded;
|
|
if Result then
|
|
exit;
|
|
//init...
|
|
s := UnitFile(APkg.Inputs, 0);
|
|
if s <> '' then
|
|
FUnitPath := ExtractFilePath(s);
|
|
s := UnitFile(APkg.Descriptions, 0);
|
|
if s <> '' then
|
|
FDescrDir := ExtractFilePath(s);
|
|
//project file - empty if not applicable (multi-package project?!)
|
|
if (AFile <> '') and (APrj.Packages.Count = 1) then
|
|
ProjectFile := AFile //only if immediately applicable!
|
|
else
|
|
ProjectDir := ExtractFilePath(AFile);
|
|
//init lists
|
|
Units := APkg.Inputs;
|
|
Descriptions := APkg.Descriptions;
|
|
Requires := APkg.Imports;
|
|
//more?
|
|
//save config!
|
|
UpdateConfig;
|
|
//finish
|
|
Result := Loaded;
|
|
end;
|
|
|
|
procedure TDocPackage.ReadConfig;
|
|
var
|
|
s: string;
|
|
begin
|
|
if Loaded then
|
|
exit;
|
|
if Config = nil then
|
|
Config := TConfigFile.Create(IniFileName);
|
|
//check config
|
|
s := Config.ReadString(SecDoc, 'projectdir', '');
|
|
if s = '' then begin
|
|
FreeAndNil(Config); //must create and fill later!
|
|
exit; //project directory MUST be known
|
|
end;
|
|
ProjectFile := Config.ReadString(SecDoc, 'projectfile', '');
|
|
FInputDir := Config.ReadString(SecDoc, 'inputdir', '');
|
|
FCompOpts := Config.ReadString(SecDoc, 'options', '');
|
|
FDescrDir := Config.ReadString(SecDoc, 'descrdir', '');
|
|
FAltDir := Config.ReadString(SecDoc, 'AltDir', '');
|
|
FAllDirs:= Config.ReadBool(SecDoc, 'AllDirs', False);
|
|
Requires.CommaText := Config.ReadString(SecDoc, 'requires', '');
|
|
//units
|
|
Config.ReadSection('units', Units);
|
|
Config.ReadSection('descrs', Descriptions);
|
|
//more?
|
|
//all done
|
|
Loaded := True;
|
|
end;
|
|
|
|
(* Initialize the package, write global config (+local?)
|
|
*)
|
|
procedure TDocPackage.UpdateConfig;
|
|
begin
|
|
//create ini file, if not already created
|
|
if Config = nil then
|
|
Config := TConfigFile.Create(IniFileName); //in document RootDir
|
|
//general information
|
|
Config.WriteString(SecDoc, 'projectdir', ProjectDir);
|
|
Config.WriteString(SecDoc, 'projectfile', ProjectFile);
|
|
Config.WriteString(SecDoc, 'inputdir', InputDir);
|
|
Config.WriteString(SecDoc, 'options', CompOpts);
|
|
Config.WriteString(SecDoc, 'descrdir', DescrDir);
|
|
Config.WriteString(SecDoc, 'AltDir', FAltDir);
|
|
Config.WriteBool(SecDoc, 'AllDirs', AllDirs);
|
|
Config.WriteString(SecDoc, 'requires', Requires.CommaText);
|
|
//units
|
|
Config.WriteSectionValues('units', Units);
|
|
Config.WriteSectionValues('descrs', Descriptions);
|
|
Config.WriteSectionValues('SrcDirs', SrcDirs);
|
|
//all done
|
|
Config.Flush;
|
|
Loaded := True;
|
|
end;
|
|
|
|
procedure TDocPackage.EnumUnits(AList: TStrings);
|
|
var
|
|
i: integer;
|
|
begin
|
|
//override to add further units (from AltDir...)
|
|
for i := 0 to Units.Count - 1 do
|
|
AList.Add(Units.Names[i]);
|
|
end;
|
|
|
|
function TDocPackage.DescrFileName(const AUnit: string): string;
|
|
begin
|
|
(* [ProjectDir +] DescrDir + AUnit + .xml
|
|
*)
|
|
Result := DescrDir;
|
|
if (Result = '') or (Result[1] = '.') then
|
|
Result := ProjectDir + Result;
|
|
Result := Result + DirectorySeparator + AUnit + '.xml';
|
|
end;
|
|
|
|
function TDocPackage.IniFileName: string;
|
|
begin
|
|
Result := Manager.RootDir + Name + '.ini';
|
|
end;
|
|
|
|
procedure TDocPackage.AddUnit(const AFile: string);
|
|
var
|
|
s: string;
|
|
begin
|
|
s := ExtractUnitName(AFile);
|
|
if s = '' then
|
|
Manager.DoLog('No unit: ' + AFile)
|
|
else
|
|
Units.Add(s + '=' + AFile);
|
|
end;
|
|
|
|
{ TFPDocManager }
|
|
|
|
constructor TFPDocManager.Create(AOwner: TComponent);
|
|
var
|
|
lst: TStringList;
|
|
begin
|
|
inherited Create(AOwner);
|
|
lst := TStringList.Create;
|
|
lst.OwnsObjects := True;
|
|
FPackages := lst;
|
|
FOptions := TCmdOptions.Create;
|
|
FNoParseUnits := TStringList.Create;
|
|
end;
|
|
|
|
destructor TFPDocManager.Destroy;
|
|
begin
|
|
SaveConfig;
|
|
FreeAndNil(Config);
|
|
//FPackages.Clear; //destructor seems NOT to clear/destroy owned object!?
|
|
FreeAndNil(FPackages);
|
|
FreeAndNil(FOptions);
|
|
FreeAndNil(FNoParseUnits);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TFPDocManager.SetFPDocDir(AValue: string);
|
|
begin
|
|
if FFPDocDir=AValue then Exit;
|
|
FFPDocDir:=AValue;
|
|
Config.WriteString(SecGen, 'FpcDocDir', FpcDocDir);
|
|
end;
|
|
|
|
procedure TFPDocManager.SetFpcDir(AValue: string);
|
|
begin
|
|
if FFpcDir=AValue then Exit;
|
|
FFpcDir:=AValue;
|
|
Config.WriteString(SecGen, 'FpcDir', FpcDir);
|
|
end;
|
|
|
|
procedure TFPDocManager.SetExcludedUnits(AValue: boolean);
|
|
begin
|
|
if FExcludedUnits=AValue then Exit;
|
|
FExcludedUnits:=AValue;
|
|
Config.WriteBool(SecGen, 'ExcludeUnits', AValue);
|
|
end;
|
|
|
|
procedure TFPDocManager.UpdatePackage(const AName: string);
|
|
var
|
|
pkg: TDocPackage;
|
|
i: integer;
|
|
s: string;
|
|
begin
|
|
if LazarusDir = '' then exit;
|
|
s := {LazarusDir +} 'docs/xml/'+AName;
|
|
if not DirectoryExists(FixPath(LazarusDir + s)) then
|
|
exit;
|
|
i := Packages.IndexOfName(AName);
|
|
if i < 0 then
|
|
exit;
|
|
pkg := Packages.Objects[i] as TDocPackage;
|
|
pkg.AltDir := s; //add descriptors when configuring the project/helper
|
|
end;
|
|
|
|
function TFPDocManager.UpdateFCL(enabled: boolean): boolean;
|
|
var
|
|
pkg: TFCLDocPackage;
|
|
begin
|
|
(* Adding to the FCL requires valid FPC and Lazarus directories (caller checks).
|
|
Then laz/docs/xml/fcl/ is added to fpc descr-dirs.
|
|
The related units have to be added as input-dirs.
|
|
Scan fpc/packages/ for candidates.
|
|
*)
|
|
//todo: implement
|
|
pkg := AddPackage('fcl') as TFCLDocPackage;
|
|
if pkg = nil then
|
|
exit(False);
|
|
if enabled then
|
|
pkg.AltDir := 'docs/xml/fcl'
|
|
else
|
|
pkg.AltDir := '';
|
|
Result := True;
|
|
end;
|
|
|
|
procedure TFPDocManager.SetLazarusDir(AValue: string);
|
|
begin
|
|
if FLazarusDir=AValue then Exit;
|
|
FLazarusDir:=AValue;
|
|
Config.WriteString(SecGen, 'LazarusDir', FLazarusDir);
|
|
//update RTL and FCL - if exist and Dir exists
|
|
UpdatePackage('rtl');
|
|
UpdatePackage('fcl');
|
|
end;
|
|
|
|
function TFPDocManager.GetNoParseUnits: TStrings;
|
|
begin
|
|
Result := FNoParseUnits;
|
|
end;
|
|
|
|
procedure TFPDocManager.SetNoParseUnits(AValue: TStrings);
|
|
begin
|
|
FNoParseUnits.Assign(AValue);
|
|
FNoParseUnits.Sorted := True;
|
|
Config.WriteSection('NoParseUnits', NoParseUnits);
|
|
end;
|
|
|
|
procedure TFPDocManager.SetOnChange(AValue: TNotifyEvent);
|
|
begin
|
|
if FOnChange=AValue then Exit;
|
|
FOnChange:=AValue;
|
|
end;
|
|
|
|
procedure TFPDocManager.SetPackage(AValue: TDocPackage);
|
|
begin
|
|
if FPackage=AValue then Exit;
|
|
FPackage:=AValue;
|
|
end;
|
|
|
|
procedure TFPDocManager.SetProfile(AValue: string);
|
|
begin
|
|
if AValue = '' then exit;
|
|
if FProfile=AValue then Exit;
|
|
if Options.Modified then
|
|
Options.SaveConfig(Config, FProfile);
|
|
FProfile:=AValue;
|
|
if not Config.SectionExists(AValue) then begin
|
|
FProfiles := FProfiles + ',' + AValue;
|
|
Config.WriteString(SecGen, 'Profiles', FProfiles);
|
|
end;
|
|
Config.WriteString(SecGen, 'Profile', FProfile);
|
|
Options.LoadConfig(Config, Profile);
|
|
end;
|
|
|
|
(* Try load config from new dir - this may fail on the first run.
|
|
*)
|
|
procedure TFPDocManager.SetRootDir(AValue: string);
|
|
var
|
|
s: string;
|
|
begin
|
|
s := IncludeTrailingPathDelimiter(AValue);
|
|
if FRootDir=s then Exit; //prevent recursion
|
|
FRootDir:=s;
|
|
//load config - not here!
|
|
end;
|
|
|
|
procedure TFPDocManager.Changed;
|
|
begin
|
|
if not Modified or (UpdateCount > 0) then
|
|
exit; //should not be called directly
|
|
FModified := False;
|
|
if Assigned(OnChange) then
|
|
FOnChange(self);
|
|
end;
|
|
|
|
function TFPDocManager.BeginTest(const ADir: string): boolean;
|
|
begin
|
|
Helper.Free; //should have been done
|
|
Helper := TFPDocHelper.Create(nil);
|
|
Helper.OnLog := OnLog;
|
|
Result := Helper.BeginTest(ADir);
|
|
if Result then
|
|
Helper.CmdOptions := Options; //set reference AND propagate!?
|
|
end;
|
|
|
|
procedure TFPDocManager.EndTest;
|
|
begin
|
|
SetCurrentDir(ExtractFileDir(RootDir));
|
|
FreeAndNil(Helper);
|
|
end;
|
|
|
|
procedure TFPDocManager.BeginUpdate;
|
|
begin
|
|
inc(UpdateCount);
|
|
end;
|
|
|
|
procedure TFPDocManager.EndUpdate;
|
|
begin
|
|
dec(UpdateCount);
|
|
if UpdateCount <= 0 then begin
|
|
UpdateCount := 0;
|
|
if Modified then
|
|
Changed;
|
|
end;
|
|
end;
|
|
|
|
(* Try load config.
|
|
Init RootDir (only when config found?)
|
|
Try load packages from their INI files
|
|
*)
|
|
function TFPDocManager.LoadConfig(const ADir: string; Force: boolean): boolean;
|
|
var
|
|
s, pf, cf: string;
|
|
i: integer;
|
|
begin
|
|
s := IncludeTrailingPathDelimiter(ADir);
|
|
cf := s + ConfigName;
|
|
Result := FileExists(cf);
|
|
if not Result and not Force then
|
|
exit;
|
|
RootDir:=s; //recurse if RootDir changed
|
|
//sanity check: only one config file!
|
|
if assigned(Config) then begin
|
|
if (Config.FileName = cf) then
|
|
exit(false) //nothing new?
|
|
else
|
|
Config.Free;
|
|
//clear packages???
|
|
end;
|
|
Config := TConfigFile.Create(cf);
|
|
//Config.CacheUpdates := True;
|
|
if not Result then
|
|
exit; //nothing to read
|
|
//read directories
|
|
FFpcDir := Config.ReadString(SecGen, 'FpcDir', '');
|
|
FFPDocDir := Config.ReadString(SecGen, 'FpcDocDir', '');
|
|
FLazarusDir:=Config.ReadString(SecGen, 'LazarusDir', '');
|
|
//read packages
|
|
Config.ReadSection(SecProjects, FPackages); //<prj>=<file>
|
|
//read detailed package information - possibly multiple packages per project!
|
|
BeginUpdate; //turn of app notification!
|
|
for i := 0 to Packages.Count - 1 do begin
|
|
//read package config (=project file name?)
|
|
s := Packages.Names[i];
|
|
pf := Packages.ValueFromIndex[i];
|
|
if pf <> '' then begin
|
|
AddProject(s, pf); //add and load project file, don't update config!
|
|
FModified := True; //force app notification
|
|
end;
|
|
end;
|
|
//more? (preferences?)
|
|
FProfiles:=Config.ReadString(SecGen, 'Profiles', 'default');
|
|
FProfile := Config.ReadString(SecGen,'Profile', 'default');
|
|
Options.LoadConfig(Config, Profile);
|
|
FExcludedUnits := Config.ReadBool(SecGen, 'ExcludeUnits', True);
|
|
Config.ReadSection('NoParseUnits', NoParseUnits);
|
|
//done, nothing modified
|
|
EndUpdate;
|
|
end;
|
|
|
|
function TFPDocManager.SaveConfig: boolean;
|
|
begin
|
|
//Options? assume saved by application?
|
|
if Options.Modified then begin
|
|
Options.SaveConfig(Config, Profile);
|
|
end;
|
|
Config.Flush;
|
|
Result := True; //for now
|
|
end;
|
|
|
|
procedure TFPDocManager.AddProfile(const AName: string);
|
|
begin
|
|
//add and select - obsolete!
|
|
Profile := AName;
|
|
end;
|
|
|
|
(* Add a DocPackage to Packages and INI.
|
|
Return package Index.
|
|
For exclusive use by Package.SetLoaded!
|
|
*)
|
|
function TFPDocManager.RegisterPackage(APkg: TDocPackage): integer;
|
|
begin
|
|
Result := Packages.IndexOfName(APkg.Name);
|
|
if Result < 0 then begin
|
|
//add package
|
|
Result := Packages.AddObject(APkg.Name + '=' + APkg.ProjectFile, APkg);
|
|
end else if Packages.Objects[Result] = nil then
|
|
Packages.Objects[Result] := APkg;
|
|
if APkg.Loaded then begin
|
|
//check/create project file?
|
|
if APkg.ProjectFile = '' then begin
|
|
if APkg.ProjectDir = '' then begin
|
|
DoLog('Missing project directory for package ' + APkg.Name);
|
|
exit(-1); //???
|
|
end;
|
|
APkg.ProjectFile := APkg.ProjectDir + APkg.Name; //to be fixed by pkg
|
|
end;
|
|
if (ExtractFileExt(APkg.ProjectFile) <> '.xml') then begin
|
|
//create project file
|
|
APkg.ProjectFile := ChangeFileExt(APkg.ProjectFile, '_prj.xml');
|
|
CreateProject(APkg.ProjectFile, APkg);
|
|
//update Packages[] string
|
|
Packages[Result] := APkg.Name + '=' + APkg.ProjectFile;
|
|
end;
|
|
Config.WriteString(SecProjects, APkg.Name, APkg.ProjectFile);
|
|
end;
|
|
FModified := True;
|
|
end;
|
|
|
|
(* Load FPDoc (XML) project file.
|
|
Called by
|
|
- init - not Dirty!
|
|
*)
|
|
function TFPDocManager.AddProject(const APkg, AFile: string): boolean;
|
|
var
|
|
pkg: TDocPackage;
|
|
i: integer;
|
|
begin
|
|
//create DocPackage
|
|
pkg := AddPackage(APkg);
|
|
if pkg.Loaded then
|
|
exit(True); //assume registered!?
|
|
//check project file
|
|
if ExtractFileExt(AFile) <> '.xml' then begin
|
|
DoLog('Not a project file: ' + AFile);
|
|
Exit(False);
|
|
end;
|
|
if not FileExists(AFile) then begin
|
|
DoLog('Missing project file: ' + AFile);
|
|
exit(False);
|
|
end;
|
|
//create helper
|
|
BeginTest(AFile);
|
|
try
|
|
//load the project file into Helper
|
|
Helper.LoadProjectFile(AFile);
|
|
if Helper.Packages.Count = 1 then begin
|
|
Helper.Package := Helper.Packages[0]; //in LoadProject?
|
|
Result := pkg.ImportProject(Helper, Helper.Package, AFile);
|
|
exit;
|
|
end;
|
|
//load all packages
|
|
for i := 0 to Helper.Packages.Count - 1 do begin
|
|
Helper.Package := Helper.Packages[i];
|
|
pkg := AddPackage(Helper.Package.Name);
|
|
if pkg.Loaded then
|
|
continue; //already initialized
|
|
pkg.ImportProject(Helper, Helper.Package, '');
|
|
end;
|
|
finally
|
|
EndTest;
|
|
end;
|
|
end;
|
|
|
|
(* Ask DocPackage to create an projectfile.
|
|
Overwrite if exists???
|
|
AFileName is any file in the project directory, required for CD!
|
|
!!! prevent recursive calls, destroying Helper !!!
|
|
*)
|
|
function TFPDocManager.CreateProject(const AFileName: string; APkg: TDocPackage
|
|
): boolean;
|
|
begin
|
|
if Helper = nil then begin
|
|
BeginTest(AFileName); //CD into project dir
|
|
try
|
|
Result := APkg.CreateProject(Helper, AFileName);
|
|
finally
|
|
EndTest;
|
|
end;
|
|
end else begin
|
|
//assume that Helper IS for APkg
|
|
Result := APkg.CreateProject(Helper, AFileName);
|
|
end;
|
|
end;
|
|
|
|
(* Return the named package, create if not found.
|
|
Rename: GetPackage?
|
|
*)
|
|
function TFPDocManager.AddPackage(AName: string): TDocPackage;
|
|
var
|
|
i: integer;
|
|
begin
|
|
AName := LowerCase(AName);
|
|
i := FPackages.IndexOfName(AName);
|
|
if i < 0 then
|
|
Result := nil
|
|
else
|
|
Result := FPackages.Objects[i] as TDocPackage;
|
|
if Result = nil then begin
|
|
{$IFDEF FCLadds}
|
|
if AName = 'fcl' then
|
|
Result := TFCLDocPackage.Create
|
|
else
|
|
{$ELSE}
|
|
{$ENDIF}
|
|
Result := TDocPackage.Create;
|
|
Result.Name := AName; //triggers load config --> register
|
|
i := FPackages.IndexOfName(AName); //already registered?
|
|
end;
|
|
if i < 0 then begin
|
|
//we MUST create an entry
|
|
Packages.AddObject(AName + '=' + Result.ProjectFile, Result);
|
|
end;
|
|
end;
|
|
|
|
function TFPDocManager.IsExtended(const APkg: string): string;
|
|
var
|
|
pkg: TDocPackage;
|
|
begin
|
|
{$IFDEF FCLadds}
|
|
pkg := AddPackage(APkg);
|
|
if pkg = nil then
|
|
Result := ''
|
|
else
|
|
Result := pkg.AltDir;
|
|
{$ELSE}
|
|
Result := '';
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TFPDocManager.ImportLpk(const AFile: string): TDocPackage;
|
|
begin
|
|
BeginUpdate;
|
|
//import the LPK file into? Here: TDocPackage, could be FPDocProject?
|
|
Result := uLpk.ImportLpk(AFile);
|
|
if Result = nil then
|
|
DoLog('Import failed on ' + AFile)
|
|
else begin
|
|
Result.Loaded := True; //import and write config file
|
|
end;
|
|
EndUpdate;
|
|
end;
|
|
|
|
(* Add the project, just created from cmdline or projectfile
|
|
*)
|
|
procedure TFPDocManager.ImportProject(APkg: TFPDocPackage; const AFile: string);
|
|
var
|
|
pkg: TDocPackage;
|
|
begin
|
|
pkg := AddPackage(APkg.Name);
|
|
pkg.ImportProject(Helper, APkg, AFile);
|
|
//update config?
|
|
Config.WriteString(SecProjects, pkg.Name, AFile);
|
|
FModified := true;
|
|
//notify app?
|
|
//Changed;
|
|
end;
|
|
|
|
function TFPDocManager.ImportCmd(const AFile: string): boolean;
|
|
var
|
|
pkg: TDocPackage;
|
|
begin
|
|
Result := False;
|
|
BeginTest(AFile); //directory!!!
|
|
try
|
|
Result := Helper.CmdToPrj(AFile);
|
|
if not Result then
|
|
exit;
|
|
pkg := AddPackage(Helper.SelectedPackage.Name); //create [and register]
|
|
pkg.Loaded := False; //force reload
|
|
if not pkg.Loaded then begin
|
|
Result := pkg.ImportProject(Helper, Helper.Package, AFile);
|
|
end;
|
|
finally
|
|
EndTest;
|
|
end;
|
|
if Result then
|
|
Changed;
|
|
end;
|
|
|
|
function TFPDocManager.MakeDoc(APkg: TDocPackage; const AUnit, AOutput: string): boolean;
|
|
begin
|
|
Result := assigned(APkg)
|
|
and BeginTest(APkg.ProjectDir)
|
|
and APkg.CreateProject(Helper, ''); //only configure, don't create file
|
|
if not Result then
|
|
exit;
|
|
try
|
|
Helper.ParseFPDocOption(Format('--output="%s"', [AOutput]));
|
|
if Options.Backend = 'chm' then begin
|
|
Helper.ParseFPDocOption('--auto-toc');
|
|
Helper.ParseFPDocOption('--auto-index');
|
|
end;
|
|
Helper.ParseFPDocOption('--make-searchable'); //always?
|
|
//Result :=
|
|
Helper.CreateUnitDocumentation(AUnit, False);
|
|
finally
|
|
EndTest;
|
|
end;
|
|
end;
|
|
|
|
function TFPDocManager.TestRun(APkg: TDocPackage; AUnit: string): boolean;
|
|
begin
|
|
BeginTest(APkg.ProjectFile);
|
|
try
|
|
try
|
|
Result := Helper.TestRun(APkg, AUnit);
|
|
except
|
|
on E: EParserError do
|
|
DoLog(Format('%s(%d,%d): %s',[e.Filename, e.Row, e.Column, e.Message]));
|
|
on E: Exception do
|
|
DoLog(E.Message);
|
|
end;
|
|
finally
|
|
EndTest;
|
|
end;
|
|
end;
|
|
|
|
function TFPDocManager.Update(APkg: TDocPackage; const AUnit: string): boolean;
|
|
begin
|
|
Result := assigned(APkg)
|
|
and BeginTest(APkg.ProjectFile);
|
|
if not Result then
|
|
exit;
|
|
try
|
|
Result := APkg.CreateProject(Helper, ''); //only configure, don't create file
|
|
if not Result then
|
|
exit;
|
|
Result := Helper.Update(APkg, AUnit);
|
|
finally
|
|
EndTest;
|
|
end;
|
|
end;
|
|
|
|
procedure TFPDocManager.DoLog(const Msg: String);
|
|
begin
|
|
if Assigned(FOnLog) then
|
|
FOnLog(self, msg);
|
|
end;
|
|
|
|
{ TFPDocHelper }
|
|
|
|
constructor TFPDocHelper.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
InputList := TStringList.Create;
|
|
DescrList := TStringList.Create;
|
|
end;
|
|
|
|
destructor TFPDocHelper.Destroy;
|
|
begin
|
|
FreeAndNil(InputList);
|
|
FreeAndNil(DescrList);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
(* Prepare MakeSkel on temporary FPDocPackage
|
|
*)
|
|
function TFPDocHelper.BeginTest(APkg: TDocPackage): boolean;
|
|
begin
|
|
if not assigned(APkg) then
|
|
exit(False);
|
|
Result := BeginTest(APkg.ProjectFile); //directory would be sufficient!
|
|
if not Result then
|
|
exit;
|
|
APkg.CreateProject(self, ''); //create project file?
|
|
Package := Packages.FindPackage(APkg.Name);
|
|
//Options?
|
|
//okay, so far
|
|
Result := assigned(Package);
|
|
end;
|
|
|
|
procedure TFPDocHelper.EndTest;
|
|
begin
|
|
//???
|
|
end;
|
|
|
|
function TFPDocHelper.BeginTest(ADir: string): boolean;
|
|
begin
|
|
Result := ADir <> '';
|
|
if not Result then
|
|
exit;
|
|
//remember dir!
|
|
if ExtractFileExt(ADir) <> '' then //todo: better check for directory!?
|
|
ADir := ExtractFileDir(ADir);
|
|
ProjectDir:=ADir;
|
|
SetCurrentDir(ProjectDir);
|
|
end;
|
|
|
|
(* Create a project from an FPDoc commandline.
|
|
Do NOT create an project file!(?)
|
|
*)
|
|
function TFPDocHelper.CmdToPrj(const AFileName: string): boolean;
|
|
var
|
|
l, w: string;
|
|
i: integer;
|
|
begin
|
|
Result := False; //in case of errors
|
|
//read the commandline
|
|
InputList.LoadFromFile(AFileName);
|
|
for i := 0 to InputList.Count - 1 do begin
|
|
l := InputList[i];
|
|
w := GetNextWord(l);
|
|
if w = 'fpdoc' then begin //contains!?
|
|
Result := True; //so far
|
|
break; //fpdoc command found
|
|
end;
|
|
end;
|
|
InputList.Clear;
|
|
if not Result then
|
|
exit;
|
|
//parse commandline
|
|
while l <> '' do begin
|
|
w := GetNextWord(l);
|
|
ParseFPDocOption(w);
|
|
end;
|
|
Result := True;
|
|
end;
|
|
|
|
function TFPDocHelper.MakeDocs(APkg: TDocPackage; const AUnit: string;
|
|
AOutput: string): boolean;
|
|
begin
|
|
Result := BeginTest(APkg); //configure and select package
|
|
if not Result then
|
|
exit;
|
|
try
|
|
ParseFPDocOption(Format('--output="%s"', [AOutput]));
|
|
CreateDocumentation(Package, False);
|
|
finally
|
|
EndTest;
|
|
end;
|
|
end;
|
|
|
|
function TFPDocHelper.TestRun(APkg: TDocPackage; AUnit: string): boolean;
|
|
begin
|
|
(* more detailed error handling?
|
|
Must CD to the project file directory!?
|
|
*)
|
|
Result := BeginTest(APkg);
|
|
if not Result then
|
|
exit;
|
|
try
|
|
//override options for test
|
|
ParseFPDocOption('--format=html');
|
|
ParseFPDocOption('-v');
|
|
ParseFPDocOption('-n');
|
|
//verbose?
|
|
CreateUnitDocumentation(AUnit, True);
|
|
finally
|
|
EndTest;
|
|
end;
|
|
end;
|
|
|
|
(* MakeSkel functionality - create skeleton or update file
|
|
using temporary Project
|
|
*)
|
|
function TFPDocHelper.Update(APkg: TDocPackage; const AUnit: string): boolean;
|
|
|
|
function DocumentUnit(const AUnit: string): boolean;
|
|
var
|
|
OutName, msg: string;
|
|
begin
|
|
if Manager.NoParseUnits.IndexOf(AUnit) >= 0 then begin
|
|
DoLog('NoParse ' + AUnit);
|
|
exit(False);
|
|
end;
|
|
InputList.Clear;
|
|
InputList.Add(UnitSpec(AUnit));
|
|
DescrList.Clear;
|
|
OutName := AUnit + '.xml';
|
|
if DescrDir <> '' then
|
|
OutName := IncludeTrailingBackslash(DescrDir) + OutName;
|
|
CmdOptions.UpdateMode := FileExists(OutName);
|
|
if CmdOptions.UpdateMode then begin
|
|
DescrList.Add(OutName);
|
|
OutName:=Manager.RootDir + 'upd.' + AUnit + '.xml';
|
|
DoLog('Update ' + OutName);
|
|
end else begin
|
|
DoLog('Create ' + OutName);
|
|
end;
|
|
msg := DocumentPackage(APkg.Name, OutName, InputList, DescrList);
|
|
Result := msg = '';
|
|
if not Result then
|
|
DoLog(msg) //+unit?
|
|
else if CmdOptions.UpdateMode then begin
|
|
CleanXML(OutName);
|
|
end;
|
|
end;
|
|
|
|
var
|
|
i: integer;
|
|
u: string;
|
|
begin
|
|
Result := BeginTest(APkg);
|
|
if not Result then
|
|
exit;
|
|
if AUnit <> '' then begin
|
|
Result := DocumentUnit(AUnit);
|
|
end else begin
|
|
for i := 0 to Package.Inputs.Count - 1 do begin
|
|
u := ExtractUnitName(Package.Inputs, i);
|
|
DocumentUnit(u);
|
|
end;
|
|
end;
|
|
EndTest;
|
|
end;
|
|
|
|
procedure TFPDocHelper.SetProjectDir(AValue: string);
|
|
begin
|
|
if FProjectDir=AValue then Exit;
|
|
FProjectDir:=AValue;
|
|
end;
|
|
|
|
end.
|
|
|