{ *************************************************************************** * * * This source is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This code is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * General Public License for more details. * * * * A copy of the GNU General Public License is available on the World * * Wide Web at . You can also * * obtain it by writing to the Free Software Foundation, * * Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. * * * *************************************************************************** Author: Mattias Gaertner / Juha Manninen Abstract: Convert Delphi projects/packages to lazarus projects/packages. This was refactored and cleaned from code in unit DelphiProject2Laz. Now it is object oriented and easier to maintain / improve. } unit ConvertDelphi; {$mode objfpc}{$H+} interface uses // RTL + FCL Classes, SysUtils, contnrs, IniFiles, // LCL Forms, Controls, Dialogs, // CodeTools CodeToolManager, DefineTemplates, CodeCache, LinkScanner, FileProcs, // LazUtils LConvEncoding, FileUtil, LazFileUtils, LazUTF8, LazStringUtils, LazLoggerBase, AvgLvlTree, // BuildIntf PackageIntf, ProjectIntf, ComponentReg, IDEExternToolIntf, // IDEIntf IDEDialogs, LazIDEIntf, IDEOptEditorIntf, EditorSyntaxHighlighterDef, // IdeUtils IdeUtilsPkgStrConsts, // IdeConfig IDEProcs, SearchPathProcs, ParsedCompilerOpts, CompilerOptions, ProjPackCommon, // IDE DialogProcs, Project, PackageDefs, PackageSystem, PackageEditor, BasePkgManager, LazarusIDEStrConsts, SourceFileManager, // Converter ConverterTypes, ConvertSettings, ConvCodeTool, MissingUnits, MissingPropertiesDlg, UsedUnits; const SettingDelphiModeTemplName = 'Setting Delphi Mode'; type TConvertUnitFlag = ( cdtlufRenameLowercase, // rename the unit lowercase cdtlufCanAbort // show 'Cancel all' button in error messages using mrAbort ); TConvertUnitFlags = set of TConvertUnitFlag; TConvertDelphiPBase = class; { TCacheUnitsThread } TCacheUnitsThread = class(TThread) private fPath: string; fSearcher: TFileSearcher; protected procedure Execute; override; public constructor Create(aConverter: TConvertDelphiPBase; aPath: string); destructor Destroy; override; public property Searcher: TFileSearcher read fSearcher; end; { TDelphiUnit } TDelphiUnit = class private // Converter for the unit, project or package this unit belongs to. // There is always an owner converter. fOwnerConverter: TConvertDelphiPBase; fOrigUnitFilename: string; // Original unit's file name, .pas fLazUnitFilename: string; // Extension of the new Lazarus file. If empty, gets the original file's ext. fLazFileExt: string; // Unit's info, for projects only. fUnitInfo: TUnitInfo; // Actual code for unit and form file. fPascalBuffer: TCodeBuffer; fLFMBuffer: TCodeBuffer; fFlags: TConvertUnitFlags; // Link for codetools, shared by classes that need it. fCTLink: TCodeToolLink; // For adding, removing and replacing unit names is uses sections. fUsedUnitsTool: TUsedUnitsTool; function GetDfmFileName: string; function CopyAndLoadFile: TModalResult; function FixLfmFilenameAndLoad(ADfmFilename: string): TModalResult; function ReduceMissingUnits: TModalResult; function ConvertUnitFile: TModalResult; function ConvertFormFile: TModalResult; function FixIncludeFiles: TModalResult; function AskUnitPathFromUser: TModalResult; protected public constructor Create(AOwnerConverter: TConvertDelphiPBase; const AFilename: string; aFlags: TConvertUnitFlags); destructor Destroy; override; function CheckFailed(PrevResult: TModalResult): TModalResult; public property LazFileExt: string read fLazFileExt write fLazFileExt; end; { TConvertDelphiPBase } // Base class for all converters. Takes care of error handling etc. // TConvertDelphiUnit ja TConvertDelphiProjPack inherit from this, // wrapping either one unit or whole project / package conversion. TConvertDelphiPBase = class private fSettings: TConvertSettings; fErrorMsg: string; // IsConsoleApp is only updated for TConvertDelphiProjPack. fIsConsoleApp: Boolean; // Units found in user defined paths. fCachedUnitNames: TStringToStringTree; // Map of case incorrect unit name -> real unit name. fCachedRealFileNames: TStringToStringTree; // The user selected path when searching missing units. fPrevSelectedPath: string; // Missing units that are commented automatically in all units. fAllCommentedUnits: TStringListUTF8Fast; function DoMissingUnits({%H-}AUsedUnitsTool: TUsedUnitsTool): integer; virtual; function GetCachedUnitPath(const AUnitName: string): string; protected function EndConvert(AStatus: TModalResult): Boolean; public constructor Create(const ADescription: string); constructor Create(const AFilename, ADescription: string); destructor Destroy; override; public property ErrorMsg: string read fErrorMsg; end; { TConvertDelphiUnit } // Delphi unit conversion. TConvertDelphiUnit = class(TConvertDelphiPBase) private fDirTemplate: TDefineTemplate; public constructor Create(aFileNames: TStrings); destructor Destroy; override; function Convert: TModalResult; end; { TConvertDelphiProjPack } // Base class for Delphi project and package conversion. // Code would be cleaner if TProject and TLazPackage inherited from same class. // Now they can't share much code. TConvertDelphiProjPack = class(TConvertDelphiPBase) private // Either Project or LazPackage. Typecasted to right types in property getter. fProjPack: iProjPack; fLazPInfoFilename: string; // .lpi or .lpk file name fDelphiPFilename: string; // .dpr or .dpk file name fLazPInfoSuffix: string; // '.lpi' or '.lpk' suffix fLazPSuffix: string; // '.lpr' or empty. '.lpk' is for the XML main package fDelphiPSuffix: string; // '.dpr' or '.dpk' suffix // Main unit with resource code fMainUnitConverter: TDelphiUnit; // Unit search path for project settings. fUnitSearchPaths: TStringListUTF8Fast; // Units that are found and will be added to project or package and converted. fUnitsToAddToProject: TStringListUTF8Fast; fFilesToDelete: TStringListUTF8Fast; fUseThreads: boolean; // The project/package uses TThread. function ConvertSub: TModalResult; procedure CleanUpCompilerOptionsSearchPaths(Options: TBaseCompilerOptions); procedure SetCompilerModeForDefineTempl(DefTempl: TDefineTemplate); procedure UnsetCompilerModeForDefineTempl(DefTempl: TDefineTemplate); function ConvertAllFormFiles(ConverterList: TObjectList): TModalResult; function ReadDelphiConfigFiles: TModalResult; function ExtractOptionsFromDOF(const DOFFilename: string): TModalResult; function ExtractOptionsFromCFG(const CFGFilename: string): TModalResult; procedure MissingUnitsSub(AUsedUnits: TUsedUnits); function DoMissingUnits(AUsedUnitsTool: TUsedUnitsTool): integer; override; function AddToProjectLater(const AFileName: string): Boolean; function MaybeDeleteFiles: TModalResult; function CheckUnitForConversion(aFileName: string): Boolean; procedure AddDependency(APackName: String); function CheckPackageDep(AUnitName: string): Boolean; function TryAddPackageDep(const AUnitName, ADefaultPkgName: string): Boolean; protected function CreateInstance: TModalResult; virtual; abstract; function CreateMainSourceFile: TModalResult; virtual; abstract; function FindAllUnits: TModalResult; virtual; abstract; function ConvertAllUnits: TModalResult; virtual; abstract; function ExtractOptionsFromDelphiSource: TModalResult; virtual; abstract; // Abstract base for the fake Project / Package virtual methods. function GetMainName: string; virtual; abstract; function SaveAndMaybeClose(const {%H-}aFilename: string): TModalResult; virtual; function ContainsFile(const aFileName: string): Boolean; virtual; abstract; function FindDependencyByName(const PackageName: string): TPkgDependency; virtual; abstract; function FirstDependency: TPkgDependency; virtual; abstract; public constructor Create(const AFilename, ADescription: string); destructor Destroy; override; function Convert: TModalResult; public property MainName: string read GetMainName; end; { TConvertDelphiProject } // Delphi project conversion. TConvertDelphiProject = class(TConvertDelphiProjPack) private function AddUnit(AFileName: string; out OutUnitInfo: TUnitInfo): TModalResult; function GetLazProject: TProject; procedure SetLazProject(const AValue: TProject); protected function CreateInstance: TModalResult; override; function CreateMainSourceFile: TModalResult; override; function FindAllUnits: TModalResult; override; function ConvertAllUnits: TModalResult; override; function ExtractOptionsFromDelphiSource: TModalResult; override; // Fake Project virtual methods. function GetMainName: string; override; function SaveAndMaybeClose(const Filename: string): TModalResult; override; function ContainsFile(const aFileName: string): Boolean; override; function FindDependencyByName(const PackageName: string): TPkgDependency; override; function FirstDependency: TPkgDependency; override; public constructor Create(const aProjectFilename: string); destructor Destroy; override; public property LazProject: TProject read GetLazProject write SetLazProject; end; { TConvertDelphiPackage } // Delphi package conversion. TConvertDelphiPackage = class(TConvertDelphiProjPack) private function AddUnit(AFileName: string): TModalResult; function GetLazPackage: TLazPackage; procedure SetLazPackage(const AValue: TLazPackage); protected function CreateInstance: TModalResult; override; function CreateMainSourceFile: TModalResult; override; function FindAllUnits: TModalResult; override; function ConvertAllUnits: TModalResult; override; function ExtractOptionsFromDelphiSource: TModalResult; override; // Fake Package virtual methods. function GetMainName: string; override; function ContainsFile(const aFileName: string): Boolean; override; function FindDependencyByName(const PackageName: string): TPkgDependency; override; function FirstDependency: TPkgDependency; override; public constructor Create(const aPackageFilename: string); destructor Destroy; override; public property LazPackage: TLazPackage read GetLazPackage write SetLazPackage; end; { TConvertedDelphiProjectDescriptor } TConvertedDelphiProjectDescriptor = class(TProjectDescriptor) private public function InitProject(AProject: TLazProject): TModalResult; override; end; { TConvertedDelphiPackageDescriptor } { TConvertedDelphiPackageDescriptor = class(TPackageDescriptor) private public procedure InitPackage(APackage: TLazPackage); override; // ToDo end; } implementation function ConvertDelphiAbsoluteToRelativeFile(const Filename: string; AProjPack: TConvertDelphiProjPack): string; // often projects use paths near to their project directory. For example: // A project /somewhere/MyProjects/project1.dpr // and a path C:\Delphi\MyProj\folder can mean, that the relative path is 'folder' var ProjectDir: String; ShortProjectDir: String; p: LongInt; begin Result:=''; // Default: ignore absolute paths ProjectDir:=ExtractFilePath(AProjPack.fLazPInfoFilename); ShortProjectDir:=PathDelim+ExtractFileName(ChompPathDelim(ProjectDir))+PathDelim; p:=System.Pos(ShortProjectDir,Filename); if (p>0) then Result:=copy(Filename,p+length(ShortProjectDir),length(Filename)); end; function ExpandDelphiFilename(const Filename: string; AProjPack: TConvertDelphiProjPack): string; var p: LongInt; begin Result:=Filename; if Result='' then exit; Result:=TrimFilename(GetForcedPathDelims(Result)); // check for $(Delphi) macro p:=System.Pos('$(DELPHI)',Result); if p>0 then begin // Delphi features are provided by FPC and Lazarus -> ignore Result:=''; end; // check for other macros p:=System.Pos('$(',Result); if p>0 then begin // path macros are not supported -> ignore Result:=''; end; if FilenameIsWinAbsolute(Result) then begin // absolute filenames are not portable Result:=ConvertDelphiAbsoluteToRelativeFile(Result, AProjPack); end; // change PathDelim Result:=TrimFilename(GetForcedPathDelims(Result)); end; function ExpandDelphiSearchPath(const SearchPath: string; AProjPack: TConvertDelphiProjPack): string; var Paths: TStrings; i: Integer; CurPath: String; j: Integer; begin Result:=''; Paths:=SplitString(SearchPath,';'); if Paths=nil then exit; try // expand Delphi paths for i:=0 to Paths.Count-1 do Paths[i]:=ExpandDelphiFilename(Paths[i], AProjPack); // remove doubles for i:=Paths.Count-1 downto 0 do begin CurPath:=Paths[i]; if (CurPath='') then Paths.Delete(i) else begin j:=i-1; while (j>=0) and (CompareText(CurPath, Paths[i])<>0) do dec(j); if j>=0 then Paths.Delete(i); end; end; Result:=''; for i:=0 to Paths.Count-1 do begin if i>0 then Result:=Result+';'; Result:=Result+Paths[i]; end; finally Paths.Free; end; end; type { TUnitsSearcher } TUnitsSearcher = class(TFileSearcher) private fConverter: TConvertDelphiPBase; protected procedure DoFileFound; override; public constructor Create(aConverter: TConvertDelphiPBase); end; { TUnitsSearcher } constructor TUnitsSearcher.Create(aConverter: TConvertDelphiPBase); begin inherited Create; fConverter := aConverter; end; procedure TUnitsSearcher.DoFileFound; var RelPath, SubPath, sUnitName, fn: String; begin RelPath:=CreateRelativePath(FileName, fConverter.fSettings.MainPath); SubPath:=ExtractFilePath(RelPath); fn:=ExtractFileName(RelPath); sUnitName:=ExtractFileNameOnly(fn); if (SubPath<>'') and (sUnitName<>'') then begin //DebugLn(['RelPath=',RelPath,'SubPath=',SubPath,'fn=',fn,'sUnitName=',sUnitName]); // Map path by unit name. fConverter.fCachedUnitNames[sUnitName]:=SubPath; // Map real unit name by uppercase unit name. fConverter.fCachedRealFileNames[UpperCase(sUnitName)]:=fn; end; end; { TCacheUnitsThread } constructor TCacheUnitsThread.Create(aConverter: TConvertDelphiPBase; aPath: string); begin inherited Create(True); FreeOnTerminate:=False; // Will be set to True before starting; // Create searcher already now. Its Stop method can be called anytime. fSearcher:=TUnitsSearcher.Create(aConverter); // The parent directory to be scanned fPath:=aPath; end; destructor TCacheUnitsThread.Destroy; begin fSearcher.Free; inherited Destroy; end; procedure TCacheUnitsThread.Execute; // Scan for unit files. This assumes that cache is not used while updating it. // The main GUI thread must wait for this thread before starting conversion. function IsRootPath(APath: String): Boolean; // Crude function, it maybe needs support for UNC drives var D: String; Len: Integer; begin D := ExtractFileDrive(APath); Len := Length(D); System.Delete(APath, 1, Len); Result := (Length(APath) = 1) and (APath[1] in AllowDirectorySeparators); end; begin //If a project is in a subfolder of the root, fPath will be root path. //Do not search the entire drive, or we may find Borland VCL files and convert them too if IsRootPath(fPath) then Sleep(1) // Let the main thread execute, avoid possible synchr. problems. else // Scan for files and store to fCachedUnitNames, path relative to fSettings.MainPath. fSearcher.Search(fPath, '*.pas'); end; { TDelphiUnit } constructor TDelphiUnit.Create(AOwnerConverter: TConvertDelphiPBase; const AFilename: string; aFlags: TConvertUnitFlags); begin inherited Create; fOwnerConverter:=AOwnerConverter; fOrigUnitFilename:=AFilename; fFlags:=AFlags; fLazFileExt:=''; fUnitInfo:=nil; if not LazarusIDE.BeginCodeTools then fOwnerConverter.fSettings.AddLogLine(mluFatal, lisConvDelphiBeginCodeToolsFailed, fLazUnitFilename); fCTLink:=Nil; // Will be created later. fUsedUnitsTool:=Nil; end; destructor TDelphiUnit.Destroy; begin fUsedUnitsTool.Free; fCTLink.Free; inherited Destroy; end; function TDelphiUnit.GetDfmFileName: string; begin Result:=ChangeFileExt(fOrigUnitFilename,'.dfm'); if FileExistsUTF8(Result) then exit; Result:=ChangeFileExt(fOrigUnitFilename,'.DFM'); if FileExistsUTF8(Result) then exit; Result:=ChangeFileExt(fOrigUnitFilename,'.xfm'); if FileExistsUTF8(Result) then exit; Result:=ChangeFileExt(fOrigUnitFilename,'.XFM'); if not FileExistsUTF8(Result) then Result:=''; end; function TDelphiUnit.CopyAndLoadFile: TModalResult; var CodeOk, CodeFixed: Boolean; begin // Convert unit in place. File must be writable. Result:=CheckFileIsWritable(fOrigUnitFilename,[mbAbort]); if Result<>mrOK then exit; // close Delphi unit file in editor. Result:=LazarusIDE.DoCloseEditorFile(fOrigUnitFilename,[cfSaveFirst]); if Result<>mrOK then exit; // Copy/rename fLazUnitFilename based on fOrigUnitFilename. Assert(Assigned(fOwnerConverter), 'TDelphiUnit.CopyAndLoadFile: fOwnerConverter not assigned.'); Result:=fOwnerConverter.fSettings.RenameDelphiToLazFile(fOrigUnitFilename, fLazFileExt, fLazUnitFilename, cdtlufRenameLowercase in fFlags); if Result<>mrOK then exit; fPascalBuffer:=nil; // Read the code in. Result:=LoadCodeBuffer(fPascalBuffer,fLazUnitFilename, [lbfCheckIfText,lbfUpdateFromDisk],true); if Result<>mrOK then exit; // Change encoding to UTF-8 if fPascalBuffer.DiskEncoding<>EncodingUTF8 then begin fOwnerConverter.fSettings.AddLogLine(mluNote, Format(lisConvDelphiChangedEncodingToUTF8, [fPascalBuffer.DiskEncoding]), fLazUnitFilename); fPascalBuffer.DiskEncoding:=EncodingUTF8; // Takes effect when buffer is saved. end; // Create a shared link for codetools. Assert(fCTLink=Nil, 'fCTLink should be Nil in CopyAndLoadFile'); fCTLink:=TCodeToolLink.Create(fPascalBuffer); fCTLink.Settings:=fOwnerConverter.fSettings; fCTLink.AskAboutError:=fOwnerConverter is TConvertDelphiProjPack; // Fix include file names. Result:=FixIncludeFiles; if Result<>mrOK then exit; CodeFixed:=False; repeat CodeOk:=True; try // Create a tool for missing units. fUsedUnitsTool:=TUsedUnitsTool.Create(fCTLink, fOrigUnitFilename); except // If CodeTool's BuildTree raised exception, try dummy replacement for // some known problematic syntax, currently only OleVariant members. if CodeFixed then Raise // There was a second exception -> we are doomed! else begin if not fCTLink.DummyReplacements then Raise; CodeOk := False; CodeFixed := True; // Try replacements only once end; end; until CodeOk; if fOwnerConverter is TConvertDelphiProjPack then with TConvertDelphiProjPack(fOwnerConverter) do begin fUsedUnitsTool.OnCheckPackageDependency:=@CheckPackageDep; fUsedUnitsTool.IsConsoleApp:=fIsConsoleApp; fUsedUnitsTool.OnCheckUnitForConversion:=@CheckUnitForConversion; end; end; function TDelphiUnit.FixLfmFilenameAndLoad(ADfmFilename: string): TModalResult; var LfmFilename: string; // Lazarus .LFM file name. DFMConverter: TDFMConverter; TempLFMBuffer: TCodeBuffer; begin Result:=mrOK; fLFMBuffer:=nil; if ADfmFilename<>'' then begin Result:=LazarusIDE.DoCloseEditorFile(ADfmFilename,[cfSaveFirst]); if Result<>mrOK then exit; // Save before using .dfm. Result:=fOwnerConverter.fSettings.MaybeBackupFile(ADfmFilename); if Result<>mrOK then exit; end; if fOwnerConverter.fSettings.SameDfmFile then LfmFilename:=ADfmFilename else begin // Create a form file name based on the unit file name. with fOwnerConverter.fSettings do LfmFilename:=DelphiToLazFilename(fOrigUnitFilename, '.lfm', cdtlufRenameLowercase in fFlags); if ADfmFilename<>'' then begin if FileExistsUTF8(LfmFilename) then if (FileAgeUTF8(LfmFilename) remove .lfm if not FileExistsUTF8(LfmFilename) then begin // TODO: update project if fOwnerConverter.fSettings.SupportDelphi then Result:=CopyFileWithErrorDialogs(ADfmFilename, LfmFilename, [mbAbort]) else Result:=RenameFileWithErrorDialogs(ADfmFilename, LfmFilename, [mbAbort]); if Result<>mrOK then exit; end; end; end; // Convert Unit .dfm file to .lfm file (without context type checking) if FileExistsUTF8(LfmFilename) then begin DFMConverter:=TDFMConverter.Create; try DFMConverter.Settings:=fOwnerConverter.fSettings; Result:=DFMConverter.ConvertDfmToLfm(LfmFilename); if Result<>mrOK then exit; finally DFMConverter.Free; end; // Read pascal unit code in. Result:=LoadCodeBuffer(TempLFMBuffer,LfmFilename, [lbfCheckIfText,lbfUpdateFromDisk],true); // Change encoding to UTF-8 if TempLFMBuffer.DiskEncoding<>EncodingUTF8 then begin fOwnerConverter.fSettings.AddLogLine(mluNote, Format(lisConvDelphiChangedEncodingToUTF8, [TempLFMBuffer.DiskEncoding]), fLazUnitFilename); TempLFMBuffer.DiskEncoding:=EncodingUTF8; TempLFMBuffer.Save; end; // Read form file code in. if not fOwnerConverter.fSettings.SameDfmFile then Result:=LoadCodeBuffer(fLFMBuffer,LfmFilename, [lbfCheckIfText,lbfUpdateFromDisk],true); if fUnitInfo<>nil then Result:=LoadLFM(fUnitInfo,fLFMBuffer,[ofOnlyIfExists],[]); end; end; function TDelphiUnit.ReduceMissingUnits: TModalResult; // Find or comment out some / all of missing units. begin Result:=mrOK; // Comment out automatically units that were commented in other files. fUsedUnitsTool.MainUsedUnits.CommentAutomatic(fOwnerConverter.fAllCommentedUnits); fUsedUnitsTool.ImplUsedUnits.CommentAutomatic(fOwnerConverter.fAllCommentedUnits); // Remove omitted units from MissingUnits. fUsedUnitsTool.MainUsedUnits.OmitUnits; fUsedUnitsTool.ImplUsedUnits.OmitUnits; // Try to find from subdirectories scanned earlier. if fOwnerConverter.DoMissingUnits(fUsedUnitsTool)=0 then exit; if fUsedUnitsTool.MissingUnitCount=0 then exit; // Interactive dialog for searching unit. Result:=AskUnitPathFromUser; end; function TDelphiUnit.ConvertUnitFile: TModalResult; var DfmFilename: string; // Delphi .DFM file name. ConvTool: TConvDelphiCodeTool; begin // Get DFM file name and close it in editor. DfmFilename:=GetDfmFileName; Result:=FixLfmFilenameAndLoad(DfmFilename); if Result<>mrOK then exit; if fOwnerConverter.fSettings.UnitsReplaceMode<>rlDisabled then begin // Find and prepare the missing units. Don't replace yet. Result:=fUsedUnitsTool.Prepare; if Result<>mrOK then exit; if fUsedUnitsTool.MissingUnitCount>0 then begin Result:=ReduceMissingUnits; if Result<>mrOK then exit; end; end; // Do the actual code conversion. ConvTool:=TConvDelphiCodeTool.Create(fCTLink); try ConvTool.IsConsoleApp:=fOwnerConverter.fIsConsoleApp; ConvTool.ResAction:=raNone; if FileExistsUTF8(ChangeFileExt(fLazUnitFilename, '.res')) then ConvTool.ResAction:=raLowerCase else if not FileExistsUTF8(ChangeFileExt(fLazUnitFilename, '.RES')) then ConvTool.ResAction:=raDelete; // No lower- or uppercase version exists -> delete ConvTool.HasFormFile:=DfmFilename<>''; ConvTool.AddUnitEvent:=@fUsedUnitsTool.AddUnitIfNeeded; Result:=ConvTool.Convert; if Result<>mrOK then exit; finally ConvTool.Free; end; // First pass to add, remove, fix and comment out units in uses sections. Result:=fUsedUnitsTool.ConvertUsed; end; function TDelphiUnit.ConvertFormFile: TModalResult; var LfmFixer: TLFMFixer; begin // Fix the LFM file and the pascal unit, updates fPascalBuffer and fLFMBuffer. if fLFMBuffer<>nil then begin LfmFixer:=TLFMFixer.Create(fCTLink,fLFMBuffer); try LfmFixer.Settings:=fOwnerConverter.fSettings; LfmFixer.UsedUnitsTool:=fUsedUnitsTool; LfmFixer.RootMustBeClassInUnit:=true; LfmFixer.RootMustBeClassInIntf:=true; LfmFixer.ObjectsMustExist:=true; if LfmFixer.ConvertAndRepair<>mrOK then begin LazarusIDE.DoJumpToCompilerMessage(true); fOwnerConverter.fErrorMsg:=Format(lisConvProblemsRepairingFormFile, [ChangeFileExt(fOrigUnitFilename, '.lfm')]); exit(mrAbort); end; finally LfmFixer.Free; end; // save LFM file Result:=SaveCodeBufferToFile(fLFMBuffer,fLFMBuffer.Filename); if Result<>mrOK then exit; end; // Second pass to add, remove, fix and comment out units in uses sections. // More changes to uses section can happen during form file conversion. Result:=fUsedUnitsTool.ConvertUsed; if Result<>mrOK then exit; Result:=mrOK; end; function TDelphiUnit.AskUnitPathFromUser: TModalResult; // Ask the user what to do with missing units. var TryAgain: Boolean; CacheUnitsThread: TCacheUnitsThread; UnitDirDialog: TSelectDirectoryDialog; begin with fUsedUnitsTool do repeat TryAgain:=False; Result:=AskMissingUnits(MainUsedUnits.MissingUnits, ImplUsedUnits.MissingUnits, ExtractFileName(fLazUnitFilename), fOwnerConverter.fSettings.SupportDelphi); case Result of // mrOK means: Comment out. mrOK: MoveMissingToComment(fOwnerConverter.fAllCommentedUnits); // mrYes means: Search for unit path. mrYes: begin UnitDirDialog:=TSelectDirectoryDialog.Create(nil); try UnitDirDialog.InitialDir:=fOwnerConverter.fPrevSelectedPath; UnitDirDialog.Title:=lisConvDelphiAllSubDirsScanned; if UnitDirDialog.Execute then begin fOwnerConverter.fPrevSelectedPath:=UnitDirDialog.FileName; // Add the new path to project if missing units are found. // We use a thread here only to reuse its code. No parallel operations now. CacheUnitsThread:=TCacheUnitsThread.Create(fOwnerConverter, fOwnerConverter.fPrevSelectedPath); CacheUnitsThread.Start; CacheUnitsThread.WaitFor; // Make sure the thread has finished before continuing. TryAgain:=fOwnerConverter.DoMissingUnits(fUsedUnitsTool)>0; end else TryAgain:=true; // User canceled. Stay with the same unit. finally UnitDirDialog.Free; end; Result:=mrOK; // Caller will check for Result<>mrOK end; // mrIgnore means: Skip this unit. The missing units list is already cleared. mrIgnore: Result:=mrOK; // Abort the whole conversion. mrAbort: fOwnerConverter.fErrorMsg:=Format(lisConvUserSelectedToEndConversion, [fOrigUnitFilename]); end; until not TryAgain; end; function TDelphiUnit.FixIncludeFiles: TModalResult; // fix include filenames var FoundIncludeFiles: TStrings; MissingIncludeFilesCodeXYPos: TFPList; CodePos: PCodeXYPosition; Msg, s: string; i: Integer; OldChange: Boolean; begin Result:=mrOK; FoundIncludeFiles:=Nil; MissingIncludeFilesCodeXYPos:=Nil; OldChange:=LazarusIDE.OpenEditorsOnCodeToolChange; LazarusIDE.OpenEditorsOnCodeToolChange:=False; with fCTLink, fOwnerConverter do try if CodeTool.DirectoryCache=nil then exit; if CodeTool.FixIncludeFilenames(Code,SrcCache,FoundIncludeFiles,MissingIncludeFilesCodeXYPos) then begin if Assigned(FoundIncludeFiles) then begin // List the include files in log. Msg:=lisConvRepairingIncludeFiles; for i:=0 to FoundIncludeFiles.Count-1 do begin fSettings.MaybeBackupFile(FoundIncludeFiles[i]); s:=CreateRelativePath(FoundIncludeFiles[i], fSettings.MainPath); if i>0 then Msg:=Msg+'; '; Msg:=Msg+s; end; fSettings.AddLogLine(mluNote, Msg, fLazUnitFilename); end; end else begin if MissingIncludeFilesCodeXYPos<>nil then begin for i:=0 to MissingIncludeFilesCodeXYPos.Count-1 do begin CodePos:=PCodeXYPosition(MissingIncludeFilesCodeXYPos[i]); Msg:=Format(lisConvDelphiMissingIncludeFile, [CodePos^.Code.Filename,IntToStr(CodePos^.y),IntToStr(CodePos^.x)]); fSettings.AddLogLine(mluError, Msg, fLazUnitFilename); end; end; fErrorMsg:=Format(lisConvProblemsFixingIncludeFile, [fOrigUnitFilename]); Result:=mrCancel; end; finally FoundIncludeFiles.Free; CodeCache.FreeListOfPCodeXYPosition(MissingIncludeFilesCodeXYPos); LazarusIDE.OpenEditorsOnCodeToolChange:=OldChange; end; end; function TDelphiUnit.CheckFailed(PrevResult: TModalResult): TModalResult; begin Result:=PrevResult; if Result=mrCancel then begin Result:=QuestionDlg(lisConvDelphiFailedConvertingUnit, Format(lisConvDelphiFailedToConvertUnit, [fOrigUnitFilename]), mtWarning, [mrIgnore, lisIgnoreAndContinue, mrAbort], 0); case Result of mrIgnore : Result:=mrOK; mrAbort : fOwnerConverter.fErrorMsg:=Format(lisConvUserSelectedToEndConversion, [fOrigUnitFilename]); end; end; end; { TConvertDelphiPBase } constructor TConvertDelphiPBase.Create(const ADescription: string); begin inherited Create; fSettings:=TConvertSettings.Create(ADescription); fIsConsoleApp:=False; // Default = GUI app. end; constructor TConvertDelphiPBase.Create(const AFilename, ADescription: string); begin Create(ADescription); fSettings.MainFilenames.Add(AFilename); fPrevSelectedPath:=fSettings.MainPath; end; destructor TConvertDelphiPBase.Destroy; begin fSettings.Free; inherited Destroy; end; function TConvertDelphiPBase.DoMissingUnits(AUsedUnitsTool: TUsedUnitsTool): integer; begin Result := 0; end; function TConvertDelphiPBase.GetCachedUnitPath(const AUnitName: string): string; begin Result:=fCachedUnitNames[AUnitName]; end; function TConvertDelphiPBase.EndConvert(AStatus: TModalResult): Boolean; begin // Show ending message if AStatus=mrOK then fSettings.AddLogLine(mluImportant, lisConvDelphiConversionReady) else begin if fErrorMsg<>'' then fSettings.AddLogLine(mluError, Format(lisConvDelphiError,[fErrorMsg])) else if CodeToolBoss.ErrorMessage<>'' then fSettings.AddLogLine(mluError, Format(lisConvDelphiError,[CodeToolBoss.ErrorMessage])); fSettings.AddLogLine(mluFatal, lisConvDelphiConversionAborted); end; // Save log messages to file. Result:=fSettings.SaveLog; end; { TConvertDelphiUnit } constructor TConvertDelphiUnit.Create(aFileNames: TStrings); var MacroName, s: String; i: Integer; begin inherited Create(lisConvDelphiConvertDelphiUnit); // Add the list of files if they exist. for i:=0 to aFileNames.Count-1 do begin s:=CleanAndExpandFilename(aFileNames[i]); if FileExistsUTF8(s) then fSettings.MainFilenames.Add(s); end; fPrevSelectedPath:=fSettings.MainPath; // use a template for compiler mode delphi for a single directory fDirTemplate:=TDefineTemplate.Create(SettingDelphiModeTemplName, 'Mode Delphi for single unit conversion', '', fSettings.MainPath, da_Directory); MacroName:=CompilerModeVars[cmDELPHI]; s:='Define'+MacroName; fDirTemplate.AddChild(TDefineTemplate.Create(s, s, MacroName, '1', da_Define)); CodeToolBoss.DefineTree.Add(fDirTemplate); // add directory template to tree end; destructor TConvertDelphiUnit.Destroy; begin CodeToolBoss.DefineTree.RemoveDefineTemplate(fDirTemplate); inherited Destroy; end; function TConvertDelphiUnit.Convert: TModalResult; var DelphiUnit: TDelphiUnit; i: Integer; begin Result:=fSettings.RunForm(Nil); if Result=mrOK then begin try try fSettings.ClearLog; for i:=0 to fSettings.MainFilenames.Count-1 do begin Application.ProcessMessages; if i>0 then fSettings.AddLogLine(mluImportant, ''); DelphiUnit:=TDelphiUnit.Create(Self, fSettings.MainFilenames[i], []); with DelphiUnit do try Result:=CopyAndLoadFile; if Result<>mrOK then Exit; Result:=ConvertUnitFile; if Result<>mrOK then Exit; Result:=SaveCodeBufferToFile(fPascalBuffer,fLazUnitFilename); if Result<>mrOK then Exit; Result:=ConvertFormFile; if Result<>mrOK then Exit; Result:=LazarusIDE.DoSaveEditorFile(fLazUnitFilename,[]); finally DelphiUnit.Free; end; end; except on e: EDelphiConverterError do begin fErrorMsg:=e.Message; end; else begin fErrorMsg:=CodeToolBoss.ErrorMessage; end; Result:=mrAbort; end; finally EndConvert(Result); end; end; end; { TConvertDelphiProjPack } constructor TConvertDelphiProjPack.Create(const AFilename, ADescription: string); begin inherited Create(AFilename, ADescription); fUseThreads:=False; fUnitSearchPaths:=TStringListUTF8Fast.Create; fUnitSearchPaths.Delimiter:=';'; fUnitSearchPaths.StrictDelimiter:=True; fCachedUnitNames:=TStringToStringTree.Create(False); fCachedRealFileNames:=TStringToStringTree.Create(True); fAllCommentedUnits:=TStringListUTF8Fast.Create; fAllCommentedUnits.Sorted:=True; fUnitsToAddToProject:=TStringListUTF8Fast.Create; fUnitsToAddToProject.Sorted:=True; fFilesToDelete:=TStringListUTF8Fast.Create; fFilesToDelete.Sorted:=True; fMainUnitConverter:=nil; end; destructor TConvertDelphiProjPack.Destroy; begin fMainUnitConverter.Free; fFilesToDelete.Free; fUnitsToAddToProject.Free; fAllCommentedUnits.Free; fCachedRealFileNames.Free; fCachedUnitNames.Free; fUnitSearchPaths.Free; inherited Destroy; end; function TConvertDelphiProjPack.Convert: TModalResult; // Create or update a lazarus project (.lpi+.lpr) or package, convert source files. var // The initial unit name cache is done in a thread so that GUI shows at once. CacheUnitsThread: TCacheUnitsThread; StartTime, EndTime: TDateTime; s: string; begin if FilenameExtIs(fSettings.MainFilename,'dproj') then begin fErrorMsg := lisConvDprojFileNotSupportedYet; Exit(mrCancel); end; // Start scanning unit files one level above project path. The GUI will appear // without delay but then we must wait for the thread before continuing. CacheUnitsThread:=TCacheUnitsThread.Create(Self, ResolveDots(fSettings.MainPath+'..'+DirectorySeparator)); try try Result:=fSettings.RunForm(CacheUnitsThread); // Get settings from user. if Result=mrOK then begin StartTime:=Now; fSettings.ClearLog; // create/open lazarus project or package file fLazPInfoFilename:=fSettings.DelphiToLazFilename(fSettings.MainFilename, fLazPInfoSuffix, false); // Find Delphi project / package file name if FilenameExtIs(fSettings.MainFilename,fDelphiPSuffix) then fDelphiPFilename:=fSettings.MainFilename else fDelphiPFilename:=ChangeFileExt(fSettings.MainFilename,fDelphiPSuffix); if not FileExistsUTF8(fDelphiPFilename) then fDelphiPFilename:=FindDiskFileCaseInsensitive(fSettings.MainFilename); // ? fDelphiPFilename:=CodeToolBoss.DirectoryCachePool.FindDiskFilename(fSettings.MainFilename); // Actual conversion. Result:=ConvertSub; end; except on e: EDelphiConverterError do begin fErrorMsg:=e.Message; end; else begin fErrorMsg:=CodeToolBoss.ErrorMessage; end; Result:=mrAbort; end; finally CacheUnitsThread.Free; EndTime:=Now; s:=FormatDateTime('hh:nn:ss', EndTime-StartTime); if (Result<>mrAbort) and (s<>'00:00:00') then fSettings.AddLogLine(mluProgress, Format(lisConvDelphiConversionTook,[s])); EndConvert(Result); end; end; function TConvertDelphiProjPack.ConvertSub: TModalResult; begin // Project / package instance. Result:=CreateInstance; if Result<>mrOK then exit; // Create main source file (.lpr/.lpk) (only copy, no conversion) fMainUnitConverter:=TDelphiUnit.Create(Self, fSettings.MainFilename,[]); if fSettings.SupportDelphi then fMainUnitConverter.LazFileExt:=ExtractFileExt(fSettings.MainFilename) else fMainUnitConverter.LazFileExt:=fLazPSuffix; // '.lpr' or '' Result:=fMainUnitConverter.CopyAndLoadFile; if Result<>mrOK then exit; fMainUnitConverter.fUsedUnitsTool.IsMainFile:=True; Result:=CreateMainSourceFile; // More actions for the main source file. if Result<>mrOK then exit; Application.ProcessMessages; // read config files (they often contain clues about paths, switches and defines) Result:=ReadDelphiConfigFiles; if Result<>mrOK then exit; fProjPack.RemoveNonExistingFiles(false); CleanUpCompilerOptionsSearchPaths(fProjPack.BaseCompilerOptions); // LCL dependency should be added automatically later for GUI applications fProjPack.AddPackageDependency('LCL'); // but in some special cases it is not. // ToDo: make an option to add NoGUI to Project.CompilerOptions.LCLWidgetType. if fProjPack is TProject then PkgBoss.OpenProjectDependencies(fProjPack as TProject, true); fProjPack.DefineTemplates.CustomDefinesChanged; SetCompilerModeForDefineTempl(fProjPack.DefineTemplates.CustomDefines); try if Result<>mrOK then exit; // get all options from the .dpr or .dpk file Result:=ExtractOptionsFromDelphiSource; if Result<>mrOK then exit; Result:=FindAllUnits; // find all files and save the project. if Result<>mrOK then exit; Application.ProcessMessages; // Convert .lpr/.lpk file. Main source file was loaded earlier. Now just convert. Result:=fMainUnitConverter.ConvertUnitFile; if Result<>mrOK then exit; Application.ProcessMessages; Result:=fMainUnitConverter.ConvertFormFile; if Result<>mrOK then exit; Application.ProcessMessages; Result:=ConvertAllUnits; // convert all files. if Result<>mrOK then exit; finally UnsetCompilerModeForDefineTempl(fProjPack.DefineTemplates.CustomDefines); end; Result:=MaybeDeleteFiles; // Delete files having same name with a LCL unit. end; function TConvertDelphiProjPack.ConvertAllFormFiles(ConverterList: TObjectList): TModalResult; // Call Convert.ConvertFormFile for all units. var Converter: TDelphiUnit; i: Integer; begin if not fSettings.SameDfmFile then begin fSettings.AddLogLine(mluImportant, ''); fSettings.AddLogLine(mluImportant, lisConvDelphiRepairingFormFiles); DebugLn(''); DebugLn('TConvertDelphiProjPack.ConvertAllFormFiles: '+lisConvDelphiRepairingFormFiles); end; Screen.BeginWaitCursor; try for i:=0 to ConverterList.Count-1 do begin Converter:=TDelphiUnit(ConverterList[i]); // Converter created in cycle1. Application.ProcessMessages; Result:=Converter.ConvertFormFile; Result:=Converter.CheckFailed(Result); if Result<>mrOK then exit; // Finally save and maybe close the file. Result:=SaveAndMaybeClose(Converter.fLazUnitFilename); if Result<>mrOK then exit; end; finally Screen.EndWaitCursor; end; Result:=mrOK; end; function TConvertDelphiProjPack.ReadDelphiConfigFiles: TModalResult; var FN, s: String; begin Result:=mrOK; FN:=MainName; if FN<>'' then begin // read .dof file s:=FindDiskFileCaseInsensitive(ChangeFileExt(FN,'.dof')); if s<>'' then begin Result:=ExtractOptionsFromDOF(s); if Result<>mrOK then exit; end; // read .cfg file s:=FindDiskFileCaseInsensitive(ChangeFileExt(FN,'.cfg')); if s<>'' then Result:=ExtractOptionsFromCFG(s); end; end; function TConvertDelphiProjPack.ExtractOptionsFromDOF(const DOFFilename: string): TModalResult; // parse .dof file and put options into Project/LazPackage var IniFile: TIniFile; function ReadDirectory(const Section, Ident: string): string; begin Result:=IniFile.ReadString(Section,Ident,''); Result:=ExpandDelphiFilename(Result, Self); end; function ReadSearchPath(const Section, Ident: string): string; var SearchPath: String; begin SearchPath:=IniFile.ReadString(Section,Ident,''); Result:=ExpandDelphiSearchPath(SearchPath, Self); end; procedure AddPackDep(const DelphiPkgName, LowerDelphiPkgNames, LazarusPkgName: string); begin if DelphiPkgName='' then exit; if Pos(';'+lowercase(DelphiPkgName)+';', ';'+LowerDelphiPkgNames+';')>0 then begin fProjPack.AddPackageDependency(LazarusPkgName); fSettings.AddLogLine(mluNote, Format(lisConvDelphiAddedPackageDependency,[LazarusPkgName]), fLazPInfoFilename); end; end; procedure ReadDelphiPackages; var DelphiPackages: String; Pkgs: TStrings; i: Integer; begin DelphiPackages:=IniFile.ReadString('Directories','Packages',''); Pkgs:=SplitString(DelphiPackages,';'); if Pkgs=nil then exit; try for i:=0 to Pkgs.Count-1 do AddPackDep(Pkgs[i],'rtl,dbrtl','FCL'); finally Pkgs.Free; end; end; procedure AddSearchPath(const SearchPath: string); begin fProjPack.BaseCompilerOptions.MergeToIncludePaths(SearchPath); fProjPack.BaseCompilerOptions.MergeToLibraryPaths(SearchPath); fProjPack.BaseCompilerOptions.MergeToUnitPaths(SearchPath); fProjPack.BaseCompilerOptions.MergeToObjectPath(SearchPath); fProjPack.BaseCompilerOptions.MergeToDebugPath(SearchPath); end; var OutputDir: String; SearchPath: String; DebugSourceDirs: String; begin try IniFile:=TIniFile.Create(UTF8ToSys(DOFFilename)); try // output directory if fProjPack is TProject then begin OutputDir:=ReadDirectory('Directories','OutputDir'); if (OutputDir<>'') then fProjPack.BaseCompilerOptions.UnitOutputDirectory:=OutputDir; end; // search path SearchPath:=ReadSearchPath('Directories','SearchPath'); if (SearchPath<>'') then AddSearchPath(SearchPath); // debug source dirs DebugSourceDirs:=ReadSearchPath('Directories','DebugSourceDirs'); if DebugSourceDirs<>'' then fProjPack.BaseCompilerOptions.MergeToDebugPath(DebugSourceDirs); // packages ReadDelphiPackages; if fProjPack is TProject then begin if IniFile.ReadString('Linker','ConsoleApp','')='0' then // does not need a windows console fProjPack.BaseCompilerOptions.Win32GraphicApp:=true; end; finally IniFile.Free; end; except on E: Exception do begin DebugLn('ExtractOptionsFromDOF failed reading "'+DOFFilename+'" '+E.Message); end; end; Result:=mrOK; end; function TConvertDelphiProjPack.ExtractOptionsFromCFG(const CFGFilename: string): TModalResult; var sl: TStringList; i: Integer; Line, s: string; c: char; begin try sl:=TStringList.Create; try sl.LoadFromFile(CFGFilename); for i:=0 to sl.Count-1 do begin Line:=sl[i]; if Line='' then continue; if (Line[1]<>'-') or (length(Line)<2) then continue; c:=Line[2]; if (c='U') or (c='I') then begin s:=ExpandDelphiSearchPath(copy(Line,4,length(Line)-4), Self); if s<>'' then case c of 'U': fProjPack.BaseCompilerOptions.MergeToUnitPaths(s); 'I': fProjPack.BaseCompilerOptions.MergeToIncludePaths(s); end; end end; finally sl.Free; end; except on E: Exception do begin DebugLn('ExtractOptionsFromCFG failed reading "'+CFGFilename+'" '+E.Message); end; end; Result:=mrOK; end; procedure TConvertDelphiProjPack.SetCompilerModeForDefineTempl(DefTempl: TDefineTemplate); begin if DefTempl.FindChildByName(SettingDelphiModeTemplName)<>nil then exit; DefTempl.ReplaceChild(CreateDefinesForFPCMode(SettingDelphiModeTemplName, cmDELPHI)); CodeToolBoss.DefineTree.ClearCache; end; procedure TConvertDelphiProjPack.UnsetCompilerModeForDefineTempl(DefTempl: TDefineTemplate); begin if DefTempl.FindChildByName(SettingDelphiModeTemplName)=nil then exit; DefTempl.DeleteChild(SettingDelphiModeTemplName); CodeToolBoss.DefineTree.ClearCache; end; procedure TConvertDelphiProjPack.CleanUpCompilerOptionsSearchPaths(Options: TBaseCompilerOptions); var BasePath, s: String; function CleanProjectSearchPath(const SearchPath: string): string; begin Result:=RemoveNonExistingPaths(SearchPath,BasePath); Result:=MinimizeSearchPath(Result); end; begin BasePath:=Options.BaseDirectory; Options.OtherUnitFiles:=CleanProjectSearchPath(Options.OtherUnitFiles); Options.IncludePath:=CleanProjectSearchPath(Options.IncludePath); Options.Libraries:=CleanProjectSearchPath(Options.Libraries); Options.ObjectPath:=CleanProjectSearchPath(Options.ObjectPath); Options.SrcPath:=CleanProjectSearchPath(Options.SrcPath); if Options.UnitOutputDirectory='' then Options.UnitOutputDirectory:='lib'+PathDelim+'$(TargetCPU)-$(TargetOS)'; Options.TargetFilename:=''; // This may have a wrong value from default compiler options. if fSettings.DelphiDefine then begin // "Borland" and "Ver150" are defined by Delphi7. // "Delphi7" and "Compiler6_Up" are defined by Jedi library based on other settings. // They are needed because Jedi.inc undefines "Borland" when "FPC" is defined. Nuts. // PUREPASCAL is defined by some code to not use x86 assembly code. s:='-dBorland -dVer150 -dDelphi7 -dCompiler6_Up -dPUREPASCAL'; Options.CustomOptions:=s; fSettings.AddLogLine(mluNote, Format(lisConvDelphiAddedCustomOptionDefines,[s]), fLazPInfoFilename); end; end; procedure TConvertDelphiProjPack.MissingUnitsSub(AUsedUnits: TUsedUnits); var mUnit, sUnitPath, RealFileName, RealUnitName: string; i: Integer; begin for i:= AUsedUnits.MissingUnits.Count-1 downto 0 do begin mUnit:=AUsedUnits.MissingUnits[i]; sUnitPath:=GetCachedUnitPath(mUnit); if sUnitPath<>'' then begin fProjPack.BaseCompilerOptions.MergeToUnitPaths(sUnitPath); fProjPack.BaseCompilerOptions.MergeToIncludePaths(sUnitPath); // Rename a unit with different casing if needed. RealFileName:=fCachedRealFileNames[UpperCase(mUnit)]; RealUnitName:=ExtractFileNameOnly(RealFileName); if (RealUnitName<>'') and (RealUnitName<>mUnit) then AUsedUnits.UnitsToFixCase[mUnit]:=RealUnitName; // Will be added later to project. AddToProjectLater(sUnitPath+RealFileName); AUsedUnits.MissingUnits.Delete(i); // No more missing, delete from list. end else if CheckPackageDep(mUnit) then AUsedUnits.MissingUnits.Delete(i); end; end; function TConvertDelphiProjPack.DoMissingUnits(AUsedUnitsTool: TUsedUnitsTool): integer; // Locate unit names from earlier cached list or from packages. // Return the number of units still missing. begin MissingUnitsSub(AUsedUnitsTool.MainUsedUnits); MissingUnitsSub(AUsedUnitsTool.ImplUsedUnits); Result:=AUsedUnitsTool.MissingUnitCount; end; function TConvertDelphiProjPack.AddToProjectLater(const AFileName: string): Boolean; var x: Integer; begin Result:=not fUnitsToAddToProject.Find(AFileName,x); if Result then // Add the file later to project if not already done. fUnitsToAddToProject.Add(AFileName); end; function TConvertDelphiProjPack.MaybeDeleteFiles: TModalResult; // Maybe delete files that are already in LCL. They are potentially copied from VCL. var s: String; i: Integer; begin for i := fFilesToDelete.Count-1 downto 0 do begin s:=fFilesToDelete[i]; // Ask confirmation from user. if IDEMessageDialog(lisConvDelphiUnitnameExistsInLCL, Format(lisConvDelphiUnitWithNameExistsInLCL,[ExtractFileNameOnly(s),s]), mtConfirmation, mbYesNo) = mrYes then begin // Delete from file system because compiler would find it otherwise. if not DeleteFileUTF8(s) then exit(mrCancel); fSettings.AddLogLine(mluNote, Format(lisConvDeletedFile,[s])); end; end; Result:=mrOK; end; function TConvertDelphiProjPack.CheckUnitForConversion(aFileName: string): Boolean; // Units in project directory but not part of the project are not reported // as missing and would not be converted. Now add them to the project/package. var UnitN: String; x: Integer; begin if ExtractFilePath(aFileName)=fSettings.MainPath then begin Result:=not ContainsFile(aFileName); // Process only files in projct dir. if Result then begin // Not in project. UnitN:=ExtractFileNameOnly(AFileName); if Assigned(PackageGraph.LCLBasePackage.FindUnit(UnitN)) or Assigned(PackageGraph.LazUtilsPackage.FindUnit(UnitN)) then begin if not fFilesToDelete.Find(aFileName, x) then fFilesToDelete.Add(AFileName); // Found also in the package, delete later. end else AddToProjectLater(aFileName); // Add to project later. end; end else Result:=False; end; procedure TConvertDelphiProjPack.AddDependency(APackName: String); // A unit was found from a package. Add the package as a dependency and open it. var Dep: TPkgDependency; begin if APackName='LCLBase' then APackName:='LCL'; Dep:=FindDependencyByName(APackName); if Assigned(Dep) then Exit; // Already added. fProjPack.AddPackageDependency(APackName); fSettings.AddLogLine(mluNote, Format(lisConvDelphiAddedPackageDependency,[APackName]), fLazPInfoFilename); Dep:=FindDependencyByName(APackName); if Assigned(Dep) then PackageGraph.OpenDependency(Dep,false); end; function TConvertDelphiProjPack.CheckPackageDep(AUnitName: string): Boolean; // Check if the given unit can be found in existing packages. Add a dependency if found. // This is called only if the unit is reported as missing. // Returns True if the unit was found. var RegComp: TRegisteredComponent; PackFile: TPkgFile; Package: TLazPackage; i, Cnt: Integer; SearchPath: String; Files: TFilenameToStringTree; FileItem: PStringToStringItem; begin Result:=False; PackFile:=PackageGraph.FindUnitInAllPackages(AUnitName, True); if Assigned(PackFile) then begin AddDependency(PackFile.LazPackage.Name); Exit(True); end; // Do heuristics. Units of some registered components are not included in // their package file. Try to find 'T'+UnitName. Helps with Indy package. RegComp := IDEComponentPalette.FindRegComponent('T'+AUnitName); if RegComp is TPkgComponent then begin PackFile:=TPkgComponent(RegComp).PkgFile; Assert(Assigned(PackFile), 'TConvertDelphiProjPack.CheckPackageDep: PackFile=Nil.'); AddDependency(PackFile.LazPackage.Name); Exit(True); end; // Try to find the unit from all open packages by their search path. // Again needed when the unit is not included in a package file. Cnt:=PackageGraph.Count; Files:=TFilenameToStringTree.Create(false); try for i:=0 to Cnt-1 do begin Package:=PackageGraph.Packages[i]; if Package.IsVirtual then continue; // skip unsaved package if PackageGraph.LazarusBasePackages.IndexOf(Package)>=0 then Continue; // Skip base packages. SearchPath:=Package.CompilerOptions.GetParsedPath(pcosUnitPath,icoNone,false) +';'+Package.SourceDirectories.CreateSearchPathFromAllFiles +';'+Package.Directory; SearchPath:=TrimSearchPath(SearchPath,'',true); Files.Clear; CollectFilesInSearchPath(SearchPath,Files); for FileItem in Files do if FilenameIsPascalUnit(FileItem^.Name) and (CompareFilenameOnly(PChar(Pointer(FileItem^.Name)),Length(FileItem^.Name), PChar(Pointer(AUnitName)),Length(AUnitName))=0) then begin AddDependency(Package.Name); Exit(True); end; end; finally Files.Free; end; // ToDo: Install the required package automatically from a repository... end; function TConvertDelphiProjPack.TryAddPackageDep(const AUnitName, ADefaultPkgName: string): Boolean; var Dep: TPkgDependency; begin Result:=False; if ADefaultPkgName='' then Exit; Dep:=FindDependencyByName(ADefaultPkgName); if Assigned(Dep) then Exit; // Already added. // Add dependency based on unit name (default is ignored). Result:=CheckPackageDep(AUnitName); if Result then Exit; // Package was not found. Add a message about a package that must be installed. fSettings.AddLogLine(mluWarning, Format(lisConvDelphiPackageRequired, [ADefaultPkgName])); end; function TConvertDelphiProjPack.SaveAndMaybeClose(const aFilename: string): TModalResult; begin Result:=mrOK; // Do nothing. Overridden in project. end; { TConvertDelphiProject } constructor TConvertDelphiProject.Create(const aProjectFilename: string); begin inherited Create(aProjectFilename, lisConvDelphiConvertDelphiProject); fLazPInfoSuffix:='.lpi'; fLazPSuffix:='.lpr'; fDelphiPSuffix:='.dpr'; end; destructor TConvertDelphiProject.Destroy; begin inherited Destroy; end; function TConvertDelphiProject.CreateInstance: TModalResult; // Open or create a project. If .lpi file does not exist, create it. var Desc: TConvertedDelphiProjectDescriptor; begin Result:=mrOK; if FileExistsUTF8(fLazPInfoFilename) then begin // there is already a lazarus project -> open it, if not already open if (Project1=nil) or (CompareFilenames(Project1.ProjectInfoFile,fLazPInfoFilename)<>0) then Result:=LazarusIDE.DoOpenProjectFile(fLazPInfoFilename,[]); end else begin // create a new lazarus project Desc:=TConvertedDelphiProjectDescriptor.Create; try Result:=LazarusIDE.DoNewProject(Desc); finally Desc.Free; end; if Assigned(Project1) then Project1.ProjectInfoFile:=fLazPInfoFilename; end; fProjPack:=Project1; if Result<>mrOK then exit; // save to disk (this makes sure, all editor changes are saved too) LazProject.SkipCheckLCLInterfaces:=True; // Don't add Interfaces unit automatically. Result:=LazarusIDE.DoSaveProject([]); end; function TConvertDelphiProject.CreateMainSourceFile: TModalResult; // if .lpr does not exists, copy the .dpr file to the .lpr // adds the .lpr as main unit to the project, if not already done var MainUnitInfo: TUnitInfo; ConvTool: TConvDelphiCodeTool; begin if LazProject.MainUnitInfo=nil then begin // add .lpr file to project as main unit MainUnitInfo:=TUnitInfo.Create(fMainUnitConverter.fPascalBuffer); Assert(Assigned(IDEEditorOptions), 'TConvertDelphiProject.CreateMainSourceFile: IDEEditorOptions is Nil.'); MainUnitInfo.DefaultSyntaxHighlighter:= IdeSyntaxHighlighters.GetIdForFileExtension(fMainUnitConverter.LazFileExt); MainUnitInfo.IsPartOfProject:=true; LazProject.AddFile(MainUnitInfo,false); LazProject.MainFileID:=0; end else begin // replace main unit in project LazProject.MainUnitInfo.Source:=fMainUnitConverter.fPascalBuffer; end; // Scan LPR file for directives. Sets fIsConsoleApp flag. ConvTool:=TConvDelphiCodeTool.Create(fMainUnitConverter.fCTLink); try fIsConsoleApp:=ConvTool.FindApptypeConsole; finally ConvTool.Free; end; fProjPack.BaseCompilerOptions.Win32GraphicApp := not fIsConsoleApp; fMainUnitConverter.fUsedUnitsTool.IsConsoleApp:=fIsConsoleApp; Result:=LazarusIDE.DoOpenEditorFile(fMainUnitConverter.fLazUnitFilename,0,0,[ofQuiet]); if Result<>mrOK then exit; Result:=mrOK; end; function TConvertDelphiProject.AddUnit(AFileName: string; out OutUnitInfo: TUnitInfo): TModalResult; // add new unit to project var RP, PureUnitName: String; begin Result:=mrOK; OutUnitInfo:=nil; if not FilenameIsAbsolute(AFileName) then AFileName:=AppendPathDelim(LazProject.Directory)+AFileName; AFileName:=TrimFilename(AFileName); if not FileExistsUTF8(AFileName) then exit(mrNo); // Create relative search path for project settings. RP:=ExtractFilePath(CreateRelativePath(AFileName, LazProject.Directory)); if (RP<>'') and (fUnitSearchPaths.IndexOf(RP)<0) then fUnitSearchPaths.Add(RP); PureUnitName:=ExtractFileNameOnly(AFileName); if fSettings.OmitProjUnits.Contains(PureUnitName) then begin fMainUnitConverter.fUsedUnitsTool.Remove(PureUnitName); fSettings.AddLogLine(mluNote, Format(lisConvDelphiProjOmittedUnit,[PureUnitName]), fLazPInfoFilename); TryAddPackageDep(PureUnitName, fSettings.OmitProjUnits[PureUnitName]); end else begin // Check unitname and create UnitInfo. OutUnitInfo:=LazProject.UnitInfoWithFilename(AFileName); if OutUnitInfo=nil then begin if FilenameHasPascalExt(AFileName) then begin OutUnitInfo:=LazProject.UnitWithUnitname(PureUnitName); Assert(OutUnitInfo=nil, Format('TConvertDelphiProject.AddUnit: Unitname %s exists twice',[PureUnitName])); end; OutUnitInfo:=TUnitInfo.Create(nil); OutUnitInfo.Filename:=AFileName; LazProject.AddFile(OutUnitInfo,false); end; OutUnitInfo.IsPartOfProject:=true; end; end; function TConvertDelphiProject.FindAllUnits: TModalResult; var FoundUnits, MisUnits, NormalUnits: TStrings; i: Integer; CurFilename: string; AllPath: string; p: LongInt; ui: TUnitInfo; begin Screen.BeginWaitCursor; FoundUnits:=nil; MisUnits:=nil; NormalUnits:=nil; try if not CodeToolBoss.FindDelphiProjectUnits(fMainUnitConverter.fPascalBuffer, FoundUnits, MisUnits, NormalUnits) then begin LazarusIDE.DoJumpToCodeToolBossError; fErrorMsg:=Format(lisConvProblemsFindingAllUnits, [fSettings.MainFilename]); exit(mrCancel); end; try // Add all units to the project fSettings.AddLogLine(mluProgress, lisConvDelphiFoundAllUnitFiles); DebugLn('TConvertDelphiProject.FindAllUnits: '+lisConvDelphiFoundAllUnitFiles); for i:=0 to FoundUnits.Count-1 do begin CurFilename:=FoundUnits[i]; p:=Pos(' in ',CurFilename); if p>0 then delete(CurFilename,1,p+3); if CurFilename='' then continue; Result:=AddUnit(SwitchPathDelims(CurFilename, True), ui); if Result=mrAbort then exit; end; for i:=0 to NormalUnits.Count-1 do begin CurFilename:=NormalUnits[i]; AllPath:=FindDiskFileCaseInsensitive(fSettings.MainPath+CurFilename+'.pas'); if AllPath<>'' then begin Result:=AddUnit(AllPath, ui); if Result=mrAbort then exit; end; end; finally AllPath:=fUnitSearchPaths.DelimitedText; LazProject.CompilerOptions.MergeToUnitPaths(AllPath); LazProject.CompilerOptions.MergeToIncludePaths(AllPath); // Clear caches LazProject.DefineTemplates.SourceDirectoriesChanged; end; // Save project Result:=LazarusIDE.DoSaveProject([sfQuietUnitCheck]); if Result<>mrOK then exit; finally FoundUnits.Free; MisUnits.Free; NormalUnits.Free; Screen.EndWaitCursor; end; Result:=mrOK; end; function TConvertDelphiProject.ConvertAllUnits: TModalResult; var ConvUnits: TObjectList; // List of ConvertDelphiUnits. function ConvertOne(AUnitInfo: TUnitInfo): TModalResult; var Converter: TDelphiUnit; begin Converter:=TDelphiUnit.Create(Self, AUnitInfo.Filename,[]); try Converter.fUnitInfo:=AUnitInfo; ConvUnits.Add(Converter); Result:=Converter.CopyAndLoadFile; if Result<>mrOK then exit; Result:=Converter.CheckFailed(Result); if Result<>mrOK then exit; Result:=Converter.ConvertUnitFile; except DebugLn(['TConvertDelphiProject.ConvertAllUnits: Exception happened while converting ', Converter.fLazUnitFilename]); ConvUnits.Remove(Converter); raise; end; end; var CurUnitInfo: TUnitInfo; i: Integer; begin Result:=mrOK; ConvUnits:=TObjectList.Create; try try try // convert all units and fix .lfm files fSettings.AddLogLine(mluImportant, ''); fSettings.AddLogLine(mluImportant, lisConvDelphiConvertingProjPackUnits); for i:=0 to LazProject.UnitCount-1 do begin CurUnitInfo:=LazProject.Units[i]; Application.ProcessMessages; // Main LPR file was converted earlier. if CurUnitInfo.IsPartOfProject and (CurUnitInfo<>LazProject.MainUnitInfo) then begin Result:=ConvertOne(CurUnitInfo); if Result=mrIgnore then Continue; if Result=mrAbort then Exit; end; end; // During conversion there were more units added to be converted. if fUnitsToAddToProject.Count > 0 then begin fSettings.AddLogLine(mluImportant, ''); fSettings.AddLogLine(mluImportant, lisConvDelphiConvertingFoundUnits); end; for i:=0 to fUnitsToAddToProject.Count-1 do begin Application.ProcessMessages; Result:=AddUnit(fUnitsToAddToProject[i], CurUnitInfo); if Result=mrNo then Continue; if Result=mrAbort then Exit; if Assigned(CurUnitInfo) then begin Result:=ConvertOne(CurUnitInfo); if Result=mrIgnore then Continue; if Result=mrAbort then Exit; end; end; if Result=mrOK then begin if fUseThreads then begin Result:=fMainUnitConverter.fUsedUnitsTool.AddThreadSupport; if Result<>mrOK then exit; end; end; except fSettings.AddLogLine(mluImportant, ''); fSettings.AddLogLine(mluError, lisConvDelphiExceptionDuringConversion); DebugLn(lisConvDelphiExceptionDuringConversion); raise; end; finally if Result=mrOK then begin // Try to convert form files also in case of an exception. // Unit name replacements etc. are implemented there. Result:=ConvertAllFormFiles(ConvUnits); // Finally save project once more Result:=LazarusIDE.DoSaveProject([sfQuietUnitCheck]); end; end; finally ConvUnits.Free; // Owns and frees converter objects. end; end; function TConvertDelphiProject.ExtractOptionsFromDelphiSource: TModalResult; begin // TODO: remove compiler directives and put them into project/package if fDelphiPFilename<>'' then begin end; Result:=mrOK; end; function TConvertDelphiProject.GetLazProject: TProject; begin Result:=fProjPack as TProject; end; procedure TConvertDelphiProject.SetLazProject(const AValue: TProject); begin fProjPack:=AValue; end; function TConvertDelphiProject.ContainsFile(const aFileName: string): Boolean; begin Result:=Assigned(LazProject.UnitInfoWithFilename(aFileName)); end; function TConvertDelphiProject.FindDependencyByName(const PackageName: string): TPkgDependency; begin Result:=LazProject.FindDependencyByName(PackageName); end; function TConvertDelphiProject.FirstDependency: TPkgDependency; begin Result:=LazProject.FirstRequiredDependency; end; function TConvertDelphiProject.GetMainName: string; begin Result:=''; if Assigned(LazProject.MainUnitInfo) then Result:=LazProject.MainUnitInfo.Filename; end; function TConvertDelphiProject.SaveAndMaybeClose(const Filename: string): TModalResult; var UnitIndex: Integer; AnUnitInfo: TUnitInfo; begin Result:=mrOK; if Filename='' then exit; UnitIndex:=LazProject.IndexOfFilename(Filename, [pfsfOnlyEditorFiles]); if UnitIndex<0 then exit; AnUnitInfo:=LazProject.Units[UnitIndex]; if AnUnitInfo.OpenEditorInfoCount=0 then exit; Result:=LazarusIDE.DoSaveEditorFile(AnUnitInfo.OpenEditorInfo[0].EditorComponent, [sfCheckAmbiguousFiles,sfQuietUnitCheck]); // The main form has UnitIndex = 1. Don't close it. if (UnitIndex<>1) and not fSettings.KeepFileOpen then Result:=LazarusIDE.DoCloseEditorFile(AnUnitInfo.OpenEditorInfo[0].EditorComponent, [cfQuiet]); end; { TConvertDelphiPackage } constructor TConvertDelphiPackage.Create(const aPackageFilename: string); begin inherited Create(aPackageFilename, lisConvDelphiConvertDelphiPackage); fLazPInfoSuffix:='.lpk'; // Main XML package file, not compatible with Delphi fLazPSuffix:=''; // '.lpk' is reserved to the main XML file fDelphiPSuffix:='.dpk'; end; destructor TConvertDelphiPackage.Destroy; begin inherited Destroy; end; function TConvertDelphiPackage.CreateInstance: TModalResult; // open new package. If .lpk does not exist, create it var PkgName: String; begin fProjPack:=nil; if FileExistsUTF8(fLazPInfoFilename) then begin // there is already a lazarus package file -> open the package editor Result:=PackageEditingInterface.DoOpenPackageFile(fLazPInfoFilename,[pofAddToRecent],true); if Result<>mrOK then exit; end; // search package in graph PkgName:=ExtractFileNameOnly(fLazPInfoFilename); fProjPack:=PackageGraph.FindPackageWithName(PkgName,nil); if fProjPack<>nil then begin // there is already a package loaded with this name ... if CompareFilenames(LazPackage.Filename,fLazPInfoFilename)<>0 then begin // ... but it is not the package file we want -> stop MessageDlg(lisConvDelphiPackageNameExists, Format(lisConvDelphiThereIsAlreadyAPackageWithTheNamePleaseCloseThisPa, [PkgName, LineEnding]), mtError, [mbAbort], 0); PackageEditingInterface.DoOpenPackageFile(LazPackage.Filename,[pofAddToRecent],true); fErrorMsg:=lisConvStoppedBecauseThereIsPackage; exit(mrAbort); end else begin Result:=mrOK; end; end else begin // there is not yet a package with this name -> create a new package with LCL as dependency fProjPack:=PackageGraph.CreateNewPackage(PkgName); PackageGraph.AddDependencyToPackage(LazPackage, PackageGraph.LCLPackage.CreateDependencyWithOwner(LazPackage)); LazPackage.Filename:=fLazPInfoFilename; fProjPack.BaseCompilerOptions.SyntaxMode:='delphi'; // open a package editor PackageEditors.OpenEditor(LazPackage,true); // save .lpk file PackageEditors.SavePackage(LazPackage,false); Result:=mrOK; end; end; function TConvertDelphiPackage.CreateMainSourceFile: TModalResult; begin Result:=mrOK; end; function TConvertDelphiPackage.AddUnit(AFileName: string): TModalResult; var PkgFile, OffendingUnit: TPkgFile; CodeBuffer: TCodeBuffer; Flags: TPkgFileFlags; PureUnitName: String; begin Result:=mrOK; if not FilenameIsAbsolute(AFileName) then AFileName:=AppendPathDelim(LazPackage.Directory)+AFileName; AFileName:=TrimFilename(AFileName); if not FileExistsUTF8(AFileName) then exit(mrNo); PkgFile:=LazPackage.FindPkgFile(AFileName,true,false); if PkgFile=nil then begin PureUnitName:=ExtractFileNameOnly(AFileName); if FilenameHasPascalExt(AFileName) then begin // Check unitname OffendingUnit:=LazPackage.FindUnit(PureUnitName); Assert(OffendingUnit=nil, Format('TConvertDelphiPackage.AddUnit: Unitname %s exists twice',[PureUnitName])); end; Flags:=[pffAddToPkgUsesSection]; // Check if the unit has a Register procedure. // ToDo: Optimize. The source is read again during unit conversion. CodeBuffer:=CodeToolBoss.LoadFile(AFilename, true, false); if (CodeBuffer<>nil) and CodeToolBoss.HasInterfaceRegisterProc(CodeBuffer) then begin Include(Flags, pffHasRegisterProc); fSettings.AddLogLine(mluNote, Format(lisConvAddingFlagForRegister,[PureUnitName]), fLazPInfoFilename); end; // Add new unit to package LazPackage.AddFile(AFileName, PureUnitName, pftUnit, Flags, cpNormal); end; end; function TConvertDelphiPackage.FindAllUnits: TModalResult; var FoundUnits, MisUnits, NormalUnits: TStrings; i: Integer; NewSearchPath, AllPath, UselessPath: String; CurFilename: string; p: LongInt; begin FoundUnits:=nil; MisUnits:=nil; NormalUnits:=nil; try if not CodeToolBoss.FindDelphiPackageUnits(fMainUnitConverter.fPascalBuffer, FoundUnits, MisUnits, NormalUnits) then begin LazarusIDE.DoJumpToCodeToolBossError; fErrorMsg:=Format(lisConvProblemsFindingAllUnits, [fSettings.MainFilename]); exit(mrCancel); end; try fSettings.AddLogLine(mluProgress, lisConvDelphiFoundAllUnitFiles); DebugLn('TConvertDelphiPackage.FindAllUnits: '+lisConvDelphiFoundAllUnitFiles); // Add all units to the package for i:=0 to FoundUnits.Count-1 do begin CurFilename:=FoundUnits[i]; p:=System.Pos(' in ',CurFilename); if p>0 then delete(CurFilename,1,p+3); if CurFilename='' then continue; Result:=AddUnit(SwitchPathDelims(CurFilename, True)); if Result=mrAbort then exit; end; for i:=0 to NormalUnits.Count-1 do begin CurFilename:=NormalUnits[i]; AllPath:=FindDiskFileCaseInsensitive(fSettings.MainPath+CurFilename+'.pas'); if AllPath<>'' then begin Result:=AddUnit(AllPath); if Result=mrAbort then exit; end; end; finally AllPath:=fProjPack.SourceDirectories.CreateSearchPathFromAllFiles; UselessPath:='.;'+VirtualDirectory+';'+VirtualTempDir+';'+LazPackage.Directory; // Set unit paths to find all project units NewSearchPath:=MergeSearchPaths(fProjPack.BaseCompilerOptions.OtherUnitFiles,AllPath); NewSearchPath:=RemoveSearchPaths(NewSearchPath,UselessPath); fProjPack.BaseCompilerOptions.OtherUnitFiles:=MinimizeSearchPath( RemoveNonExistingPaths(NewSearchPath,LazPackage.Directory)); // Set include path NewSearchPath:=MergeSearchPaths(fProjPack.BaseCompilerOptions.IncludePath,AllPath); NewSearchPath:=RemoveSearchPaths(NewSearchPath,UselessPath); fProjPack.BaseCompilerOptions.IncludePath:=MinimizeSearchPath( RemoveNonExistingPaths(NewSearchPath,LazPackage.Directory)); // Clear caches fProjPack.DefineTemplates.SourceDirectoriesChanged; CodeToolBoss.DefineTree.ClearCache; end; // Save package Result:=PackageEditors.SavePackage(LazPackage,false); if Result<>mrOK then exit; finally FoundUnits.Free; MisUnits.Free; NormalUnits.Free; end; Result:=mrOK; end; function TConvertDelphiPackage.ConvertAllUnits: TModalResult; var ConvUnits: TObjectList; // List of ConvertDelphiUnits. function ConvertOne(APkgFile: TPkgFile): TModalResult; var Converter: TDelphiUnit; begin Converter:=TDelphiUnit.Create(Self, APkgFile.Filename,[]); try ConvUnits.Add(Converter); Result:=Converter.CopyAndLoadFile; if Result<>mrOK then exit; Result:=Converter.CheckFailed(Result); if Result<>mrOK then exit; Result:=Converter.ConvertUnitFile; except DebugLn(['TConvertDelphiProject.ConvertAllUnits: Exception happened while converting ', Converter.fLazUnitFilename]); ConvUnits.Remove(Converter); raise; end; end; var i: Integer; PkgFile: TPkgFile; begin Result:=mrOK; ConvUnits:=TObjectList.Create; try try try // Convert all units and fix .lfm files fSettings.AddLogLine(mluImportant, ''); fSettings.AddLogLine(mluImportant, lisConvDelphiConvertingProjPackUnits); for i:=0 to LazPackage.FileCount-1 do begin PkgFile:=LazPackage.Files[i]; Application.ProcessMessages; ConvertOne(PkgFile); if Result=mrIgnore then Continue; if Result=mrAbort then Exit; end; // During conversion there were more units added to be converted. if fUnitsToAddToProject.Count > 0 then begin fSettings.AddLogLine(mluImportant, ''); fSettings.AddLogLine(mluImportant, lisConvDelphiConvertingFoundUnits); end; { ToDo: add more units for i:=0 to fUnitsToAddToProject.Count-1 do begin Application.ProcessMessages; Result:=AddUnit(fUnitsToAddToProject[i]); if Result in [mrNo, mrCancel] then Continue; if Result=mrAbort then Exit; if Assigned(CurUnitInfo) then begin Result:=ConvertOne(CurUnitInfo); if Result=mrIgnore then Continue; if Result=mrAbort then Exit; end; end; } except fSettings.AddLogLine(mluImportant, ''); fSettings.AddLogLine(mluError, lisConvDelphiExceptionDuringConversion); DebugLn(lisConvDelphiExceptionDuringConversion); raise; end; finally if Result=mrOK then begin // Try to convert form files also in case of an exception. // Unit name replacements etc. are implemented there. Result:=ConvertAllFormFiles(ConvUnits); // Finally save the package once more Result:=PackageEditors.SavePackage(LazPackage,false); end; end; finally ConvUnits.Free; // Owns and frees converter objects. end; end; function TConvertDelphiPackage.ExtractOptionsFromDelphiSource: TModalResult; begin // TODO: use fDelphiPFilename and LazPackage to get options. if fDelphiPFilename<>'' then begin end; Result:=mrOK; end; function TConvertDelphiPackage.GetLazPackage: TLazPackage; begin Result:=fProjPack as TLazPackage; end; procedure TConvertDelphiPackage.SetLazPackage(const AValue: TLazPackage); begin fProjPack:=AValue; end; function TConvertDelphiPackage.GetMainName: string; begin Result:=LazPackage.Filename; end; function TConvertDelphiPackage.ContainsFile(const aFileName: string): Boolean; begin Result:=Assigned(LazPackage.FindPkgFile(aFileName, True, False)); end; function TConvertDelphiPackage.FindDependencyByName(const PackageName: string): TPkgDependency; begin Result:=LazPackage.FindDependencyByName(PackageName); end; function TConvertDelphiPackage.FirstDependency: TPkgDependency; begin Result:=LazPackage.FirstRequiredDependency; end; { TConvertedDelphiProjectDescriptor } function TConvertedDelphiProjectDescriptor.InitProject(AProject: TLazProject): TModalResult; begin Result:=inherited InitProject(AProject); AProject.LazCompilerOptions.SyntaxMode:='delphi'; end; end.