Bläddra i källkod

* Added support for project file

git-svn-id: trunk@16711 -
michael 14 år sedan
förälder
incheckning
853a1eb31c

+ 3 - 0
.gitattributes

@@ -11967,6 +11967,8 @@ utils/fpdoc/fpde/xpms.pp svneol=native#text/plain
 utils/fpdoc/fpdoc.css -text
 utils/fpdoc/fpdoc.lpi svneol=native#text/plain
 utils/fpdoc/fpdoc.pp svneol=native#text/plain
+utils/fpdoc/fpdocproj.pas svneol=native#text/plain
+utils/fpdoc/fpdocxmlopts.pas svneol=native#text/plain
 utils/fpdoc/intl/Makefile svneol=native#text/plain
 utils/fpdoc/intl/dglobals.de.po svneol=native#text/plain
 utils/fpdoc/intl/dglobals.sk.po svneol=native#text/plain
@@ -11977,6 +11979,7 @@ utils/fpdoc/intl/fpdocstr.de.po svneol=native#text/plain
 utils/fpdoc/intl/makeskel.de.po svneol=native#text/plain
 utils/fpdoc/makeskel.lpi svneol=native#text/plain
 utils/fpdoc/makeskel.pp svneol=native#text/plain
+utils/fpdoc/sample-project.xml svneol=native#text/plain
 utils/fpdoc/sh_pas.pp svneol=native#text/plain
 utils/fpdoc/testunit.pp svneol=native#text/plain
 utils/fpdoc/testunit.xml svneol=native#text/plain

+ 5 - 5
utils/fpdoc/dw_lintmpl.pp

@@ -89,8 +89,8 @@ Type
     procedure StartChapter(ChapterName : String); override;
     procedure StartOverview(WithAccess : Boolean); override;
     procedure EndOverview; override;
-    procedure WriteOverviewMember(ALabel,AName,Access,ADescr : String); override;
-    procedure WriteOverviewMember(ALabel,AName,ADescr : String); override;
+    procedure WriteOverviewMember(const ALabel,AName,Access,ADescr : String); override;
+    procedure WriteOverviewMember(const ALabel,AName,ADescr : String); override;
     Class Function FileNameExtension : String; override;
     // Description node conversion. Overrides for TFPDocWriter.
     procedure DescrBeginBold; override;
@@ -374,7 +374,7 @@ begin
 end;
 
 
-function TTemplateWriter.FileNameExtension: String;
+class function TTemplateWriter.FileNameExtension: String;
 begin
   Result:=TTemplateExtension;
 end;
@@ -520,7 +520,7 @@ begin
   { End of overview }
 end;
 
-procedure TTemplateWriter.WriteOverviewMember(ALabel,AName,Access,ADescr : String);
+procedure TTemplateWriter.WriteOverviewMember(Const ALabel,AName,Access,ADescr : String);
 
 begin
   { Write one entry in property overview:
@@ -531,7 +531,7 @@ begin
   }
 end;
 
-procedure TTemplateWriter.WriteOverviewMember(ALabel,AName,ADescr : String);
+procedure TTemplateWriter.WriteOverviewMember(Const ALabel,AName,ADescr : String);
 
 begin
   { Write one entry in method overview:

+ 76 - 27
utils/fpdoc/fpdoc.lpi

@@ -4,94 +4,143 @@
     <Version Value="9"/>
     <General>
       <Flags>
-        <SaveClosedFiles Value="False"/>
         <SaveOnlyProjectUnits Value="True"/>
-        <MainUnitHasUsesSectionForAllUnits Value="False"/>
         <MainUnitHasCreateFormStatements Value="False"/>
         <MainUnitHasTitleStatement Value="False"/>
-        <LRSInOutputDirectory Value="False"/>
       </Flags>
       <SessionStorage Value="InProjectDir"/>
       <MainUnit Value="0"/>
+      <Title Value="FPDoc Documentation generator"/>
+      <ResourceType Value="res"/>
+      <UseXPManifest Value="True"/>
+      <Icon Value="0"/>
     </General>
+    <i18n>
+      <EnableI18N LFM="False"/>
+    </i18n>
     <VersionInfo>
       <StringTable ProductVersion=""/>
     </VersionInfo>
+    <BuildModes Count="1">
+      <Item1 Name="Default" Default="True"/>
+    </BuildModes>
     <PublishOptions>
       <Version Value="2"/>
-      <IgnoreBinaries Value="False"/>
       <IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/>
-      <ExcludeFileFilter Value="*.(bak|ppu|ppw|o|so);*~;backup"/>
+      <ExcludeFileFilter Value="*.(bak|ppu|o|so);*~;backup"/>
     </PublishOptions>
     <RunParams>
       <local>
         <FormatVersion Value="1"/>
-        <LaunchingApplication PathPlusParams="/usr/X11R6/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/>
+        <CommandLineParams Value="--project=fpdoc.xml"/>
+        <LaunchingApplication PathPlusParams="/usr/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/>
       </local>
     </RunParams>
-    <Units Count="11">
+    <RequiredPackages Count="1">
+      <Item1>
+        <PackageName Value="FCL"/>
+      </Item1>
+    </RequiredPackages>
+    <Units Count="14">
       <Unit0>
         <Filename Value="fpdoc.pp"/>
         <IsPartOfProject Value="True"/>
         <UnitName Value="FPDoc"/>
       </Unit0>
       <Unit1>
-        <Filename Value="dwriter.pp"/>
+        <Filename Value="dglobals.pp"/>
         <IsPartOfProject Value="True"/>
-        <UnitName Value="dWriter"/>
+        <UnitName Value="dGlobals"/>
       </Unit1>
       <Unit2>
-        <Filename Value="dwlinear.pp"/>
+        <Filename Value="dw_dxml.pp"/>
         <IsPartOfProject Value="True"/>
-        <UnitName Value="dwlinear"/>
+        <UnitName Value="dw_dxml"/>
       </Unit2>
       <Unit3>
-        <Filename Value="dw_latex.pp"/>
+        <Filename Value="dw_html.pp"/>
         <IsPartOfProject Value="True"/>
-        <UnitName Value="dw_LaTeX"/>
+        <UnitName Value="dw_html"/>
       </Unit3>
       <Unit4>
-        <Filename Value="dw_xml.pp"/>
+        <Filename Value="dw_ipflin.pas"/>
         <IsPartOfProject Value="True"/>
-        <UnitName Value="dw_XML"/>
+        <UnitName Value="dw_ipflin"/>
       </Unit4>
       <Unit5>
-        <Filename Value="dw_html.pp"/>
+        <Filename Value="dw_latex.pp"/>
         <IsPartOfProject Value="True"/>
-        <UnitName Value="dw_HTML"/>
+        <UnitName Value="dw_latex"/>
       </Unit5>
       <Unit6>
-        <Filename Value="dw_man.pp"/>
+        <Filename Value="dwlinear.pp"/>
         <IsPartOfProject Value="True"/>
-        <UnitName Value="dw_man"/>
+        <UnitName Value="dwlinear"/>
       </Unit6>
       <Unit7>
         <Filename Value="dw_linrtf.pp"/>
         <IsPartOfProject Value="True"/>
-        <UnitName Value="dw_LinRTF"/>
+        <UnitName Value="dw_linrtf"/>
       </Unit7>
       <Unit8>
-        <Filename Value="dw_txt.pp"/>
+        <Filename Value="dw_man.pp"/>
         <IsPartOfProject Value="True"/>
-        <UnitName Value="dw_txt"/>
+        <UnitName Value="dw_man"/>
       </Unit8>
       <Unit9>
-        <Filename Value="dglobals.pp"/>
+        <Filename Value="dwriter.pp"/>
         <IsPartOfProject Value="True"/>
-        <UnitName Value="dGlobals"/>
+        <UnitName Value="dwriter"/>
       </Unit9>
       <Unit10>
-        <Filename Value="dw_ipflin.pas"/>
+        <Filename Value="dw_txt.pp"/>
         <IsPartOfProject Value="True"/>
-        <UnitName Value="dw_ipflin"/>
+        <UnitName Value="dw_txt"/>
       </Unit10>
+      <Unit11>
+        <Filename Value="dw_xml.pp"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="dw_xml"/>
+      </Unit11>
+      <Unit12>
+        <Filename Value="fpdocproj.pas"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="fpdocproj"/>
+      </Unit12>
+      <Unit13>
+        <Filename Value="fpdocxmlopts.pas"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="fpdocxmlopts"/>
+      </Unit13>
     </Units>
   </ProjectOptions>
   <CompilerOptions>
     <Version Value="9"/>
-    </Parsing>
+    <Target>
+      <Filename Value="fpdoc"/>
+    </Target>
+    <SearchPaths>
+      <IncludeFiles Value="$(ProjOutDir)"/>
+      <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
+    </SearchPaths>
     <Other>
+      <CompilerMessages>
+        <UseMsgFile Value="True"/>
+      </CompilerMessages>
       <CompilerPath Value="$(CompPath)"/>
     </Other>
   </CompilerOptions>
+  <Debugging>
+    <Exceptions Count="3">
+      <Item1>
+        <Name Value="EAbort"/>
+      </Item1>
+      <Item2>
+        <Name Value="ECodetoolError"/>
+      </Item2>
+      <Item3>
+        <Name Value="EFOpenError"/>
+      </Item3>
+    </Exceptions>
+  </Debugging>
 </CONFIG>

+ 208 - 111
utils/fpdoc/fpdoc.pp

@@ -16,7 +16,7 @@
 program FPDoc;
 
 uses
-  SysUtils, Classes, Gettext, DOM, XMLWrite, PasTree, PParser,
+  SysUtils, Classes, Gettext, DOM, XMLWrite, PasTree, PParser, custapp,
   dGlobals,  // GLobal definitions, constants.
   dwriter,   // TFPDocWriter definition.
   dwlinear,  // Linear (abstract) writer
@@ -27,29 +27,44 @@ uses
   dw_ipflin, // IPF writer (new linear output)
   dw_man,    // Man page writer
   dw_linrtf, // linear RTF writer
-  dw_txt;    // TXT writer
+  dw_txt, fpdocproj, fpdocxmlopts;    // TXT writer
 
 const
-  OSTarget: String = {$I %FPCTARGETOS%};
-  CPUTarget: String = {$I %FPCTARGETCPU%};
-  FPCVersion: String = {$I %FPCVERSION%};
-  FPCDate: String = {$I %FPCDATE%};
+  DefOSTarget    = {$I %FPCTARGETOS%};
+  DefCPUTarget   = {$I %FPCTARGETCPU%};
+  DefFPCVersion  = {$I %FPCVERSION%};
+  DefFPCDate     = {$I %FPCDATE%};
 
-var
-  Backend : String;
-  BackendOptions : TStrings;
-  InputFiles, DescrFiles: TStringList;
-  PackageName, DocLang, ContentFile : String;
-  Engine: TFPDocEngine;
-  StopOnParserError : Boolean;
+Type
+
+  { TFPDocAplication }
+
+  TFPDocAplication = Class(TCustomApplication)
+  private
+    FProject : TFPDocProject;
+    FProjectFile : Boolean;
+    FPackage : TFPDocPackage;
+  Protected
+    procedure ParseCommandLine;
+    procedure Parseoption(const S: String);
+    Procedure Usage(AnExitCode : Byte);
+    procedure CreateDocumentation(APackage : TFPDocPackage; Options : TEngineOptions);
+    Procedure DoRun; override;
+  Public
+    Constructor Create(AOwner : TComponent); override;
+    Destructor Destroy; override;
+    Function SelectedPackage : TFPDocPackage;
+  end;
 
-Procedure Usage(AnExitCode : Byte);
+
+Procedure TFPDocAplication.Usage(AnExitCode : Byte);
 
 Var
   I,P : Integer;
   S : String;
   L : TStringList;
   C : TFPDocWriterClass;
+  Backend : String;
 
 begin
   Writeln(Format(SCmdLineHelp,[ExtractFileName(Paramstr(0))]));
@@ -74,6 +89,7 @@ begin
   Writeln(SUsageOption190);
   L:=TStringList.Create;
   Try
+    Backend:=FProject.OPtions.Backend;
     If (Backend='') then
       begin
       Writeln;
@@ -90,8 +106,8 @@ begin
     else
       begin
       Writeln;
-      Writeln(Format(SUsageFormatSpecific,[Lowercase(Backend)]));
-      C:=GetWriterClass(backend);
+      Writeln(Format(SUsageFormatSpecific,[Lowercase(backend)]));
+      C:=GetWriterClass(Backend);
       C.Usage(L);
       If L.Count>0 then
         For I:=0 to (L.Count-1) div 2 do
@@ -106,42 +122,88 @@ begin
   Halt(AnExitCode);
 end;
 
-procedure InitOptions;
+destructor TFPDocAplication.Destroy;
+
 begin
-  InputFiles := TStringList.Create;
-  DescrFiles := TStringList.Create;
-  BackendOptions := TStringList.Create;
-  Engine := TFPDocEngine.Create;
-  StopOnParserError:=False;
+  FreeAndNil(FProject);
+  Inherited;
 end;
 
-procedure FreeOptions;
+function TFPDocAplication.SelectedPackage: TFPDocPackage;
 begin
-  Engine.Free;
-  BackendOptions.Free;
-  DescrFiles.Free;
-  InputFiles.Free;
+  Result:=FPackage;
+  if (FPackage=Nil) or (FPackage.Name='') then
+    begin
+    Writeln(SNeedPackageName);
+    Usage(1);
+    end;
 end;
 
-procedure ReadContentFile(const AParams: String);
+
+procedure TFPDocAplication.ParseCommandLine;
+
+  Function ProjectOpt(Const s : string) : boolean;
+
+  begin
+    Result:=(Copy(s,1,3)='-p=') or (Copy(s,1,10)='--project=');
+  end;
+
+  Function PackageOpt(Const s : string) : boolean;
+
+  begin
+    Result:=((Copy(s,1,3)='-a=') or (Copy(s,1,10)='--package='));
+  end;
+
 var
-  i: Integer;
+  i : Integer;
+  s : string;
+
 begin
-  i := Pos(',', AParams);
-  Engine.ReadContentFile(Copy(AParams, 1, i - 1),
-    Copy(AParams, i + 1, Length(AParams)));
+  // Check project
+  for i := 1 to ParamCount do
+    begin
+    s:=ParamStr(I);
+    If ProjectOpt(S) then
+      ParseOption(s);
+    If (FProject.Packages.Count=1) then
+      FPackage:=FProject.Packages[0]
+    else if (FProject.Options.DefaultPackageName<>'') then
+      Fpackage:=FProject.Packages.FindPackage(FProject.Options.DefaultPackageName);
+    end;
+  If FProject.Packages.Count=0 then
+    begin
+    FPackage:=FProject.Packages.Add as  TFPDocPackage;
+    end;
+  // Check package
+  for i := 1 to ParamCount do
+    begin
+    s:=ParamStr(I);
+    If PackageOpt(S) then
+      ParseOption(s);
+    end;
+  for i := 1 to ParamCount do
+    begin
+    s:=ParamStr(I);
+    If Not (ProjectOpt(s) or PackageOpt(S)) then
+      ParseOption(s);
+    end;
+  if (FPackage=Nil) or (FPackage.Name='') then
+    begin
+    Writeln(SNeedPackageName);
+    Usage(1);
+    end;
 end;
 
-procedure ParseOption(const s: String);
+procedure TFPDocAplication.Parseoption(Const S : String);
 
-  procedure AddToFileList(List: TStringList; const FileName: String);
+  procedure AddToFileList(List: TStrings; const FileName: String);
   var
     f: Text;
     s: String;
   begin
     if Copy(FileName, 1, 1) = '@' then
     begin
-      Assign(f, Copy(FileName, 2, Length(FileName)));
+      AssignFile(f, Copy(FileName, 2, Length(FileName)));
       Reset(f);
       while not EOF(f) do
       begin
@@ -161,13 +223,13 @@ begin
   if (s = '-h') or (s = '--help') then
     Usage(0)
   else if s = '--hide-protected' then
-    Engine.HideProtected := True
+    FProject.Options.HideProtected := True
   else if s = '--warn-no-node' then
-    Engine.WarnNoNode := True
+    FProject.Options.WarnNoNode := True
   else if s = '--show-private' then
-    Engine.HidePrivate := False
+    FProject.Options.ShowPrivate := False
   else if s = '--stop-on-parser-error' then
-    StopOnParserError := True
+    FProject.Options.StopOnParseError := True
   else
     begin
     i := Pos('=', s);
@@ -181,102 +243,122 @@ begin
       Cmd := s;
       SetLength(Arg, 0);
       end;
-    if Cmd = '--descr' then
-      AddToFileList(DescrFiles, Arg)
+    if (Cmd = '--project') or (Cmd='-p') then
+      begin
+      FProjectFile:=True;
+      With TXMLFPDocOptions.Create(self) do
+        try
+          LoadOptionsFromFile(FProject,Arg);
+        finally
+          Free;
+        end;
+      end
+    else if (Cmd = '--descr') then
+      AddToFileList(SelectedPackage.Descriptions, Arg)
     else if (Cmd = '-f') or (Cmd = '--format') then
       begin
       Arg:=UpperCase(Arg);
       If FindWriterClass(Arg)=-1 then
         WriteLn(StdErr, Format(SCmdLineInvalidFormat, [Arg]))
       else
-        BackEnd:=Arg;
+        FProject.Options.BackEnd:=Arg;
       end
     else if (Cmd = '-l') or (Cmd = '--lang') then
-      DocLang := Arg
+      FProject.Options.Language := Arg
     else if (Cmd = '-i') or (Cmd = '--input') then
-      AddToFileList(InputFiles, Arg)
+      AddToFileList(SelectedPackage.Inputs, Arg)
     else if (Cmd = '-o') or (Cmd = '--output') then
-      Engine.Output := Arg
+      SelectedPackage.Output := Arg
     else if Cmd = '--content' then
-      ContentFile := Arg
+      SelectedPackage.ContentFile := Arg
     else if Cmd = '--import' then
-      ReadContentFile(Arg)
+      SelectedPackage.Imports.Add(Arg)
     else if Cmd = '--package' then
-      PackageName := Arg
+      begin
+      If FProjectFile then
+        FPackage:=FProject.Packages.FindPackage(Arg)
+      else
+        FPackage.Name:=Arg;
+      end
     else if Cmd = '--ostarget' then
-      OSTarget := Arg
+      FProject.Options.OSTarget := Arg
     else if Cmd = '--cputarget' then
-      CPUTarget := Arg
+      FProject.Options.CPUTarget := Arg
     else if Cmd = '--mo-dir' then
-      modir := Arg
+      FProject.Options.modir := Arg
     else if Cmd = '--parse-impl' then
-      Engine.InterfaceOnly:=false
+      FProject.Options.InterfaceOnly:=false
     else
       begin
-      BackendOptions.Add(Cmd);
-      BackendOptions.Add(Arg);
+      FProject.Options.BackendOptions.Add(Cmd);
+      FProject.Options.BackendOptions.Add(Arg);
       end;
     end;
 end;
 
-procedure ParseCommandLine;
-
-var
-  i: Integer;
-
-begin
-  for i := 1 to ParamCount do
-    ParseOption(ParamStr(i));
-  If (BackEnd='') then
-    BackEnd:='html';
-  if (PackageName='') then
-    begin
-    Writeln(SNeedPackageName);
-    Usage(1);
-    end;
-end;
 
-procedure CreateDocumentation;
+procedure TFPDocAplication.CreateDocumentation(APackage : TFPDocPackage; Options : TEngineOptions);
 
 var
-  i: Integer;
+  i,j: Integer;
   WriterClass : TFPDocWriterClass;
   Writer : TFPDocWriter;
+  Engine : TFPDocEngine;
+  Cmd,Arg : String;
 
 begin
-  for i := 0 to DescrFiles.Count - 1 do
-    Engine.AddDocFile(DescrFiles[i]);
-  Engine.SetPackageName(PackageName);
-  if Length(DocLang) > 0 then
-    TranslateDocStrings(DocLang);
-  for i := 0 to InputFiles.Count - 1 do
-    try
-      ParseSource(Engine, InputFiles[i], OSTarget, CPUTarget);
-    except
-      on e: EParserError do
-        If StopOnParserError then
-          Raise
-        else 
-          WriteLn(StdErr, Format('%s(%d,%d): %s',
-                  [e.Filename, e.Row, e.Column, e.Message]));
-    end;
-  WriterClass:=GetWriterClass(Backend);
-  Writer:=WriterClass.Create(Engine.Package,Engine);
-  With Writer do
-    Try
-      If BackendOptions.Count>0 then
-        for I:=0 to ((BackendOptions.Count-1) div 2) do
-          If not InterPretOption(BackendOptions[I*2],BackendOptions[I*2+1]) then
-            WriteLn(StdErr, Format(SCmdLineInvalidOption,[BackendOptions[I*2]+' '+BackendOptions[I*2+1]]));
-      WriteDoc;
-    Finally
-      Free;
-    end;
-  if Length(ContentFile) > 0 then
-    Engine.WriteContentFile(ContentFile);
+  Engine:=TFPDocEngine.Create;
+  try
+    For J:=0 to Apackage.Imports.Count-1 do
+      begin
+      Arg:=Apackage.Imports[j];
+      i := Pos(',', Arg);
+      Engine.ReadContentFile(Copy(Arg,1,i-1),Copy(Arg,i+1,Length(Arg)));
+      end;
+    for i := 0 to APackage.Descriptions.Count - 1 do
+      Engine.AddDocFile(APackage.Descriptions[i]);
+    Engine.SetPackageName(APackage.Name);
+    Engine.Output:=APackage.Output;
+    Engine.HideProtected:=Options.HideProtected;
+    Engine.HidePrivate:=Not Options.ShowPrivate;
+    if Length(Options.Language) > 0 then
+      TranslateDocStrings(Options.Language);
+    for i := 0 to Fpackage.Inputs.Count - 1 do
+      try
+        ParseSource(Engine, APackage.Inputs[i], Options.OSTarget, Options.CPUTarget);
+      except
+        on e: EParserError do
+          If Options.StopOnParseError then
+            Raise
+          else
+            WriteLn(StdErr, Format('%s(%d,%d): %s',
+                    [e.Filename, e.Row, e.Column, e.Message]));
+      end;
+    WriterClass:=GetWriterClass(Options.Backend);
+    Writer:=WriterClass.Create(Engine.Package,Engine);
+    With Writer do
+      Try
+        If Options.BackendOptions.Count>0 then
+          for I:=0 to ((Options.BackendOptions.Count-1) div 2) do
+            begin
+            Cmd:=Options.BackendOptions[I*2];
+            Arg:=Options.BackendOptions[I*2+1];
+            If not InterPretOption(Cmd,Arg) then
+              WriteLn(StdErr, Format(SCmdLineInvalidOption,[Cmd+'='+Arg]));
+            end;
+        WriteDoc;
+      Finally
+        Free;
+      end;
+    if Length(FPackage.ContentFile) > 0 then
+      Engine.WriteContentFile(FPackage.ContentFile);
+  finally
+    FreeAndNil(Engine);
+  end;
 end;
 
 
+Procedure TFPDocAplication.DoRun;
 
 begin
 {$IFDEF Unix}
@@ -285,15 +367,30 @@ begin
   gettext.TranslateResourceStrings('intl/fpdoc.%s.mo');
 {$ENDIF}
   WriteLn(STitle);
-  WriteLn(Format(SVersion, [FPCVersion, FPCDate]));
+  WriteLn(Format(SVersion, [DefFPCVersion, DefFPCDate]));
   WriteLn(SCopyright);
   WriteLn;
-  InitOptions;
-  Try
-    ParseCommandLine;
-    CreateDocumentation;
-    WriteLn(SDone);
-  Finally
-    FreeOptions;
-  end;
+  ParseCommandLine;
+  CreateDocumentation(FPackage,FProject.Options);
+  WriteLn(SDone);
+  Terminate;
+end;
+
+constructor TFPDocAplication.Create(AOwner: TComponent);
+begin
+  inherited Create(AOwner);
+  StopOnException:=true;
+  FProject:=TFPDOCproject.Create(Nil);
+  FProject.Options.StopOnParseError:=False;
+  FProject.Options.CPUTarget:=DefCPUTarget;
+  FProject.Options.OSTarget:=DefOSTarget;
+end;
+
+begin
+  With TFPDocAplication.Create(Nil) do
+    try
+      Run;
+    finally
+      Free;
+    end;
 end.

+ 236 - 0
utils/fpdoc/fpdocproj.pas

@@ -0,0 +1,236 @@
+unit fpdocproj;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils;
+
+Type
+
+  { TFPDocPackage }
+
+  TFPDocPackage = Class(TCollectionItem)
+  private
+    FContent: String;
+    FDescriptions: TStrings;
+    FIMports: TStrings;
+    FinPuts: TStrings;
+    FName: String;
+    FOutput: String;
+  Public
+    constructor Create(ACollection: TCollection); override;
+    destructor destroy; override;
+    procedure Assign(Source : TPersistent); override;
+    Property Name : String Read FName Write FName;
+    Property Inputs : TStrings Read FinPuts;
+    Property Descriptions : TStrings Read FDescriptions;
+    Property Imports : TStrings read FIMports;
+    Property ContentFile : String Read FContent Write FContent;
+    Property Output : String Read FOutput Write FOutput;
+  end;
+
+  { TFPDocPackages }
+
+  TFPDocPackages = Class(TCollection)
+  private
+    function GetP(AIndex : Integer): TFPDocPackage;
+    procedure SetP(AIndex : Integer; const AValue: TFPDocPackage);
+  Public
+    Function IndexOfPackage(Const AName : String) : Integer;
+    Function FindPackage(Const AName : String) : TFPDOcPackage;
+    Property Packages[AIndex : Integer] : TFPDocPackage Read GetP Write SetP; Default;
+  end;
+
+  { TEngineOptions }
+
+  TEngineOptions = Class(TPersistent)
+  private
+    FBackEndoptions: TStrings;
+    FCPUTarget: String;
+    FDefaultPackageName: String;
+    FFormat: String;
+    FHidePrivate: Boolean;
+    FHideProtected: Boolean;
+    FIO: Boolean;
+    FLanguage: String;
+    FMoDir: String;
+    FOSTarget: String;
+    FSOPE: Boolean;
+    FWarnNoNode: Boolean;
+    procedure SetBackendOptions(const AValue: TStrings);
+  Public
+    Constructor Create;
+    Destructor Destroy; override;
+    procedure Assign(Source : TPersistent); override;
+  Published
+    Property OSTarget : String Read FOSTarget Write FOStarget;
+    Property CPUTarget : String Read FCPUTarget Write FCPUTarget;
+    Property Language : String Read FLanguage Write fLanguage;
+    Property Backend : String Read FFormat Write FFormat;
+    Property BackendOptions : TStrings Read FBackEndoptions Write SetBackendOptions;
+    Property StopOnParseError : Boolean Read FSOPE Write FSOPE;
+    Property HideProtected : Boolean Read FHideProtected Write FHideProtected;
+    Property WarnNoNode : Boolean Read FWarnNoNode Write FWarnNoNode;
+    Property ShowPrivate : Boolean Read FHidePrivate Write FHidePrivate;
+    Property InterfaceOnly : Boolean Read FIO Write FIO;
+    Property MoDir : String Read FMoDir Write FMODir;
+    Property DefaultPackageName : String Read FDefaultPackageName Write FDefaultPackageName;
+  end;
+
+  { TFPDocProject }
+
+  TFPDocProject = Class(TComponent)
+  private
+    FOptions: TEngineOptions;
+    FPackages: TFPDocPackages;
+    procedure setOptions(const AValue: TEngineOptions);
+  Public
+    Constructor Create(AOwner : TComponent); override;
+    Destructor Destroy; override;
+  Published
+    Property Packages : TFPDocPackages Read FPackages Write FPackages;
+    Property Options : TEngineOptions Read FOptions Write setOptions;
+  end;
+
+implementation
+
+{ TEngineOptions }
+
+procedure TEngineOptions.SetBackendOptions(const AValue: TStrings);
+begin
+  if FBackEndoptions=AValue then exit;
+  FBackEndoptions.Assign(AValue);
+end;
+
+constructor TEngineOptions.Create;
+begin
+  FBackendOptions:=TStringList.Create;
+end;
+
+destructor TEngineOptions.Destroy;
+begin
+  FreeAndNil(FBackendOptions);
+  inherited Destroy;
+end;
+
+procedure TEngineOptions.Assign(Source: TPersistent);
+
+var
+  O : TEngineOptions;
+
+begin
+  if (Source is TEngineOptions) then
+    begin
+    O:=Source as TEngineOptions;
+    FBackEndoptions.Assign(O.BackendOptions);
+    FCPUTarget:=O.CPUTarget;
+    FFormat:=O.Backend;
+    FLanguage:=O.Language;
+    FOSTarget:=O.OSTarget;
+    FSOPE:=O.StopOnParseError;
+    HideProtected:=O.HideProtected;
+    WarnNoNode:=O.WarnNoNode;
+    ShowPrivate:=O.ShowPrivate;
+    InterfaceOnly:=O.InterfaceOnly;
+    MoDir:=O.MoDir;
+    end
+  else
+    inherited Assign(Source);
+end;
+
+{ TFPDocProject }
+
+procedure TFPDocProject.setOptions(const AValue: TEngineOptions);
+begin
+  if FOptions=AValue then exit;
+  FOptions.Assign(AValue);
+end;
+
+constructor TFPDocProject.Create(AOwner: TComponent);
+begin
+  inherited Create(AOwner);
+  FPackages:=TFPDocPackages.Create(TFPDocPackage);
+  FOptions:=TEngineOptions.Create;
+end;
+
+destructor TFPDocProject.Destroy;
+begin
+  FreeAndNil(Foptions);
+  FreeAndNil(FPackages);
+  inherited Destroy;
+end;
+
+{ TFPDocPackages }
+
+function TFPDocPackages.GetP(AIndex : Integer): TFPDocPackage;
+begin
+  Result:=TFPDocPackage(Items[AIndex]);
+end;
+
+procedure TFPDocPackages.SetP(AIndex : Integer; const AValue: TFPDocPackage);
+begin
+  Items[AIndex]:=AValue;
+end;
+
+function TFPDocPackages.IndexOfPackage(const AName: String): Integer;
+
+begin
+  Result:=Count-1;
+  While (Result>=0) and (CompareText(GetP(Result).Name,AName)<>0) do
+    Dec(Result)
+end;
+
+function TFPDocPackages.FindPackage(const AName: String): TFPDOcPackage;
+
+Var
+  I : Integer;
+
+begin
+  I:=IndexOfPackage(AName);
+  If (I=-1) then
+    Result:=Nil
+  else
+    Result:=GetP(I);
+end;
+
+{ TFPDocPackage }
+
+constructor TFPDocPackage.Create(ACollection: TCollection);
+begin
+  inherited Create(ACollection);
+  FImports:=TStringList.Create;
+  FDescriptions:=TStringList.Create;
+  FInputs:=TStringList.Create;
+end;
+
+destructor TFPDocPackage.destroy;
+begin
+  FreeAndNil(FDescriptions);
+  FreeAndNil(FIMports);
+  FreeAndNil(FinPuts);
+  inherited destroy;
+end;
+
+procedure TFPDocPackage.Assign(Source: TPersistent);
+
+Var
+  P : TFPDocPackage;
+
+begin
+  If Source is TFPDocPackage then
+    begin
+    P:=Source as TFPDocPackage;
+    Fname:=P.Name;
+    FContent:=P.ContentFile;
+    FImports.Assign(P.Imports);
+    FInputs.Assign(P.Inputs);
+    FDescriptions.Assign(P.Descriptions);
+    end
+  else
+    inherited Assign(Source);
+end;
+
+end.
+

+ 235 - 0
utils/fpdoc/fpdocxmlopts.pas

@@ -0,0 +1,235 @@
+unit fpdocxmlopts;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, fpdocproj, dom;
+
+Type
+
+  { TXMLFPocOptions }
+
+  { TXMLFPDocOptions }
+
+  TXMLFPDocOptions = Class(TComponent)
+  Protected
+    Procedure Error(Const Msg : String);
+    Procedure Error(Const Fmt : String; Args : Array of Const);
+    Procedure LoadPackage(APackage : TFPDocPackage; E : TDOMElement); virtual;
+    Procedure LoadPackages(Packages : TFPDocPackages; E : TDOMElement);
+    Procedure LoadEngineOptions(Options : TEngineOptions; E : TDOMElement); virtual;
+  Public
+    Procedure LoadOptionsFromFile(AProject : TFPDocProject; Const AFileName : String);
+    Procedure LoadFromXML(AProject : TFPDocProject; XML : TXMLDocument); virtual;
+  end;
+  EXMLFPdoc = Class(Exception);
+
+implementation
+
+Uses XMLRead;
+
+Resourcestring
+  SErrInvalidRootNode = 'Invalid options root node: Got "%s", expected "docproject"';
+  SErrNoPackagesNode = 'No "packages" node found in docproject';
+  SErrNoInputFile = 'unit tag without file attribute found';
+  SErrNoDescrFile = 'description tag without file attribute';
+
+{ TXMLFPDocOptions }
+
+Function IndexOfString(S : String; List : Array of string) : Integer;
+
+begin
+  S:=UpperCase(S);
+  Result:=High(List);
+  While (Result>=0) and (S<>UpperCase(List[Result])) do
+    Dec(Result);
+end;
+
+procedure TXMLFPDocOptions.Error(Const Msg: String);
+begin
+  Raise EXMLFPDoc.Create(Msg);
+end;
+
+procedure TXMLFPDocOptions.Error(const Fmt: String; Args: array of const);
+begin
+  Raise EXMLFPDoc.CreateFmt(Fmt,Args);
+end;
+
+
+
+procedure TXMLFPDocOptions.LoadPackage(APackage: TFPDocPackage; E: TDOMElement);
+
+  Function LoadInput(I : TDOMElement) : String;
+
+  Var
+    S : String;
+
+  begin
+    Result:=I['file'];
+    If (Result='') then
+      Error(SErrNoInputFile);
+    S:=I['options'];
+    if (S<>'') then
+      Result:=S+' '+Result;
+  end;
+
+  Function LoadDescription(I : TDOMElement) : String;
+
+  Var
+    S : String;
+
+  begin
+    Result:=I['file'];
+    If (Result='') then
+      Error(SErrNoDescrFile);
+  end;
+
+Const
+  OpCount = 0;
+  OpNames : Array[0..OpCount] of string
+          = ('');
+Var
+  N,S : TDOMNode;
+  O : TDomElement;
+
+begin
+  APackage.Name:=E['name'];
+  APackage.output:=E['output'];
+  APackage.ContentFile:=E['contentfile'];
+  N:=E.FirstChild;
+  While (N<>Nil) do
+    begin
+    If (N.NodeType=ELEMENT_NODE) then
+      begin
+      O:=N as TDOMElement;
+      If (O.NodeName='units') then
+        begin
+        S:=O.FirstChild;
+        While (S<>Nil) do
+          begin
+          If (S.NodeType=Element_Node) and (S.NodeName='unit') then
+            APackage.Inputs.add(LoadInput(S as TDomElement));
+          S:=S.NextSibling;
+          end;
+        end
+      else If (O.NodeName='descriptions') then
+        begin
+        S:=O.FirstChild;
+        While (S<>Nil) do
+          begin
+          If (S.NodeType=Element_Node) and (S.NodeName='description') then
+            APackage.Descriptions.add(LoadDescription(S as TDomElement));
+          S:=S.NextSibling;
+          end;
+        end
+      end;
+    N:=N.NextSibling;
+    end;
+end;
+
+procedure TXMLFPDocOptions.LoadPackages(Packages: TFPDocPackages; E: TDOMElement
+  );
+
+Var
+  N : TDOMNode;
+
+begin
+  N:=E.FirstChild;
+  While (N<>Nil) do
+    begin
+    If (N.NodeName='package') and (N.NodeType=ELEMENT_NODE) then
+      LoadPackage(Packages.Add as TFPDocPackage, N as TDOMElement);
+    N:=N.NextSibling;
+    end;
+end;
+
+procedure TXMLFPDocOptions.LoadEngineOptions(Options: TEngineOptions;
+  E: TDOMElement);
+
+  Function TrueValue(V : String) : Boolean;
+
+  begin
+    V:=LowerCase(V);
+    Result:=(v='true') or (v='1') or (v='yes');
+  end;
+
+Const
+  NCount = 10;
+  ONames : Array[0..NCount] of string
+         = ('hide-protected','warn-no-node','show-private',
+            'stop-on-parser-error', 'ostarget','cputarget',
+            'mo-dir','parse-impl','format', 'language',
+            'package');
+
+Var
+  O : TDOMnode;
+  N,V : String;
+
+begin
+  O:=E.FirstChild;
+  While (O<>Nil) do
+    begin
+    If (O.NodeType=Element_NODE) and (O.NodeName='option') then
+      begin
+      N:=LowerCase(TDOMElement(o)['name']);
+      V:=TDOMElement(o)['value'];
+      Case IndexOfString(N,ONames) of
+        0 : Options.HideProtected:=TrueValue(v);
+        1 : Options.WarnNoNode:=TrueValue(v);
+        2 : Options.ShowPrivate:=TrueValue(v);
+        3 : Options.StopOnParseError:=TrueValue(v);
+        4 : Options.ostarget:=v;
+        5 : Options.cputarget:=v;
+        6 : Options.MoDir:=V;
+        7 : Options.InterfaceOnly:=Not TrueValue(V);
+        8 : Options.Backend:=V;
+        9 : Options.Language:=v;
+        10 : Options.DefaultPackageName:=V;
+      else
+        Options.BackendOptions.add('--'+n);
+        Options.BackendOptions.add(v);
+      end;
+      end;
+    O:=O.NextSibling
+    end;
+end;
+
+procedure TXMLFPDocOptions.LoadOptionsFromFile(AProject: TFPDocProject;
+  const AFileName: String);
+
+Var
+  XML : TXMLDocument;
+
+begin
+   XMLRead.ReadXMLFile(XML,AFileName);
+   try
+     LoadFromXML(AProject,XML);
+   finally
+     FreeAndNil(XML);
+   end;
+end;
+
+procedure TXMLFPDocOptions.LoadFromXML(AProject: TFPDocProject;
+  XML: TXMLDocument);
+
+Var
+  E : TDOMElement;
+  N : TDomNode;
+
+begin
+  E:=XML.DocumentElement;
+  if (E.NodeName<>'docproject') then
+    Error(SErrInvalidRootNode,[E.NodeName]);
+  N:=E.FindNode('packages');
+  If (N=Nil) or (N.NodeType<>ELEMENT_NODE) then
+    Error(SErrNoPackagesNode);
+  LoadPackages(AProject.Packages,N as TDomElement);
+  N:=E.FindNode('options');
+  If (N<>Nil) and (N.NodeType=ELEMENT_NODE) then
+    LoadEngineOptions(AProject.Options,N as TDOMElement);
+end;
+
+end.
+

+ 29 - 0
utils/fpdoc/sample-project.xml

@@ -0,0 +1,29 @@
+<docproject>
+  <packages>
+    <!-- Multiple packages can be entered. 
+         If only one is specified, it is selected. 
+         "name" is a mandatory attribute
+         a "units" tag is required, and a "descriptions" tag as well
+    -->
+    <package name="fpdoc" output="docdir" contentfile="fpdoc.cnt">
+      <!-- All input files, one "unit" tag per unit -->
+      <units>
+        <!-- "file" is a mandatory attribute, "options" is not mandatory -->
+        <unit file="dglobals.pp" options="-S2"/>
+      </units>
+      <descriptions>
+        <!-- Description files here. One "description" tag per file.
+            "file" is the only mandatory attribute -->
+        <description file="dglobals.xml"/>
+      </descriptions>
+    </package>
+  </packages>
+  <options>
+    <!-- All command-line options can be specified here with the same name
+         and value as on the actual command-line. Boolean options must have
+         a value of 'true', '1' or 'yes' -->
+    <option name="format" value="html"/>
+    <option name="hide-protected" value="true"/>
+    <option name="footer-date" value="yyyy-mm-dd"/>
+  </options>
+</docproject>