mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-16 05:39:24 +00:00
Reverts some of the version 2.0 project functionality, as there are a number of challenges around populating files for folders that don't have project definitions, particularly if user attempts to load a folder that is not a project folder after all but happens, e.g. to have a source folder in it.
282 lines
8.6 KiB
ObjectPascal
282 lines
8.6 KiB
ObjectPascal
(*
|
|
Name: Keyman.Developer.System.Project.ProjectLoader
|
|
Copyright: Copyright (C) SIL International.
|
|
Documentation:
|
|
Description:
|
|
Create Date: 1 Aug 2006
|
|
|
|
Modified Date: 24 Aug 2015
|
|
Authors: mcdurdin
|
|
Related Files:
|
|
Dependencies:
|
|
|
|
Bugs:
|
|
Todo:
|
|
Notes:
|
|
History: 01 Aug 2006 - mcdurdin - Initial version
|
|
19 Mar 2007 - mcdurdin - I708 - Files disappearing from project
|
|
19 Nov 2007 - mcdurdin - I1157 - const string parameters
|
|
10 Jun 2014 - mcdurdin - I4212 - Crash opening project in read-only location [CrashID:tike.exe_9.0.443.0_005E202D_EOleException]
|
|
04 May 2015 - mcdurdin - I4688 - V9.0 - Add build path to project settings
|
|
04 May 2015 - mcdurdin - I4694 - V9.0 - Split UI actions from non-UI actions in projects
|
|
05 May 2015 - mcdurdin - I4698 - V9.0 - Split project and user preferences files
|
|
24 Aug 2015 - mcdurdin - I4865 - Add treat hints and warnings as errors into project
|
|
24 Aug 2015 - mcdurdin - I4866 - Add warn on deprecated features to project and compile
|
|
|
|
*)
|
|
unit Keyman.Developer.System.Project.ProjectLoader;
|
|
|
|
interface
|
|
|
|
uses
|
|
System.Classes,
|
|
System.SysUtils,
|
|
Winapi.Windows,
|
|
Xml.XMLDoc,
|
|
Xml.XMLIntf,
|
|
|
|
Keyman.Developer.System.Project.ProjectFile,
|
|
utilsystem;
|
|
|
|
// Corresponds to kmc/projectLoader.ts; TypeScript implementation is master version
|
|
|
|
type
|
|
EProjectLoader = class(Exception);
|
|
|
|
TProjectLoader = class
|
|
private
|
|
FFileName: string;
|
|
FProject: TProject;
|
|
procedure LoadUser;
|
|
procedure LoadProjectFromFile; // I4698
|
|
public
|
|
constructor Create(AProject: TProject; AFileName: string);
|
|
procedure Execute;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
System.Variants,
|
|
System.Win.ComObj,
|
|
|
|
Keyman.Developer.System.Project.kmnProjectFile,
|
|
Keyman.Developer.System.Project.kpsProjectFile,
|
|
Keyman.Developer.System.Project.ProjectFiles,
|
|
Keyman.Developer.System.Project.ProjectFileType,
|
|
|
|
utildir,
|
|
utilfiletypes;
|
|
|
|
{ TProjectLoader }
|
|
|
|
constructor TProjectLoader.Create(AProject: TProject; AFileName: string);
|
|
begin
|
|
inherited Create;
|
|
FProject := AProject;
|
|
FFileName := AFileName;
|
|
end;
|
|
|
|
procedure TProjectLoader.Execute; // I4698
|
|
begin
|
|
LoadProjectFromFile;
|
|
end;
|
|
|
|
procedure TProjectLoader.LoadProjectFromFile;
|
|
var
|
|
n, i: Integer;
|
|
doc: IXMLDocument;
|
|
node, root: IXMLNode;
|
|
pf: TProjectFile;
|
|
hasUserState: Boolean;
|
|
begin
|
|
try
|
|
doc := LoadXMLDocument(FFileName);
|
|
except
|
|
on E:Exception do
|
|
raise EProjectLoader.Create('Error loading project file: '+E.Message);
|
|
end;
|
|
|
|
hasUserState := FileExists(ChangeFileExt(FFileName, Ext_ProjectSourceUser));
|
|
|
|
root := doc.DocumentElement;
|
|
if root.NodeName <> 'KeymanDeveloperProject' then
|
|
raise EProjectLoader.Create('Not a Keyman Developer project file');
|
|
|
|
{ Load options }
|
|
|
|
node := root.ChildNodes.FindNode('Options'); // I4688
|
|
if node <> nil then
|
|
begin
|
|
if not VarIsNull(node.ChildValues['Version']) then
|
|
begin
|
|
FProject.Options.Version := ProjectVersionFromString(VarToStr(node.ChildValues['Version']));
|
|
if FProject.Options.Version = pvUnknown then
|
|
raise EProjectLoader.Create('This project format is version '+VarToStr(node.ChildValues['Version'])+', which is not supported by this version of Keyman Developer.');
|
|
end;
|
|
|
|
// Set default project options based on what we learned above
|
|
FProject.Options.Assign(DefaultProjectOptions[FProject.Options.Version]);
|
|
|
|
if not VarIsNull(node.ChildValues['BuildPath']) then
|
|
FProject.Options.BuildPath := DosSlashes(VarToStr(node.ChildValues['BuildPath']));
|
|
|
|
if not VarIsNull(node.ChildValues['SourcePath']) then
|
|
FProject.Options.SourcePath := DosSlashes(VarToStr(node.ChildValues['SourcePath']));
|
|
|
|
if not VarIsNull(node.ChildValues['CompilerWarningsAsErrors']) then
|
|
FProject.Options.CompilerWarningsAsErrors := node.ChildValues['CompilerWarningsAsErrors'];
|
|
|
|
if not VarIsNull(node.ChildValues['WarnDeprecatedCode']) then
|
|
FProject.Options.WarnDeprecatedCode := node.ChildValues['WarnDeprecatedCode'];
|
|
|
|
if not VarIsNull(node.ChildValues['CheckFilenameConventions']) then
|
|
FProject.Options.CheckFilenameConventions := node.ChildValues['CheckFilenameConventions'];
|
|
|
|
if not VarIsNull(node.ChildValues['SkipMetadataFiles']) then
|
|
FProject.Options.SkipMetadataFiles := node.ChildValues['SkipMetadataFiles'];
|
|
|
|
if not VarIsNull(node.ChildValues['ProjectType']) then
|
|
begin
|
|
FProject.Options.ProjectType := ProjectTypeFromString(VarToStr(node.ChildValues['ProjectType']));
|
|
if FProject.Options.ProjectType = ptUnknown then
|
|
// Support projects without a defined projecttype
|
|
FProject.Options.ProjectType := ptKeyboard;
|
|
end;
|
|
end;
|
|
|
|
{ Load root nodes first - I708 }
|
|
|
|
if FProject.Options.Version = pv10 then
|
|
begin
|
|
for i := 0 to root.ChildNodes['Files'].ChildNodes.Count - 1 do
|
|
begin
|
|
node := root.ChildNodes['Files'].ChildNodes[i];
|
|
if node.NodeName <> 'File' then Continue;
|
|
if node.ChildNodes.FindNode('ParentFileID') = nil then // ChildValues['ParentFileID'] then
|
|
begin
|
|
if not VarIsNull(node.ChildValues['Filepath']) then
|
|
begin
|
|
// I1152 - Avoid crashes when .kpj file is invalid
|
|
pf := CreateProjectFile(FProject, ExpandFileNameClean(FFileName, node.ChildValues['Filepath']), nil);
|
|
pf.Load(node);
|
|
if not hasUserState then
|
|
pf.LoadState(node);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{ Load child nodes }
|
|
|
|
for i := 0 to root.ChildNodes['Files'].ChildNodes.Count - 1 do
|
|
begin
|
|
node := root.ChildNodes['Files'].ChildNodes[i];
|
|
if node.NodeName <> 'File' then Continue;
|
|
|
|
if node.ChildNodes.FindNode('ParentFileID') <> nil then // ChildValues['ParentFileID'] then
|
|
begin
|
|
n := FProject.Files.IndexOfID(node.ChildValues['ParentFileID']);
|
|
if n < 0 then Continue;
|
|
pf := CreateProjectFile(FProject, ExpandFileNameClean(FFileName, node.ChildValues['Filepath']), FProject.Files[n]);
|
|
pf.Load(node);
|
|
if not hasUserState then
|
|
pf.LoadState(node);
|
|
end;
|
|
end;
|
|
|
|
{ Load MRU from old project files }
|
|
|
|
node := root.ChildNodes['MRU'];
|
|
if Assigned(node) then
|
|
begin
|
|
for i := 0 to node.ChildNodes.Count - 1 do
|
|
begin
|
|
if node.ChildNodes[i].NodeName <> 'File' then Continue;
|
|
FProject.MRU.Append(node.ChildNodes[i].ChildNodes['FullPath'].NodeValue);
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
FProject.PopulateFiles;
|
|
|
|
LoadUser;
|
|
end;
|
|
|
|
procedure TProjectLoader.LoadUser; // I4698
|
|
var
|
|
doc: IXMLDocument;
|
|
i: Integer;
|
|
node, root: IXMLNode;
|
|
fsroot: IXMLNode;
|
|
n: Integer;
|
|
state: IXMLDocument;
|
|
viewState: IXMLNode;
|
|
begin
|
|
if not FileExists(ChangeFileExt(FFileName, Ext_ProjectSourceUser)) then Exit;
|
|
|
|
try
|
|
doc := LoadXMLDocument(ChangeFileExt(FFileName, Ext_ProjectSourceUser));
|
|
except
|
|
on E:Exception do
|
|
raise EProjectLoader.Create('Error loading project user settings file: '+E.Message);
|
|
end;
|
|
|
|
|
|
root := doc.DocumentElement;
|
|
if root.NodeName <> 'KeymanDeveloperProjectUser' then
|
|
raise EProjectLoader.Create('Not a Keyman Developer project .user file');
|
|
|
|
{ Convert 9.0-era <state> node to <ViewState> }
|
|
|
|
fsroot := root.ChildNodes.FindNode('state');
|
|
if Assigned(fsroot) and not VarIsNull(fsroot.NodeValue) and (root.ChildNodes.FindNode('ViewState') = nil) then
|
|
begin
|
|
state := LoadXMLData(fsroot.NodeValue);
|
|
viewState := root.AddChild('ViewState');
|
|
for i := 0 to state.DocumentElement.ChildNodes.Count - 1 do
|
|
viewState.ChildNodes.Add(state.DocumentElement.ChildNodes[i].CloneNode(True));
|
|
|
|
try
|
|
doc.SaveToFile(ChangeFileExt(FFileName, Ext_ProjectSourceUser));
|
|
except
|
|
on E:EOleException do // I4212
|
|
begin
|
|
;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
viewState := root.ChildNodes.FindNode('ViewState');
|
|
if Assigned(viewState) then
|
|
begin
|
|
FProject.DisplayState := viewState.XML;
|
|
end;
|
|
|
|
{ Load root nodes first - I708 }
|
|
|
|
fsroot := root.ChildNodes.FindNode('FileStates');
|
|
if Assigned(fsroot) then
|
|
begin
|
|
for i := 0 to fsroot.ChildNodes.Count - 1 do
|
|
begin
|
|
node := fsroot.ChildNodes[i];
|
|
if node.NodeName <> 'FileState' then Continue;
|
|
if node.ChildNodes.FindNode('ID') = nil then Continue;
|
|
n := FProject.Files.IndexOfID(node.ChildValues['ID']);
|
|
if n >= 0 then
|
|
FProject.Files[n].LoadState(node);
|
|
end;
|
|
end;
|
|
|
|
node := root.ChildNodes['MRU'];
|
|
if Assigned(node) then
|
|
begin
|
|
for i := 0 to node.ChildNodes.Count - 1 do
|
|
begin
|
|
if node.ChildNodes[i].NodeName <> 'File' then Continue;
|
|
FProject.MRU.Append(node.ChildNodes[i].ChildNodes['FullPath'].NodeValue);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
end.
|