diff --git a/Demos/FMX/Android/Demo00/AndroidManifest.template.xml b/Demos/FMX/Android/Demo00/AndroidManifest.template.xml new file mode 100644 index 00000000..10c95df9 --- /dev/null +++ b/Demos/FMX/Android/Demo00/AndroidManifest.template.xml @@ -0,0 +1,45 @@ + + + + + + <%uses-permission%> + + + + <%provider%> + <%application-meta-data%> + <%uses-libraries%> + <%services%> + + + + + + + + + + <%activity%> + <%receivers%> + + + diff --git a/Demos/FMX/Android/Demo00/AppEnvironment.pas b/Demos/FMX/Android/Demo00/AppEnvironment.pas new file mode 100644 index 00000000..6aadc653 --- /dev/null +++ b/Demos/FMX/Android/Demo00/AppEnvironment.pas @@ -0,0 +1,165 @@ +(**************************************************************************) +(* *) +(* Module: Unit 'AppEnvironment' Copyright (c) 2021 *) +(* *) +(* Lucas Moura Belo - lmbelo *) +(* lucas.belo@live.com *) +(* Brazil *) +(* *) +(* PyScripter *) +(* e-mail: pyscripter@gmail.com *) +(* *) +(* Project pages: https://github.com/Embarcadero/python4delphi *) +(* https://github.com/pyscripter/python4delphi *) +(**************************************************************************) +(* Functionality: App environment set up *) +(* *) +(* *) +(**************************************************************************) +(* This source code is distributed with no WARRANTY, for no reason or use.*) +(* Everyone is allowed to use and change this code free for his own tasks *) +(* and projects, as long as this header and its copyright text is intact. *) +(* For changed versions of this code, which are public distributed the *) +(* following additional conditions have to be fullfilled: *) +(* 1) The header has to contain a comment on the change and the author of *) +(* it. *) +(* 2) A copy of the changed source has to be sent to the above E-Mail *) +(* address or my then valid address, if this is possible to the *) +(* author. *) +(* The second condition has the target to maintain an up to date central *) +(* version of the component. If this condition is not acceptable for *) +(* confidential or legal reasons, everyone is free to derive a component *) +(* or to generate a diff file to my or other original sources. *) +(**************************************************************************) +unit AppEnvironment; + +interface + +uses + System.Classes, System.SysUtils, System.Threading, System.Zip, PythonEngine; + +type + IProgressNotifier = interface + ['{7A2D1743-D4D8-4093-B372-04D814536708}'] + procedure Start(const ADescription, AFirstAction: string; const ATotal: Int64); + procedure Update(const ACurrentAction: string; const AProgress: Int64); + procedure Stop(); + end; + + TAppEnvInit = reference to procedure(const AInitialized: boolean; const ALastErrorMsg: string); + + TAppEnvironment = class + private + FInitialized: boolean; + FProgressNotifier: IProgressNotifier; + procedure OnZipProgressEvent(Sender: TObject; FileName: string; Header: TZipHeader; Position: Int64); + procedure DoInitializeEnvironmentAsync(const APythonEngine: TPythonEngine; + const ACheckPyLib: boolean = true); + public + constructor Create(const AProgressNotifier: IProgressNotifier); + + procedure InitializeEnvironmentAsync(const APythonEngine: TPythonEngine; + const ACheckPyLib: boolean; const AAppEnvInit: TAppEnvInit); + + property Initialized: boolean read FInitialized; + end; + +implementation + +uses + System.IOUtils, FMX.Dialogs, PythonLoad; + +{ TAppEnvironment } + +constructor TAppEnvironment.Create(const AProgressNotifier: IProgressNotifier); +begin + Assert(Assigned(AProgressNotifier), '"AProgressNotifier" undefined'); + FInitialized := false; + FProgressNotifier := AProgressNotifier; +end; + +procedure TAppEnvironment.DoInitializeEnvironmentAsync(const APythonEngine: TPythonEngine; + const ACheckPyLib: boolean = true); +begin + try + //Lock user iteractions + TThread.Synchronize(nil, procedure() begin + FProgressNotifier.Start('Searching for Python installation', String.Empty, 0); + end); + + Sleep(200); + + //Python distibution unzip + TPythonLoad.Extract( + procedure(const AFolderExists: boolean; var AReplaceFiles: boolean) begin + if not AFolderExists then begin + TThread.Synchronize(nil, procedure() begin + FProgressNotifier.Start('Installing Python', String.Empty, 0); + end); + end; + AReplaceFiles := false; + end, OnZipProgressEvent); + + //Configure Python for Android + TThread.Synchronize(nil, procedure() begin + FProgressNotifier.Start('Configuring Python for Android', String.Empty, 3); + FProgressNotifier.Update('Check for files', 1) + end); + + TPythonLoad.Configure(APythonEngine); + Sleep(1000); + + //Load python library + TThread.Synchronize(nil, procedure() begin + FProgressNotifier.Start('Loading Python', String.Empty, 3); + FProgressNotifier.Update('Loading and mapping library', 2) + end); + + TThread.Synchronize(nil, procedure() begin + APythonEngine.LoadDll(); + end); + Sleep(1000); + + //All done notification + TThread.Synchronize(nil, procedure() begin + FProgressNotifier.Start('Loading environment', String.Empty, 3); + FProgressNotifier.Update('All ready', 3) + end); + Sleep(1000); + finally + TThread.Synchronize(nil, procedure() begin + FProgressNotifier.Stop(); + end); + end; +end; + +procedure TAppEnvironment.InitializeEnvironmentAsync( + const APythonEngine: TPythonEngine; const ACheckPyLib: boolean; + const AAppEnvInit: TAppEnvInit); +begin + TTask.Run( + procedure() begin + try + DoInitializeEnvironmentAsync(APythonEngine, ACheckPyLib); + FInitialized := true; + if Assigned(AAppEnvInit) then + AAppEnvInit(true, String.Empty); + except + on E: Exception do begin + if Assigned(AAppEnvInit) then + AAppEnvInit(false, E.Message); + end; + end; + end); +end; + +procedure TAppEnvironment.OnZipProgressEvent(Sender: TObject; FileName: string; + Header: TZipHeader; Position: Int64); +begin + TThread.Queue(nil, procedure() begin + FProgressNotifier.Start('Extracting files', String.Empty, Header.UncompressedSize); + FProgressNotifier.Update(TPath.GetFileName(FileName), Position); + end); +end; + +end. diff --git a/Demos/FMX/Android/Demo00/MainForm.fmx b/Demos/FMX/Android/Demo00/MainForm.fmx new file mode 100644 index 00000000..0a3310ca --- /dev/null +++ b/Demos/FMX/Android/Demo00/MainForm.fmx @@ -0,0 +1,180 @@ +object PyMainForm: TPyMainForm + Left = 0 + Top = 0 + Caption = 'MainForm' + ClientHeight = 743 + ClientWidth = 349 + FormFactor.Width = 320 + FormFactor.Height = 480 + FormFactor.Devices = [Desktop] + OnCreate = FormCreate + OnDestroy = FormDestroy + DesignerMasterStyle = 3 + object spInputOutput: TSplitter + Align = Top + Cursor = crVSplit + MinSize = 20.000000000000000000 + Position.Y = 265.000000000000000000 + Size.Width = 349.000000000000000000 + Size.Height = 8.000000000000000000 + Size.PlatformDefault = False + end + object tbTop: TToolBar + Size.Width = 349.000000000000000000 + Size.Height = 48.000000000000000000 + Size.PlatformDefault = False + TabOrder = 0 + object Label1: TLabel + Align = Client + Size.Width = 349.000000000000000000 + Size.Height = 48.000000000000000000 + Size.PlatformDefault = False + StyleLookup = 'toollabel' + TextSettings.HorzAlign = Center + Text = 'Python4Delphi - PyLoad' + TabOrder = 1 + end + end + object tbBottom: TToolBar + Align = Bottom + Position.Y = 695.000000000000000000 + Size.Width = 349.000000000000000000 + Size.Height = 48.000000000000000000 + Size.PlatformDefault = False + TabOrder = 1 + object btnRun: TButton + Action = actRun + Align = Center + Enabled = True + ImageIndex = -1 + Size.Width = 48.000000000000000000 + Size.Height = 48.000000000000000000 + Size.PlatformDefault = False + StyleLookup = 'playtoolbutton' + TabOrder = 0 + end + end + object Layout1: TLayout + Align = Top + Position.Y = 48.000000000000000000 + Size.Width = 349.000000000000000000 + Size.Height = 217.000000000000000000 + Size.PlatformDefault = False + TabOrder = 5 + object mmOutput: TMemo + Touch.InteractiveGestures = [Pan, LongTap, DoubleTap] + DataDetectorTypes = [] + ReadOnly = True + Align = Client + Margins.Left = 10.000000000000000000 + Margins.Top = 10.000000000000000000 + Margins.Right = 10.000000000000000000 + Size.Width = 329.000000000000000000 + Size.Height = 207.000000000000000000 + Size.PlatformDefault = False + TabOrder = 1 + Viewport.Width = 321.000000000000000000 + Viewport.Height = 199.000000000000000000 + end + object Layout2: TLayout + Anchors = [akRight, akBottom] + Position.X = 284.000000000000000000 + Position.Y = 163.000000000000000000 + TabOrder = 6 + object Circle1: TCircle + Align = Contents + Fill.Color = claGainsboro + Size.Width = 50.000000000000000000 + Size.Height = 50.000000000000000000 + Size.PlatformDefault = False + Stroke.Thickness = 0.000000000000000000 + end + object btnClearOutput: TButton + Action = actClearOutput + Align = Client + Enabled = True + ImageIndex = -1 + Size.Width = 48.000000000000000000 + Size.Height = 48.000000000000000000 + Size.PlatformDefault = False + StyleLookup = 'trashtoolbutton' + TabOrder = 3 + end + end + end + object Layout3: TLayout + Align = Client + Size.Width = 349.000000000000000000 + Size.Height = 422.000000000000000000 + Size.PlatformDefault = False + TabOrder = 6 + object mmInput: TMemo + Touch.InteractiveGestures = [Pan, LongTap, DoubleTap] + DataDetectorTypes = [] + Align = Client + Margins.Left = 10.000000000000000000 + Margins.Right = 10.000000000000000000 + Margins.Bottom = 10.000000000000000000 + Size.Width = 329.000000000000000000 + Size.Height = 412.000000000000000000 + Size.PlatformDefault = False + TabOrder = 0 + Viewport.Width = 321.000000000000000000 + Viewport.Height = 404.000000000000000000 + end + object Layout4: TLayout + Anchors = [akRight, akBottom] + Position.X = 284.000000000000000000 + Position.Y = 358.000000000000000000 + TabOrder = 6 + object Circle2: TCircle + Align = Contents + Fill.Color = claGainsboro + Size.Width = 50.000000000000000000 + Size.Height = 50.000000000000000000 + Size.PlatformDefault = False + Stroke.Thickness = 0.000000000000000000 + end + object btnClearInput: TButton + Action = actClearInput + Align = Client + Enabled = True + ImageIndex = -1 + Size.Width = 48.000000000000000000 + Size.Height = 48.000000000000000000 + Size.PlatformDefault = False + StyleLookup = 'trashtoolbutton' + TabOrder = 3 + end + end + end + object PythonEngine1: TPythonEngine + AutoLoad = False + IO = PythonGUIInputOutput1 + Left = 258 + Top = 56 + end + object PythonGUIInputOutput1: TPythonGUIInputOutput + UnicodeIO = True + RawOutput = False + Output = mmOutput + Left = 259 + Top = 112 + end + object ActionList1: TActionList + Left = 288 + Top = 320 + object actRun: TAction + Text = 'Run' + OnExecute = actRunExecute + end + object actClearOutput: TAction + Text = 'actClearOutput' + OnExecute = actClearOutputExecute + end + object actClearInput: TAction + Text = 'actClearInput' + OnExecute = actClearInputExecute + end + end +end diff --git a/Demos/FMX/Android/Demo00/MainForm.pas b/Demos/FMX/Android/Demo00/MainForm.pas new file mode 100644 index 00000000..bdd42c71 --- /dev/null +++ b/Demos/FMX/Android/Demo00/MainForm.pas @@ -0,0 +1,170 @@ +(**************************************************************************) +(* *) +(* Module: Unit 'MainForm' Copyright (c) 2021 *) +(* *) +(* Lucas Moura Belo - lmbelo *) +(* lucas.belo@live.com *) +(* Brazil *) +(* *) +(* PyScripter *) +(* e-mail: pyscripter@gmail.com *) +(* *) +(* Project pages: https://github.com/Embarcadero/python4delphi *) +(* https://github.com/pyscripter/python4delphi *) +(**************************************************************************) +(* Functionality: MainForm of PyLoad *) +(* Python4Delphi on Android *) +(* *) +(**************************************************************************) +(* This source code is distributed with no WARRANTY, for no reason or use.*) +(* Everyone is allowed to use and change this code free for his own tasks *) +(* and projects, as long as this header and its copyright text is intact. *) +(* For changed versions of this code, which are public distributed the *) +(* following additional conditions have to be fullfilled: *) +(* 1) The header has to contain a comment on the change and the author of *) +(* it. *) +(* 2) A copy of the changed source has to be sent to the above E-Mail *) +(* address or my then valid address, if this is possible to the *) +(* author. *) +(* The second condition has the target to maintain an up to date central *) +(* version of the component. If this condition is not acceptable for *) +(* confidential or legal reasons, everyone is free to derive a component *) +(* or to generate a diff file to my or other original sources. *) +(**************************************************************************) + +unit MainForm; + +interface + +uses + System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, + FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Memo.Types, + FMX.Ani, FMX.StdCtrls, FMX.Controls.Presentation, FMX.ScrollBox, FMX.Memo, + PythonEngine, FMX.PythonGUIInputOutput, System.Actions, FMX.ActnList, + FMX.Objects, FMX.Layouts, FMX.Platform, AppEnvironment, ProgressFrame, + System.Threading; + +type + TPyMainForm = class(TForm) + spInputOutput: TSplitter; + tbTop: TToolBar; + tbBottom: TToolBar; + btnRun: TButton; + PythonEngine1: TPythonEngine; + PythonGUIInputOutput1: TPythonGUIInputOutput; + ActionList1: TActionList; + actRun: TAction; + Layout1: TLayout; + mmOutput: TMemo; + actClearOutput: TAction; + Layout2: TLayout; + btnClearOutput: TButton; + Circle1: TCircle; + Layout3: TLayout; + mmInput: TMemo; + Layout4: TLayout; + Circle2: TCircle; + btnClearInput: TButton; + actClearInput: TAction; + Label1: TLabel; + procedure actRunExecute(Sender: TObject); + procedure actClearOutputExecute(Sender: TObject); + procedure actClearInputExecute(Sender: TObject); + procedure FormCreate(Sender: TObject); + procedure FormDestroy(Sender: TObject); + private + FAppEnv: TAppEnvironment; + function AppEventHandler(AAppEvent: TApplicationEvent; AContext: TObject): boolean; + procedure ConfigurePython(); + procedure DisableComponents(); + procedure EnableComponents(); + public + { Public declarations } + end; + +var + PyMainForm: TPyMainForm; + +implementation + +uses + PythonLoad, System.IOUtils; + +{$R *.fmx} + +{ TPyMainForm } + +procedure TPyMainForm.FormCreate(Sender: TObject); +begin + DisableComponents(); + FAppEnv := TAppEnvironment.Create(TProgressViewFrame.Create(Self, Self)); + + var LAppEventService := IFMXApplicationEventService(nil); + if TPlatformServices.Current.SupportsPlatformService( + IFMXApplicationEventService, IInterface(LAppEventService)) then + LAppEventService.SetApplicationEventHandler(AppEventHandler) + else begin + Log.d('Platform service "IFMXApplicationEventService" not supported.'); + Halt(1); + end; +end; + +procedure TPyMainForm.FormDestroy(Sender: TObject); +begin + FAppEnv.Free(); +end; + +procedure TPyMainForm.actClearInputExecute(Sender: TObject); +begin + mmInput.Lines.Clear(); +end; + +procedure TPyMainForm.actClearOutputExecute(Sender: TObject); +begin + mmOutput.Lines.Clear(); +end; + +procedure TPyMainForm.actRunExecute(Sender: TObject); +begin + PythonEngine1.ExecString(AnsiString(mmInput.Lines.Text)); +end; + +function TPyMainForm.AppEventHandler(AAppEvent: TApplicationEvent; + AContext: TObject): boolean; +begin + case AAppEvent of + TApplicationEvent.FinishedLaunching: ConfigurePython(); + end; + Result := true; +end; + +procedure TPyMainForm.ConfigurePython; +begin + FAppEnv.InitializeEnvironmentAsync(PythonEngine1, true, + procedure(const AInitialized: boolean; const ALastErrorMsg: string) begin + if AInitialized then + EnableComponents() + else + TThread.Synchronize(nil, + procedure + begin + ShowMessage(ALastErrorMsg); + end); + end); +end; + +procedure TPyMainForm.DisableComponents; +begin + btnRun.Enabled := false; + btnClearOutput.Enabled := false; + btnClearInput.Enabled := false; +end; + +procedure TPyMainForm.EnableComponents; +begin + btnRun.Enabled := true; + btnClearOutput.Enabled := true; + btnClearInput.Enabled := true; +end; + +end. diff --git a/Demos/FMX/Android/Demo00/ProgressFrame.fmx b/Demos/FMX/Android/Demo00/ProgressFrame.fmx new file mode 100644 index 00000000..cc459926 --- /dev/null +++ b/Demos/FMX/Android/Demo00/ProgressFrame.fmx @@ -0,0 +1,62 @@ +object ProgressViewFrame: TProgressViewFrame + Align = Client + Locked = True + HitTest = False + Size.Width = 313.000000000000000000 + Size.Height = 168.000000000000000000 + Size.PlatformDefault = False + object pnlBackground: TPanel + Align = Client + Opacity = 0.000000000000000000 + Size.Width = 313.000000000000000000 + Size.Height = 168.000000000000000000 + Size.PlatformDefault = False + TabOrder = 2 + end + object pnlContent: TPanel + Align = Center + Size.Width = 300.000000000000000000 + Size.Height = 168.000000000000000000 + Size.PlatformDefault = False + TabOrder = 1 + object lbDescription: TLabel + Align = Top + Margins.Left = 10.000000000000000000 + Margins.Top = 45.000000000000000000 + Margins.Right = 10.000000000000000000 + Position.X = 10.000000000000000000 + Position.Y = 45.000000000000000000 + Size.Width = 280.000000000000000000 + Size.Height = 24.000000000000000000 + Size.PlatformDefault = False + Text = 'lbDescription' + TabOrder = 0 + end + object lblCurrentAction: TLabel + Align = Top + Margins.Left = 10.000000000000000000 + Margins.Top = 2.000000000000000000 + Margins.Right = 10.000000000000000000 + Position.X = 10.000000000000000000 + Position.Y = 95.000000000000000000 + Size.Width = 280.000000000000000000 + Size.Height = 21.000000000000000000 + Size.PlatformDefault = False + TextSettings.WordWrap = False + Text = 'lblCurrentAction' + TabOrder = 1 + end + object pbProgress: TProgressBar + Align = Top + Orientation = Horizontal + Margins.Left = 10.000000000000000000 + Margins.Top = 4.000000000000000000 + Margins.Right = 10.000000000000000000 + Position.X = 10.000000000000000000 + Position.Y = 73.000000000000000000 + Size.Width = 280.000000000000000000 + Size.Height = 20.000000000000000000 + Size.PlatformDefault = False + end + end +end diff --git a/Demos/FMX/Android/Demo00/ProgressFrame.pas b/Demos/FMX/Android/Demo00/ProgressFrame.pas new file mode 100644 index 00000000..47c35a01 --- /dev/null +++ b/Demos/FMX/Android/Demo00/ProgressFrame.pas @@ -0,0 +1,101 @@ +(**************************************************************************) +(* *) +(* Module: Unit 'ProgressFrame' Copyright (c) 2021 *) +(* *) +(* Lucas Moura Belo - lmbelo *) +(* lucas.belo@live.com *) +(* Brazil *) +(* *) +(* PyScripter *) +(* e-mail: pyscripter@gmail.com *) +(* *) +(* Project pages: https://github.com/Embarcadero/python4delphi *) +(* https://github.com/pyscripter/python4delphi *) +(**************************************************************************) +(* Functionality: Environment loading progress report *) +(* *) +(* *) +(**************************************************************************) +(* This source code is distributed with no WARRANTY, for no reason or use.*) +(* Everyone is allowed to use and change this code free for his own tasks *) +(* and projects, as long as this header and its copyright text is intact. *) +(* For changed versions of this code, which are public distributed the *) +(* following additional conditions have to be fullfilled: *) +(* 1) The header has to contain a comment on the change and the author of *) +(* it. *) +(* 2) A copy of the changed source has to be sent to the above E-Mail *) +(* address or my then valid address, if this is possible to the *) +(* author. *) +(* The second condition has the target to maintain an up to date central *) +(* version of the component. If this condition is not acceptable for *) +(* confidential or legal reasons, everyone is free to derive a component *) +(* or to generate a diff file to my or other original sources. *) +(**************************************************************************) +unit ProgressFrame; + +interface + +uses + System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, + FMX.Types, FMX.Graphics, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls, + FMX.Controls.Presentation, FMX.Layouts, AppEnvironment; + +type + TProgressViewFrame = class(TFrame, IProgressNotifier) + pnlContent: TPanel; + lbDescription: TLabel; + lblCurrentAction: TLabel; + pbProgress: TProgressBar; + pnlBackground: TPanel; + private + { Private declarations } + public + constructor Create(AOwner: TComponent; const AForm: TCustomForm); reintroduce; + + procedure Start(const ADescription, AFirstAction: string; const ATotal: Int64); + procedure Update(const ACurrentAction: string; const AProgress: Int64); + procedure Stop(); + end; + +implementation + +uses + Math; + +{$R *.fmx} + +{ TProgressViewFrame } + +constructor TProgressViewFrame.Create(AOwner: TComponent; + const AForm: TCustomForm); +begin + inherited Create(AOwner); + lblCurrentAction.Text := String.Empty; + Parent := AForm; + Align := TAlignLayout.Contents; + pnlContent.Width := Math.Min(AForm.Width - 40, 410); +end; + +procedure TProgressViewFrame.Start(const ADescription, AFirstAction: string; + const ATotal: Int64); +begin + lbDescription.Text := ADescription; + lblCurrentAction.Text := AFirstAction; + pbProgress.Min := 0; + pbProgress.Max := ATotal; + pbProgress.Value := 0; + pnlContent.Visible := not ADescription.IsEmpty(); +end; + +procedure TProgressViewFrame.Stop; +begin + Self.Visible := false; +end; + +procedure TProgressViewFrame.Update(const ACurrentAction: string; const AProgress: Int64); +begin + lblCurrentAction.Text := ACurrentAction; + pbProgress.Value := AProgress; +end; + +end. diff --git a/Demos/FMX/Android/Demo00/PyLoad.dpr b/Demos/FMX/Android/Demo00/PyLoad.dpr new file mode 100644 index 00000000..b5301f77 --- /dev/null +++ b/Demos/FMX/Android/Demo00/PyLoad.dpr @@ -0,0 +1,17 @@ +program PyLoad; + +uses + System.StartUpCopy, + FMX.Forms, + MainForm in 'MainForm.pas' {PyMainForm}, + PythonLoad in 'PythonLoad.pas', + ProgressFrame in 'ProgressFrame.pas' {ProgressViewFrame: TFrame}, + AppEnvironment in 'AppEnvironment.pas'; + +{$R *.res} + +begin + Application.Initialize; + Application.CreateForm(TPyMainForm, PyMainForm); + Application.Run; +end. diff --git a/Demos/FMX/Android/Demo00/PyLoad.dproj b/Demos/FMX/Android/Demo00/PyLoad.dproj new file mode 100644 index 00000000..1c1e5d23 --- /dev/null +++ b/Demos/FMX/Android/Demo00/PyLoad.dproj @@ -0,0 +1,1840 @@ + + + {652D9A8E-1625-4F00-9B29-2E3D31D7A186} + 19.2 + FMX + True + Debug + Android64 + 37915 + Application + PyLoad.dpr + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + .\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + true + true + true + true + true + true + true + true + $(BDS)\bin\delphi_PROJECTICON.ico + $(BDS)\bin\delphi_PROJECTICNS.icns + PyLoad + + + DBXSqliteDriver;RESTComponents;DBXInterBaseDriver;emsclientfiredac;tethering;DataSnapFireDAC;bindcompfmx;fmx;FireDACIBDriver;FireDACDBXDriver;dbexpress;IndyCore;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;soapserver;bindengine;CloudService;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;soaprtl;DbxCommonDriver;xmlrtl;soapmidas;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + true + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_192x192.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png + android-support-v4.dex.jar;cloud-messaging.dex.jar;com-google-android-gms.play-services-ads-base.17.2.0.dex.jar;com-google-android-gms.play-services-ads-identifier.16.0.0.dex.jar;com-google-android-gms.play-services-ads-lite.17.2.0.dex.jar;com-google-android-gms.play-services-ads.17.2.0.dex.jar;com-google-android-gms.play-services-analytics-impl.16.0.8.dex.jar;com-google-android-gms.play-services-analytics.16.0.8.dex.jar;com-google-android-gms.play-services-base.16.0.1.dex.jar;com-google-android-gms.play-services-basement.16.2.0.dex.jar;com-google-android-gms.play-services-gass.17.2.0.dex.jar;com-google-android-gms.play-services-identity.16.0.0.dex.jar;com-google-android-gms.play-services-maps.16.1.0.dex.jar;com-google-android-gms.play-services-measurement-base.16.4.0.dex.jar;com-google-android-gms.play-services-measurement-sdk-api.16.4.0.dex.jar;com-google-android-gms.play-services-stats.16.0.1.dex.jar;com-google-android-gms.play-services-tagmanager-v4-impl.16.0.8.dex.jar;com-google-android-gms.play-services-tasks.16.0.1.dex.jar;com-google-android-gms.play-services-wallet.16.0.1.dex.jar;com-google-firebase.firebase-analytics.16.4.0.dex.jar;com-google-firebase.firebase-common.16.1.0.dex.jar;com-google-firebase.firebase-iid-interop.16.0.1.dex.jar;com-google-firebase.firebase-iid.17.1.1.dex.jar;com-google-firebase.firebase-measurement-connector.17.0.1.dex.jar;com-google-firebase.firebase-messaging.17.5.0.dex.jar;fmx.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar + + + DBXSqliteDriver;RESTComponents;DBXInterBaseDriver;emsclientfiredac;tethering;DataSnapFireDAC;bindcompfmx;fmx;FireDACIBDriver;FireDACDBXDriver;dbexpress;IndyCore;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;soapserver;bindengine;CloudService;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;soaprtl;DbxCommonDriver;xmlrtl;soapmidas;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + true + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_192x192.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png + android-support-v4.dex.jar;cloud-messaging.dex.jar;com-google-android-gms.play-services-ads-base.17.2.0.dex.jar;com-google-android-gms.play-services-ads-identifier.16.0.0.dex.jar;com-google-android-gms.play-services-ads-lite.17.2.0.dex.jar;com-google-android-gms.play-services-ads.17.2.0.dex.jar;com-google-android-gms.play-services-analytics-impl.16.0.8.dex.jar;com-google-android-gms.play-services-analytics.16.0.8.dex.jar;com-google-android-gms.play-services-base.16.0.1.dex.jar;com-google-android-gms.play-services-basement.16.2.0.dex.jar;com-google-android-gms.play-services-gass.17.2.0.dex.jar;com-google-android-gms.play-services-identity.16.0.0.dex.jar;com-google-android-gms.play-services-maps.16.1.0.dex.jar;com-google-android-gms.play-services-measurement-base.16.4.0.dex.jar;com-google-android-gms.play-services-measurement-sdk-api.16.4.0.dex.jar;com-google-android-gms.play-services-stats.16.0.1.dex.jar;com-google-android-gms.play-services-tagmanager-v4-impl.16.0.8.dex.jar;com-google-android-gms.play-services-tasks.16.0.1.dex.jar;com-google-android-gms.play-services-wallet.16.0.1.dex.jar;com-google-firebase.firebase-analytics.16.4.0.dex.jar;com-google-firebase.firebase-common.16.1.0.dex.jar;com-google-firebase.firebase-iid-interop.16.0.1.dex.jar;com-google-firebase.firebase-iid.17.1.1.dex.jar;com-google-firebase.firebase-measurement-connector.17.0.1.dex.jar;com-google-firebase.firebase-messaging.17.5.0.dex.jar;fmx.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar + + + DBXSqliteDriver;RESTComponents;fmxase;DBXInterBaseDriver;emsclientfiredac;tethering;DataSnapFireDAC;bindcompfmx;fmx;FireDACIBDriver;FireDACDBXDriver;dbexpress;IndyCore;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;soapserver;bindengine;CloudService;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;soaprtl;DbxCommonDriver;xmlrtl;soapmidas;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSPhotoLibraryAddUsageDescription=The reason for adding to the photo library;NSCameraUsageDescription=The reason for accessing the camera;NSFaceIDUsageDescription=The reason for accessing the face id;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSSiriUsageDescription=The reason for accessing Siri;ITSAppUsesNonExemptEncryption=false;NSBluetoothAlwaysUsageDescription=The reason for accessing bluetooth;NSBluetoothPeripheralUsageDescription=The reason for accessing bluetooth peripherals;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSMotionUsageDescription=The reason for accessing the accelerometer;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers + iPhoneAndiPad + true + Debug + $(MSBuildProjectName) + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_1024x1024.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_180x180.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImageDark_2x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_3x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImageDark_3x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SettingIcon_58x58.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SettingIcon_87x87.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_NotificationIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_NotificationIcon_60x60.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImage_2x.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageDark_2x.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SettingIcon_58x58.png + $(BDS)\bin\Artwork\iOS\iPad\FM_NotificationIcon_40x40.png + + + DBXSqliteDriver;RESTComponents;fmxase;DBXInterBaseDriver;emsclientfiredac;tethering;DataSnapFireDAC;bindcompfmx;fmx;FireDACIBDriver;FireDACDBXDriver;dbexpress;IndyCore;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;soapserver;bindengine;CloudService;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;soaprtl;DbxCommonDriver;xmlrtl;soapmidas;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSPhotoLibraryAddUsageDescription=The reason for adding to the photo library;NSCameraUsageDescription=The reason for accessing the camera;NSFaceIDUsageDescription=The reason for accessing the face id;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSSiriUsageDescription=The reason for accessing Siri;ITSAppUsesNonExemptEncryption=false;NSBluetoothAlwaysUsageDescription=The reason for accessing bluetooth;NSBluetoothPeripheralUsageDescription=The reason for accessing bluetooth peripherals;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSMotionUsageDescription=The reason for accessing the accelerometer;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers + iPhoneAndiPad + true + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_1024x1024.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_180x180.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImageDark_2x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_3x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImageDark_3x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SettingIcon_58x58.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SettingIcon_87x87.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_NotificationIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_NotificationIcon_60x60.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImage_2x.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageDark_2x.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SettingIcon_58x58.png + $(BDS)\bin\Artwork\iOS\iPad\FM_NotificationIcon_40x40.png + 10.0 + + + DBXSqliteDriver;RESTComponents;fmxase;DBXInterBaseDriver;emsclientfiredac;tethering;DataSnapFireDAC;FireDACMSSQLDriver;bindcompfmx;DBXOracleDriver;inetdb;fmx;FireDACIBDriver;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;soapserver;bindengine;DBXMySQLDriver;CloudService;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;FireDACPgDriver;FireDACASADriver;FireDACTDataDriver;soaprtl;DbxCommonDriver;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;FireDACDSDriver;rtl;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;bindcomp;DBXInformixDriver;IndyIPClient;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage) + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSCameraUsageDescription=The reason for accessing the camera;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSMotionUsageDescription=The reason for accessing the accelerometer;NSDesktopFolderUsageDescription=The reason for accessing the Desktop folder;NSDocumentsFolderUsageDescription=The reason for accessing the Documents folder;NSDownloadsFolderUsageDescription=The reason for accessing the Downloads folder;NSNetworkVolumesUsageDescription=The reason for accessing files on a network volume;NSRemovableVolumesUsageDescription=The reason for accessing files on a removable volume;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers + Debug + true + + + DBXSqliteDriver;RESTComponents;fmxase;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;bindcompvclsmp;emsclientfiredac;tethering;svnui;DataSnapFireDAC;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;svn;DBXOracleDriver;inetdb;emsedge;fmx;FireDACIBDriver;fmxdae;vcledge;FireDACDBXDriver;dbexpress;IndyCore;vclx;Python;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;DataSnapConnectors;VCLRESTComponents;soapserver;vclie;bindengine;DBXMySQLDriver;CloudService;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;vcl;IndyIPServer;DBXSybaseASEDriver;IndySystem;FireDACDb2Driver;bindcompvclwinx;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;FireDAC;emshosting;FireDACSqliteDriver;FireDACPgDriver;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;soaprtl;DbxCommonDriver;PythonVcl;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;vclwinx;FireDACDSDriver;rtl;emsserverresource;DbxClientDriver;PythonFmx;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage) + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + $(BDS)\bin\default_app.manifest + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + DBXSqliteDriver;RESTComponents;fmxase;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;bindcompvclsmp;emsclientfiredac;tethering;DataSnapFireDAC;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;DBXOracleDriver;inetdb;emsedge;fmx;FireDACIBDriver;fmxdae;vcledge;FireDACDBXDriver;dbexpress;IndyCore;vclx;Python;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;DataSnapConnectors;VCLRESTComponents;soapserver;vclie;bindengine;DBXMySQLDriver;CloudService;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;vcl;IndyIPServer;DBXSybaseASEDriver;IndySystem;FireDACDb2Driver;bindcompvclwinx;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;FireDAC;emshosting;FireDACSqliteDriver;FireDACPgDriver;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;soaprtl;DbxCommonDriver;PythonVcl;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;vclwinx;FireDACDSDriver;rtl;emsserverresource;DbxClientDriver;PythonFmx;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage) + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + $(BDS)\bin\default_app.manifest + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + + 1 + #000000 + + + + 1 + #000000 + + + false + true + PerMonitorV2 + + + true + PerMonitorV2 + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + + 1 + #000000 + + + + 1 + #000000 + + + true + PerMonitorV2 + + + true + PerMonitorV2 + + + + MainSource + + +
PyMainForm
+ fmx +
+ + +
ProgressViewFrame
+ fmx + TFrame +
+ + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + Application + + + + PyLoad.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + + + .\assets\internal + build.zip + false + + + + + PyLoad.exe + true + + + + + ic_launcher.png + true + + + + + splash_image.png + true + + + + + splash_image.png + true + + + + + ic_launcher.png + true + + + + + ic_notification.png + true + + + + + libPyLoad.so + true + + + + + ic_launcher.png + true + + + + + library\lib\arm64-v8a\ + libpython3.9d.so + true + + + + + true + + + + + ic_notification.png + true + + + + + classes.dex + true + + + + + libPyLoad.so + true + + + + + true + + + + + styles.xml + true + + + + + splash_image.png + true + + + + + ic_notification.png + true + + + + + libPyLoad.so + true + + + + + ic_notification.png + true + + + + + ic_notification.png + true + + + + + true + + + + + true + + + + + ic_notification.png + true + + + + + ic_launcher.png + true + + + + + true + + + + + library\lib\armeabi-v7a\ + libpython3.9.so + true + + + + + true + + + + + ic_launcher.png + true + + + + + ic_launcher.png + true + + + + + ic_notification.png + true + + + + + true + + + + + ic_launcher.png + true + + + + + splash_image.png + true + + + + + libPyLoad.so + true + + + + + splash_image.png + true + + + + + splash_image.png + true + + + + + .\assets\internal + build.zip + false + + + + + libPyLoad.so + true + + + + + splash_image.png + true + + + + + .\assets\internal + build.zip + false + + + + + ic_launcher.png + true + + + + + classes.dex + true + + + + + classes.dex + true + + + + + ic_notification.png + true + + + + + true + + + + + true + + + + + ic_launcher.png + true + + + + + styles.xml + true + + + + + ic_launcher.png + true + + + + + ic_launcher.png + true + + + + + library\lib\armeabi-v7a\ + libpython3.9d.so + true + + + + + ic_launcher.png + true + + + + + true + + + + + splash_image.png + true + + + + + splash_image.png + true + + + + + true + + + + + splash_image.png + true + + + + + true + + + + + styles.xml + true + + + + + libPyLoad.so + true + + + + + splash_image.png + true + + + + + true + + + + + libPyLoad.so + true + + + + + ic_launcher.png + true + + + + + ic_notification.png + true + + + + + ic_launcher.png + true + + + + + ic_notification.png + true + + + + + true + + + + + ic_notification.png + true + + + + + ic_launcher.png + true + + + + + ic_notification.png + true + + + + + libPyLoad.so + true + + + + + libPyLoad.so + true + + + + + ic_notification.png + true + + + + + true + + + + + ic_notification.png + true + + + + + ic_notification.png + true + + + + + .\assets\internal + build.zip + false + + + + + true + + + + + libPyLoad.so + true + + + + + ic_launcher.png + true + + + + + libPyLoad.so + true + + + + + ic_notification.png + true + + + + + libPyLoad.so + true + + + + + ic_notification.png + true + + + + + true + + + + + ic_launcher.png + true + + + + + ic_launcher.png + true + + + + + ic_launcher.png + true + + + + + ic_notification.png + true + + + + + classes.dex + true + + + + + splash_image.png + true + + + + + ic_launcher.png + true + + + + + splash_image.png + true + + + + + splash_image.png + true + + + + + ic_notification.png + true + + + + + true + + + + + ic_launcher.png + true + + + + + splash_image.png + true + + + + + ic_launcher.png + true + + + + + libPyLoad.so + true + + + + + library\lib\arm64-v8a\ + libpython3.9.so + true + + + + + true + + + + + true + + + + + true + + + + + ic_notification.png + true + + + + + ic_launcher.png + true + + + + + true + + + + + splash_image.png + true + + + + + true + + + + + libPyLoad.so + true + + + + + ic_launcher.png + true + + + + + styles.xml + true + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + Contents\MacOS + 1 + .framework + + + Contents\MacOS + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + Contents\Resources\StartUp\ + 0 + + + Contents\Resources\StartUp\ + 0 + + + 0 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen + 64 + + + ..\$(PROJECTNAME).launchscreen + 64 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + Contents + 1 + + + Contents + 1 + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + Contents\MacOS + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + True + True + True + True + True + True + True + + + 12 + + + + + + call $(PROJECTDIR)\..\PyDistFinder\bin\pydistfinder.exe UNZIP $(PROJECTDIR) arm true false + False + + False + + False + + + call $(PROJECTDIR)\..\PyDistFinder\bin\pydistfinder.exe UNZIP $(PROJECTDIR) aarch64 true fals + False + + False + + False + + + call $(PROJECTDIR)\..\PyDistFinder\bin\pydistfinder.exe UNZIP $(PROJECTDIR) arm false false + False + + False + + False + + + call $(PROJECTDIR)\..\PyDistFinder\bin\pydistfinder.exe UNZIP $(PROJECTDIR) aarch64 false false + False + + False + + False + +
diff --git a/Demos/FMX/Android/Demo00/PythonLoad.pas b/Demos/FMX/Android/Demo00/PythonLoad.pas new file mode 100644 index 00000000..0a0891a1 --- /dev/null +++ b/Demos/FMX/Android/Demo00/PythonLoad.pas @@ -0,0 +1,136 @@ +(**************************************************************************) +(* *) +(* Module: Unit 'PythonLoad' Copyright (c) 2021 *) +(* *) +(* Lucas Moura Belo - lmbelo *) +(* lucas.belo@live.com *) +(* Brazil *) +(* *) +(* PyScripter *) +(* e-mail: pyscripter@gmail.com *) +(* *) +(* Project pages: https://github.com/Embarcadero/python4delphi *) +(* https://github.com/pyscripter/python4delphi *) +(**************************************************************************) +(* Functionality: Load python distribution *) +(* *) +(* *) +(**************************************************************************) +(* This source code is distributed with no WARRANTY, for no reason or use.*) +(* Everyone is allowed to use and change this code free for his own tasks *) +(* and projects, as long as this header and its copyright text is intact. *) +(* For changed versions of this code, which are public distributed the *) +(* following additional conditions have to be fullfilled: *) +(* 1) The header has to contain a comment on the change and the author of *) +(* it. *) +(* 2) A copy of the changed source has to be sent to the above E-Mail *) +(* address or my then valid address, if this is possible to the *) +(* author. *) +(* The second condition has the target to maintain an up to date central *) +(* version of the component. If this condition is not acceptable for *) +(* confidential or legal reasons, everyone is free to derive a component *) +(* or to generate a diff file to my or other original sources. *) +(**************************************************************************) +unit PythonLoad; + +interface + +uses + System.SysUtils, System.Zip, PythonEngine; + +type + TExtractEvent = reference to procedure(const AFolderExists: boolean; var AReplaceFiles: boolean); + TPythonLoad = class + public + class function GetPyZip(): string; static; + class function GetPyRoot(): string; static; + class function GetPyHome(): string; static; + class function GetPyBin(): string; static; + class function GetPyLib(): string; static; + + class procedure Extract(const AExtractEvent: TExtractEvent; + const AZipProgress: TZipProgressEvent); static; + class procedure Configure(const APythonEngine: TPythonEngine; + const ACheckPyLib: boolean = true); static; + end; + +implementation + +uses + System.IOUtils; + +const + PY_KNOWN_VER = 1; + +{ TPythonLoad } + +class procedure TPythonLoad.Extract(const AExtractEvent: TExtractEvent; + const AZipProgress: TZipProgressEvent); +begin + var LPyRoot := TPythonLoad.GetPyRoot(); + var LFolderExists := TDirectory.Exists(LPyRoot); + var LReplaceFiles := false; + + if Assigned(AExtractEvent) then + AExtractEvent(LFolderExists, LReplaceFiles); + + if LFolderExists then + if not LReplaceFiles then + Exit() + else + TDirectory.Delete(LPyRoot, true); + + var LPyZip := TPythonLoad.GetPyZip(); + if not TFile.Exists(LPyZip) then + raise Exception.Create('Python compressed distribution not found.'); + + TZipFile.ExtractZipFile(LPyZip, LPyRoot, AZipProgress); +end; + +class function TPythonLoad.GetPyBin: string; +begin + Result := TPath.Combine(GetPyHome(), 'bin'); +end; + +class function TPythonLoad.GetPyHome: string; +begin + Result := TPath.Combine(GetPyRoot(), 'usr'); +end; + +class function TPythonLoad.GetPyLib: string; +begin + Result := TPath.Combine(GetPyHome(), 'lib'); +end; + +class function TPythonLoad.GetPyRoot: string; +begin + Result := TPath.Combine(TPath.GetDocumentsPath(), 'build'); +end; + +class function TPythonLoad.GetPyZip: string; +begin + Result := TPath.Combine(TPath.GetDocumentsPath(), 'build.zip'); +end; + +class procedure TPythonLoad.Configure(const APythonEngine: TPythonEngine; + const ACheckPyLib: boolean); +begin + if ACheckPyLib then begin + var LPyLibFile := TPath.Combine(TPath.GetLibraryPath(), + PYTHON_KNOWN_VERSIONS[PY_KNOWN_VER].DllName); + + if not TFile.Exists(LPyLibFile) then + raise Exception.Create('Python library not found at application''s data folder.' + + #13#10 + + LPyLibFile); + end; + + APythonEngine.UseLastKnownVersion := false; + APythonEngine.ProgramName := TPythonLoad.GetPyBin(); + APythonEngine.PythonHome := TPythonLoad.GetPyHome(); + APythonEngine.RegVersion := PYTHON_KNOWN_VERSIONS[PY_KNOWN_VER].RegVersion; + APythonEngine.DllName := PYTHON_KNOWN_VERSIONS[PY_KNOWN_VER].DllName; + APythonEngine.APIVersion := PYTHON_KNOWN_VERSIONS[PY_KNOWN_VER].APIVersion; +end; + +end. diff --git a/Demos/FMX/Android/PyDistFinder/pydistfinder.dpr b/Demos/FMX/Android/PyDistFinder/pydistfinder.dpr new file mode 100644 index 00000000..01dcf5e3 --- /dev/null +++ b/Demos/FMX/Android/PyDistFinder/pydistfinder.dpr @@ -0,0 +1,223 @@ +(**************************************************************************) +(* *) +(* Module: Unit 'pydistfinder' Copyright (c) 2021 *) +(* *) +(* Lucas Moura Belo - lmbelo *) +(* lucas.belo@live.com *) +(* Brazil *) +(* *) +(* PyScripter *) +(* e-mail: pyscripter@gmail.com *) +(* *) +(* Project pages: https://github.com/Embarcadero/python4delphi *) +(* https://github.com/pyscripter/python4delphi *) +(**************************************************************************) +(* Functionality: Python distribution compressor/uncompressor *) +(* Making Python distribution available for Android *) +(* *) +(**************************************************************************) +(* This source code is distributed with no WARRANTY, for no reason or use.*) +(* Everyone is allowed to use and change this code free for his own tasks *) +(* and projects, as long as this header and its copyright text is intact. *) +(* For changed versions of this code, which are public distributed the *) +(* following additional conditions have to be fullfilled: *) +(* 1) The header has to contain a comment on the change and the author of *) +(* it. *) +(* 2) A copy of the changed source has to be sent to the above E-Mail *) +(* address or my then valid address, if this is possible to the *) +(* author. *) +(* The second condition has the target to maintain an up to date central *) +(* version of the component. If this condition is not acceptable for *) +(* confidential or legal reasons, everyone is free to derive a component *) +(* or to generate a diff file to my or other original sources. *) +(**************************************************************************) + +program pydistfinder; + +{$APPTYPE CONSOLE} + +{$R *.res} + +uses + System.SysUtils, + System.IOUtils, + System.Zip; + +const + ZIP_CMD = 'ZIP'; + UNZIP_CMD = 'UNZIP'; + PYTHON_DIST_FOLDER_NAME = 'dist'; + PYTHON_BUILD_FOLDER_NAME = 'build'; + PYTHON_BUILD_ZIP_FILE_NAME = 'build.zip'; + +function FindPythonDistFolder(AProjectPath: string): string; +begin + //look for dist folder into project's dir + var LTarget := TPath.Combine(AProjectPath, PYTHON_DIST_FOLDER_NAME); + //go up until find dist folder or empty if not exists + while (AProjectPath <> String.Empty) and not TDirectory.Exists(LTarget) do begin + AProjectPath := TDirectory.GetParent(AProjectPath); + LTarget := TPath.Combine(AProjectPath, PYTHON_DIST_FOLDER_NAME) + end; + + if TDirectory.Exists(LTarget) then + Result := LTarget + else + Result := String.Empty; +end; + +function FindPythonTargetFolder(const ATargetPath, APlatform, AWithDebugSymbols: string): string; +begin + Result := String.Empty; + if (not ATargetPath.IsEmpty()) then + WriteLn(Format('Python''s distribution folder found at: %s', [ATargetPath])) + else begin + WriteLn('Python''s distribution folder (dist) not found in project directory tree.'); + ExitCode := 3; + Exit(); + end; + + Result := ATargetPath; + + if (APlatform = 'arm') then begin + Result := TPath.Combine(Result, 'arm'); + if (AWithDebugSymbols = 'true') then begin + Result := TPath.Combine(Result, 'Python3.9d') + end else begin + Result := TPath.Combine(Result, 'Python3.9'); + end; + end else begin + Result := TPath.Combine(Result, 'aarch64'); + if (AWithDebugSymbols = 'true') then begin + Result := TPath.Combine(Result, 'Python3.9d') + end else begin + Result := TPath.Combine(Result, 'Python3.9'); + end; + end; +end; + +procedure ZipFolder(const APath, APlatform, AWithDebugSymbols, AClear: string); +begin + WriteLn(''); + Writeln(Format('Compressing Python distribution', [])); + + var LCompressor := procedure(const ABuildPath, ABuildZipFile: string) + begin + Writeln(Format('Build folder is: %s', [ABuildPath])); + WriteLn(''); + WriteLn('Compressing...'); + WriteLn(''); + TZipFile.ZipDirectoryContents(ABuildZipFile, ABuildPath); + WriteLn(Format('File created: %s', [ABuildZipFile])); + end; + + var LBuildPath := TPath.Combine(APath, PYTHON_BUILD_FOLDER_NAME); + //Check for folder structure + if not TDirectory.Exists(LBuildPath) then begin + WriteLn('Invalid directory structure. Expected directory not found: ' + LBuildPath); + ExitCode := 2; + Exit(); + end; + + var LBuildZipFile := TPath.Combine(APath, PYTHON_BUILD_ZIP_FILE_NAME); + if TFile.Exists(LBuildZipFile) then begin + if (AClear = 'true') then begin + TFile.Delete(LBuildZipFile); + LCompressor(LBuildPath, LBuildZipFile); + end else begin + WriteLn('File already exists: ' + LBuildZipFile); + end; + end else + LCompressor(LBuildPath, LBuildZipFile); +end; + +procedure UnzipFile(const APath, APlatform, AWithDebugSymbols, AClear: string); +begin + WriteLn(''); + Writeln(Format('Decompressing Python distribution', [])); + WriteLn(''); + + var LDecompressor := procedure(const ABuildZipFile, ABuildPath: string) + begin + Writeln(Format('Build zip file is: %s', [ABuildZipFile])); + WriteLn(''); + WriteLn('Decompressing...'); + WriteLn(''); + TZipFile.ExtractZipFile(ABuildZipFile, ABuildPath); + WriteLn(Format('Folder created: %s', [ABuildPath])); + end; + + var LZipFile := TPath.Combine(APath, PYTHON_BUILD_ZIP_FILE_NAME); + //Check for folder structure + if not TFile.Exists(LZipFile) then begin + WriteLn('Invalid directory structure. Expected file not found: ' + LZipFile); + ExitCode := 2; + Exit(); + end; + + var LBuildPath := TPath.Combine(APath, PYTHON_BUILD_FOLDER_NAME); + if TDirectory.Exists(LBuildPath) then begin + if (AClear = 'true') then begin + TDirectory.Delete(LBuildPath, true); + LDecompressor(LZipFile, LBuildPath); + end else begin + WriteLn('Folder already exists: ' + LBuildPath); + end; + end else + LDecompressor(LZipFile, LBuildPath); +end; + +procedure MakeDist(const ACmd, AProjectPath, APlatform, AWithDebugSymbols, AClear: string); +begin + WriteLn(''); + Writeln(Format('Command set to: %s', [ACmd])); + Writeln(Format('Platform set to: %s', [APlatform])); + Writeln(Format('With debug symbols set to: %s', [AWithDebugSymbols])); + Writeln(Format('Clear if exists set to: %s', [AClear])); + WriteLn(''); + + var LPath := FindPythonTargetFolder(FindPythonDistFolder(AProjectPath), + APlatform, AWithDebugSymbols); + + if LPath.IsEmpty then + Exit(); + + if (ACmd = ZIP_CMD) then + ZipFolder(LPath, APlatform, AWithDebugSymbols, AClear) + else if (ACmd = UNZIP_CMD) then + UnzipFile(LPath, APlatform, AWithDebugSymbols, AClear); +end; + +begin + try + WriteLn(''); + WriteLn(''); + + WriteLn('#######################################################'); + WriteLn('# #'); + WriteLn('# Python4Delphi - Android #'); + WriteLn('# #'); + WriteLn('#######################################################'); + WriteLn('# #'); + WriteLn('# #'); + WriteLn('# Python distribution #'); + WriteLn('# finder #'); + WriteLn('# #'); + WriteLn('# #'); + WriteLn('#######################################################'); + + WriteLn(''); + WriteLn(''); + + if (ParamCount < 4) or (ParamCount > 5) then begin + WriteLn('Invalid parameters'); + ExitCode := 1; + end else if ParamCount = 4 then + MakeDist(ParamStr(1), ParamStr(2), ParamStr(3), ParamStr(4), ParamStr(5)) + else + MakeDist(ParamStr(1), ParamStr(2), ParamStr(3), ParamStr(4), 'false'); + except + on E: Exception do + Writeln(E.ClassName, ': ', E.Message); + end; +end. diff --git a/Demos/FMX/Android/PyDistFinder/pydistfinder.dproj b/Demos/FMX/Android/PyDistFinder/pydistfinder.dproj new file mode 100644 index 00000000..008421e8 --- /dev/null +++ b/Demos/FMX/Android/PyDistFinder/pydistfinder.dproj @@ -0,0 +1,1023 @@ + + + {6AF6B9EC-BBEB-4C74-9FEC-0FFFD760DBB8} + 19.2 + None + True + Release + Win32 + 1 + Console + pydistfinder.dpr + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + .\$(Platform)\$(Config) + .\bin + false + false + false + false + false + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + pydistfinder + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + + + DBXSqliteDriver;RESTComponents;DBXInterBaseDriver;emsclientfiredac;tethering;DataSnapFireDAC;bindcompfmx;fmx;FireDACIBDriver;FireDACDBXDriver;dbexpress;IndyCore;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;soapserver;bindengine;CloudService;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;soaprtl;DbxCommonDriver;xmlrtl;soapmidas;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_192x192.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png + android-support-v4.dex.jar;cloud-messaging.dex.jar;com-google-android-gms.play-services-ads-base.17.2.0.dex.jar;com-google-android-gms.play-services-ads-identifier.16.0.0.dex.jar;com-google-android-gms.play-services-ads-lite.17.2.0.dex.jar;com-google-android-gms.play-services-ads.17.2.0.dex.jar;com-google-android-gms.play-services-analytics-impl.16.0.8.dex.jar;com-google-android-gms.play-services-analytics.16.0.8.dex.jar;com-google-android-gms.play-services-base.16.0.1.dex.jar;com-google-android-gms.play-services-basement.16.2.0.dex.jar;com-google-android-gms.play-services-gass.17.2.0.dex.jar;com-google-android-gms.play-services-identity.16.0.0.dex.jar;com-google-android-gms.play-services-maps.16.1.0.dex.jar;com-google-android-gms.play-services-measurement-base.16.4.0.dex.jar;com-google-android-gms.play-services-measurement-sdk-api.16.4.0.dex.jar;com-google-android-gms.play-services-stats.16.0.1.dex.jar;com-google-android-gms.play-services-tagmanager-v4-impl.16.0.8.dex.jar;com-google-android-gms.play-services-tasks.16.0.1.dex.jar;com-google-android-gms.play-services-wallet.16.0.1.dex.jar;com-google-firebase.firebase-analytics.16.4.0.dex.jar;com-google-firebase.firebase-common.16.1.0.dex.jar;com-google-firebase.firebase-iid-interop.16.0.1.dex.jar;com-google-firebase.firebase-iid.17.1.1.dex.jar;com-google-firebase.firebase-measurement-connector.17.0.1.dex.jar;com-google-firebase.firebase-messaging.17.5.0.dex.jar;fmx.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar + + + DBXSqliteDriver;RESTComponents;DBXInterBaseDriver;emsclientfiredac;tethering;DataSnapFireDAC;bindcompfmx;fmx;FireDACIBDriver;FireDACDBXDriver;dbexpress;IndyCore;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;soapserver;bindengine;CloudService;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;soaprtl;DbxCommonDriver;xmlrtl;soapmidas;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_192x192.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png + android-support-v4.dex.jar;cloud-messaging.dex.jar;com-google-android-gms.play-services-ads-base.17.2.0.dex.jar;com-google-android-gms.play-services-ads-identifier.16.0.0.dex.jar;com-google-android-gms.play-services-ads-lite.17.2.0.dex.jar;com-google-android-gms.play-services-ads.17.2.0.dex.jar;com-google-android-gms.play-services-analytics-impl.16.0.8.dex.jar;com-google-android-gms.play-services-analytics.16.0.8.dex.jar;com-google-android-gms.play-services-base.16.0.1.dex.jar;com-google-android-gms.play-services-basement.16.2.0.dex.jar;com-google-android-gms.play-services-gass.17.2.0.dex.jar;com-google-android-gms.play-services-identity.16.0.0.dex.jar;com-google-android-gms.play-services-maps.16.1.0.dex.jar;com-google-android-gms.play-services-measurement-base.16.4.0.dex.jar;com-google-android-gms.play-services-measurement-sdk-api.16.4.0.dex.jar;com-google-android-gms.play-services-stats.16.0.1.dex.jar;com-google-android-gms.play-services-tagmanager-v4-impl.16.0.8.dex.jar;com-google-android-gms.play-services-tasks.16.0.1.dex.jar;com-google-android-gms.play-services-wallet.16.0.1.dex.jar;com-google-firebase.firebase-analytics.16.4.0.dex.jar;com-google-firebase.firebase-common.16.1.0.dex.jar;com-google-firebase.firebase-iid-interop.16.0.1.dex.jar;com-google-firebase.firebase-iid.17.1.1.dex.jar;com-google-firebase.firebase-measurement-connector.17.0.1.dex.jar;com-google-firebase.firebase-messaging.17.5.0.dex.jar;fmx.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar + + + DBXSqliteDriver;RESTComponents;fmxase;DBXInterBaseDriver;emsclientfiredac;tethering;DataSnapFireDAC;bindcompfmx;fmx;FireDACIBDriver;FireDACDBXDriver;dbexpress;IndyCore;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;soapserver;bindengine;CloudService;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;soaprtl;DbxCommonDriver;xmlrtl;soapmidas;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) + + + DBXSqliteDriver;RESTComponents;fmxase;DBXInterBaseDriver;emsclientfiredac;tethering;DataSnapFireDAC;bindcompfmx;fmx;FireDACIBDriver;FireDACDBXDriver;dbexpress;IndyCore;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;soapserver;bindengine;CloudService;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;soaprtl;DbxCommonDriver;xmlrtl;soapmidas;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) + + + RESTComponents;emsclientfiredac;DataSnapFireDAC;FireDACADSDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;inetdb;emsedge;fmx;FireDACIBDriver;dbexpress;IndyCore;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;DataSnapConnectors;soapserver;bindengine;CloudService;FireDACOracleDriver;FireDACMySQLDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;IndySystem;FireDACDb2Driver;FireDACInfxDriver;FireDAC;emshosting;FireDACSqliteDriver;FireDACPgDriver;FireDACASADriver;FireDACTDataDriver;soaprtl;DbxCommonDriver;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;rtl;emsserverresource;DbxClientDriver;CustomIPTransport;bindcomp;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;dbrtl;IndyProtocols;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage) + + + DBXSqliteDriver;RESTComponents;fmxase;DBXInterBaseDriver;emsclientfiredac;tethering;DataSnapFireDAC;FireDACMSSQLDriver;bindcompfmx;DBXOracleDriver;inetdb;fmx;FireDACIBDriver;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;soapserver;bindengine;DBXMySQLDriver;CloudService;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;FireDACPgDriver;FireDACASADriver;FireDACTDataDriver;soaprtl;DbxCommonDriver;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;FireDACDSDriver;rtl;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;bindcomp;DBXInformixDriver;IndyIPClient;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage) + true + + + DBXSqliteDriver;RESTComponents;fmxase;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;bindcompvclsmp;emsclientfiredac;tethering;svnui;DataSnapFireDAC;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;svn;DBXOracleDriver;inetdb;emsedge;fmx;FireDACIBDriver;fmxdae;vcledge;FireDACDBXDriver;dbexpress;IndyCore;vclx;Python;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;DataSnapConnectors;VCLRESTComponents;soapserver;vclie;bindengine;DBXMySQLDriver;CloudService;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;vcl;IndyIPServer;DBXSybaseASEDriver;IndySystem;FireDACDb2Driver;bindcompvclwinx;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;FireDAC;emshosting;FireDACSqliteDriver;FireDACPgDriver;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;soaprtl;DbxCommonDriver;PythonVcl;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;vclwinx;FireDACDSDriver;rtl;emsserverresource;DbxClientDriver;PythonFmx;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage) + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + DBXSqliteDriver;RESTComponents;fmxase;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;bindcompvclsmp;emsclientfiredac;tethering;DataSnapFireDAC;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;DBXOracleDriver;inetdb;emsedge;fmx;FireDACIBDriver;fmxdae;vcledge;FireDACDBXDriver;dbexpress;IndyCore;vclx;Python;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;DataSnapConnectors;VCLRESTComponents;soapserver;vclie;bindengine;DBXMySQLDriver;CloudService;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;vcl;IndyIPServer;DBXSybaseASEDriver;IndySystem;FireDACDb2Driver;bindcompvclwinx;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;FireDAC;emshosting;FireDACSqliteDriver;FireDACPgDriver;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;soaprtl;DbxCommonDriver;PythonVcl;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;vclwinx;FireDACDSDriver;rtl;emsserverresource;DbxClientDriver;PythonFmx;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage) + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + false + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + 1033 + (None) + + + + MainSource + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Application + + + + pydistfinder.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + + + true + + + + + true + + + + + true + + + + + pydistfinder.exe + true + + + + + pydistfinder.exe + true + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + Contents\MacOS + 1 + .framework + + + Contents\MacOS + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + Contents\Resources\StartUp\ + 0 + + + Contents\Resources\StartUp\ + 0 + + + 0 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen + 64 + + + ..\$(PROJECTNAME).launchscreen + 64 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + Contents + 1 + + + Contents + 1 + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + Contents\MacOS + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + False + False + False + False + False + False + True + False + + + 12 + + + + + diff --git a/Demos/FMX/Android/dist/aarch64/Python3.9/build.zip b/Demos/FMX/Android/dist/aarch64/Python3.9/build.zip new file mode 100644 index 00000000..6bc0df08 Binary files /dev/null and b/Demos/FMX/Android/dist/aarch64/Python3.9/build.zip differ diff --git a/Demos/FMX/Android/dist/aarch64/Python3.9d/build.zip b/Demos/FMX/Android/dist/aarch64/Python3.9d/build.zip new file mode 100644 index 00000000..8951bb31 Binary files /dev/null and b/Demos/FMX/Android/dist/aarch64/Python3.9d/build.zip differ diff --git a/Demos/FMX/Android/dist/arm/Python3.9/build.zip b/Demos/FMX/Android/dist/arm/Python3.9/build.zip new file mode 100644 index 00000000..61a9aae3 Binary files /dev/null and b/Demos/FMX/Android/dist/arm/Python3.9/build.zip differ diff --git a/Demos/FMX/Android/dist/arm/Python3.9d/build.zip b/Demos/FMX/Android/dist/arm/Python3.9d/build.zip new file mode 100644 index 00000000..34e76d58 Binary files /dev/null and b/Demos/FMX/Android/dist/arm/Python3.9d/build.zip differ diff --git a/Source/Definition.Inc b/Source/Definition.Inc index 8de97164..9f6f310e 100644 --- a/Source/Definition.Inc +++ b/Source/Definition.Inc @@ -202,6 +202,10 @@ {$ENDIF UNIX} {$ENDIF FPC} +{$IFDEF CPU64BITS} + {$DEFINE CPUX64} +{$ENDIF} + {$IFDEF DELPHIXE_OR_HIGHER} {$DEFINE EXTENDED_RTTI} {$ENDIF DELPHIXE_OR_HIGHER} diff --git a/Source/MethodCallBack.pas b/Source/MethodCallBack.pas index 1d242308..d4f0fbd1 100644 --- a/Source/MethodCallBack.pas +++ b/Source/MethodCallBack.pas @@ -17,6 +17,7 @@ (* Morgan Martinet (p4d@mmm-experts.com) *) (* Samuel Iseli (iseli@vertec.ch) *) (* Andrey Gruzdev (andrey.gruzdev@gmail.com) *) +(* Lucas Belo (lucas.belo@live.com) *) (**************************************************************************) (* This source code is distributed with no WARRANTY, for no reason or use.*) (* Everyone is allowed to use and change this code free, as long as this *) @@ -32,7 +33,7 @@ interface uses SysUtils; type - TCallType = (ctSTDCALL, ctCDECL); + TCallType = (ctSTDCALL, ctCDECL, ctARMSTD); TCallBack = procedure of object; function GetCallBack( self: TObject; method: Pointer; @@ -258,6 +259,34 @@ function CodeMemPageCount: integer; end; end; +procedure DeleteCallBack( Proc: Pointer); +begin + FreeCodeMem(Proc); +end; + +procedure FreeCallBacks; +var + page, nextpage: PCodeMemPage; +begin + // free each allocated page + page := CodeMemPages; + while page <> nil do + begin + nextpage := page^.Next; + + // free the memory + {$IFDEF MSWINDOWS} + VirtualFree(page, 0, MEM_RELEASE); + {$ELSE} + //FreeMem(page); + munmap(page,PageSize); + {$ENDIF} + + page := nextpage; + end; + CodeMemPages := nil; +end; + function GetOfObjectCallBack( CallBack: TCallBack; argnum: Integer; calltype: TCallType): Pointer; begin @@ -266,15 +295,17 @@ function GetOfObjectCallBack( CallBack: TCallBack; argnum, calltype); end; -{$IFDEF CPUX64} -{$DEFINE 64_BIT_CALLBACK} -{$ELSE} -{$IFDEF MACOS} -{$DEFINE ALIGNED_32_BIT_CALLBACK} -{$ELSE} -{$DEFINE SIMPLE_32_BIT_CALLBACK} -{$ENDIF MACOS} -{$ENDIF CPUX64} +{$IFNDEF CPUARM} + {$IFDEF CPUX64} + {$DEFINE 64_BIT_CALLBACK} + {$ELSE} + {$IFDEF MACOS} + {$DEFINE ALIGNED_32_BIT_CALLBACK} + {$ELSE} + {$DEFINE SIMPLE_32_BIT_CALLBACK} + {$ENDIF MACOS} + {$ENDIF CPUX64} +{$ENDIF CPUARM} {$IFDEF SIMPLE_32_BIT_CALLBACK} // win32 inplementation @@ -565,35 +596,126 @@ function GetCallBack( self: TObject; method: Pointer; end; {$ENDIF} -procedure DeleteCallBack( Proc: Pointer); +{$IFDEF CPUARM32} +function GetCallBack(Self: TObject; Method: Pointer; ArgNum: Integer; + CallType: TCallType): Pointer; +const + S1: array[0..123] of byte = ( +//big-endian +//offset : + {+ 0:} $80, $40, $2d, $e9, // push {r7, lr} + {+ 4:} $0d, $70, $a0, $e1, // mov r7, sp + {+ 8:} $1e, $04, $2d, $e9, // push {r1, r2, r3, r4, sl} + {+ c:} $5c, $40, $9f, $e5, // ldr r4, [pc, #92] ; 70 + {+ 10:} $00, $00, $54, $e3, // cmp r4, #0 + {+ 14:} $04, $d0, $4d, $c0, // subgt sp, sp, r4 + {+ 18:} $04, $50, $a0, $c1, // movgt r5, r4 + {+ 1c:} $04, $50, $85, $c2, // addgt r5, r5, #4 + {+ 20:} $04, $60, $a0, $c1, // movgt r6, r4 + {+ 24:} $04, $60, $46, $c2, // subgt r6, r6, #4 + {+ 28:} $09, $00, $00, $cb, // blgt 54 + {+ 2c:} $0f, $00, $2d, $e9, // push {r0, r1, r2, r3} + {+ 30:} $3c, $00, $9f, $e5, // ldr r0, [pc, #60] ; 74 + {+ 34:} $0e, $00, $bd, $e8, // pop {r1, r2, r3} + {+ 38:} $38, $a0, $9f, $e5, // ldr sl, [pc, #56] ; 78 + {+ 3c:} $3a, $ff, $2f, $e1, // blx sl + {+ 40:} $00, $00, $54, $e3, // cmp r4, #0 + {+ 44:} $04, $d0, $8d, $c0, // addgt sp, sp, r4 + {+ 48:} $04, $40, $9d, $e4, // pop {r4} ; (ldr r4, [sp], #4) + {+ 4c:} $1e, $04, $bd, $e8, // pop {r1, r2, r3, r4, sl} + {+ 50:} $80, $80, $bd, $e8, // pop {r7, pc} +//offset + 00000054 : + {+ 54:} $05, $a0, $97, $e7, // ldr sl, [r7, r5] + {+ 58:} $06, $a0, $8d, $e7, // str sl, [sp, r6] + {+ 5c:} $04, $50, $45, $e2, // sub r5, r5, #4 + {+ 60:} $04, $60, $46, $e2, // sub r6, r6, #4 + {+ 64:} $00, $00, $56, $e3, // cmp r6, #0 + {+ 68:} $f9, $ff, $ff, $aa, // bge 54 + {+ 6c:} $1e, $ff, $2f, $e1, // bx lr +//offset + 00000070 + {+ 70:} $00, $00, $00, $00, // stack space for stack parameters + {+ 74:} $00, $00, $00, $00, // Self + {+ 78:} $00, $00, $00, $00 // Method +); +const + ARM_INSTRUCTION_SIZE = 4; + ARM_ARGUMENT_COUNT_IN_REGISTERS = 4; +var + P, Q: PByte; + LLiteralPool: TArray; + I: Integer; begin - FreeCodeMem(Proc); + GetCodeMem(Q, SizeOf(S1)); + P := Q; + Move(S1, P^, SizeOf(S1)); + + LLiteralPool := TArray.Create( + Pointer((ArgNum - ARM_ARGUMENT_COUNT_IN_REGISTERS) * ARM_INSTRUCTION_SIZE), + Self, + Method); + + Inc(P, Length(S1) - (Length(LLiteralPool) * SizeOf(pointer))); + for I := Low(LLiteralPool) to High(LLiteralPool) do begin + Move(LLiteralPool[I], P^, SizeOf(pointer)); + Inc(P, SizeOf(pointer)); + end; + + Result := Pointer(Q); //set arm mode end; +{$ENDIF CPUARM32} -procedure FreeCallBacks; +{$IFDEF CPUARM64} +function GetCallBack(Self: TObject; Method: Pointer; ArgNum: Integer; + CallType: TCallType): Pointer; +const + S1: array[0..79] of byte = ( +//big-endian +//offset <_start>: + $fd, $7b, $bf, $a9, // stp x29, x30, [sp, #-16]! + $fd, $03, $00, $91, // mov x29, sp + $e0, $07, $bf, $a9, // stp x0, x1, [sp, #-16]! + $e2, $0f, $bf, $a9, // stp x2, x3, [sp, #-16]! + $e4, $17, $bf, $a9, // stp x4, x5, [sp, #-16]! + $e6, $1f, $bf, $a9, // stp x6, x7, [sp, #-16]! + $0a, $00, $00, $10, // adr x10, #0 <_start+0x18> + $40, $15, $40, $f9, // ldr x0, [x10, #40] + $49, $19, $40, $f9, // ldr x9, [x10, #48] + $e7, $2f, $c1, $a8, // ldp x7, x11, [sp], #16 + $e5, $1b, $c1, $a8, // ldp x5, x6, [sp], #16 + $e3, $13, $c1, $a8, // ldp x3, x4, [sp], #16 + $e1, $0b, $c1, $a8, // ldp x1, x2, [sp], #16 + $20, $01, $3f, $d6, // blr x9 + $fd, $7b, $c1, $a8, // ldp x29, x30, [sp], #16 + $c0, $03, $5f, $d6, // ret + $00, $00, $00, $00, // .word 0x00000000 //Self + $00, $00, $00, $00, // .word 0x00000000 + $00, $00, $00, $00, // .word 0x00000000 //Method + $00, $00, $00, $00 // .word 0x00000000 +); var - page, nextpage: PCodeMemPage; + P, Q: PByte; + LLiteralPool: TArray; + I: Integer; begin - // free each allocated page - page := CodeMemPages; - while page <> nil do - begin - nextpage := page^.Next; + GetCodeMem(Q, SizeOf(S1)); + P := Q; + Move(S1, P^, SizeOf(S1)); - // free the memory - {$IFDEF MSWINDOWS} - VirtualFree(page, 0, MEM_RELEASE); - {$ELSE} - //FreeMem(page); - munmap(page,PageSize); - {$ENDIF} + LLiteralPool := TArray.Create(Self, Method); - page := nextpage; + Inc(P, Length(S1) - (Length(LLiteralPool) * SizeOf(pointer))); + for I := Low(LLiteralPool) to High(LLiteralPool) do begin + Move(LLiteralPool[I], P^, SizeOf(pointer)); + Inc(P, SizeOf(pointer)); end; - CodeMemPages := nil; + + Result := Pointer(Q); //set arm mode end; +{$ENDIF CPUARM64} initialization + finalization FreeCallBacks; + end. diff --git a/Source/PythonEngine.pas b/Source/PythonEngine.pas index b186569c..2780ece4 100644 --- a/Source/PythonEngine.pas +++ b/Source/PythonEngine.pas @@ -95,7 +95,8 @@ interface {$IF not Defined(FPC) and (CompilerVersion >= 23)} const {$IF CompilerVersion >= 33} - pidSupportedPlatforms = pidWin32 or pidWin64 or pidOSX32 or pidOSX64 or pidLinux64; + pidSupportedPlatforms = pidWin32 or pidWin64 or pidOSX32 or pidOSX64 + or pidLinux64 or pidAndroid32Arm or pidAndroid64Arm; {$ELSE} pidSupportedPlatforms = pidWin32 or pidWin64 or pidOSX32; {$IFEND} @@ -154,6 +155,18 @@ TPythonVersionProp = record (DllName: 'libpython3.10.dylib'; RegVersion: '3.10'; APIVersion: 1013) ); {$ENDIF} +{$IFDEF ANDROID} + PYTHON_KNOWN_VERSIONS: array[1..1] of TPythonVersionProp = + {$IFDEF DEBUG} + ( + (DllName: 'libpython3.9d.so'; RegVersion: '3.9'; APIVersion: 1013) + ); + {$ELSE} + ( + (DllName: 'libpython3.9.so'; RegVersion: '3.9'; APIVersion: 1013) + ); + {$ENDIF} +{$ENDIF} COMPILED_FOR_PYTHON_VERSION_INDEX = High(PYTHON_KNOWN_VERSIONS); @@ -177,6 +190,13 @@ TPythonVersionProp = record Py_NE = 3; Py_GT = 4; Py_GE = 5; + + {$IFDEF CPUARM} + DEFAULT_CALLBACK_TYPE: TCallType = TCallType.ctARMSTD; + {$ELSE} + DEFAULT_CALLBACK_TYPE: TCallType = TCallType.ctCDECL; + {$ENDIF CPUARM} + type // Delphi equivalent used by TPyObject TRichComparisonOpcode = (pyLT, pyLE, pyEQ, pyNE, pyGT, pyGE); @@ -204,6 +224,7 @@ TPythonVersionProp = record WCharTString = UnicodeString; {$ENDIF} + const { Type flags (tp_flags) @@ -402,7 +423,7 @@ TPythonVersionProp = record newfunc = function ( subtype: PPyTypeObject; args, kwds : PPyObject) : PPyObject; cdecl; allocfunc = function ( self: PPyTypeObject; nitems : NativeInt) : PPyObject; cdecl; - PyNumberMethods = {$IFNDEF CPUX64}packed{$ENDIF} record + PyNumberMethods = {$IFDEF CPUX86}packed{$ENDIF} record nb_add : binaryfunc; nb_subtract : binaryfunc; nb_multiply : binaryfunc; @@ -442,7 +463,7 @@ TPythonVersionProp = record end; PPyNumberMethods = ^PyNumberMethods; - PySequenceMethods = {$IFNDEF CPUX64}packed{$ENDIF} record + PySequenceMethods = {$IFDEF CPUX86}packed{$ENDIF} record sq_length : lenfunc; sq_concat : binaryfunc; sq_repeat : ssizeargfunc; @@ -456,37 +477,37 @@ TPythonVersionProp = record end; PPySequenceMethods = ^PySequenceMethods; - PyMappingMethods = {$IFNDEF CPUX64}packed{$ENDIF} record + PyMappingMethods = {$IFDEF CPUX86}packed{$ENDIF} record mp_length : lenfunc; mp_subscript : binaryfunc; mp_ass_subscript : objobjargproc; end; PPyMappingMethods = ^PyMappingMethods; - Py_complex = {$IFNDEF CPUX64}packed{$ENDIF} record + Py_complex = {$IFDEF CPUX86}packed{$ENDIF} record real : double; imag : double; end; - PyObject = {$IFNDEF CPUX64}packed{$ENDIF} record + PyObject = {$IFDEF CPUX86}packed{$ENDIF} record ob_refcnt: NativeInt; ob_type: PPyTypeObject; end; - _frozen = {$IFNDEF CPUX64}packed{$ENDIF} record + _frozen = {$IFDEF CPUX86}packed{$ENDIF} record name : PAnsiChar; code : PByte; size : Integer; end; - PySliceObject = {$IFNDEF CPUX64}packed{$ENDIF} record + PySliceObject = {$IFDEF CPUX86}packed{$ENDIF} record ob_refcnt: NativeInt; ob_type: PPyTypeObject; start, stop, step: PPyObject; end; PPyMethodDef = ^PyMethodDef; - PyMethodDef = {$IFNDEF CPUX64}packed{$ENDIF} record + PyMethodDef = {$IFDEF CPUX86}packed{$ENDIF} record ml_name: PAnsiChar; ml_meth: PyCFunction; ml_flags: Integer; @@ -495,11 +516,11 @@ TPythonVersionProp = record // structmember.h PPyMemberDef = ^PyMemberDef; - PyMemberDef = {$IFNDEF CPUX64}packed{$ENDIF} record + PyMemberDef = {$IFDEF CPUX86}packed{$ENDIF} record name : PAnsiChar; _type : integer; offset : NativeInt; - flags : integer; + flags : Integer; doc : PAnsiChar; end; @@ -511,7 +532,7 @@ TPythonVersionProp = record setter = function ( obj, value : PPyObject; context : Pointer) : integer; cdecl; PPyGetSetDef = ^PyGetSetDef; - PyGetSetDef = {$IFNDEF CPUX64}packed{$ENDIF} record + PyGetSetDef = {$IFDEF CPUX86}packed{$ENDIF} record name : PAnsiChar; get : getter; _set : setter; @@ -522,7 +543,7 @@ TPythonVersionProp = record wrapperfunc = function (self, args: PPyObject; wrapped : Pointer) : PPyObject; cdecl; pwrapperbase = ^wrapperbase; - wrapperbase = {$IFNDEF CPUX64}packed{$ENDIF} record + wrapperbase = {$IFDEF CPUX86}packed{$ENDIF} record name : PAnsiChar; wrapper : wrapperfunc; doc : PAnsiChar; @@ -537,7 +558,7 @@ TPythonVersionProp = record } PPyDescrObject = ^PyDescrObject; - PyDescrObject = {$IFNDEF CPUX64}packed{$ENDIF} record + PyDescrObject = {$IFDEF CPUX86}packed{$ENDIF} record // Start of the Head of an object ob_refcnt : NativeInt; ob_type : PPyTypeObject; @@ -547,7 +568,7 @@ TPythonVersionProp = record end; PPyMethodDescrObject = ^PyMethodDescrObject; - PyMethodDescrObject = {$IFNDEF CPUX64}packed{$ENDIF} record + PyMethodDescrObject = {$IFDEF CPUX86}packed{$ENDIF} record // Start of PyDescr_COMMON // Start of the Head of an object ob_refcnt : NativeInt; @@ -560,7 +581,7 @@ TPythonVersionProp = record end; PPyMemberDescrObject = ^PyMemberDescrObject; - PyMemberDescrObject = {$IFNDEF CPUX64}packed{$ENDIF} record + PyMemberDescrObject = {$IFDEF CPUX86}packed{$ENDIF} record // Start of PyDescr_COMMON // Start of the Head of an object ob_refcnt : NativeInt; @@ -573,7 +594,7 @@ TPythonVersionProp = record end; PPyGetSetDescrObject = ^PyGetSetDescrObject; - PyGetSetDescrObject = {$IFNDEF CPUX64}packed{$ENDIF} record + PyGetSetDescrObject = {$IFDEF CPUX86}packed{$ENDIF} record // Start of PyDescr_COMMON // Start of the Head of an object ob_refcnt : NativeInt; @@ -586,7 +607,7 @@ TPythonVersionProp = record end; PPyWrapperDescrObject = ^PyWrapperDescrObject; - PyWrapperDescrObject = {$IFNDEF CPUX64}packed{$ENDIF} record + PyWrapperDescrObject = {$IFDEF CPUX86}packed{$ENDIF} record // Start of PyDescr_COMMON // Start of the Head of an object ob_refcnt : NativeInt; @@ -600,7 +621,7 @@ TPythonVersionProp = record end; PPyModuleDef_Base = ^PyModuleDef_Base; - PyModuleDef_Base = {$IFNDEF CPUX64}packed{$ENDIF} record + PyModuleDef_Base = {$IFDEF CPUX86}packed{$ENDIF} record // Start of the Head of an object ob_refcnt : NativeInt; ob_type : PPyTypeObject; @@ -611,7 +632,7 @@ TPythonVersionProp = record end; PPyModuleDef = ^PyModuleDef; - PyModuleDef = {$IFNDEF CPUX64}packed{$ENDIF} record + PyModuleDef = {$IFDEF CPUX86}packed{$ENDIF} record m_base : PyModuleDef_Base; m_name : PAnsiChar; m_doc : PAnsiChar; @@ -625,7 +646,7 @@ TPythonVersionProp = record // object.h - PyTypeObject = {$IFNDEF CPUX64}packed{$ENDIF} record + PyTypeObject = {$IFDEF CPUX86}packed{$ENDIF} record ob_refcnt: NativeInt; ob_type: PPyTypeObject; ob_size: NativeInt; // Number of items in variable part @@ -724,7 +745,7 @@ TPythonVersionProp = record // Parse tree node interface PNode = ^node; - node = {$IFNDEF CPUX64}packed{$ENDIF} record + node = {$IFDEF CPUX86}packed{$ENDIF} record n_type : smallint; n_str : PAnsiChar; n_lineno : integer; @@ -734,7 +755,7 @@ TPythonVersionProp = record end; PPyCompilerFlags = ^PyCompilerFlags; - PyCompilerFlags = {$IFNDEF CPUX64}packed{$ENDIF} record + PyCompilerFlags = {$IFDEF CPUX86}packed{$ENDIF} record flags : integer; cf_feature_version : integer; //added in Python 3.8 end; @@ -766,7 +787,7 @@ TPythonVersionProp = record { # of bytes for year, month, day, hour, minute, second, and usecond. } _PyDateTime_DATETIME_DATASIZE = 10; type - PyDateTime_Delta = {$IFNDEF CPUX64}packed{$ENDIF} record + PyDateTime_Delta = {$IFDEF CPUX86}packed{$ENDIF} record // Start of the Head of an object ob_refcnt : NativeInt; ob_type : PPyTypeObject; @@ -778,7 +799,7 @@ TPythonVersionProp = record end; PPyDateTime_Delta = ^PyDateTime_Delta; - PyDateTime_TZInfo = {$IFNDEF CPUX64}packed{$ENDIF} record // a pure abstract base clase + PyDateTime_TZInfo = {$IFDEF CPUX86}packed{$ENDIF} record // a pure abstract base clase // Start of the Head of an object ob_refcnt : NativeInt; ob_type : PPyTypeObject; @@ -800,7 +821,7 @@ TPythonVersionProp = record * convenient to cast to, when getting at the hastzinfo member of objects * starting with _PyTZINFO_HEAD. *} - _PyDateTime_BaseTZInfo = {$IFNDEF CPUX64}packed{$ENDIF} record + _PyDateTime_BaseTZInfo = {$IFDEF CPUX86}packed{$ENDIF} record // Start of _PyTZINFO_HEAD // Start of the Head of an object ob_refcnt : NativeInt; @@ -823,7 +844,7 @@ TPythonVersionProp = record unsigned char data[_PyDateTime_TIME_DATASIZE]; } - _PyDateTime_BaseTime = {$IFNDEF CPUX64}packed{$ENDIF} record // hastzinfo false + _PyDateTime_BaseTime = {$IFDEF CPUX86}packed{$ENDIF} record // hastzinfo false // Start of _PyDateTime_TIMEHEAD // Start of _PyTZINFO_HEAD // Start of the Head of an object @@ -838,7 +859,7 @@ TPythonVersionProp = record end; _PPyDateTime_BaseTime = ^_PyDateTime_BaseTime; - PyDateTime_Time = {$IFNDEF CPUX64}packed{$ENDIF} record // hastzinfo true + PyDateTime_Time = {$IFDEF CPUX86}packed{$ENDIF} record // hastzinfo true // Start of _PyDateTime_TIMEHEAD // Start of _PyTZINFO_HEAD // Start of the Head of an object @@ -861,7 +882,7 @@ TPythonVersionProp = record * the plain date type is a base class for datetime, so it must also have * a hastzinfo member (although it's unused there). *} - PyDateTime_Date = {$IFNDEF CPUX64}packed{$ENDIF} record + PyDateTime_Date = {$IFDEF CPUX86}packed{$ENDIF} record // Start of _PyTZINFO_HEAD // Start of the Head of an object ob_refcnt : NativeInt; @@ -880,7 +901,7 @@ TPythonVersionProp = record unsigned char data[_PyDateTime_DATETIME_DATASIZE]; } - _PyDateTime_BaseDateTime = {$IFNDEF CPUX64}packed{$ENDIF} record // hastzinfo false + _PyDateTime_BaseDateTime = {$IFDEF CPUX86}packed{$ENDIF} record // hastzinfo false // Start of _PyTZINFO_HEAD // Start of the Head of an object ob_refcnt : NativeInt; @@ -893,7 +914,7 @@ TPythonVersionProp = record end; _PPyDateTime_BaseDateTime = ^_PyDateTime_BaseDateTime; - PyDateTime_DateTime = {$IFNDEF CPUX64}packed{$ENDIF} record // hastzinfo true + PyDateTime_DateTime = {$IFDEF CPUX86}packed{$ENDIF} record // hastzinfo true // Start of _PyDateTime_DATETIMEHEAD // Start of _PyTZINFO_HEAD // Start of the Head of an object @@ -1585,10 +1606,13 @@ TPythonInterface=class(TDynamicDll) Py_GetCopyright : function : PAnsiChar; cdecl; Py_GetExecPrefix : function : PAnsiChar; cdecl; Py_GetPath : function : PAnsiChar; cdecl; + + Py_SetPath : procedure (path: PWCharT); cdecl; + Py_SetPythonHome : procedure (home : PWCharT); cdecl; Py_GetPythonHome : function : PWCharT; cdecl; Py_GetPrefix : function : PAnsiChar; cdecl; - Py_GetProgramName : function : PAnsiChar; cdecl; + Py_GetProgramName : function : PWCharT; cdecl; PyParser_SimpleParseStringFlags : function ( str : PAnsiChar; start, flags : Integer) : PNode; cdecl; PyNode_Free : procedure( n : PNode ); cdecl; @@ -1771,6 +1795,7 @@ TPythonEngine = class(TPythonInterface) FAutoFinalize: Boolean; FProgramName: WCharTString; FPythonHome: WCharTString; + FPythonPath: WCharTString; FInitThreads: Boolean; FOnPathInitialization: TPathInitializationEvent; FOnSysPathInit: TSysPathInitEvent; @@ -1792,6 +1817,8 @@ TPythonEngine = class(TPythonInterface) FPyDateTime_DateTimeTZType: PPyObject; function GetPythonHome: UnicodeString; function GetProgramName: UnicodeString; + function GetPythonPath: UnicodeString; + procedure SetPythonPath(const Value: UnicodeString); protected procedure Initialize; @@ -1911,6 +1938,7 @@ TPythonEngine = class(TPythonInterface) property IOPythonModule: TObject read FIOPythonModule; {TPythonModule} property PythonHome: UnicodeString read GetPythonHome write SetPythonHome; property ProgramName: UnicodeString read GetProgramName write SetProgramName; + property PythonPath: UnicodeString read GetPythonPath write SetPythonPath; published property AutoFinalize: Boolean read FAutoFinalize write FAutoFinalize default True; property VenvPythonExe: string read FVenvPythonExe write FVenvPythonExe; @@ -3557,6 +3585,7 @@ procedure TPythonInterface.MapDll; Py_GetCopyright := Import('Py_GetCopyright'); Py_GetExecPrefix := Import('Py_GetExecPrefix'); Py_GetPath := Import('Py_GetPath'); + Py_SetPath := Import('Py_SetPath'); Py_SetPythonHome := Import('Py_SetPythonHome'); Py_GetPythonHome := Import('Py_GetPythonHome'); Py_GetPrefix := Import('Py_GetPrefix'); @@ -4219,8 +4248,10 @@ procedure TPythonEngine.Initialize; if Assigned(Py_SetProgramName) and (Length(FProgramName) > 0) then Py_SetProgramName(PWCharT(FProgramName)); AssignPyFlags; - if Length(FPythonHome) > 0 then + if Assigned(Py_SetPythonHome) and (PythonHome <> '') then Py_SetPythonHome(PWCharT(FPythonHome)); + if Assigned(Py_SetPath) and (PythonPath <> '') then + Py_SetPath(PWCharT(FPythonPath)); Py_Initialize; if Assigned(Py_IsInitialized) then FInitialized := Py_IsInitialized() <> 0 @@ -4477,6 +4508,18 @@ function TPythonEngine.GetPythonHome: UnicodeString; {$ENDIF} end; +function TPythonEngine.GetPythonPath: UnicodeString; +begin +{$IFDEF POSIX} + if (Length(FPythonPath) > 0) then + Result := UCS4StringToUnicodeString(FPythonPath) + else + Result := ''; +{$ELSE} + Result := FPythonPath; +{$ENDIF} +end; + function TPythonEngine.GetProgramName: UnicodeString; begin {$IFDEF POSIX} @@ -4498,6 +4541,15 @@ procedure TPythonEngine.SetPythonHome(const PythonHome: UnicodeString); {$ENDIF} end; +procedure TPythonEngine.SetPythonPath(const Value: UnicodeString); +begin +{$IFDEF POSIX} + FPythonPath := UnicodeStringToUCS4String(Value); +{$ELSE} + FPythonPath := Value; +{$ENDIF} +end; + procedure TPythonEngine.SetProgramName(const ProgramName: UnicodeString); begin {$IFDEF POSIX} @@ -6242,7 +6294,7 @@ function TMethodsContainer.AddDelphiMethod( AMethodName : PAnsiChar; ADocString : PAnsiChar ) : PPyMethodDef; begin Result := AddMethod( AMethodName, - GetOfObjectCallBack( TCallBack(ADelphiMethod), 2, ctCDECL), + GetOfObjectCallBack( TCallBack(ADelphiMethod), 2, DEFAULT_CALLBACK_TYPE), ADocString ); end; @@ -6251,7 +6303,7 @@ function TMethodsContainer.AddDelphiMethodWithKeywords( AMethodName : PAnsiCh ADocString : PAnsiChar ) : PPyMethodDef; begin Result := AddMethod( AMethodName, - GetOfObjectCallBack( TCallBack(ADelphiMethod), 3, ctCDECL), + GetOfObjectCallBack( TCallBack(ADelphiMethod), 3, DEFAULT_CALLBACK_TYPE), ADocString ); Result^.ml_flags := Result^.ml_flags or METH_KEYWORDS; end; @@ -8071,7 +8123,7 @@ procedure TPythonType.InitServices; begin tp_init := TPythonType_InitSubtype; tp_alloc := TPythonType_AllocSubtypeInst; - tp_new := GetCallBack( Self, @TPythonType.NewSubtypeInst, 3, ctCDECL); + tp_new := GetCallBack( Self, @TPythonType.NewSubtypeInst, 3, DEFAULT_CALLBACK_TYPE); tp_free := FreeSubtypeInst; tp_methods := MethodsData; tp_members := MembersData; @@ -8281,7 +8333,7 @@ procedure TPythonType.AddTypeVar; begin meth := CreateMethod; FCreateFuncDef.ml_name := PAnsiChar(FCreateFuncName); - FCreateFuncDef.ml_meth := GetOfObjectCallBack( TCallBack(meth), 2, ctCDECL); + FCreateFuncDef.ml_meth := GetOfObjectCallBack( TCallBack(meth), 2, DEFAULT_CALLBACK_TYPE); FCreateFuncDef.ml_flags := METH_VARARGS; FCreateFuncDef.ml_doc := PAnsiChar(FCreateFuncDoc); FCreateFunc := Engine.PyCFunction_NewEx(@FCreateFuncDef, nil, nil) diff --git a/Source/VarPyth.pas b/Source/VarPyth.pas index d9849082..071cbc0b 100644 --- a/Source/VarPyth.pas +++ b/Source/VarPyth.pas @@ -148,7 +148,7 @@ TNamedParamDesc = record {$IFDEF DELPHIXE2_OR_HIGHER} {$DEFINE USESYSTEMDISPINVOKE} //Delphi 2010 DispInvoke is buggy - {$IF defined(OSX64) or defined(LINUX) or not defined(DELPHI10_4_OR_HIGHER)} + {$IF defined(OSX64) or defined(LINUX) or defined(CPUARM) or not defined(DELPHI10_4_OR_HIGHER)} {$DEFINE PATCHEDSYSTEMDISPINVOKE} //To correct memory leaks {$IFEND} {$ENDIF} @@ -949,7 +949,7 @@ procedure SetClearVarToEmptyParam(var V: TVarData); CPropertyGet = $02; CPropertySet = $04; -{$IF defined(PATCHEDSYSTEMDISPINVOKE) and (defined(OSX64) or defined(LINUX))} +{$IF defined(PATCHEDSYSTEMDISPINVOKE) and (defined(OSX64) or defined(LINUX) or defined(CPUARM))} { Fixes https://quality.embarcadero.com/browse/RSP-28097 } @@ -964,7 +964,10 @@ procedure _DispInvokeError; raise EVariantDispatchError.Create(SDispatchError); end; +{$IFDEF CPUARM} + function GetDispatchInvokeArgs(CallDesc: PCallDesc; Params: Pointer; var Strings: TStringRefList; OrderLTR : Boolean): TVarDataArray; +{$IF defined(OSX64) or defined(LINUX) or defined(CPUARM64)} const { Parameter type masks - keep in sync with decl.h/ap* enumerations} atString = $48; @@ -998,7 +1001,11 @@ function GetDispatchInvokeArgs(CallDesc: PCallDesc; Params: Pointer; var Strings if (ArgType and atTypeMask) = atString then begin PVarData(PVarParm)^.VType := varByRef or varOleStr; +{$IFDEF NEXTGEN} + PVarData(PVarParm)^.VPointer := Strings[StringCount].FromUTF8( PUTF8String(Temp) ); +{$ELSE !NEXTGEN} PVarData(PVarParm)^.VPointer := Strings[StringCount].FromAnsi( PAnsiString(Temp)); +{$ENDIF NEXTGEN} Inc(StringCount); end else @@ -1088,6 +1095,18 @@ function GetDispatchInvokeArgs(CallDesc: PCallDesc; Params: Pointer; var Strings end; atString: begin +{$IFDEF NEXTGEN} + PVarParm^.VType := varOleStr; + Temp := VarArgGetValue(VAList, Pointer); + if PUTF8String(Temp)^ <> '' then + begin + PVarParm^.VPointer := PWideChar(Strings[StringCount].FromUTF8(PUTF8String(Temp))^); + Strings[StringCount].UTF8 := nil; + Inc(StringCount); + end + else + PVarParm^.VPointer := _EmptyBSTR; +{$ELSE !NEXTGEN} PVarParm^.VType := varOleStr; Temp := VarArgGetValue(VAList, Pointer); if AnsiString(Temp) <> '' then @@ -1102,6 +1121,7 @@ function GetDispatchInvokeArgs(CallDesc: PCallDesc; Params: Pointer; var Strings end else PVarParm^.VPointer := _EmptyBSTR; +{$ENDIF NEXTGEN} end; atUString: begin @@ -1129,6 +1149,352 @@ function GetDispatchInvokeArgs(CallDesc: PCallDesc; Params: Pointer; var Strings end; end; end; +{$ELSE !(defined(OSX64) or defined(LINUX) or defined(CPUARM64))} +const + { Parameter type masks - keep in sync with decl.h/ap* enumerations} + atString = $48; + atUString = $4A; + atVarMask = $3F; + atTypeMask = $7F; + atByRef = $80; +var + I: Integer; + ArgType: Byte; + PVarParam: PVarData; + StringCount: Integer; +begin + StringCount := 0; + SetLength(Result, CallDesc^.ArgCount); + for I := 0 to CallDesc^.ArgCount-1 do + begin + ArgType := CallDesc^.ArgTypes[I]; + + if OrderLTR then + PVarParam := @Result[I] + else + PVarParam := @Result[CallDesc^.ArgCount-I-1]; + + if (ArgType and atByRef) = atByRef then + begin + if (ArgType and atTypeMask) = atString then + begin + PVarData(PVarParam)^.VType := varByRef or varOleStr; + PVarData(PVarParam)^.VPointer := Strings[StringCount].FromAnsi(PAnsiString(Params^)); + Inc(StringCount); + end + else + if (ArgType and atTypeMask) = atUString then + begin + PVarData(PVarParam)^.VType := varByRef or varOleStr; + PVarData(PVarParam)^.VPointer := Strings[StringCount].FromUnicode(PUnicodeString(Params^)); + Inc(StringCount); + end + else + begin + if ((ArgType and atTypeMask) = varVariant) and + ((PVarData(Params^)^.VType = varString) or (PVarData(Params^)^.VType = varUString)) then + VarCast(PVariant(Params^)^, PVariant(Params^)^, varOleStr); + //PVarData(PVarParm)^.VType := varByRef or (ArgType and atTypeMask); + + ArgType := ArgType and atTypeMask; + if DispatchUnsignedAsSigned then + case ArgType of + varUInt64: ArgType := varInt64; + varUInt32: ArgType := varInteger; + varWord: ArgType := varSmallint; + varByte: ArgType := varShortInt; + end; + + PVarData(PVarParam)^.VType := varByRef or ArgType; + + PVarData(PVarParam)^.VPointer := PPointer(Params)^; + + var LArgType := PVarData(Params^)^.VType; + if LArgType = PythonVariantType.VarType then + begin + PVarData(PVarParam)^.VType := PythonVariantType.VarType; + end; + end; + Inc(PByte(Params), SizeOf(Pointer)); + end + else // ByVal + begin + PVarParam^.VType := ArgType; + case ArgType of + varEmpty, varNull: ; // Only need to set VType + varSmallint: PVarParam^.VSmallInt := PSmallInt(Params)^; + varInteger: PVarParam^.VInteger := PInteger(Params)^; + varSingle: PVarParam^.VSingle := PSingle(Params)^; + varDouble: PVarParam^.VDouble := PDouble(Params)^; + varCurrency: PVarParam^.VCurrency := PCurrency(Params)^; + varDate: PVarParam^.VDate := PDateTime(Params)^; + varOleStr: PVarParam^.VPointer := PPointer(Params)^; + varDispatch: PVarParam^.VDispatch := PPointer(Params)^; + varError: PVarParam^.VError := HRESULT($80020004); //DISP_E_PARAMNOTFOUND; + varBoolean: PVarParam^.VBoolean := PBoolean(Params)^; + varVariant: + begin + PVarParam^.VType := varEmpty; + {$IFDEF CPUX64} + PVariant(PVarParam)^ := PVariant(Params^)^; + {$ELSE} + PVariant(PVarParam)^ := PVariant(Params^)^; //it arrives here as a pointer for 32bits also + {$ENDIF} + end; + varUnknown: PVarParam^.VUnknown := PPointer(Params)^; + varShortInt: PVarParam^.VShortInt := PShortInt(Params)^; + varByte: PVarParam^.VByte := PByte(Params)^; + varWord: + begin + if DispatchUnsignedAsSigned then + begin + PVarParam^.VType := varInteger; + PVarParam^.VInteger := Integer(PWord(Params)^); + end else + PVarParam^.VWord := PWord(Params)^; + end; + varUInt32: + begin + if DispatchUnsignedAsSigned then + begin + PVarParam^.VType := varInteger; + PVarParam^.VInteger := Integer(PCardinal(Params)^); + end else + PVarParam^.VUInt32 := PCardinal(Params)^; + end; + varInt64: PVarParam^.VInt64 := PInt64(Params)^; + varUInt64: + begin + if DispatchUnsignedAsSigned then + begin + PVarParam^.VType := varInt64; + PVarParam^.VInt64 := Int64(PInt64(Params)^); + end else + PVarParam^.VUInt64 := PUInt64(Params)^; + end; + atString: + begin + PVarParam^.VType := varOleStr; + if PAnsiString(Params)^ <> '' then + begin + PVarParam^.VPointer := PWideChar(Strings[StringCount].FromAnsi(PAnsiString(Params))^); + Strings[StringCount].Ansi := nil; + Inc(StringCount); + end + else + PVarParam^.VPointer := _EmptyBSTR; + end; + atUString: + begin + PVarParam^.VType := varOleStr; + if PUnicodeString(Params)^ <> '' then + begin + PVarParam^.VPointer := PWideChar(Strings[StringCount].FromUnicode(PUnicodeString(Params))^); + Strings[StringCount].Unicode := nil; + Inc(StringCount); + end + else + PVarParam^.VPointer := _EmptyBSTR; + end; + else + // Unsupported Var Types + //varDecimal = $000E; { vt_decimal 14 } {UNSUPPORTED as of v6.x code base} + //varUndef0F = $000F; { undefined 15 } {UNSUPPORTED per Microsoft} + //varRecord = $0024; { VT_RECORD 36 } + //varString = $0100; { Pascal string 256 } {not OLE compatible } + //varAny = $0101; { Corba any 257 } {not OLE compatible } + //varUString = $0102; { Unicode string 258 } {not OLE compatible } + _DispInvokeError; + end; + case ArgType of + varError: ; // don't increase param pointer +{$IFDEF CPUARM} + varDouble, varCurrency, varDate, varInt64, varUInt64: + Inc(PByte(Params), 8); + varVariant: + {$IFDEF CPUX64} + Inc(PByte(Params), SizeOf(Variant)); + {$ELSE} + Inc(PByte(Params), SizeOf(Pointer)); + {$ENDIF CPUX64} +{$ENDIF CPUARM} + else + Inc(PByte(Params), SizeOf(Pointer)); + end; + end; + end; +end; +{$IFEND defined(OSX64) or defined(LINUX) or defined(CPUARM64)} + +{$ELSE} + +function GetDispatchInvokeArgs(CallDesc: PCallDesc; Params: Pointer; var Strings: TStringRefList; OrderLTR : Boolean): TVarDataArray; +const + { Parameter type masks - keep in sync with decl.h/ap* enumerations} + atString = $48; + atUString = $4A; + atVarMask = $3F; + atTypeMask = $7F; + atByRef = $80; +var + I: Integer; + ArgType: Byte; + PVarParm: PVarData; + StringCount: Integer; + VAList: TVarArgList; + Temp: Pointer; +begin + VAList := TVarArgList(Params^); + StringCount := 0; + SetLength(Result, CallDesc^.ArgCount); + for I := 0 to CallDesc^.ArgCount-1 do + begin + ArgType := CallDesc^.ArgTypes[I]; + + if OrderLTR then + PVarParm := @Result[I] + else + PVarParm := @Result[CallDesc^.ArgCount-I-1]; + + if (ArgType and atByRef) = atByRef then + begin + Temp := VarArgGetValue(VAList, Pointer); + if (ArgType and atTypeMask) = atString then + begin + PVarData(PVarParm)^.VType := varByRef or varOleStr; + PVarData(PVarParm)^.VPointer := Strings[StringCount].FromAnsi( PAnsiString(Temp)); + Inc(StringCount); + end + else + if (ArgType and atTypeMask) = atUString then + begin + PVarData(PVarParm)^.VType := varByRef or varOleStr; + PVarData(PVarParm)^.VPointer := Strings[StringCount].FromUnicode(PUnicodeString(Temp)); + Inc(StringCount); + end + else + begin + if ((ArgType and atTypeMask) = varVariant) and + ((PVarData(Temp)^.VType = varString) or (PVarData(Temp)^.VType = varUString)) then + VarCast(PVariant(Temp)^, PVariant(Temp)^, varOleStr); + //PVarData(PVarParm)^.VType := varByRef or (ArgType and atTypeMask); + + ArgType := ArgType and atTypeMask; + if DispatchUnsignedAsSigned then + case ArgType of + varUInt64: ArgType := varInt64; + varUInt32: ArgType := varInteger; + varWord: ArgType := varSmallint; + varByte: ArgType := varShortInt; + end; + PVarData(PVarParm)^.VType := varByRef or ArgType; + + PVarData(PVarParm)^.VPointer := Temp; + end; + end + else // ByVal + begin + PVarParm^.VType := ArgType; + case ArgType of + varEmpty, varNull: ; // Only need to set VType + varInteger: PVarParm^.VInteger := VarArgGetValue(VAList, Integer); + varSingle: PVarParm^.VSingle := VarArgGetValue(VAList, Single); + varDouble: PVarParm^.VDouble := VarArgGetValue(VAList, Double); + varCurrency: PVarParm^.VCurrency := VarArgGetValue(VAList, Currency); + varDate: PVarParm^.VDate := VarArgGetValue(VAList, TDateTime); + varOleStr: PVarParm^.VPointer := VarArgGetValue(VAList, Pointer); + varDispatch: PVarParm^.VDispatch := VarArgGetValue(VAList, Pointer); + varError: PVarParm^.VError := HRESULT($80020004); //DISP_E_PARAMNOTFOUND; + varBoolean: PVarParm^.VBoolean := VarArgGetValue(VAList, Boolean); + varVariant: + begin + PVarParm^.VType := varEmpty; +{$IFDEF CPUX64} + +// PVariant(PVarParm)^ := PVariant(Params^)^; + PVariant(PVarParm)^ := VarArgGetValue(VAList, PVariant)^; +{$ELSE} +// PVariant(PVarParm)^ := PVariant(Params)^; + PVariant(PVarParm)^ := VarArgGetValue(VAList, Variant); +{$ENDIF} + end; + varUnknown: PVarParm^.VUnknown := VarArgGetValue(VAList, Pointer); + varSmallint: PVarParm^.VSmallInt := VarArgGetValue(VAList, SmallInt); + varShortInt: PVarParm^.VShortInt := VarArgGetValue(VAList, ShortInt); + varByte: PVarParm^.VByte := VarArgGetValue(VAList, Byte); + varWord: + begin + if DispatchUnsignedAsSigned then + begin + PVarParm^.VType := varInteger; + PVarParm^.VInteger := Integer(VarArgGetValue(VAList, Word)); + end else + PVarParm^.VWord := VarArgGetValue(VAList, Word); + end; + varUInt32: + begin + if DispatchUnsignedAsSigned then + begin + PVarParm^.VType := varInteger; + PVarParm^.VInteger := Integer(VarArgGetValue(VAList, Cardinal)); + end else + PVarParm^.VUInt32 := VarArgGetValue(VAList, Cardinal); + end; + varInt64: PVarParm^.VInt64 := VarArgGetValue(VAList, Int64); + varUInt64: + begin + if DispatchUnsignedAsSigned then + begin + PVarParm^.VType := varInt64; + PVarParm^.VInt64 := VarArgGetValue(VAList, Int64); //Int64(PInt64(Params)^); + end else + PVarParm^.VUInt64 := VarArgGetValue(VAList, UInt64); //PUInt64(Params)^; + end; + atString: + begin + PVarParm^.VType := varOleStr; + Temp := VarArgGetValue(VAList, Pointer); + if AnsiString(Temp) <> '' then + begin + { + This line causes a crash and is replaced with the one below in line with unicode strings + PVarParm^.VPointer := PWideChar(Strings[StringCount].FromAnsi(PAnsiString(Temp))^); + } + PVarParm^.VPointer := PWideChar(Strings[StringCount].FromAnsi(@AnsiString(Temp))^); + Strings[StringCount].Ansi := nil; + Inc(StringCount); + end + else + PVarParm^.VPointer := _EmptyBSTR; + end; + atUString: + begin + PVarParm^.VType := varOleStr; + Temp := VarArgGetValue(VAList, Pointer); + if UnicodeString(Temp) <> '' then + begin + PVarParm^.VPointer := PWideChar(Strings[StringCount].FromUnicode(@UnicodeString(Temp))^); + Strings[StringCount].Unicode := nil; + Inc(StringCount); + end + else + PVarParm^.VPointer := _EmptyBSTR; + end; + else + // Unsupported Var Types + //varDecimal = $000E; { vt_decimal 14 } {UNSUPPORTED as of v6.x code base} + //varUndef0F = $000F; { undefined 15 } {UNSUPPORTED per Microsoft} + //varRecord = $0024; { VT_RECORD 36 } + //varString = $0100; { Pascal string 256 } {not OLE compatible } + //varAny = $0101; { Corba any 257 } {not OLE compatible } + //varUString = $0102; { Unicode string 258 } {not OLE compatible } + _DispInvokeError; + end; + end; + end; +end; +{$ENDIF CPUARM} + {$IFEND} {$IFDEF DELPHIXE7_OR_HIGHER} @@ -1295,6 +1661,7 @@ procedure TPythonVariantType.DispInvoke(Dest: PVarData; Var NewCallDesc : TCallDesc; begin + if CallDesc^.NamedArgCount > 0 then GetNamedParams; try if (CallDesc^.CallType = CPropertyGet) and (CallDesc^.ArgCount = 1) then begin @@ -2404,8 +2771,8 @@ function TVarPyEnumerator.GetCurrent: Variant; function TVarPyEnumerator.MoveNext: Boolean; begin - Result := True; try + Result := True; FCurrent := BuiltinModule.next(FIterator); except on E: EPyStopIteration do @@ -2419,7 +2786,7 @@ function TVarPyEnumerator.MoveNext: Boolean; function VarPyIterate(const AValue: Variant): TVarPyEnumerateHelper; begin - Result.Create(AValue); + Result := TVarPyEnumerateHelper.Create(AValue); end; { TVarPyEnumerateHelper } @@ -2431,11 +2798,13 @@ constructor TVarPyEnumerateHelper.Create(const AValue: Variant); function TVarPyEnumerateHelper.GetEnumerator: TVarPyEnumerator; begin - Result.Create(FIterable); + Result := TVarPyEnumerator.Create(FIterable); end; initialization PythonVariantType := TPythonVariantType.Create; + finalization FreeAndNil(PythonVariantType); + end. diff --git a/Source/fmx/FMX.PythonGUIInputOutput.pas b/Source/fmx/FMX.PythonGUIInputOutput.pas index 3bb5e82c..38afae86 100644 --- a/Source/fmx/FMX.PythonGUIInputOutput.pas +++ b/Source/fmx/FMX.PythonGUIInputOutput.pas @@ -15,7 +15,8 @@ interface const PID_SUPPORTED_PLATFORMS = pidWin32 or pidWin64 or pidOSX32 or pidOSX64 - or pidLinux64; + or pidLinux64 + or pidAndroid32Arm or pidAndroid64Arm; type [ComponentPlatformsAttribute(PID_SUPPORTED_PLATFORMS)] diff --git a/Tests/FMX/Android/MethodCallBackTest.pas b/Tests/FMX/Android/MethodCallBackTest.pas new file mode 100644 index 00000000..f4d2044f --- /dev/null +++ b/Tests/FMX/Android/MethodCallBackTest.pas @@ -0,0 +1,262 @@ +(**************************************************************************) +(* *) +(* Module: Unit 'MethodCallbackTest' Copyright (c) 2021 *) +(* *) +(* Lucas Moura Belo - lmbelo *) +(* lucas.belo@live.com *) +(* BH, Brazil *) +(* *) +(* PyScripter *) +(* e-mail: pyscripter@gmail.com *) +(* *) +(* Project pages: https://github.com/Embarcadero/python4delphi *) +(* https://github.com/pyscripter/python4delphi *) +(**************************************************************************) +(* Functionality: Test unit for MethodCallback *) +(* *) +(* *) +(**************************************************************************) +(* This source code is distributed with no WARRANTY, for no reason or use.*) +(* Everyone is allowed to use and change this code free for his own tasks *) +(* and projects, as long as this header and its copyright text is intact. *) +(* For changed versions of this code, which are public distributed the *) +(* following additional conditions have to be fullfilled: *) +(* 1) The header has to contain a comment on the change and the author of *) +(* it. *) +(* 2) A copy of the changed source has to be sent to the above E-Mail *) +(* address or my then valid address, if this is possible to the *) +(* author. *) +(* The second condition has the target to maintain an up to date central *) +(* version of the component. If this condition is not acceptable for *) +(* confidential or legal reasons, everyone is free to derive a component *) +(* or to generate a diff file to my or other original sources. *) +(**************************************************************************) + +unit MethodCallBackTest; + +interface + +uses + DUnitX.TestFramework, + MethodCallback, + PythonLoad; + +implementation + +type + TTwoArgArmStdFunction = function (arg1, arg2: string): integer; + TThreeArgArmStdProcedure = procedure (arg1, arg2, arg3: string); + + TFourArgArmStdFunction = function(arg1, arg2, arg3, arg4: integer): integer; + TFiveArgArmStdFunction = function(arg1, arg2, arg3, arg4, arg5: integer): integer; + + TMyFuncCallback = function(arg1, arg2: string): integer of object; + TMyProcCallback = procedure (arg1, arg2, arg3: string) of object; + + TTestObj = class + public + Argument1: string; + Argument2: string; + Argument3: string; + function TwoArgArmStdFunction(arg1, arg2: string): integer; + procedure ThreeArgArmStdProcedure(arg1, arg2, arg3: string); + function FourArgArmStdFunction(arg1, arg2, arg3, arg4: integer): integer; + function FiveArgArmStdFunction(arg1, arg2, arg3, arg4, arg5: integer): integer; + end; + + [TestFixture] + TMethodCallbackTest = class + private + fTestObj: TTestObj; + public + [SetupFixture] + procedure SetupFixture; + [TearDownFixture] + procedure Teardown; + [Test] + procedure TestDeleteOnEmptyAllocator; + [Test] + procedure TestCallBackArmStd; + [Test] + procedure TestOfObjectCallBackArmStd; + [Test] + procedure TestDeleteCallBack; + [Test] + procedure TestFourArgArmStdFunction; + [Test] + procedure TestFiveArgArmStdFunction; + [Test] + procedure TestMemoryMgmt; + end; + +{ TTestObj } + +function TTestObj.FiveArgArmStdFunction(arg1, arg2, arg3, arg4, + arg5: integer): integer; +begin + Result := arg1 * arg4 + arg2 * arg5 + arg3; +end; + +function TTestObj.FourArgArmStdFunction(arg1, arg2, arg3, arg4: integer): integer; +begin + Result := arg1 * arg3 + arg2 * arg4; +end; + +procedure TTestObj.ThreeArgArmStdProcedure(arg1, arg2, arg3: string); +begin + Argument1:=arg1; + Argument2:=arg2; + Argument3:=arg3; +end; + +function TTestObj.TwoArgArmStdFunction(arg1, arg2: string): integer; +begin + Argument1:=arg1; + Argument2:=arg2; + result:=1; +end; + +{ TMethodCallbackTest } + +procedure TMethodCallbackTest.SetupFixture; +begin + fTestObj:=TTestObj.Create; +end; + +procedure TMethodCallbackTest.Teardown; +begin + fTestObj.Free; + FreeCallBacks; +end; + +procedure TMethodCallbackTest.TestMemoryMgmt; +const + AllocCount = {$IFDEF CPUARM} + {$IFDEF CPUARM32} + 31 + {$ELSE} + 46 + {$ENDIF CPUARM32} + {$ELSE} + {$IFDEF CPUX64} + {$IFDEF MSWINDOWS} + 51 + {$ELSE} + 88 + {$ENDIF} + {$ELSE} + 90 + {$ENDIF} + {$ENDIF}; +var + i: integer; + ptr: Pointer; +begin + //---Test the code-memory manager + + FreeCallBacks; + Assert.AreEqual(0, CodeMemPageCount); + + for i:=1 to AllocCount do + ptr:=GetCallBack(fTestObj, @TTestObj.ThreeArgArmStdProcedure, 5, ctArmStd); + + // there should still be 1 page allocated + Assert.AreEqual(1, CodeMemPageCount); + + // get one callback more and we should have 2 pages + ptr:=GetCallBack(fTestObj, @TTestObj.ThreeArgArmStdProcedure, 5, ctArmStd); + // getting CodeMemPageCount would crash as the next page pointer was overwritten by the + // last allocation + Assert.AreEqual(2, CodeMemPageCount); +end; + +procedure TMethodCallbackTest.TestCallBackArmStd; +var + ptr: pointer; + func: TTwoArgArmStdFunction; +begin + ptr:=GetCallBack(fTestObj, @TTestObj.TwoArgArmStdFunction, 2, ctArmStd); + + //---call method through pointer + func:=TTwoArgArmStdFunction(ptr); + + Assert.AreEqual(1, func('first arg', 'second arg')); + Assert.AreEqual(string('first arg'), fTestObj.Argument1); + Assert.AreEqual(string('second arg'), fTestObj.Argument2); +end; + +procedure TMethodCallbackTest.TestDeleteCallBack; +var + ptr1, ptr2, ptr3: Pointer; + proc: TThreeArgArmStdProcedure; + func: TTwoArgArmStdFunction; +begin + //---we create 3 callbacks and delete them. + // if there aren't any AV, we assume it works... + ptr1:=GetCallBack(fTestObj, @TTestObj.ThreeArgArmStdProcedure, 3, ctArmStd); + ptr2:=GetCallBack(fTestObj, @TTestObj.TwoArgArmStdFunction, 2, ctArmStd); + DeleteCallBack(ptr1); + ptr1:=GetCallBack(fTestObj, @TTestObj.TwoArgArmStdFunction, 2, ctArmStd); + ptr3:=GetCallBack(fTestObj, @TTestObj.ThreeArgArmStdProcedure, 3, ctArmStd); + + func:=TTwoArgArmStdFunction(ptr1); + func('arg1', 'arg2'); + func:=TTwoArgArmStdFunction(ptr2); + func('arg1', 'arg2'); + proc:=TThreeArgArmStdProcedure(ptr3); + proc('arg1', 'arg2', 'arg3'); + + DeleteCallBack(ptr1); + DeleteCallBack(ptr2); + DeleteCallback(ptr3); + Assert.Pass; +end; + +procedure TMethodCallbackTest.TestDeleteOnEmptyAllocator; +var + ptr1 : Pointer; +begin + ptr1 := nil; + DeleteCallBack(ptr1); + Assert.Pass(); +end; + +procedure TMethodCallbackTest.TestFiveArgArmStdFunction; +Var + CallBack : TFiveArgArmStdFunction; +begin + CallBack := GetCallBack(fTestObj, @TTestObj.FiveArgArmStdFunction, 5, ctArmStd); + Assert.AreEqual(CallBack(1,2,3,4,5), 1*4+2*5+3); + DeleteCallBack(@CallBack); +end; + +procedure TMethodCallbackTest.TestFourArgArmStdFunction; +Var + CallBack : TFourArgArmStdFunction; +begin + CallBack := GetCallBack(fTestObj, @TTestObj.FourArgArmStdFunction, 4, ctArmStd); + Assert.AreEqual(CallBack(1,2,3,4), 1*3+2*4); + DeleteCallBack(@CallBack); +end; + +procedure TMethodCallbackTest.TestOfObjectCallBackArmStd; +var + ptr: pointer; + func: TTwoArgArmStdFunction; + cb: TMyFuncCallBack; +begin + cb:=fTestObj.TwoArgArmStdFunction; + ptr:=GetOfObjectCallBack(TCallBack(cb), 2, ctARMSTD); + + //---call method through pointer + func:=TTwoArgArmStdFunction(ptr); + + Assert.AreEqual(1, func('first arg', 'second arg')); + Assert.AreEqual('first arg', fTestObj.Argument1); + Assert.AreEqual('second arg', fTestObj.Argument2); +end; + +initialization + TDUnitX.RegisterTestFixture(TMethodCallBackTest); + +end. diff --git a/Tests/FMX/Android/NumberServicesTest.pas b/Tests/FMX/Android/NumberServicesTest.pas new file mode 100644 index 00000000..d9d3ccfc --- /dev/null +++ b/Tests/FMX/Android/NumberServicesTest.pas @@ -0,0 +1,822 @@ +(**************************************************************************) +(* *) +(* Module: Unit 'NumberServicesTest' Copyright (c) 2021 *) +(* *) +(* Lucas Moura Belo - lmbelo *) +(* lucas.belo@live.com *) +(* BH, Brazil *) +(* *) +(* PyScripter *) +(* e-mail: pyscripter@gmail.com *) +(* *) +(* Project pages: https://github.com/Embarcadero/python4delphi *) +(* https://github.com/pyscripter/python4delphi *) +(**************************************************************************) +(* Functionality: Test unit for numeric operations *) +(* *) +(* *) +(**************************************************************************) +(* This source code is distributed with no WARRANTY, for no reason or use.*) +(* Everyone is allowed to use and change this code free for his own tasks *) +(* and projects, as long as this header and its copyright text is intact. *) +(* For changed versions of this code, which are public distributed the *) +(* following additional conditions have to be fullfilled: *) +(* 1) The header has to contain a comment on the change and the author of *) +(* it. *) +(* 2) A copy of the changed source has to be sent to the above E-Mail *) +(* address or my then valid address, if this is possible to the *) +(* author. *) +(* The second condition has the target to maintain an up to date central *) +(* version of the component. If this condition is not acceptable for *) +(* confidential or legal reasons, everyone is free to derive a component *) +(* or to generate a diff file to my or other original sources. *) +(**************************************************************************) + +unit NumberServicesTest; + +interface + +uses + DUnitX.TestFramework, + PythonEngine; + +type + TRandomInteger = class(TObject) + strict private + FValue: Integer; + private + procedure SetValue(const Value: Integer); + public + constructor Create; + property Value: Integer read FValue write SetValue; + end; + + PyTRandomInteger = class(TPyObject) + private + FRandomInteger: TRandomInteger; + + public + constructor CreateWith(PythonType: TPythonType; args: PPyObject); override; + + // Basic services + function Repr: PPyObject; override; + + // Number services + function NbAdd(obj: PPyObject): PPyObject; override; // 0 + function NbSubtract(obj: PPyObject): PPyObject; override; // 1 + function NbMultiply(obj: PPyObject): PPyObject; override; // 2 + function NbRemainder(obj: PPyObject): PPyObject; override; // 4 {3 is for obsolete nsDivide} + function NbDivmod(obj: PPyObject): PPyObject; override; // 5 + function NbPower(ob1, ob2: PPyObject): PPyObject; override; // 6 + function NbNegative: PPyObject; override; // 7 + function NbPositive: PPyObject; override; // 8 + function NbAbsolute: PPyObject; override; // 9 + function NbInvert: PPyObject; override; // 11 (10 for obsolete nsNonZero) + function NbLShift(obj: PPyObject): PPyObject; override; // 12 + function NbRShift(obj: PPyObject): PPyObject; override; // 13 + function NbAnd(obj: PPyObject): PPyObject; override; // 14 + function NbXor(obj: PPyObject): PPyObject; override; // 15 + function NbOr(obj: PPyObject): PPyObject; override; // 16 + function NbInt : PPyObject; override; // 18 (17 is for obsolete nsCoerce) + function NbFloat : PPyObject; override; // 20 (19 is for obsolete nsLong) + function NbFloorDivide(obj: PPyObject): PPyObject; override; // 23 (21 is for obsolete nsOct, 22 for nsHex) + function NbTrueDivide(obj: PPyObject): PPyObject; override; // 24 + function NbMatrixMultiply(obj: PPyObject): PPyObject; override; // 25 + function NbBool: Integer; override; // 26 + private + function PerformArithmeticOp(obj: PPyObject; op: string): PPyObject; + public + destructor Destroy; override; + end; + + [TestFixture] + TTestNumberServices = class(TObject) + private + PythonType_TRndInt: TPythonType; + FPythonModule : TPythonModule; + PythonEngine : TPythonEngine; + pdvainteger: integer; + pdvbinteger: integer; + pdvc : TPythonDelphiVar; + public + [SetupFixture] + procedure SetupFixture; + + [TearDownFixture] + procedure TearDownFixture; + + [Test] + procedure TestAdd; // 0 + [Test] + procedure TestSubtract; // 1 + [Test] + procedure TestMultiply; // 2 + [Test] + procedure TestRemainder; // 4 + [Test] + procedure TestDivMod; // 5 + [Test] + procedure TestPower; // 6 + [Test] + procedure TestNegative; // 7 + [Test] + procedure TestPositive; // 8 + [Test] + procedure TestAbsolute; // 9 + [Test] + procedure TestInvert; // 11 + [Test] + procedure TestLShift; // 12 + [Test] + procedure TestRShift; // 13 + [Test] + procedure TestAnd; // 14 + [Test] + procedure TestXor; // 15 + [Test] + procedure TestOr; // 16 + [Test] + procedure TestInt; // 18 + [Test] + procedure TestFloat; // 20 + [Test] + procedure TestFloorDivide; // 23 + [Test] + procedure TestTrueDivide; // 24 + [Test] + procedure TestMatrixMultiply; // 25 + [Test] + procedure TestBool; // 26 + end; + +var PrintResults: Boolean = False; + +implementation + +uses + System.Variants, + System.SysUtils, + System.Math, + PythonLoad; + +procedure AddPythonType(var PythonType: TPythonType; + Name: string; + TypeName: AnsiString; + Engine: TPythonEngine; + Module: TPythonModule; + PyObjectClass: TPyObjectClass; + Basic: TBasicServices; + Number: TNumberServices); +begin + if not Assigned(PythonType) then + begin + PythonType := TPythonType.Create(Module); + PythonType.Engine := Engine; + PythonType.Module := Module; + PythonType.Name := Name; + PythonType.Services.Basic := Basic; + PythonType.Services.Mapping := []; + PythonType.Services.Sequence := []; + PythonType.Services.Number := Number; + PythonType.TypeName := TypeName; + PythonType.PyObjectClass := PyObjectClass; + PythonType.GenerateCreateFunction := False; + + PythonType.Initialize; + end; +end; + + function GetVal(AModule : PPyObject; AVarName : AnsiString) : PPyObject; + begin + with GetPythonEngine do + begin + Result := PyObject_GetAttrString(AModule, PAnsiChar(AVarName)); + if PyErr_Occurred <> nil then + PyErr_Clear + else + Py_XDecRef(Result); // keep a borrowed reference. + end + end; + +procedure TTestNumberServices.SetupFixture; +var + Py : PPyObject; + valpy: TPyObject; + val: PyTRandomInteger; +begin + PythonEngine := TPythonEngine.Create(nil); + PythonEngine.Name := 'PythonEngine'; + TPythonLoad.Configure(PythonEngine); + PythonEngine.LoadDll; + + // python module + FPythonModule := TPythonModule.Create(GetPythonEngine); + FPythonModule.Engine := GetPythonEngine; + FPythonModule.ModuleName := 'testing123'; + FPythonModule.Initialize; + + PythonType_TRndInt := nil; + AddPythonType(PythonType_TRndInt, 'PythonType_RndInt', 'TRandomInteger', + GetPythonEngine, FPythonModule, PyTRandomInteger, + [ bsGetAttrO, bsSetAttrO, bsRepr ], + [ nsAdd, + nsSubtract, + nsMultiply, + nsTrueDivide, + nsRemainder, + nsDivmod, + nsPower, + nsNegative, + nsPositive, + nsAbsolute, + nsInvert, + nsLShift, + nsRShift, + nsAnd, + nsXor, + nsOr, + nsInt, + nsFloat, + nsFloorDivide, + nsMatrixMultiply, + nsBool ]); + + // import our module + GetPythonEngine.Run_CommandAsString('import ' + FPythonModule.ModuleName, single_input); + + pdvc := TPythonDelphiVar.Create(nil); + pdvc.Engine := GetPythonEngine; + pdvc.Module := '__main__'; + pdvc.VarName := 'c'; + pdvc.Initialize; + + GetPythonEngine.Run_CommandAsString('aa = testing123.TRandomInteger()', single_input); + GetPythonEngine.Run_CommandAsString('bb = testing123.TRandomInteger()', single_input); + + py := GetVal(GetPythonEngine.GetMainModule, 'aa'); + valpy := PythonToDelphi(py); + val := valpy as PyTRandomInteger; + if Assigned(Py) then + begin + pdvainteger := val.FRandomInteger.Value; + end; + + py := GetVal(GetPythonEngine.GetMainModule, 'bb'); + valpy := PythonToDelphi(py); + val := valpy as PyTRandomInteger; + if Assigned(Py) then + begin + pdvbinteger := val.FRandomInteger.Value; + end; +end; + +{ PyTRandomInteger } + +function PythonToTRandomInteger(obj: PPyObject): TRandomInteger; +begin + if obj = GetPythonEngine.Py_None then + Result := nil + else + Result := TRandomInteger(PyTRandomInteger(PythonToDelphi(obj)).FRandomInteger); +end; + +function PyTRandomInteger.Repr: PPyObject; +var + info: string; +begin + with GetPythonEngine do + begin + info := 'NIL'; + + // regular + if Assigned(FRandomInteger) then + begin + info := IntToStr(FRandomInteger.Value); + end; + + Result := VariantAsPyObject(info); + end; +end; + + +{ PyTRandomInteger } + +constructor PyTRandomInteger.CreateWith(PythonType: TPythonType; + args: PPyObject); +var + val1: PPyObject; +begin + with GetPythonEngine do + begin + try + inherited; + + // create object + FRandomInteger := TRandomInteger.Create; + + // try to parse the parameter + val1 := nil; + if Assigned(args) then + // try to parse + if (PyArg_ParseTuple(args, '|O:CreateWith', @val1) <> 0) and Assigned(val1) then + FRandomInteger.Value := PythonToTRandomInteger(val1).Value; + except + on e: Exception do + PyErr_SetString(PyExc_Exception^, PAnsiChar(AnsiString(e.Message))); + end; + end; +end; + +function PyTRandomInteger.NbAdd(obj: PPyObject): PPyObject; // 0 +begin + Result := PerformArithmeticOp(obj, '+'); +end; + +function PyTRandomInteger.NbSubtract(obj: PPyObject): PPyObject; // 1 +begin + Result := PerformArithmeticOp(obj, '-'); +end; + +function PyTRandomInteger.NbMultiply(obj: PPyObject): PPyObject; // 2 +begin + Result := PerformArithmeticOp(obj, '*'); +end; + +function PyTRandomInteger.NbRemainder(obj: PPyObject): PPyObject; // 4 +begin + Result := PerformArithmeticOp(obj, '%'); +end; + +function PyTRandomInteger.NbDivmod(obj: PPyObject): PPyObject; // 5 +begin + Result := PerformArithmeticOp(obj, 'divmod'); +end; + +function PyTRandomInteger.NbPower(ob1, ob2: PPyObject): PPyObject; // 6 +begin + Result := PerformArithmeticOp(ob1, '^'); +end; + +function PyTRandomInteger.NbNegative: PPyObject; // 7 +var + arg1: Integer; +begin + with GetPythonEngine do + begin + arg1 := FRandomInteger.Value; + Result := VariantAsPyObject(-arg1); + end +end; + +function PyTRandomInteger.NbPositive: PPyObject; // 8 +var + arg1: Integer; +begin + with GetPythonEngine do + begin + arg1 := FRandomInteger.Value; + Result := VariantAsPyObject(+arg1); + end +end; + +destructor PyTRandomInteger.Destroy; +begin + FRandomInteger.Free; + inherited; +end; + +function PyTRandomInteger.NbAbsolute: PPyObject; // 9 +begin + with GetPythonEngine do + begin + Result := VariantAsPyObject(Abs(FRandomInteger.Value)); + end; +end; + +function PyTRandomInteger.NbInvert: PPyObject; // 11 +begin + with GetPythonEngine do + begin + Result := VariantAsPyObject(1-(FRandomInteger.Value+1)); + end; +end; + +function PyTRandomInteger.NbLShift(obj: PPyObject): PPyObject; // 12 +begin + Result := PerformArithmeticOp(obj, '<<'); +end; + +function PyTRandomInteger.NbRShift(obj: PPyObject): PPyObject; // 13 +begin + Result := PerformArithmeticOp(obj, '>>'); +end; + +function PyTRandomInteger.NbAnd(obj: PPyObject): PPyObject; // 14 +begin + Result := PerformArithmeticOp(obj, 'and'); +end; + +function PyTRandomInteger.NbXor(obj: PPyObject): PPyObject; // 15 +begin + Result := PerformArithmeticOp(obj, 'xor'); +end; + +function PyTRandomInteger.NbOr(obj: PPyObject): PPyObject; // 16 +begin + Result := PerformArithmeticOp(obj, 'or'); +end; + +function PyTRandomInteger.NbInt: PPyObject; // 18 +begin + with GetPythonEngine do + begin + Result := VariantAsPyObject(FRandomInteger.Value); + end; +end; + +function PyTRandomInteger.NbFloat: PPyObject; // 20 +begin + with GetPythonEngine do + begin + Result := VariantAsPyObject(Single(FRandomInteger.Value)); + end; +end; + +function PyTRandomInteger.nbFloorDivide(obj: PPyObject): PPyObject; // 23 +begin + Result := PerformArithmeticOp(obj, 'floordivide'); +end; + +function PyTRandomInteger.NbTrueDivide(obj: PPyObject) : PPyObject; // 24 +begin + Result := PerformArithmeticOp(obj, '/'); +end; + +function PyTRandomInteger.NbMatrixMultiply(obj: PPyObject): PPyObject; // 25 +begin + Result := PerformArithmeticOp(obj, '@'); +end; + + +function PyTRandomInteger.NbBool: Integer; // 26 +begin + with GetPythonEngine do + begin + Result := IfThen(FRandomInteger.Value=0, Ord(False), Ord(True)); + end; +end; + + +function PyTRandomInteger.PerformArithmeticOp(obj: PPyObject; op: string): PPyObject; +var + val: TPyObject; + arg1, arg2: Integer; + VarArray: Variant; +begin + with GetPythonEngine do + begin + Result := ReturnNone; + // convert to delphi object + val := PythonToDelphi(obj); + // we can only add the same type + if (val.PythonType = Self.PythonType) then + begin + arg1 := FRandomInteger.Value; + arg2 := PyTRandomInteger(val).FRandomInteger.Value; + case op[1] of + '+': Result := VariantAsPyObject(arg1 + arg2); + '-': Result := VariantAsPyObject(arg1 - arg2); + '/': Result := VariantAsPyObject(arg1 div arg2); + '*': Result := VariantAsPyObject(arg1 * arg2); + '@': Result := VariantAsPyObject(not (arg1 * arg2)); // intentionally different from '*' + '%': Result := VariantAsPyObject(arg1 mod arg2); + '^': Result := VariantAsPyObject(System.Math.Power(Double(arg1), Double(arg2))); + else + begin + if op='divmod' then + begin + VarArray := VarArrayCreate([0, 1], varInteger); + VarArrayPut(VarArray, arg1 div arg2, [0]); + VarArrayPut(VarArray, arg1 mod arg2, [1]); + Result := VariantAsPyObject(VarArray); + end + + else if op='>>' then + begin + Result := VariantAsPyObject(arg1 shr arg2); + end + + else if op='<<' then + begin + Result := VariantAsPyObject(arg1 shl arg2); + end + + else if op='and' then + begin + Result := VariantAsPyObject(arg1 and arg2); + end + + else if op='xor' then + begin + Result := VariantAsPyObject(arg1 xor arg2); + end + + else if op='or' then + begin + Result := VariantAsPyObject(arg1 or arg2); + end + + else if op='floordivide' then + begin + Result := VariantAsPyObject(arg1 div arg2); + end; + end; + end; + end + else // the arguments were not right + Result := nil; + end; +end; + +{ TRandomInteger } + +constructor TRandomInteger.Create; +begin + inherited; + FValue := 1+Random(100); // Result interval [1, 101] so safe to test division +end; + +procedure TRandomInteger.SetValue(const Value: Integer); +begin + FValue := Value; +end; + +procedure TTestNumberServices.TearDownFixture; +begin + PythonEngine.Free; + pdvc.Free; +end; + +// nsAdd +procedure TTestNumberServices.TestAdd; // 0 +var + pdvinteger: integer; +begin + GetPythonEngine.Run_CommandAsString('c.Value=(aa+bb)', single_input); + if PrintResults then GetPythonEngine.Run_CommandAsString('print(c)', single_input); + + pdvinteger := pdvc.Value; + Assert.AreEqual(pdvinteger, pdvainteger+pdvbinteger); +end; + +// nsSubtract +procedure TTestNumberServices.TestSubtract; // 1 +var + pdvinteger: integer; +begin + GetPythonEngine.Run_CommandAsString('c.Value=(aa-bb)', single_input); + if PrintResults then GetPythonEngine.Run_CommandAsString('print(c)', single_input); + + pdvinteger := pdvc.Value; + Assert.AreEqual(pdvinteger, pdvainteger-pdvbinteger); +end; + +// nsMultiply +procedure TTestNumberServices.TestMultiply; // 2 +var + pdvinteger: integer; +begin + GetPythonEngine.Run_CommandAsString('c.Value=(aa*bb)', single_input); + if PrintResults then GetPythonEngine.Run_CommandAsString('print(c)', single_input); + + pdvinteger := pdvc.Value; + Assert.AreEqual(pdvinteger, pdvainteger*pdvbinteger); +end; + +// nsRemainder +procedure TTestNumberServices.TestRemainder; // 4 +var + pdvinteger: integer; +begin + GetPythonEngine.Run_CommandAsString('c.Value=(aa%bb)', single_input); + if PrintResults then GetPythonEngine.Run_CommandAsString('print(c)', single_input); + + pdvinteger := pdvc.Value; + Assert.AreEqual(pdvinteger, pdvainteger mod pdvbinteger); +end; + +// nsDivmod +procedure TTestNumberServices.TestDivMod; // 5 +var + VarArr: Variant; + res0, res1: Integer; +begin + GetPythonEngine.Run_CommandAsString('c.Value=divmod(aa,bb)', single_input); + if PrintResults then GetPythonEngine.Run_CommandAsString('print(c)', single_input); + + VarArr := pdvc.Value; + res0 := VarArr[0]; + res1 := VarArr[1]; + + Assert.AreEqual(res0, pdvainteger div pdvbinteger); + Assert.AreEqual(res1, pdvainteger mod pdvbinteger); +end; + +// nsPower +procedure TTestNumberServices.TestPower; // 6 +var + pdvdouble: double; +begin + GetPythonEngine.Run_CommandAsString('c.Value=float(aa**bb)', single_input); + if PrintResults then GetPythonEngine.Run_CommandAsString('print(c)', single_input); + + pdvdouble := pdvc.Value; + Assert.AreEqual(Double(pdvdouble), {Round}(System.Math.Power(Double(pdvainteger), Double(pdvbinteger)))); + +end; + +// nsNegative +procedure TTestNumberServices.TestNegative; // 7 +var + pdvinteger: integer; +begin + GetPythonEngine.Run_CommandAsString('c.Value=(-aa)', single_input); + if PrintResults then GetPythonEngine.Run_CommandAsString('print(c)', single_input); + + pdvinteger := pdvc.Value; + Assert.AreEqual(pdvinteger, -pdvainteger); +end; + +// nsPositive +procedure TTestNumberServices.TestPositive; // 8 +var + pdvinteger: integer; +begin + GetPythonEngine.Run_CommandAsString('c.Value=int(+aa)', single_input); + if PrintResults then GetPythonEngine.Run_CommandAsString('print(c)', single_input); + + pdvinteger := pdvc.Value; + Assert.AreEqual(pdvinteger, +pdvainteger); +end; + +// nsAbsolute +procedure TTestNumberServices.TestAbsolute; // 9 +var + pdvinteger: integer; +begin + GetPythonEngine.Run_CommandAsString('c.Value=abs(-aa)', single_input); + if PrintResults then GetPythonEngine.Run_CommandAsString('print(c)', single_input); + + pdvinteger := pdvc.Value; + Assert.AreEqual(pdvinteger, +pdvainteger); +end; + +// nsInvert +procedure TTestNumberServices.TestInvert; // 11 +var + pdvinteger: Integer; +begin + GetPythonEngine.Run_CommandAsString('c.Value=~aa', single_input); + if PrintResults then GetPythonEngine.Run_CommandAsString('print(c)', single_input); + + pdvinteger := pdvc.Value; + Assert.AreEqual(pdvinteger, 1-(pdvainteger+1)); +end; + +// nsLShift +procedure TTestNumberServices.TestLShift; // 12 +var + pdvinteger: integer; +begin + GetPythonEngine.Run_CommandAsString('c.Value=(aa<>bb)', single_input); + if PrintResults then GetPythonEngine.Run_CommandAsString('print(c)', single_input); + + pdvinteger := pdvc.Value; + Assert.AreEqual(pdvinteger, pdvainteger shr pdvbinteger); +end; + +// nsAnd +procedure TTestNumberServices.TestAnd; // 14 +var + pdvinteger: integer; +begin + GetPythonEngine.Run_CommandAsString('c.Value=(aa&bb)', single_input); + if PrintResults then GetPythonEngine.Run_CommandAsString('print(c)', single_input); + + pdvinteger := pdvc.Value; + Assert.AreEqual(pdvinteger, pdvainteger and pdvbinteger); +end; + +// nsXor +procedure TTestNumberServices.TestXor; // 15 +var + pdvinteger: integer; +begin + GetPythonEngine.Run_CommandAsString('c.Value=(aa^bb)', single_input); + if PrintResults then GetPythonEngine.Run_CommandAsString('print(c)', single_input); + + pdvinteger := pdvc.Value; + Assert.AreEqual(pdvinteger, pdvainteger xor pdvbinteger); +end; + +// nsOr +procedure TTestNumberServices.TestOr; // 16 +var + pdvinteger: integer; +begin + GetPythonEngine.Run_CommandAsString('c.Value=(aa|bb)', single_input); + if PrintResults then GetPythonEngine.Run_CommandAsString('print(c)', single_input); + + pdvinteger := pdvc.Value; + Assert.AreEqual(pdvinteger, pdvainteger or pdvbinteger); +end; + +// nsInt +procedure TTestNumberServices.TestInt; // 18 +var + pdvinteger: Integer; +begin + GetPythonEngine.Run_CommandAsString('c.Value=int(aa)', single_input); + if PrintResults then GetPythonEngine.Run_CommandAsString('print(c)', single_input); + + pdvinteger := pdvc.Value; + Assert.AreEqual(pdvinteger, pdvainteger); +end; + +// nsFloat +procedure TTestNumberServices.TestFloat; // 20 +var + pdvsingle: Single; +begin + GetPythonEngine.Run_CommandAsString('c.Value=float(aa)', single_input); + if PrintResults then GetPythonEngine.Run_CommandAsString('print(c)', single_input); + + pdvsingle := pdvc.Value; + Assert.AreEqual(pdvsingle, single(pdvainteger)); +end; + +// nsFloorDivide +procedure TTestNumberServices.TestFloorDivide; // 23 +var + pdvinteger: integer; +begin + GetPythonEngine.Run_CommandAsString('c.Value=(aa//bb)', single_input); + if PrintResults then GetPythonEngine.Run_CommandAsString('print(c)', single_input); + + pdvinteger := pdvc.Value; + Assert.AreEqual(pdvinteger, pdvainteger div pdvbinteger); +end; + +// nsTrueDivide +procedure TTestNumberServices.TestTrueDivide; // 24 +var + pdvinteger: integer; +begin + GetPythonEngine.Run_CommandAsString('c.Value=(aa/bb)', single_input); + if PrintResults then GetPythonEngine.Run_CommandAsString('print(c)', single_input); + + pdvinteger := pdvc.Value; + Assert.AreEqual(pdvinteger, pdvainteger div pdvbinteger); +end; + +// nsMatrixMultiply +procedure TTestNumberServices.TestMatrixMultiply; // 25 +var + pdvinteger: integer; +begin + GetPythonEngine.Run_CommandAsString('c.Value=(aa @ bb)', single_input); + if PrintResults then GetPythonEngine.Run_CommandAsString('print(c)', single_input); + + pdvinteger := pdvc.Value; + Assert.AreEqual(pdvinteger, not (pdvainteger * pdvbinteger)); // not really a matrix mult, but who cares! +end; + +// nsBool +procedure TTestNumberServices.TestBool; // 26 +var + pdvinteger: Integer; +begin + GetPythonEngine.Run_CommandAsString('c.Value= bool(aa)', single_input); + if PrintResults then GetPythonEngine.Run_CommandAsString('print(c)', single_input); + + pdvinteger := pdvc.Value; + Assert.AreEqual(pdvinteger=0, pdvainteger=0) +end; + +initialization + TDUnitX.RegisterTestFixture(TTestNumberServices); + ReportMemoryLeaksOnShutdown := True; + +end. diff --git a/Tests/FMX/Android/P4DAndroidTest.dpr b/Tests/FMX/Android/P4DAndroidTest.dpr new file mode 100644 index 00000000..c711bef2 --- /dev/null +++ b/Tests/FMX/Android/P4DAndroidTest.dpr @@ -0,0 +1,18 @@ +program P4DAndroidTest; +uses + System.StartUpCopy, + FMX.Forms, + DUNitX.Loggers.MobileGUI, + MethodCallBackTest in 'MethodCallBackTest.pas', + NumberServicesTest in 'NumberServicesTest.pas', + PyEnvTest in 'PyEnvTest.pas', + PythonLoad in 'PythonLoad.pas', + VarPythTest in 'VarPythTest.pas', + WrapDelphiTest in 'WrapDelphiTest.pas'; + +{$R *.res} +begin + Application.Initialize; + Application.CreateForm(TMobileGUITestRunner, MobileGUITestRunner); + Application.Run; +end. diff --git a/Tests/FMX/Android/P4DAndroidTest.dproj b/Tests/FMX/Android/P4DAndroidTest.dproj new file mode 100644 index 00000000..c82d4739 --- /dev/null +++ b/Tests/FMX/Android/P4DAndroidTest.dproj @@ -0,0 +1,1800 @@ + + + {652D9A8E-1625-4F00-9B29-2E3D31D7A186} + 19.2 + FMX + True + Debug + Android + 37915 + Application + P4DAndroidTest.dpr + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + .\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + true + true + true + true + true + true + true + true + $(BDS)\bin\delphi_PROJECTICON.ico + $(BDS)\bin\delphi_PROJECTICNS.icns + P4DAndroidTest + + + DBXSqliteDriver;RESTComponents;DBXInterBaseDriver;emsclientfiredac;tethering;DataSnapFireDAC;bindcompfmx;fmx;FireDACIBDriver;FireDACDBXDriver;dbexpress;IndyCore;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;soapserver;bindengine;CloudService;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;soaprtl;DbxCommonDriver;xmlrtl;soapmidas;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + true + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_192x192.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png + android-support-v4.dex.jar;cloud-messaging.dex.jar;com-google-android-gms.play-services-ads-base.17.2.0.dex.jar;com-google-android-gms.play-services-ads-identifier.16.0.0.dex.jar;com-google-android-gms.play-services-ads-lite.17.2.0.dex.jar;com-google-android-gms.play-services-ads.17.2.0.dex.jar;com-google-android-gms.play-services-analytics-impl.16.0.8.dex.jar;com-google-android-gms.play-services-analytics.16.0.8.dex.jar;com-google-android-gms.play-services-base.16.0.1.dex.jar;com-google-android-gms.play-services-basement.16.2.0.dex.jar;com-google-android-gms.play-services-gass.17.2.0.dex.jar;com-google-android-gms.play-services-identity.16.0.0.dex.jar;com-google-android-gms.play-services-maps.16.1.0.dex.jar;com-google-android-gms.play-services-measurement-base.16.4.0.dex.jar;com-google-android-gms.play-services-measurement-sdk-api.16.4.0.dex.jar;com-google-android-gms.play-services-stats.16.0.1.dex.jar;com-google-android-gms.play-services-tagmanager-v4-impl.16.0.8.dex.jar;com-google-android-gms.play-services-tasks.16.0.1.dex.jar;com-google-android-gms.play-services-wallet.16.0.1.dex.jar;com-google-firebase.firebase-analytics.16.4.0.dex.jar;com-google-firebase.firebase-common.16.1.0.dex.jar;com-google-firebase.firebase-iid-interop.16.0.1.dex.jar;com-google-firebase.firebase-iid.17.1.1.dex.jar;com-google-firebase.firebase-measurement-connector.17.0.1.dex.jar;com-google-firebase.firebase-messaging.17.5.0.dex.jar;fmx.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar + + + DBXSqliteDriver;RESTComponents;DBXInterBaseDriver;emsclientfiredac;tethering;DataSnapFireDAC;bindcompfmx;fmx;FireDACIBDriver;FireDACDBXDriver;dbexpress;IndyCore;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;soapserver;bindengine;CloudService;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;soaprtl;DbxCommonDriver;xmlrtl;soapmidas;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + true + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_192x192.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png + android-support-v4.dex.jar;cloud-messaging.dex.jar;com-google-android-gms.play-services-ads-base.17.2.0.dex.jar;com-google-android-gms.play-services-ads-identifier.16.0.0.dex.jar;com-google-android-gms.play-services-ads-lite.17.2.0.dex.jar;com-google-android-gms.play-services-ads.17.2.0.dex.jar;com-google-android-gms.play-services-analytics-impl.16.0.8.dex.jar;com-google-android-gms.play-services-analytics.16.0.8.dex.jar;com-google-android-gms.play-services-base.16.0.1.dex.jar;com-google-android-gms.play-services-basement.16.2.0.dex.jar;com-google-android-gms.play-services-gass.17.2.0.dex.jar;com-google-android-gms.play-services-identity.16.0.0.dex.jar;com-google-android-gms.play-services-maps.16.1.0.dex.jar;com-google-android-gms.play-services-measurement-base.16.4.0.dex.jar;com-google-android-gms.play-services-measurement-sdk-api.16.4.0.dex.jar;com-google-android-gms.play-services-stats.16.0.1.dex.jar;com-google-android-gms.play-services-tagmanager-v4-impl.16.0.8.dex.jar;com-google-android-gms.play-services-tasks.16.0.1.dex.jar;com-google-android-gms.play-services-wallet.16.0.1.dex.jar;com-google-firebase.firebase-analytics.16.4.0.dex.jar;com-google-firebase.firebase-common.16.1.0.dex.jar;com-google-firebase.firebase-iid-interop.16.0.1.dex.jar;com-google-firebase.firebase-iid.17.1.1.dex.jar;com-google-firebase.firebase-measurement-connector.17.0.1.dex.jar;com-google-firebase.firebase-messaging.17.5.0.dex.jar;fmx.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar + + + DBXSqliteDriver;RESTComponents;fmxase;DBXInterBaseDriver;emsclientfiredac;tethering;DataSnapFireDAC;bindcompfmx;fmx;FireDACIBDriver;FireDACDBXDriver;dbexpress;IndyCore;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;soapserver;bindengine;CloudService;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;soaprtl;DbxCommonDriver;xmlrtl;soapmidas;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSPhotoLibraryAddUsageDescription=The reason for adding to the photo library;NSCameraUsageDescription=The reason for accessing the camera;NSFaceIDUsageDescription=The reason for accessing the face id;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSSiriUsageDescription=The reason for accessing Siri;ITSAppUsesNonExemptEncryption=false;NSBluetoothAlwaysUsageDescription=The reason for accessing bluetooth;NSBluetoothPeripheralUsageDescription=The reason for accessing bluetooth peripherals;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSMotionUsageDescription=The reason for accessing the accelerometer;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers + iPhoneAndiPad + true + Debug + $(MSBuildProjectName) + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_1024x1024.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_180x180.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImageDark_2x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_3x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImageDark_3x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SettingIcon_58x58.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SettingIcon_87x87.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_NotificationIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_NotificationIcon_60x60.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImage_2x.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageDark_2x.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SettingIcon_58x58.png + $(BDS)\bin\Artwork\iOS\iPad\FM_NotificationIcon_40x40.png + + + DBXSqliteDriver;RESTComponents;fmxase;DBXInterBaseDriver;emsclientfiredac;tethering;DataSnapFireDAC;bindcompfmx;fmx;FireDACIBDriver;FireDACDBXDriver;dbexpress;IndyCore;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;soapserver;bindengine;CloudService;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;soaprtl;DbxCommonDriver;xmlrtl;soapmidas;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSPhotoLibraryAddUsageDescription=The reason for adding to the photo library;NSCameraUsageDescription=The reason for accessing the camera;NSFaceIDUsageDescription=The reason for accessing the face id;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSSiriUsageDescription=The reason for accessing Siri;ITSAppUsesNonExemptEncryption=false;NSBluetoothAlwaysUsageDescription=The reason for accessing bluetooth;NSBluetoothPeripheralUsageDescription=The reason for accessing bluetooth peripherals;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSMotionUsageDescription=The reason for accessing the accelerometer;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers + iPhoneAndiPad + true + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_1024x1024.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_180x180.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImageDark_2x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_3x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImageDark_3x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SettingIcon_58x58.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SettingIcon_87x87.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_NotificationIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_NotificationIcon_60x60.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImage_2x.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageDark_2x.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SettingIcon_58x58.png + $(BDS)\bin\Artwork\iOS\iPad\FM_NotificationIcon_40x40.png + 10.0 + + + DBXSqliteDriver;RESTComponents;fmxase;DBXInterBaseDriver;emsclientfiredac;tethering;DataSnapFireDAC;FireDACMSSQLDriver;bindcompfmx;DBXOracleDriver;inetdb;fmx;FireDACIBDriver;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;soapserver;bindengine;DBXMySQLDriver;CloudService;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;FireDACPgDriver;FireDACASADriver;FireDACTDataDriver;soaprtl;DbxCommonDriver;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;FireDACDSDriver;rtl;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;bindcomp;DBXInformixDriver;IndyIPClient;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage) + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSCameraUsageDescription=The reason for accessing the camera;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSMotionUsageDescription=The reason for accessing the accelerometer;NSDesktopFolderUsageDescription=The reason for accessing the Desktop folder;NSDocumentsFolderUsageDescription=The reason for accessing the Documents folder;NSDownloadsFolderUsageDescription=The reason for accessing the Downloads folder;NSNetworkVolumesUsageDescription=The reason for accessing files on a network volume;NSRemovableVolumesUsageDescription=The reason for accessing files on a removable volume;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers + Debug + true + + + DBXSqliteDriver;RESTComponents;fmxase;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;bindcompvclsmp;emsclientfiredac;tethering;svnui;DataSnapFireDAC;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;svn;DBXOracleDriver;inetdb;emsedge;fmx;FireDACIBDriver;fmxdae;vcledge;FireDACDBXDriver;dbexpress;IndyCore;vclx;Python;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;DataSnapConnectors;VCLRESTComponents;soapserver;vclie;bindengine;DBXMySQLDriver;CloudService;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;vcl;IndyIPServer;DBXSybaseASEDriver;IndySystem;FireDACDb2Driver;bindcompvclwinx;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;FireDAC;emshosting;FireDACSqliteDriver;FireDACPgDriver;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;soaprtl;DbxCommonDriver;PythonVcl;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;vclwinx;FireDACDSDriver;rtl;emsserverresource;DbxClientDriver;PythonFmx;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage) + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + $(BDS)\bin\default_app.manifest + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + DBXSqliteDriver;RESTComponents;fmxase;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;bindcompvclsmp;emsclientfiredac;tethering;DataSnapFireDAC;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;DBXOracleDriver;inetdb;emsedge;fmx;FireDACIBDriver;fmxdae;vcledge;FireDACDBXDriver;dbexpress;IndyCore;vclx;Python;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;DataSnapConnectors;VCLRESTComponents;soapserver;vclie;bindengine;DBXMySQLDriver;CloudService;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;vcl;IndyIPServer;DBXSybaseASEDriver;IndySystem;FireDACDb2Driver;bindcompvclwinx;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;FireDAC;emshosting;FireDACSqliteDriver;FireDACPgDriver;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;soaprtl;DbxCommonDriver;PythonVcl;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;vclwinx;FireDACDSDriver;rtl;emsserverresource;DbxClientDriver;PythonFmx;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage) + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + $(BDS)\bin\default_app.manifest + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + 1 + C:\Users\User\Documents\Embarcadero\Studio\Projects\Embarcadero\python4delphi\Source\;$(Debugger_DebugSourcePath) + #000000 + + + 1 + #000000 + + + false + true + PerMonitorV2 + + + true + PerMonitorV2 + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + 1 + #000000 + + + 1 + #000000 + + + true + PerMonitorV2 + + + true + PerMonitorV2 + + + + MainSource + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Application + + + + P4DAndroidTest.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + + + ic_launcher.png + true + + + + + splash_image.png + true + + + + + library\lib\arm64-v8a\ + libpython3.9d.so + true + + + + + splash_image.png + true + + + + + ic_launcher.png + true + + + + + ic_notification.png + true + + + + + library\lib\armeabi-v7a\ + libpython3.9d.so + true + + + + + ic_launcher.png + true + + + + + true + + + + + ic_notification.png + true + + + + + classes.dex + true + + + + + styles.xml + true + + + + + true + + + + + ic_notification.png + true + + + + + splash_image.png + true + + + + + libPyLoad.so + true + + + + + ic_notification.png + true + + + + + ic_notification.png + true + + + + + true + + + + + true + + + + + ic_notification.png + true + + + + + ic_launcher.png + true + + + + + true + + + + + library\lib\armeabi-v7a\ + libpython3.9.so + true + + + + + true + + + + + ic_launcher.png + true + + + + + ic_launcher.png + true + + + + + ic_notification.png + true + + + + + true + + + + + ic_launcher.png + true + + + + + splash_image.png + true + + + + + libPyLoad.so + true + + + + + libP4DAndroidTest.so + true + + + + + splash_image.png + true + + + + + splash_image.png + true + + + + + libPyLoad.so + true + + + + + .\assets\internal + build.zip + true + + + + + splash_image.png + true + + + + + .\assets\internal + build.zip + false + + + + + ic_launcher.png + true + + + + + classes.dex + true + + + + + classes.dex + true + + + + + ic_notification.png + true + + + + + true + + + + + true + + + + + ic_launcher.png + true + + + + + styles.xml + true + + + + + ic_launcher.png + true + + + + + ic_launcher.png + true + + + + + ic_launcher.png + true + + + + + true + + + + + splash_image.png + true + + + + + splash_image.png + true + + + + + true + + + + + splash_image.png + true + + + + + true + + + + + splash_image.png + true + + + + + styles.xml + true + + + + + libPyLoad.so + true + + + + + true + + + + + ic_launcher.png + true + + + + + ic_notification.png + true + + + + + ic_launcher.png + true + + + + + libP4DAndroidTest.so + true + + + + + ic_notification.png + true + + + + + true + + + + + ic_notification.png + true + + + + + ic_launcher.png + true + + + + + ic_notification.png + true + + + + + libPyLoad.so + true + + + + + libP4DAndroidTest.so + true + + + + + libPyLoad.so + true + + + + + ic_notification.png + true + + + + + true + + + + + ic_notification.png + true + + + + + ic_notification.png + true + + + + + .\assets\internal + build.zip + false + + + + + true + + + + + libPyLoad.so + true + + + + + ic_launcher.png + true + + + + + ic_notification.png + true + + + + + libPyLoad.so + true + + + + + ic_notification.png + true + + + + + true + + + + + ic_launcher.png + true + + + + + ic_launcher.png + true + + + + + P4DAndroidTest.exe + true + + + + + ic_launcher.png + true + + + + + ic_notification.png + true + + + + + classes.dex + true + + + + + splash_image.png + true + + + + + ic_launcher.png + true + + + + + splash_image.png + true + + + + + splash_image.png + true + + + + + ic_notification.png + true + + + + + true + + + + + ic_launcher.png + true + + + + + splash_image.png + true + + + + + ic_launcher.png + true + + + + + libPyLoad.so + true + + + + + .\assets\internal + build.zip + true + + + + + library\lib\arm64-v8a\ + libpython3.9.so + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + ic_notification.png + true + + + + + ic_launcher.png + true + + + + + true + + + + + splash_image.png + true + + + + + true + + + + + libP4DAndroidTest.so + true + + + + + libPyLoad.so + true + + + + + ic_launcher.png + true + + + + + styles.xml + true + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + Contents\MacOS + 1 + .framework + + + Contents\MacOS + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + Contents\Resources\StartUp\ + 0 + + + Contents\Resources\StartUp\ + 0 + + + 0 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen + 64 + + + ..\$(PROJECTNAME).launchscreen + 64 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + Contents + 1 + + + Contents + 1 + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + Contents\MacOS + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + True + True + True + True + True + True + True + + + 12 + + + + + diff --git a/Tests/FMX/Android/PyEnvTest.pas b/Tests/FMX/Android/PyEnvTest.pas new file mode 100644 index 00000000..d76098b2 --- /dev/null +++ b/Tests/FMX/Android/PyEnvTest.pas @@ -0,0 +1,106 @@ +(**************************************************************************) +(* *) +(* Module: Unit 'PyEnvTest' Copyright (c) 2021 *) +(* *) +(* Lucas Moura Belo - lmbelo *) +(* lucas.belo@live.com *) +(* BH, Brazil *) +(* *) +(* PyScripter *) +(* e-mail: pyscripter@gmail.com *) +(* *) +(* Project pages: https://github.com/Embarcadero/python4delphi *) +(* https://github.com/pyscripter/python4delphi *) +(**************************************************************************) +(* Functionality: Test unit for Python's environment *) +(* *) +(* *) +(**************************************************************************) +(* This source code is distributed with no WARRANTY, for no reason or use.*) +(* Everyone is allowed to use and change this code free for his own tasks *) +(* and projects, as long as this header and its copyright text is intact. *) +(* For changed versions of this code, which are public distributed the *) +(* following additional conditions have to be fullfilled: *) +(* 1) The header has to contain a comment on the change and the author of *) +(* it. *) +(* 2) A copy of the changed source has to be sent to the above E-Mail *) +(* address or my then valid address, if this is possible to the *) +(* author. *) +(* The second condition has the target to maintain an up to date central *) +(* version of the component. If this condition is not acceptable for *) +(* confidential or legal reasons, everyone is free to derive a component *) +(* or to generate a diff file to my or other original sources. *) +(**************************************************************************) + +unit PyEnvTest; + +interface + +uses + DUnitX.TestFramework, PythonEngine; + +type + [TestFixture] + TPyEnvTest = class + private + FPythonEngine: TPythonEngine; + public + [SetupFixture] + procedure SetupFixture; + [TearDownFixture] + procedure TearDownFixture; + [Test] + procedure TestLibFile(); + [Test] + procedure TestZipFile(); + [Test] + procedure TestExtraction(); + [Test] + procedure TestConfigure(); + end; + +implementation + +uses + PythonLoad; + +{ TPyEnvTest } + +procedure TPyEnvTest.SetupFixture; +begin + FPythonEngine := TPythonEngine.Create(nil); + FPythonEngine.Name := 'PythonEngine'; +end; + +procedure TPyEnvTest.TearDownFixture; +begin + FPythonEngine.Free(); +end; + +procedure TPyEnvTest.TestConfigure; +begin + TPythonLoad.Configure(FPythonEngine); + FPythonEngine.LoadDll; + Assert.IsTrue(FPythonEngine.IsHandleValid()); +end; + +procedure TPyEnvTest.TestExtraction; +begin + TPythonLoad.Extract(); + Assert.IsTrue(TPythonLoad.HasPythonDist()); +end; + +procedure TPyEnvTest.TestLibFile; +begin + Assert.IsTrue(TPythonLoad.HasPythonLib()); +end; + +procedure TPyEnvTest.TestZipFile; +begin + Assert.IsTrue(TPythonLoad.HasPythonZip()); +end; + +initialization + TDUnitX.RegisterTestFixture(TPyEnvTest); + +end. diff --git a/Tests/FMX/Android/PythonLoad.pas b/Tests/FMX/Android/PythonLoad.pas new file mode 100644 index 00000000..30562a00 --- /dev/null +++ b/Tests/FMX/Android/PythonLoad.pas @@ -0,0 +1,135 @@ +(**************************************************************************) +(* *) +(* Module: Unit 'PythonLoad' Copyright (c) 2021 *) +(* *) +(* Lucas Moura Belo - lmbelo *) +(* lucas.belo@live.com *) +(* BH, Brazil *) +(* *) +(* PyScripter *) +(* e-mail: pyscripter@gmail.com *) +(* *) +(* Project pages: https://github.com/Embarcadero/python4delphi *) +(* https://github.com/pyscripter/python4delphi *) +(**************************************************************************) +(* Functionality: Load python distribution *) +(* *) +(* *) +(**************************************************************************) +(* This source code is distributed with no WARRANTY, for no reason or use.*) +(* Everyone is allowed to use and change this code free for his own tasks *) +(* and projects, as long as this header and its copyright text is intact. *) +(* For changed versions of this code, which are public distributed the *) +(* following additional conditions have to be fullfilled: *) +(* 1) The header has to contain a comment on the change and the author of *) +(* it. *) +(* 2) A copy of the changed source has to be sent to the above E-Mail *) +(* address or my then valid address, if this is possible to the *) +(* author. *) +(* The second condition has the target to maintain an up to date central *) +(* version of the component. If this condition is not acceptable for *) +(* confidential or legal reasons, everyone is free to derive a component *) +(* or to generate a diff file to my or other original sources. *) +(**************************************************************************) + +unit PythonLoad; + +interface + +uses + System.SysUtils, System.Zip, PythonEngine; + +type + TExtractEvent = reference to procedure(const AFolderExists: boolean; var AReplaceFiles: boolean); + TPythonLoad = class + public + class function GetPyZip(): string; static; + class function GetPyRoot(): string; static; + class function GetPyHome(): string; static; + class function GetPyBin(): string; static; + class function GetPyLib(): string; static; + + class function HasPythonLib(): boolean; static; + class function HasPythonZip(): boolean; static; + class function HasPythonDist(): boolean; static; + class procedure Extract(); static; + class procedure Configure(const APythonEngine: TPythonEngine); static; + end; + +implementation + +uses + System.IOUtils; + +const + PY_KNOWN_VER = 1; + +{ TPythonLoad } + +class procedure TPythonLoad.Extract(); +begin + var LPyRoot := TPythonLoad.GetPyRoot(); + var LPyZip := TPythonLoad.GetPyZip(); + + if not TDirectory.Exists(LPyRoot) then + TZipFile.ExtractZipFile(LPyZip, LPyRoot, nil); +end; + +class function TPythonLoad.GetPyBin: string; +begin + Result := TPath.Combine(GetPyHome(), 'bin'); +end; + +class function TPythonLoad.GetPyHome: string; +begin + Result := TPath.Combine(GetPyRoot(), 'usr'); +end; + +class function TPythonLoad.GetPyLib: string; +begin + Result := TPath.Combine(GetPyHome(), 'lib'); +end; + +class function TPythonLoad.GetPyRoot: string; +begin + Result := TPath.Combine(TPath.GetDocumentsPath(), 'build'); +end; + +class function TPythonLoad.GetPyZip: string; +begin + Result := TPath.Combine(TPath.GetDocumentsPath(), 'build.zip'); +end; + +class function TPythonLoad.HasPythonDist: boolean; +begin + Result := TDirectory.Exists(TPythonLoad.GetPyRoot()); +end; + +class function TPythonLoad.HasPythonLib: boolean; +begin + Result := TFile.Exists(TPath.Combine(TPath.GetLibraryPath(), + PYTHON_KNOWN_VERSIONS[PY_KNOWN_VER].DllName)); +end; + +class function TPythonLoad.HasPythonZip: boolean; +begin + Result := TFile.Exists(TPythonLoad.GetPyZip()); +end; + +class procedure TPythonLoad.Configure(const APythonEngine: TPythonEngine); +begin + APythonEngine.AutoLoad := False; + APythonEngine.UseLastKnownVersion := false; + APythonEngine.ProgramName := TPythonLoad.GetPyBin(); + APythonEngine.PythonHome := TPythonLoad.GetPyHome(); + APythonEngine.RegVersion := PYTHON_KNOWN_VERSIONS[PY_KNOWN_VER].RegVersion; + APythonEngine.DllName := PYTHON_KNOWN_VERSIONS[PY_KNOWN_VER].DllName; + APythonEngine.APIVersion := PYTHON_KNOWN_VERSIONS[PY_KNOWN_VER].APIVersion; + APythonEngine.FatalAbort := False; + APythonEngine.FatalMsgDlg := False; + APythonEngine.AutoFinalize := True; + APythonEngine.InitThreads := True; + APythonEngine.PyFlags := [pfInteractive]; +end; + +end. diff --git a/Tests/FMX/Android/VarPythTest.pas b/Tests/FMX/Android/VarPythTest.pas new file mode 100644 index 00000000..ca1e0043 --- /dev/null +++ b/Tests/FMX/Android/VarPythTest.pas @@ -0,0 +1,1084 @@ +(**************************************************************************) +(* *) +(* Module: Unit 'VarPythTest' Copyright (c) 2021 *) +(* *) +(* Lucas Moura Belo - lmbelo *) +(* lucas.belo@live.com *) +(* BH, Brazil *) +(* *) +(* PyScripter *) +(* e-mail: pyscripter@gmail.com *) +(* *) +(* Project pages: https://github.com/Embarcadero/python4delphi *) +(* https://github.com/pyscripter/python4delphi *) +(**************************************************************************) +(* Functionality: Test unit for variants *) +(* *) +(* *) +(**************************************************************************) +(* This source code is distributed with no WARRANTY, for no reason or use.*) +(* Everyone is allowed to use and change this code free for his own tasks *) +(* and projects, as long as this header and its copyright text is intact. *) +(* For changed versions of this code, which are public distributed the *) +(* following additional conditions have to be fullfilled: *) +(* 1) The header has to contain a comment on the change and the author of *) +(* it. *) +(* 2) A copy of the changed source has to be sent to the above E-Mail *) +(* address or my then valid address, if this is possible to the *) +(* author. *) +(* The second condition has the target to maintain an up to date central *) +(* version of the component. If this condition is not acceptable for *) +(* confidential or legal reasons, everyone is free to derive a component *) +(* or to generate a diff file to my or other original sources. *) +(**************************************************************************) + +unit VarPythTest; + +interface + +uses + DUnitX.TestFramework, + PythonEngine, + PythonLoad; + +type + {$M+} + [TestFixture] + TVarPythTest = class + private + FPythonEngine: TPythonEngine; + public + [SetupFixture] + procedure SetupFixture; + [TearDownFixture] + procedure TearDownFixture; + [Test] + procedure TestIterator; + [Test] + procedure TestIntegers; + [Test] + procedure TestFloats; + [Test] + procedure TestStrings; + [Test] + procedure TestSequences; + [Test] + procedure TestMappings; + [Test] + procedure TestDates; + [Test] + procedure TestObjects; + end; + +implementation + +uses + SysUtils, StrUtils, + Variants, + VarPyth; + +{ TVarPythTest } + +procedure TVarPythTest.SetupFixture; +begin + FPythonEngine := TPythonEngine.Create(nil); + FPythonEngine.Name := 'PythonEngine'; + TPythonLoad.Configure(FPythonEngine); + FPythonEngine.LoadDll; +end; +procedure TVarPythTest.TearDownFixture; +begin + FPythonEngine.Free(); +end; +procedure TVarPythTest.TestDates; +var + a, b, _timeMod : Variant; + c : Variant; + _date, _date2 : TDateTime; + _year, _month, _day : Word; + _year2, _month2, _day2 : Word; + _hour, _min, _sec, _msec : Word; + _hour2, _min2, _sec2, _msec2 : Word; +begin + _timeMod := Import('time'); // get the time module of Python + _date := Now; + DecodeDate( _date, _year, _month, _day ); + DecodeTime( _date, _hour, _min, _sec, _msec ); + b := _timeMod.localtime(_timeMod.time()); // same as Now in Delphi + a := VarPythonCreate(_date); + Assert.IsTrue( a.Length = 9 ); + Assert.IsTrue( a.GetItem(0) = _year ); + Assert.IsTrue( a.GetItem(1) = _month ); + Assert.IsTrue( a.GetItem(2) = _day ); + Assert.IsTrue( a.GetItem(3) = _hour ); + Assert.IsTrue( a.GetItem(4) = _min ); + Assert.IsTrue( a.GetItem(5) = _sec ); + Assert.IsTrue( b.Length = 9 ); + Assert.IsTrue( b.GetItem(0) = a.GetItem(0) ); + Assert.IsTrue( b.GetItem(1) = a.GetItem(1) ); + Assert.IsTrue( b.GetItem(2) = a.GetItem(2) ); + Assert.IsTrue( b.GetItem(3) = a.GetItem(3) ); + Assert.IsTrue( b.GetItem(4) = a.GetItem(4) ); + Assert.IsTrue( b.GetItem(5) = a.GetItem(5) ); + Assert.IsTrue( b.GetItem(6) = a.GetItem(6) ); + Assert.IsTrue( b.GetItem(7) = a.GetItem(7) ); + // don't test the 9th item of the tuple, because it's the daylight saving, + // and it's not computed by the Python for Delphi. + //Assert.IsTrue( b.GetItem(8) = a.GetItem(8) ); + _date2 := b; + DecodeDate( _date2, _year2, _month2, _day2 ); + DecodeTime( _date2, _hour2, _min2, _sec2, _msec2 ); + Assert.IsTrue( _year2 = _year ); + Assert.IsTrue( _month2 = _month ); + Assert.IsTrue( _day2 = _day ); + Assert.IsTrue( _hour2 = _hour ); + Assert.IsTrue( _min2 = _min ); + Assert.IsTrue( _sec2 = _sec ); + // test new datetime module + _timeMod := Import('datetime'); // get the datetime module of Python + //or _timeMod := DatetimeModule; // get the datetime module of Python + a := _timeMod.datetime(2002, 12, 30, 22, 15, 38, 827738); + Assert.IsTrue(VarIsPythonDateTime(a)); + Assert.IsTrue(VarIsPythonDate(a)); + Assert.IsTrue(not VarIsPythonTime(a)); + Assert.IsTrue(not VarIsPythonDateTimeDelta(a)); + Assert.IsTrue(a.year = 2002); + Assert.IsTrue(a.month = 12); + Assert.IsTrue(a.day = 30); + Assert.IsTrue(a.hour = 22); + Assert.IsTrue(a.minute = 15); + Assert.IsTrue(a.second = 38); + Assert.IsTrue(a.microsecond = 827738); + _date := a; + DecodeDate( _date, _year, _month, _day ); + DecodeTime( _date, _hour, _min, _sec, _msec ); + Assert.IsTrue(_year = 2002); + Assert.IsTrue(_month = 12); + Assert.IsTrue(_day = 30); + Assert.IsTrue(_hour = 22); + Assert.IsTrue(_min = 15); + Assert.IsTrue(_sec = 38); + Assert.IsTrue(_msec = 827738 div 1000); + a := _timeMod.date(2002, 12, 30); + Assert.IsTrue(not VarIsPythonDateTime(a)); + Assert.IsTrue(VarIsPythonDate(a)); + Assert.IsTrue(not VarIsPythonTime(a)); + Assert.IsTrue(not VarIsPythonDateTimeDelta(a)); + _date := a; + DecodeDate( _date, _year, _month, _day ); + DecodeTime( _date, _hour, _min, _sec, _msec ); + Assert.IsTrue(_year = 2002); + Assert.IsTrue(_month = 12); + Assert.IsTrue(_day = 30); + Assert.IsTrue(_hour = 0); + Assert.IsTrue(_min = 0); + Assert.IsTrue(_sec = 0); + Assert.IsTrue(_msec = 0); + Assert.IsTrue(a.year = 2002); + Assert.IsTrue(a.month = 12); + Assert.IsTrue(a.day = 30); + a := _timeMod.time(22, 15, 38, 827738); + Assert.IsTrue(not VarIsPythonDateTime(a)); + Assert.IsTrue(not VarIsPythonDate(a)); + Assert.IsTrue(VarIsPythonTime(a)); + Assert.IsTrue(not VarIsPythonDateTimeDelta(a)); + Assert.IsTrue(a.hour = 22); + Assert.IsTrue(a.minute = 15); + Assert.IsTrue(a.second = 38); + Assert.IsTrue(a.microsecond = 827738); + _date := a; + DecodeTime( _date, _hour, _min, _sec, _msec ); + Assert.IsTrue(_hour = 22); + Assert.IsTrue(_min = 15); + Assert.IsTrue(_sec = 38); + Assert.IsTrue(_msec = 827738 div 1000); + a := DatetimeModule.datetime(2002, 12, 30, 22, 15, 38, 827738); + b := _timeMod.datetime(2002, 12, 30, 22, 16, 38, 827738); + c := b - a; + Assert.IsTrue(VarIsPythonDateTimeDelta(c)); + Assert.IsTrue(c.days = 0); + Assert.IsTrue(c.seconds = 60); + Assert.IsTrue(c.microseconds = 0); + _date := c; + Assert.IsTrue(Trunc(_date)=0); + DecodeTime( _date, _hour, _min, _sec, _msec ); + Assert.IsTrue(_hour = 0); + Assert.IsTrue(_min = 1); + Assert.IsTrue(_sec = 0); + Assert.IsTrue(_msec = 0); + c := a - b; + Assert.IsTrue(VarIsPythonDateTimeDelta(c)); + Assert.IsTrue(c.days = -1); + Assert.IsTrue(c.seconds = 86340); + Assert.IsTrue(c.microseconds = 0); + _date := c; + Assert.IsTrue(Trunc(_date)=0); + Assert.IsTrue(_date<0); + DecodeTime( _date, _hour, _min, _sec, _msec ); + Assert.IsTrue(_hour = 0); + Assert.IsTrue(_min = 1); + Assert.IsTrue(_sec = 0); + Assert.IsTrue(_msec = 0); + c := a + (b-a); + Assert.IsTrue(VarIsPythonDateTime(c)); + Assert.IsTrue(c = b); + Assert.IsTrue(c <> a); + Assert.IsTrue(a < b); + Assert.IsTrue(b > a); + GetPythonEngine.DatetimeConversionMode := dcmToDatetime; + try + _date := EncodeDate(2003, 01, 28) + EncodeTime(12, 22, 33, 450); + a := VarPythonCreate(_date); + Assert.IsTrue(VarIsPythonDateTime(c)); + _date2 := a; + DecodeDate( _date, _year, _month, _day ); + DecodeTime( _date, _hour, _min, _sec, _msec ); + DecodeDate( _date2, _year2, _month2, _day2 ); + DecodeTime( _date2, _hour2, _min2, _sec2, _msec2 ); + Assert.IsTrue( _year2 = _year ); + Assert.IsTrue( _month2 = _month ); + Assert.IsTrue( _day2 = _day ); + Assert.IsTrue( _hour2 = _hour ); + Assert.IsTrue( _min2 = _min ); + Assert.IsTrue( _sec2 = _sec ); + Assert.IsTrue( _msec2 = _msec ); + Assert.IsTrue(a.year = 2003); + Assert.IsTrue(a.month = 01); + Assert.IsTrue(a.day = 28); + Assert.IsTrue(a.hour = 12); + Assert.IsTrue(a.minute = 22); + Assert.IsTrue(a.second = 33); + Assert.IsTrue(a.microsecond = 450000); + finally + GetPythonEngine.DatetimeConversionMode := dcmToTuple; + end; +end; +procedure TVarPythTest.TestFloats; +var + a, b, c : Variant; + dbl_a, dbl_b, dbl_c : Double; + int : Integer; +begin + // initialize the operands + dbl_a := 2.5; + a := VarPythonCreate(dbl_a); + Assert.IsTrue(VarIsPython(a)); + Assert.IsTrue(VarIsPythonNumber(a)); + Assert.IsTrue(VarIsPythonFloat(a)); + Assert.IsTrue(Double(a) = 2.5); + dbl_b := 3.2; + b := VarPythonCreate(dbl_b); + Assert.IsTrue(VarIsPython(b)); + Assert.IsTrue(VarIsPythonNumber(b)); + Assert.IsTrue(VarIsPythonFloat(b)); + Assert.IsTrue(Double(b) = dbl_b); // note that Assert.IsTrue(Double(b) = 3.2) fails. + // arithmetic operations + //---------------------- + // addition + c := a + b; + // check result of operation + Assert.IsTrue( Double(c) = (dbl_a + dbl_b) ); + // check that operation did not change the content of operands. + Assert.IsTrue(Double(a) = dbl_a); + Assert.IsTrue(Double(b) = dbl_b); + // now with a litteral + c := a + b + 1; + Assert.IsTrue( Double(c) = (dbl_a+dbl_b+1) ); + c := a + 1 + b; + Assert.IsTrue( Double(c) = (dbl_a+1+dbl_b) ); + c := 1 + a + b; + Assert.IsTrue( Double(c) = (1+dbl_a+dbl_b) ); + // substraction + c := b - a; + Assert.IsTrue( Double(c) = (dbl_b - dbl_a) ); + // now with a litteral + c := b - a - 1; + Assert.IsTrue( Double(c) = (dbl_b-dbl_a-1) ); + c := b - 1 - a; + Assert.IsTrue( Double(c) = (dbl_b-1-dbl_a) ); + c := 1 - b - a; + Assert.IsTrue( Double(c) = (1-dbl_b-dbl_a) ); + // multiplication + c := a * b; + dbl_c := dbl_a * dbl_b; + Assert.IsTrue( Double(c) = dbl_c ); + // now with a litteral + c := a * b * 2; + dbl_c := dbl_a * dbl_b * 2; + Assert.IsTrue( Double(c) = dbl_c ); + c := a * 2 * b; + dbl_c := dbl_a * 2 * dbl_b; + Assert.IsTrue( Double(c) = dbl_c ); + c := 2 * a * b; + dbl_c := 2 * dbl_a * dbl_b; + Assert.IsTrue( Double(c) = dbl_c ); + // division: in Python a division between 2 integers is the same as the integer division + c := b / a; + dbl_c := dbl_b / dbl_a; + Assert.IsTrue( Double(c) = dbl_c ); + // negation + c := -a; + Assert.IsTrue( Double(c) = -dbl_a ); + // comparisons + //------------ + // equal + c := a = b; + Assert.IsTrue(c = False); + c := a = a; + Assert.IsTrue(c = True); + Assert.IsTrue( a = dbl_a); + // not equal + c := a <> b; + Assert.IsTrue(c = True); + Assert.IsTrue( not (c = b) ); + c := a <> a; + Assert.IsTrue(c = False); + Assert.IsTrue( a = dbl_a); + // greater than + c := a > b; Assert.IsTrue(c = False); + c := b > a; Assert.IsTrue(c = True); + Assert.IsTrue( a > (dbl_a-1)); + // greater or equal than + c := a >= b; Assert.IsTrue(c = False); + c := b >= a; Assert.IsTrue(c = True); + c := a >= a; Assert.IsTrue(c = True); + Assert.IsTrue( a >= dbl_a ); + // less than + c := a < b; Assert.IsTrue(c = True); + c := b < a; Assert.IsTrue(c = False); + Assert.IsTrue( a < dbl_b); + // less or equal than + c := a <= b; Assert.IsTrue(c = True); + c := b <= a; Assert.IsTrue(c = False); + c := a <= a; Assert.IsTrue(c = True); + Assert.IsTrue( a <= dbl_a); + // parenthesis + c := a * ((a * b) / b); + dbl_c := dbl_a * ((dbl_a * dbl_b) / dbl_b); + Assert.IsTrue( c = dbl_c ); + // copy + c := a; + Assert.IsTrue( c = a); + Assert.IsTrue( VarIsSame(c, a) ); // checks if 2 variants share the same Python object. + // casts + int := a; + Assert.IsTrue(int = 2); +end; +procedure TVarPythTest.TestIntegers; +var + a, b, c : Variant; + big : Int64; +begin + // initialize the operands + a := VarPythonCreate(2); + Assert.IsTrue(VarIsPython(a)); + Assert.IsTrue(VarIsPythonNumber(a)); + Assert.IsTrue(VarIsPythonInteger(a)); + Assert.IsTrue(Integer(a) = 2); + b := VarPythonCreate(3); + Assert.IsTrue(VarIsPython(b)); + Assert.IsTrue(VarIsPythonNumber(b)); + Assert.IsTrue(VarIsPythonInteger(b)); + Assert.IsTrue(Integer(b) = 3); + // arithmetic operations + //---------------------- + // addition + c := a + b; + // check result of operation + Assert.IsTrue( Integer(c) = 5 ); + // check that operation did not change the content of operands. + Assert.IsTrue(Integer(a) = 2); + Assert.IsTrue(Integer(b) = 3); + // now with a litteral + c := a + b + 1; + Assert.IsTrue( Integer(c) = 6 ); + c := a + 1 + b; + Assert.IsTrue( Integer(c) = 6 ); + c := 1 + a + b; + Assert.IsTrue( Integer(c) = 6 ); + // substraction + c := b - a; + Assert.IsTrue( Integer(c) = 1 ); + // now with a litteral + c := b - a - 1; + Assert.IsTrue( Integer(c) = 0 ); + c := b - 1 - a; + Assert.IsTrue( Integer(c) = 0 ); + c := 1 - b - a; + Assert.IsTrue( Integer(c) = -4 ); + // multiplication + c := a * b; + Assert.IsTrue( Integer(c) = 6 ); + // now with a litteral + c := a * b * 2; + Assert.IsTrue( Integer(c) = 12 ); + c := a * 2 * b; + Assert.IsTrue( Integer(c) = 12 ); + c := 2 * a * b; + Assert.IsTrue( Integer(c) = 12 ); + // integer division + c := b div a; + Assert.IsTrue( Integer(c) = 1 ); + // division: in Python a division between 2 integers is the same as the integer division + c := b / a; + Assert.IsTrue( c = 1.5 ); + Assert.IsTrue( Integer(c) = 2 ); + // modulus + c := b mod a; + Assert.IsTrue( Integer(c) = 1 ); + c := BuiltinModule.divmod(b, a); // this returns a tuple whose first item is the result of the division, + // and second item the modulo. + if VarIsPythonSequence(c) and (c.Length = 2) then + begin + Assert.IsTrue(Integer(c.GetItem(0)) = 1); // division + Assert.IsTrue(Integer(c.GetItem(1)) = 1); // modulo + end; + // power + c := BuiltinModule.pow(a, b); + Assert.IsTrue(c = 8); + // negation + c := -a; + Assert.IsTrue( Integer(c) = -2 ); + // logical operations + //------------------ + // inverse + c := not a; // in python it would be: c = ~2 + Assert.IsTrue( Integer(c) = -3 ); + // shift left (<<) + c := a shl b; + Assert.IsTrue( Integer(c) = 16 ); + c := a shl 1; + Assert.IsTrue( Integer(c) = 4 ); + // shift right (>>) + c := a shl b; + c := c shr b; + Assert.IsTrue( Integer(c) = Integer(a) ); + c := b shr 1; + Assert.IsTrue( Integer(c) = 1 ); + // and + c := a and (a*5); + Assert.IsTrue( Integer(c) = Integer(a) ); + c := a and 6; + Assert.IsTrue( Integer(c) = Integer(a) ); + // or + c := a or b; + Assert.IsTrue( Integer(c) = 3 ); + c := a or 3; + Assert.IsTrue( Integer(c) = 3 ); + // xor + c := a xor b; + Assert.IsTrue( Integer(c) = 1 ); + c := a xor 3; + Assert.IsTrue( Integer(c) = 1 ); + // comparisons + //------------ + // equal + c := a = b; + Assert.IsTrue(c = False); + c := a = a; + Assert.IsTrue(c = True); + Assert.IsTrue( a = 2); + // not equal + c := a <> b; + Assert.IsTrue(c = True); + Assert.IsTrue( not (c = b) ); + c := a <> a; + Assert.IsTrue(c = False); + Assert.IsTrue( a = 2); + // greater than + c := a > b; Assert.IsTrue(c = False); + c := b > a; Assert.IsTrue(c = True); + Assert.IsTrue( a > 1); + // greater or equal than + c := a >= b; Assert.IsTrue(c = False); + c := b >= a; Assert.IsTrue(c = True); + c := a >= a; Assert.IsTrue(c = True); + Assert.IsTrue( a >= 2 ); + // less than + c := a < b; Assert.IsTrue(c = True); + c := b < a; Assert.IsTrue(c = False); + Assert.IsTrue( a < 6); + // less or equal than + c := a <= b; Assert.IsTrue(c = True); + c := b <= a; Assert.IsTrue(c = False); + c := a <= a; Assert.IsTrue(c = True); + Assert.IsTrue( a <= 2); + // parenthesis + c := a * ((a * b) div b); + Assert.IsTrue( c = a*2 ); + // copy + c := a; + Assert.IsTrue( c = a); + Assert.IsTrue( VarIsSame(c, a) ); // checks if 2 variants share the same Python object. + // test long long (Int64) + big := Int64(MaxInt)*4; + b := VarPythonCreate(big); + Assert.IsTrue( b = big ); + Assert.IsTrue( b <> big+1 ); + Assert.IsTrue( b > MaxInt ); + Assert.IsTrue( MaxInt < b ); + Assert.IsTrue( b+1 = big+1 ); + Assert.IsTrue( b*2 = big*2 ); + Assert.IsTrue( b div 2 = big div 2 ); + c := VarPythonCreate(True); + Assert.IsTrue(VarIsBool(c)); + Assert.IsTrue(VarIsTrue(c)); + c := VarPythonCreate(False); + Assert.IsTrue(VarIsBool(c)); + Assert.IsTrue(not VarIsTrue(c)); +end; +procedure TVarPythTest.TestIterator; +var + Module: Variant; + Count: integer; +begin + Count := 0; + for Module in VarPyIterate(SysModule.modules) do begin + Count := Count + 1; + Log(Module); + end; + Assert.IsTrue(Count = len(SysModule.modules)); +end; +procedure TVarPythTest.TestMappings; +var + a, b, c, keys, values : Variant; +begin + // initialize the operands + a := NewPythonDict; + Assert.IsTrue(VarIsPython(a)); + Assert.IsTrue(VarIsPythonMapping(a)); + Assert.IsTrue(VarIsPythonDict(a)); + // There is a bug in D2010 in which Char('a') gets translated to integer parameter + a.SetItem( string('a'), 1 ); + a.SetItem( string('b'), 2 ); + a.SetItem( string('c'), 3 ); + Assert.IsTrue(a.Length = 3); // this is a special property that does the same as: len(a) in Python + Assert.IsTrue(a.Length() = 3); // this is a special method that does the same as the special property + Assert.IsTrue(len(a) = 3); + Assert.IsTrue(a.GetItem(string('a')) = 1); // this is a special method that lets you do the same as: a[0] in Python + Assert.IsTrue(a.GetItem(string('b')) = 2); + Assert.IsTrue(a.GetItem(string('c')) = 3); + + + b := NewPythonDict; + Assert.IsTrue(VarIsPython(b)); + Assert.IsTrue(VarIsPythonMapping(b)); + Assert.IsTrue(VarIsPythonDict(b)); + b.SetItem( string('d'), 4 ); + b.SetItem( string('e'), 5 ); + b.SetItem( string('f'), 6 ); + Assert.IsTrue(b.Length = 3); + Assert.IsTrue(b.Length() = 3); + Assert.IsTrue(len(b) = 3); + Assert.IsTrue(b.GetItem(string('d')) = 4); + Assert.IsTrue(b.GetItem(string('e')) = 5); + Assert.IsTrue(b.GetItem(string('f')) = 6); + + // copy + c := a; + Assert.IsTrue( c = a); + Assert.IsTrue( VarIsSame(c, a) ); // checks if 2 variants share the same Python object. + + // dict methods + Assert.IsTrue( Boolean(a.__contains__(string('a'))) ); + Assert.IsTrue( not Boolean(a.__contains__('abc')) ); + keys := BuiltinModule.list(a.keys()); + keys.sort(); + Assert.IsTrue( keys = VarPythonCreate(VarArrayOf(['a', 'b', 'c']))); + values := BuiltinModule.list(a.values()); + values.sort(); + Assert.IsTrue( values = VarPythonCreate(VarArrayOf([1, 2, 3]))); + c := a; + c.DeleteItem(string('a')); + Assert.IsTrue( not Boolean(c.__contains__(string('a'))) ); + + // test string values + a := NewPythonDict; + a.SetItem( string('a'), 'Hello'); + a.SetItem( string('b'), 'World!'); + a.SetItem( string('c'), ''); + Assert.IsTrue(a.GetItem(string('a')) = 'Hello'); + Assert.IsTrue(a.GetItem(string('b')) = 'World!'); + Assert.IsTrue(a.GetItem(string('c')) = ''); +end; + +procedure TVarPythTest.TestObjects; +var + _main, f, a, b, c : Variant; + val : Integer; + _str : String; +const + Script = + 'class XYZ(object):' + sLineBreak + + ' pass' + sLineBreak + + '' + sLineBreak + + 'class Foo:' + sLineBreak + + ' def __init__(Self, Value=0):' + sLineBreak + + ' Self.Value = Value' + sLineBreak + + ' def __del__(Self):' + sLineBreak + + ' print("delete", Self)' + sLineBreak + + ' def __add__(self, other):' + sLineBreak + + ' return Foo(self.Value + other.Value)' + sLineBreak + + ' def Inc(Self, AValue = 1):' + sLineBreak + + ' Self.Value = Self.Value + AValue' + sLineBreak + + ' def GetSelf(Self):' + sLineBreak + + ' return Self' + sLineBreak + + ' def GetValue(Self):' + sLineBreak + + ' return Self.Value' + sLineBreak + + ' def SetABC(Self, A, B, C):' + sLineBreak + + ' Self.A = A' + sLineBreak + + ' Self.B = B' + sLineBreak + + ' Self.C = C' + sLineBreak + + ' def Add(Self, AFooInst):' + sLineBreak + + ' Self.Value = Self.Value + AFooInst.Value' + sLineBreak + + 'class Bar(Foo):' + sLineBreak + + ' def Inc(Self, AValue = 1):' + sLineBreak + + ' Self.Value = Self.Value - AValue' + sLineBreak + + 'def Add(a, b):' + sLineBreak + + ' return a + b' + sLineBreak + + 'def MakeList(a, b, c, d):' + sLineBreak + + ' return [a, b, c, d]' + sLineBreak + + '' + sLineBreak + + 'f = Foo()' + sLineBreak + + 'print("Created", f)' + sLineBreak + + 'f.Inc()' + sLineBreak + + 'f.Inc(2)' + sLineBreak + + 'b = Bar()' + sLineBreak + + 'b.Inc()' + sLineBreak + + 'b.Inc(2)'; + +begin + FPythonEngine.ExecString(Script); + _main := MainModule; + Assert.IsTrue( VarIsPythonModule(_main) ); + Assert.IsTrue( VarIsPythonModule(SysModule) ); + Assert.IsTrue( Import('sys').version = SysModule.version ); + Log(SysModule.version); + Assert.IsTrue( Boolean(SysModule.modules.Contains(GetPythonEngine.ExecModule)) ); // if __main__ in sys.modules + Assert.IsTrue( VarIsSameType(_main, SysModule) ); + Assert.IsTrue( _type(_main).__name__ = 'module'); + Assert.IsTrue( BuiltinModule.type(_main).__name__ = 'module'); + + Assert.IsTrue( VarIsPythonClass(_main.Foo) ); + Assert.IsTrue( VarIsPythonClass(_main.Bar) ); + Assert.IsTrue( VarIsPythonClass(_main.XYZ) ); + Assert.IsTrue( not VarIsPythonClass(_main.Foo.__add__) ); + Assert.IsTrue( not VarIsPythonClass(_main.f) ); + Assert.IsTrue( VarIsPythonCallable(_main.Foo) ); + Assert.IsTrue( VarIsPythonCallable(_main.Foo) ); + Assert.IsTrue( VarIsTrue(BuiltinModule.callable(_main.Foo)) ); + Assert.IsTrue( VarIsSame(_main.f.__class__, _main.Foo) ); + Assert.IsTrue( VarIsPythonMethod(_main.f.Inc) ); + Assert.IsTrue( VarIsPythonCallable(_main.f.Inc) ); + Assert.IsTrue( VarIsTrue(BuiltinModule.callable(_main.f.Inc)) ); + Assert.IsTrue( VarIsPythonFunction(_main.Add) ); + Assert.IsTrue( VarIsPythonCallable(_main.Add) ); + Assert.IsTrue( VarIsInstanceOf(_main.f, _main.Foo) ); + Assert.IsTrue( VarIsTrue(BuiltinModule.isinstance(_main.f, _main.Foo)) ); + Assert.IsTrue( VarIsSubclassOf(_main.Bar, _main.Foo) ); + Assert.IsTrue( VarIsTrue(BuiltinModule.issubclass(_main.Bar, _main.Foo)) ); + Assert.IsTrue( not VarIsSubclassOf(_main.Foo, _main.Bar) ); + Assert.IsTrue( VarIsInstanceOf(_main.b, _main.Foo) ); + Assert.IsTrue( not VarIsInstanceOf(_main.f, _main.Bar) ); + Assert.IsTrue( VarIsTrue( BuiltinModule.vars(_main).__contains__(string('f')) ) ); + Assert.IsTrue( VarIsTrue( BuiltinModule.dir(_main).Contains(string('f')) ) ); + + f := _main.Foo(); // new instance of class Foo + Log('Instanciate class Foo: ' + f); + f.Inc(); // call a method without any arg, because there's a default arg. + f.Inc(2); // call a method with one arg, overriding the default arg. + Assert.IsTrue( VarIsPythonNumber(f.Value) ); + Assert.IsTrue( VarIsPythonInteger(f.Value) ); + Assert.IsTrue( f.Value = _main.f.Value ); // compare the result with what we did in the script + Assert.IsTrue( f.GetValue() = _main.f.GetValue() ); // compare the result with what we did in the script + Assert.IsTrue( VarIsPython( f.GetSelf() ) ); + Assert.IsTrue( VarIsSame( f.GetSelf(), f ) ); + Assert.IsTrue( BuiltinModule.getattr(f, 'Value') = f.Value ); + // python (+) operator overloading + a := _main.Foo(10); + b := _main.Foo(5); + c := a + b; + Assert.IsTrue(a.Value = 10); + Assert.IsTrue(b.Value = 5); + Assert.IsTrue(c.Value = 15); + Log('Test -> a, b, c : ' + a.Value + ', ' + b.Value + ', ' + c.Value); + // cascading calls + Assert.IsTrue( f.GetSelf().GetSelf().GetSelf().GetSelf().GetValue() = _main.f.GetValue() ); + Assert.IsTrue( Boolean(f.__dict__.__contains__('Value')) ); + Assert.IsTrue( VarIsTrue( BuiltinModule.hasattr(f, 'Value') ) ); + _str := 'Value'; + Assert.IsTrue( Boolean(f.__dict__.__contains__(_str)) ); // check with a string var + Assert.IsTrue( Boolean( BuiltinModule.hasattr(f, _str) ) ); + val := f.Value; + f.Add(f); // passing itself as an argument + Assert.IsTrue( f.Value = val*2 ); + // check param order + f.SetABC(1, 2, 3); + Assert.IsTrue(f.A = 1); + Assert.IsTrue(f.B = 2); + Assert.IsTrue(f.C = 3); + // add a property to an instance + f.Z := 99; + Assert.IsTrue(f.Z = 99); + // add a var to a module + _main.Z := 99; + Assert.IsTrue(_main.Z = 99); + // check none + Assert.IsTrue( VarIsNone(None) ); + Assert.IsTrue( VarIsNone(VarPythonCreate([1, Null, 3]).GetItem(1)) ); // Null is casted to None + Assert.IsTrue( VarIsNone(VarPythonCreate([1, None, 3]).GetItem(1)) ); + Assert.IsTrue( VarIsNone(f.Inc()) ); + Assert.IsTrue( f.Inc() = None ); + Assert.IsTrue( not Boolean(None) ); // if not None: + Assert.IsTrue( not VarIsTrue(None) ); // if not None: + Assert.IsTrue( Boolean(f) ); // if f: + Assert.IsTrue( VarIsTrue(f) ); // if f: + + // call a function + Assert.IsTrue( _main.Add(2, 2) = 4 ); + // call a function with a mix of regular parameters and named parameters + f := _main.MakeList(1, 2, 3, 4); + Assert.IsTrue(VarIsPythonList(f)); + Assert.IsTrue(f.Length = 4); + Assert.IsTrue(f.GetItem(0) = 1); + Assert.IsTrue(f.GetItem(1) = 2); + Assert.IsTrue(f.GetItem(2) = 3); + Assert.IsTrue(f.GetItem(3) = 4); + f := _main.MakeList(1, d:=3, c:=4, b:=2); + Assert.IsTrue(VarIsPythonList(f)); + Assert.IsTrue(f.Length = 4); + Assert.IsTrue(f.GetItem(0) = 1); + Assert.IsTrue(f.GetItem(1) = 2); + Assert.IsTrue(f.GetItem(2) = 4); + Assert.IsTrue(f.GetItem(3) = 3); + f := _main.MakeList(1, 2, d:= 3, c:=4); + Assert.IsTrue(VarIsPythonList(f)); + Assert.IsTrue(f.Length = 4); + Assert.IsTrue(f.GetItem(0) = 1); + Assert.IsTrue(f.GetItem(1) = 2); + Assert.IsTrue(f.GetItem(2) = 4); + Assert.IsTrue(f.GetItem(3) = 3); + f := _main.MakeList(1, 2, 3, d:=4); + Assert.IsTrue(VarIsPythonList(f)); + Assert.IsTrue(f.Length = 4); + Assert.IsTrue(f.GetItem(0) = 1); + Assert.IsTrue(f.GetItem(1) = 2); + Assert.IsTrue(f.GetItem(2) = 3); + Assert.IsTrue(f.GetItem(3) = 4); + f := _main.MakeList(b:=1, a:=2, d:= 3, c:=4); + Assert.IsTrue(VarIsPythonList(f)); + Assert.IsTrue(f.Length = 4); + Assert.IsTrue(f.GetItem(0) = 2); + Assert.IsTrue(f.GetItem(1) = 1); + Assert.IsTrue(f.GetItem(2) = 4); + Assert.IsTrue(f.GetItem(3) = 3); +end; +procedure TVarPythTest.TestSequences; +var + a, b, c : Variant; + iter : Variant; + cpt : Integer; +begin + // initialize the operands + // you can either use the overloaded function with an array of const + // or use the VarArrayOf function that returns an array of variants that will + // be casted to a Python list. + a := VarPythonCreate([1, 2, 3]); + Assert.IsTrue(VarIsPython(a)); + Assert.IsTrue(VarIsPythonSequence(a)); + Assert.IsTrue(VarIsPythonList(a)); + Assert.IsTrue(a.Length = 3); // this is a special property that does the same as: len(a) in Python + Assert.IsTrue(a.Length() = 3); // this is a special method that does the same as the special property + Assert.IsTrue(len(a) = 3); + Assert.IsTrue(a.GetItem(0) = 1); // this is a special method that lets you do the same as: a[0] in Python + Assert.IsTrue(a.GetItem(1) = 2); + Assert.IsTrue(a.GetItem(2) = 3); + Assert.IsTrue(string(a) = '[1, 2, 3]'); + // indexed access using brackets when the sequence is a property of an object (module, instance...) + MainModule.a := VarPythonCreate([1, 2, 3]); + Assert.IsTrue(MainModule.a[1] = 2); + + b := VarPythonCreate(VarArrayOf([4, 5, 6])); + Assert.IsTrue(VarIsPython(b)); + Assert.IsTrue(VarIsPythonSequence(b)); + Assert.IsTrue(VarIsPythonList(b)); + Assert.IsTrue(b.Length = 3); + Assert.IsTrue(b.Length() = 3); + Assert.IsTrue(len(b) = 3); + Assert.IsTrue(b.GetItem(0) = 4); + Assert.IsTrue(b.GetItem(1) = 5); + Assert.IsTrue(b.GetItem(2) = 6); + Assert.IsTrue(string(b) = '[4, 5, 6]'); + // concatenation + c := a + b; + // check result of operation + Assert.IsTrue(string(c) = '[1, 2, 3, 4, 5, 6]'); + // check that operation did not change the content of operands. + Assert.IsTrue(string(a) = '[1, 2, 3]'); + Assert.IsTrue(string(b) = '[4, 5, 6]'); + // now with a litteral: note that with D6 SP1, we can't concatenate a custom variant with an var array of variants + c := a + b + VarPythonCreate(['Hello', 'World!', 3.14]); + Assert.IsTrue( string(c) = '[1, 2, 3, 4, 5, 6, ''Hello'', ''World!'', 3.14]' ); + c := a + VarPythonCreate(['Hello', 'World!', 3.14]) + b; + Assert.IsTrue( string(c) = '[1, 2, 3, ''Hello'', ''World!'', 3.14, 4, 5, 6]' ); + c := VarPythonCreate(['Hello', 'World!', 3.14]) + a + b; + Assert.IsTrue( string(c) = '[''Hello'', ''World!'', 3.14, 1, 2, 3, 4, 5, 6]' ); + + // multiplication + c := a * 3; // in Python the multiplication of sequence concatenates n times the sequence + Assert.IsTrue( string(c) = '[1, 2, 3, 1, 2, 3, 1, 2, 3]' ); + + // comparisons + //------------ + + // equal + c := a = b; + Assert.IsTrue(c = False); + c := a = a; + Assert.IsTrue(c = True); + Assert.IsTrue( string(a) = '[1, 2, 3]'); + + // not equal + c := a <> b; + Assert.IsTrue(c = True); + Assert.IsTrue( not (c = b) ); + c := a <> a; + Assert.IsTrue(c = False); + Assert.IsTrue( string(a) = '[1, 2, 3]'); + + // greater than + c := a > b; Assert.IsTrue(c = False); + c := b > a; Assert.IsTrue(c = True); + Assert.IsTrue( string(a) > '[1, 1, 1]'); + + // greater or equal than + c := a >= b; Assert.IsTrue(c = False); + c := b >= a; Assert.IsTrue(c = True); + c := a >= a; Assert.IsTrue(c = True); + Assert.IsTrue( string(a) >= '[1, 2, 3]' ); + + // less than + c := a < b; Assert.IsTrue(c = True); + c := b < a; Assert.IsTrue(c = False); + Assert.IsTrue( string(a) < '[4, 4, 4]'); + + // less or equal than + c := a <= b; Assert.IsTrue(c = True); + c := b <= a; Assert.IsTrue(c = False); + c := a <= a; Assert.IsTrue(c = True); + Assert.IsTrue( string(a) <= '[1, 2, 3]'); + + // copy + c := a; + Assert.IsTrue( c = a); + Assert.IsTrue( VarIsSame(c, a) ); // checks if 2 variants share the same Python object. + + // sequence methods: + c := b + a; + c.sort(); // note that you must you the parenthesis to distinguish the call between a method or a property. + Assert.IsTrue( c = (a+b) ); + + c := NewPythonList; // facility for building sequences + Assert.IsTrue( not VarIsTrue(c) ); // c is false because it's an empty collection + c.append(1); + c.append(2); + c.append(3); + Assert.IsTrue( VarIsTrue(c) ); // c is true because it's not an empty collection + Assert.IsTrue(c = a); + Assert.IsTrue( c.pop() = 3 ); + Assert.IsTrue( string(c) = '[1, 2]'); + + c := NewPythonList(3); // facility for building sequences + c.SetItem(0, 1); + c.SetItem(1, 2); + c.SetItem(2, 3); + Assert.IsTrue(c = a); + c.DeleteItem(1); + Assert.IsTrue(c = VarPythonCreate([1,3])); + + Assert.IsTrue(VarPythonCreate([1,2,3,4]).GetSlice(1, 3) = VarPythonCreate([2,3])); // same as x = [1,2,3,4]; x[1:3] + Assert.IsTrue(VarPythonCreate([1,2,3,4]).GetSlice(1, Ellipsis) = VarPythonCreate([2,3,4])); // same as x = [1,2,3,4]; x[1:] + Assert.IsTrue(VarPythonCreate([1,2,3,4]).GetSlice(1, -1) = VarPythonCreate([2,3])); // same as x = [1,2,3,4]; x[1:-1] + c := VarPythonCreate([1,2,3,4]); + c.SetSlice(1, 3, VarPythonCreate([7, 8, 9])); + Assert.IsTrue( c = VarPythonCreate([1, 7, 8, 9, 4]) ); + Assert.IsTrue( Boolean(c.Contains( 7 )) ); // same as 7 in c + Assert.IsTrue( not Boolean(c.Contains( 77 )) ); + c.DelSlice(1,3); + Assert.IsTrue( c = VarPythonCreate([1,9,4]) ); + + c := VarPythonCreate([1, 2, 3, 4], stTuple); // test a tuple + Assert.IsTrue( VarIsPythonTuple(c) ); + Assert.IsTrue( VarIsPythonSequence(c) ); + Assert.IsTrue( c.GetItem(1) = 2 ); + Assert.IsTrue( c.Length = 4 ); + c := NewPythonTuple(3); + c.SetItem(0, 1); + c.SetItem(1, 2); + c.SetItem(2, 3); + Assert.IsTrue( VarIsPythonTuple(c) ); + Assert.IsTrue( VarIsPythonSequence(c) ); + Assert.IsTrue( c.GetItem(1) = 2 ); + Assert.IsTrue( c.Length = 3 ); + + // test iterator + iter := BuiltinModule.iter(VarPythonCreate([1, 2, 3, 4], stTuple)); + Assert.IsTrue(VarIsPythonIterator(iter)); + Assert.IsTrue(iter.__next__() = 1); + Assert.IsTrue(iter.__next__() = 2); + Assert.IsTrue(iter.__next__() = 3); + Assert.IsTrue(iter.__next__() = 4); + try + iter.__next__(); + except + on E: EPyStopIteration do + begin + Assert.IsTrue(True); //Ok. + end + else + Assert.IsTrue(False, 'expected stop exception'); + end; + cpt := 0; + iter := VarPyth.iter(VarPythonCreate([1, 2, 3, 4], stTuple)); + Assert.IsTrue(VarIsPythonIterator(iter)); + try + while True do + begin + a := iter.__next__(); + Inc(cpt); + Assert.IsTrue(a = cpt); + end; + except + on E: EPyStopIteration do + begin + Assert.IsTrue(True); //Ok. + end + else + Assert.IsTrue(False, 'expected stop exception'); + end; + Assert.IsTrue(cpt = 4); +end; +procedure TVarPythTest.TestStrings; +var + a, b, c : Variant; + w : WideString; + _obj : PPyObject; +begin + // initialize the operands + a := VarPythonCreate('abc'); + Assert.IsTrue(VarIsPython(a)); + Assert.IsTrue(VarIsPythonString(a)); + Assert.IsTrue(string(a) = 'abc'); + b := VarPythonCreate('def'); + Assert.IsTrue(VarIsPython(b)); + Assert.IsTrue(VarIsPythonString(b)); + Assert.IsTrue(string(b) = 'def'); + // concatenation + c := a + b; + // check result of operation + Assert.IsTrue( string(c) = 'abcdef' ); + // check that operation did not change the content of operands. + Assert.IsTrue(string(a) = 'abc'); + Assert.IsTrue(string(b) = 'def'); + // now with a litteral + c := a + b + '!'; + Assert.IsTrue( string(c) = 'abcdef!' ); + c := a + '!' + b; + Assert.IsTrue( string(c) = 'abc!def' ); + c := '!' + a + b; + Assert.IsTrue( string(c) = '!abcdef' ); + // multiplication + c := a * 3; // in Python the multiplication of string concatenates n times the string + Assert.IsTrue( string(c) = 'abcabcabc' ); + // comparisons + //------------ + // equal + c := a = b; + Assert.IsTrue(c = False); + c := a = a; + Assert.IsTrue(c = True); + Assert.IsTrue( a = 'abc'); + // not equal + c := a <> b; + Assert.IsTrue(c = True); + Assert.IsTrue( not (c = b) ); + c := a <> a; + Assert.IsTrue(c = False); + Assert.IsTrue( a = 'abc'); + // greater than + c := a > b; Assert.IsTrue(c = False); + c := b > a; Assert.IsTrue(c = True); + Assert.IsTrue( a > 'aaa'); + // greater or equal than + c := a >= b; Assert.IsTrue(c = False); + c := b >= a; Assert.IsTrue(c = True); + c := a >= a; Assert.IsTrue(c = True); + Assert.IsTrue( a >= 'abc' ); + // less than + c := a < b; Assert.IsTrue(c = True); + c := b < a; Assert.IsTrue(c = False); + Assert.IsTrue( a < 'bbb'); + // less or equal than + c := a <= b; Assert.IsTrue(c = True); + c := b <= a; Assert.IsTrue(c = False); + c := a <= a; Assert.IsTrue(c = True); + Assert.IsTrue( a <= 'abc'); + // copy + c := a; + Assert.IsTrue( c = a); + Assert.IsTrue( VarIsSame(c, a) ); // checks if 2 variants share the same Python object. + // empty strings + a := VarPythonCreate(''); + Assert.IsTrue(a.length = 0); + Assert.IsTrue(a = ''); + Assert.IsTrue(string(a) = ''); + // Unicode strings + b := VarPythonEval( 'u"Hello world!"' ); + Assert.IsTrue( VarIsPythonUnicode(b) ); + w := FPythonEngine.PyUnicodeAsString(ExtractPythonObjectFrom(b)); + Assert.IsTrue( w = 'Hello world!'); + Assert.IsTrue( b = 'Hello world!'); + Assert.IsTrue( b <> a ); + _obj := FPythonEngine.PyUnicodeFromString(w); + try + c := VarPythonCreate( _obj ); + finally + FPythonEngine.Py_XDecRef(_obj); + end; + Assert.IsTrue(b = c); + Assert.IsTrue(c = w); + Assert.IsTrue( c = 'Hello world!'); + w := b; + Assert.IsTrue( b = w); + Assert.IsTrue( w = 'Hello world!'); + Assert.IsTrue( Length(w) = 12 ); + Assert.IsTrue( Length(w) = b.Length() ); + c := FPythonEngine.PyObjectAsVariant(ExtractPythonObjectFrom(b)); + Assert.IsTrue( c = b ); + Assert.IsTrue( c = w ); + Assert.IsTrue( c = 'Hello world!'); + Assert.IsTrue( VarType(c) and VarTypeMask = varUString ); + c := VarPythonCreate(w); + Assert.IsTrue( c = 'Hello world!'); + Assert.IsTrue( c = w ); + c := VarPythonCreate([w]); + Assert.IsTrue( VarIsPythonUnicode(c.GetItem(0)) ); + Assert.IsTrue( c.GetItem(0) = 'Hello world!'); + Assert.IsTrue( c.GetItem(0) = w ); + c := w; + b := VarPythonCreate(c); + Assert.IsTrue( VarIsPythonUnicode(b) ); + Assert.IsTrue( b = c ); + Assert.IsTrue( b = w ); + // empty strings + a := VarPythonEval( 'u""' ); + Assert.IsTrue(a.length = 0); + Assert.IsTrue(a = ''); + Assert.IsTrue(string(a) = ''); + Assert.IsTrue(WideString(a) = ''); +end; + +initialization + TDUnitX.RegisterTestFixture(TVarPythTest); + +end. diff --git a/Tests/FMX/Android/WrapDelphiTest.pas b/Tests/FMX/Android/WrapDelphiTest.pas new file mode 100644 index 00000000..453b6dda --- /dev/null +++ b/Tests/FMX/Android/WrapDelphiTest.pas @@ -0,0 +1,534 @@ +(**************************************************************************) +(* *) +(* Module: Unit 'WrapDelphiTest' Copyright (c) 2021 *) +(* *) +(* Lucas Moura Belo - lmbelo *) +(* lucas.belo@live.com *) +(* BH, Brazil *) +(* *) +(* PyScripter *) +(* e-mail: pyscripter@gmail.com *) +(* *) +(* Project pages: https://github.com/Embarcadero/python4delphi *) +(* https://github.com/pyscripter/python4delphi *) +(**************************************************************************) +(* Functionality: Test unit for WrapDelphi module *) +(* *) +(* *) +(**************************************************************************) +(* This source code is distributed with no WARRANTY, for no reason or use.*) +(* Everyone is allowed to use and change this code free for his own tasks *) +(* and projects, as long as this header and its copyright text is intact. *) +(* For changed versions of this code, which are public distributed the *) +(* following additional conditions have to be fullfilled: *) +(* 1) The header has to contain a comment on the change and the author of *) +(* it. *) +(* 2) A copy of the changed source has to be sent to the above E-Mail *) +(* address or my then valid address, if this is possible to the *) +(* author. *) +(* The second condition has the target to maintain an up to date central *) +(* version of the component. If this condition is not acceptable for *) +(* confidential or legal reasons, everyone is free to derive a component *) +(* or to generate a diff file to my or other original sources. *) +(**************************************************************************) + +unit WrapDelphiTest; + +interface + +uses + Types, + DUnitX.TestFramework, + PythonEngine, + WrapDelphi; + +type + TFruit = (Apple, Banana, Orange); + TFruits = set of TFruit; + + {$M+} + ITestInterface = interface(IInterface) + ['{AD50ADF2-2691-47CA-80AB-07AF1EDA8C89}'] + procedure SetString(const S: string); + function GetString: string; + end; + {$M-} + + TSubRecord = record + DoubleField: double; + end; + + TTestRecord = record + StringField: string; + SubRecord: TSubRecord; + procedure SetStringField(S: string); + end; + + TFruitDynArray = TArray; + TStaticArray = array[0..999] of Int64; + TTestRttiAccess = class + private + FFruit: TFruit; + FFruits: TFruits; + public + FruitField :TFruit; + FruitsField: TFruits; + StringField: string; + DoubleField: double; + ObjectField: TObject; + RecordField: TTestRecord; + InterfaceField: ITestInterface; + function GetData: TObject; + procedure BuyFruits(AFruits: TFruits); + procedure SellFruits(const AFruits: TFruitDynArray); + procedure SellFruitsInt(const AFruits:TIntegerDynArray); + function GetDynArray: TInt64DynArray; + function GetStaticArray: TStaticArray; + property Fruit: TFruit read FFruit write FFruit; + property Fruits: TFruits read FFruits write FFruits; + function SetStringField(var Value: Integer): string; overload; + function SetStringField(const Value: string): string; overload; + procedure PassVariantArray(const Value: Variant); + end; + + TTestInterfaceImpl = class(TInterfacedObject, ITestInterface) + private + FString: string; + procedure SetString(const S: string); + function GetString: string; + end; + + [TestFixture] + TTestWrapDelphi = class(TObject) + private + PythonEngine: TPythonEngine; + DelphiModule: TPythonModule; + PyDelphiWrapper: TPyDelphiWrapper; + Rtti_Var: Variant; + TestRttiAccess: TTestRttiAccess; + Rec: TTestRecord; + Rtti_Rec: Variant; + FTestInterface: ITestInterface; + Rtti_Interface: Variant; + public + [SetupFixture] + procedure SetupFixture; + [TearDownFixture] + procedure TearDownFixture; + [Test] + procedure TestEnumProperty; + [Test] + procedure TestSetProperty; + [Test] + procedure TestDoubleField; + [Test] + procedure TestEnumField; + [Test] + procedure TestSetField; + [Test] + procedure TestStringField; + [Test] + procedure TestSetProps; + [Test] + procedure TestObjectField; + [Test] + procedure TestMethodCall; + [Test] + procedure TestRecord; + [Test] + procedure TestRecordField; + [Test] + procedure TestInterface; + [Test] + procedure TestInterfaceField; + [Test] + procedure TestDynArrayParameters; + [Test] + procedure TestGetDynArray; + [Test] + procedure TestGetStaticArray; + [Test] + procedure TestMethodWithVarAndOverload; + [Test] + procedure TestFreeReturnedObject; + [Test] + procedure TestPassVariantArray; + end; + +implementation + +Uses + System.SysUtils, + System.Variants, + System.Classes, + System.Rtti, + VarPyth, + WrapDelphiClasses, + PythonLoad; + + +{ TTestRTTIAccess } + +procedure TTestRttiAccess.BuyFruits(AFruits: TFruits); +begin + Fruits := AFruits; +end; + +{ TTestWrapDelphi } + +procedure TTestWrapDelphi.TestFreeReturnedObject; +begin + PythonEngine.ExecString( + 'from delphi import rtti_var' + sLineBreak + + 'obj = rtti_var.GetData()' + sLineBreak + + 'obj.Free()' + ); + Assert.Pass; +end; + +procedure TTestWrapDelphi.SetupFixture; +var + Py : PPyObject; +begin + PythonEngine := TPythonEngine.Create(nil); + PythonEngine.Name := 'PythonEngine'; + TPythonLoad.Configure(PythonEngine); + + DelphiModule := TPythonModule.Create(nil); + + DelphiModule.Name := 'DelphiModule'; + DelphiModule.Engine := PythonEngine; + DelphiModule.ModuleName := 'delphi'; + + PyDelphiWrapper := TPyDelphiWrapper.Create(nil); + + PyDelphiWrapper.Name := 'PyDelphiWrapper'; + PyDelphiWrapper.Engine := PythonEngine; + PyDelphiWrapper.Module := DelphiModule; + + PythonEngine.LoadDll; + // Then wrap the an instance our TTestRTTIAccess + // It will allow us to to test access to public fields and methods as well + // public (as well as published) properties. + // This time we would like the object to be destroyed when the PyObject + // is destroyed, so we need to set its Owned property to True; + TestRttiAccess := TTestRTTIAccess.Create; + TestRttiAccess.InterfaceField := TTestInterfaceImpl.Create; + Py := PyDelphiWrapper.Wrap(TestRttiAccess, TObjectOwnership.soReference); + DelphiModule.SetVar('rtti_var', Py); + PythonEngine.Py_DecRef(Py); + Py := PyDelphiWrapper.WrapRecord(@Rec, TRttiContext.Create.GetType(TypeInfo(TTestRecord)) as TRttiStructuredType); + DelphiModule.SetVar('rtti_rec', Py); + PythonEngine.Py_DecRef(Py); + FTestInterface := TTestInterfaceImpl.Create; + Py := PyDelphiWrapper.WrapInterface(TValue.From(FTestInterface)); + DelphiModule.SetVar('rtti_interface', Py); + PythonEngine.Py_DecRef(Py); + PythonEngine.ExecString('from delphi import rtti_var, rtti_rec, rtti_interface'); + Rtti_Var := MainModule.rtti_var; + Rtti_Rec := MainModule.rtti_rec; + Rtti_Interface := MainModule.rtti_interface; +end; + +procedure TTestWrapDelphi.TearDownFixture; +begin + VarClear(Rtti_Var); + VarClear(Rtti_Rec); + VarClear(Rtti_Interface); + PythonEngine.Free; + PyDelphiWrapper.Free; + DelphiModule.Free; + TestRttiAccess.Free; +end; + +procedure TTestWrapDelphi.TestDoubleField; +begin + TestRttiAccess.DoubleField := 3.14; + Assert.AreEqual(double(TestRttiAccess.DoubleField), double(3.14)); + Rtti_Var.DoubleField := variant(double(1.23)); //implicitly cast to a variant to avoid a bug present in 10.4.2 + Assert.AreEqual(double(Rtti_Var.DoubleField), double(1.23)); +end; + +procedure TTestWrapDelphi.TestEnumField; +begin + TestRttiAccess.FruitField := Apple; + Assert.IsTrue(RTTI_var.FruitField = 'Apple'); + Rtti_Var.FruitField := 'Banana'; + Assert.IsTrue(TestRttiAccess.FruitField = Banana); +end; + +procedure TTestWrapDelphi.TestEnumProperty; +// Enumeration values are converted to/from strings +begin + TestRttiAccess.Fruit := Apple; + Assert.IsTrue(RTTI_var.Fruit = 'Apple'); + Rtti_Var.Fruit := 'Banana'; + Assert.IsTrue(TestRttiAccess.Fruit = Banana); +end; + +procedure TTestWrapDelphi.TestGetDynArray; +var + List: Variant; +begin + List := Rtti_Var.GetDynArray(); + Assert.IsTrue(VarIsPythonList(List)); + Assert.AreEqual(1000000, Integer(len(List))); + Assert.AreEqual(Int64(999999), Int64(PythonEngine.PyObjectAsVariant(PythonEngine.PyList_GetItem(ExtractPythonObjectFrom(List), 999999)))); +end; + +procedure TTestWrapDelphi.TestGetStaticArray; +var + List: Variant; +begin + List := Rtti_Var.GetStaticArray(); + Assert.IsTrue(VarIsPythonList(List)); + Assert.AreEqual(1000, Integer(len(List))); + Assert.AreEqual(Int64(999), Int64(PythonEngine.PyObjectAsVariant(PythonEngine.PyList_GetItem(ExtractPythonObjectFrom(List), 999)))); +end; + +procedure TTestWrapDelphi.TestInterface; +begin + Rtti_Interface.SetString('Test'); + Assert.IsTrue(Rtti_Interface.GetString() = 'Test'); +end; + +procedure TTestWrapDelphi.TestInterfaceField; +begin + Rtti_Interface.SetString('New Value'); + Assert.IsTrue(Rtti_Interface.GetString() = 'New Value'); + Rtti_Var.InterfaceField.SetString('Old Value'); + Assert.IsTrue(Rtti_Var.InterfaceField.GetString() = 'Old Value'); + // Assign interface + Rtti_Var.InterfaceField := Rtti_Interface; + Assert.IsTrue(Rtti_Var.InterfaceField.GetString() = 'New Value'); + Rtti_Var.InterfaceField := None; + Assert.IsTrue(VarIsNone(Rtti_Var.InterfaceField)); +end; + +procedure TTestWrapDelphi.TestMethodCall; +begin + TestRttiAccess.Fruits := []; + Assert.AreEqual(string(Rtti_Var.Fruits), '[]'); + Rtti_Var.BuyFruits(VarPythonCreate(['Apple', 'Banana'], stList)); + Assert.AreEqual(string(Rtti_Var.Fruits), '[''Apple'', ''Banana'']'); +end; + +procedure TTestWrapDelphi.TestObjectField; +{ + Demonstrating and testing: + Subclassing Delphi components in Python + Creating Delphi objects in Python + Assigning objects to object fields +} +Var + Script: AnsiString; + myComp: Variant; +begin + Script := + 'from delphi import Component' + sLineBreak + + 'class MyComponent(Component):' + SLineBreak + + ' def __init__(self, Owner):' + SLineBreak + + ' self._x = None' + SLineBreak + + '' + SLineBreak + + ' @property' + SLineBreak + + ' def x(self):' + SLineBreak + + ' return self._x' + SLineBreak + + '' + SLineBreak + + ' @x.setter' + SLineBreak + + ' def x(self, value):' + SLineBreak + + ' self._x = value' + SLineBreak + + '' + SLineBreak + + 'myComp = MyComponent(None)'; + ; + + PythonEngine.ExecString(Script); + myComp := MainModule.myComp; + // accessing inherited property + Assert.IsTrue(myComp.Name = ''); + myComp.Name := 'NoName'; + Assert.IsTrue(myComp.Name = 'NoName'); + // accessing subclass property + myComp.x := variant(double(3.14)); //implicitly cast to a variant to avoid a bug present in 10.4.2 + Assert.IsTrue(myComp.x = 3.14); + + // Setting an object field + rtti_var.ObjectField := myComp; + Assert.IsTrue(rtti_var.ObjectField.Name = 'NoName'); + Assert.AreEqual(TComponent(TestRttiAccess.ObjectField).Name, 'NoName'); + rtti_var.ObjectField := None; + Assert.IsTrue(rtti_var.ObjectField = None); +end; + +procedure TTestWrapDelphi.TestPassVariantArray; +begin + PythonEngine.ExecString( + 'from delphi import rtti_var' + sLineBreak + + 'rtti_var.PassVariantArray([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])' + ); + Assert.Pass; +end; + +procedure TTestWrapDelphi.TestRecord; +begin + Rtti_rec.StringField := 'abcd'; + Assert.IsTrue(rtti_rec.StringField = 'abcd'); + Rtti_rec.SetStringField('1234'); + Assert.IsTrue(rtti_rec.StringField = '1234'); + Assert.AreEqual(Rec.StringField, '1234'); + Rtti_rec.SubRecord.DoubleField := variant(double(3.14)); //implicitly cast to a variant to avoid a bug present in 10.4.2 + Assert.IsTrue(rtti_rec.SubRecord.DoubleField = 3.14); + Assert.AreEqual(Rec.SubRecord.DoubleField, 3.14); +end; + +procedure TTestWrapDelphi.TestRecordField; +Var + RecValue: Variant; +begin + RecValue := rtti_var.RecordField; + RecValue.StringField := 'abc'; + rtti_var.RecordField := RecValue; + Assert.IsTrue(rtti_var.RecordField.StringField = 'abc'); +end; + +procedure TTestWrapDelphi.TestSetField; +// Sets are converted to/from list of strings +begin + TestRttiAccess.FruitsField := []; + Assert.AreEqual(string(Rtti_Var.FruitsField), '[]'); + Rtti_Var.FruitsField := VarPythonCreate(['Apple', 'Banana'], stList); + Assert.AreEqual(string(Rtti_Var.FruitsField), '[''Apple'', ''Banana'']'); + Assert.IsTrue(TestRttiAccess.FruitsField = [Apple, Banana]); +end; + +procedure TTestWrapDelphi.TestSetProperty; +begin + TestRttiAccess.Fruits := []; + Assert.AreEqual(string(Rtti_Var.Fruits), '[]'); + Rtti_Var.Fruits := VarPythonCreate(['Apple', 'Banana'], stList); + Assert.AreEqual(string(Rtti_Var.Fruits), '[''Apple'', ''Banana'']'); + Assert.IsTrue(TestRttiAccess.Fruits = [Apple, Banana]); +end; + +procedure TTestWrapDelphi.TestSetProps; +begin + rtti_var.SetProps(StringField := 'abc', DoubleField := 1.234); + Assert.AreEqual(TestRttiAccess.StringField, 'abc'); + Assert.AreEqual(TestRttiAccess.DoubleField, 1.234); +end; + +procedure TTestWrapDelphi.TestStringField; +begin + TestRttiAccess.StringField := 'Hi'; + Assert.AreEqual(string(Rtti_Var.StringField), 'Hi'); + Rtti_Var.StringField := 'P4D'; + Assert.AreEqual(TestRttiAccess.StringField, 'P4D'); +end; + +procedure TTestWrapDelphi.TestDynArrayParameters; +{var + rc: TRttiContext; + rt: TRttiType; + rm: TRttiMethod; + rp: TArray; + ra: TArray;} +begin +{ rc := TRttiContext.Create; + rt := rc.GetType(TypeInfo(TTestRttiAccess)); + rm := rt.GetMethod('SellFruitsInt'); + rp := rm.GetParameters; + SetLength(ra, 1); + ra[0] := TValue.FromArray(TypeInfo(TIntegerDynArray), [TValue.From(0)]); + rm.Invoke(TestRttiAccess, ra);} + TestRttiAccess.Fruits := [TFruit.Apple, TFruit.Banana, TFruit.Orange]; + Rtti_Var.SellFruitsInt(VarPythonCreate([0, 1], stList)); + Assert.IsTrue(TestRttiAccess.Fruits = [Orange]); + TestRttiAccess.Fruits := [TFruit.Apple, TFruit.Banana, TFruit.Orange]; + Rtti_Var.SellFruits(VarPythonCreate([Ord(TFruit.Apple), Ord(TFruit.Banana)], stList)); + Assert.IsTrue(TestRttiAccess.Fruits = [Orange]); +end; + +procedure TTestWrapDelphi.TestMethodWithVarAndOverload; +begin + Rtti_Var.SetStringField('test'); + Assert.AreEqual('test', TestRttiAccess.StringField); +end; + +function TTestRttiAccess.SetStringField(const Value: string): string; +begin + StringField := Value; + Result := StringField; +end; + +function TTestRttiAccess.SetStringField(var Value: Integer): string; +begin + StringField := IntToStr(Value); + Result := StringField; +end; + +function TTestRttiAccess.GetData: TObject; +begin + Result := TStringList.Create; +end; + +function TTestRttiAccess.GetDynArray: TInt64DynArray; +var + I: Integer; +begin + SetLength(Result, 1000000); + for I := 0 to Length(Result) - 1 do + Result[I] := I; +end; + +function TTestRttiAccess.GetStaticArray: TStaticArray; +var + I: Integer; +begin + for I := 0 to Length(Result) - 1 do + Result[I] := I; +end; + +procedure TTestRttiAccess.PassVariantArray(const Value: Variant); +begin + Assert.IsTrue(VarIsArray(Value) and (VarArrayHighBound(Value, 1) = 9)); +end; + +procedure TTestRttiAccess.SellFruits(const AFruits: TFruitDynArray); +var + Fruit: TFruit; +begin + for Fruit in AFruits do + Exclude(FFruits, Fruit); +end; + +procedure TTestRttiAccess.SellFruitsInt(const AFruits:TIntegerDynArray); +var + Fruit: Integer; +begin + for Fruit in AFruits do + Exclude(FFruits, TFruit(Fruit)); +end; + +{ TTestRecord } + +procedure TTestRecord.SetStringField(S: string); +begin + Self.StringField := S; +end; + +{ TTestInterfaceImpl } + +function TTestInterfaceImpl.GetString: string; +begin + Result := FString; +end; + +procedure TTestInterfaceImpl.SetString(const S: string); +begin + FString := S; +end; + +initialization + TDUnitX.RegisterTestFixture(TTestWrapDelphi); + ReportMemoryLeaksOnShutdown := True; + +end.