mirror of
https://github.com/danieleteti/delphimvcframework.git
synced 2024-11-15 15:55:54 +01:00
b22a41ca91
0b6857bd Updated unit tests and automatic tasks 35d130d6 VERSION_1_4_4 2e4a80f4 Removed PasDoc documentation; Added unit tests for LogLayoutToLogFormat 8f61bc22 Removed TLogAppenderOptions, DEFAULT_LOG_FILENAME_FORMAT is now TLogLayout.* (there many possibilities); FileName layout use placeholders instead of indices (as format function does). be43f781 Merge branch 'master' into v2.0 git-subtree-dir: lib/loggerpro git-subtree-split: 0b6857bd8d51f5acb246561564b3c3bbd963d192
615 lines
21 KiB
Python
615 lines
21 KiB
Python
from invoke import task, context, Exit
|
|
import os
|
|
import subprocess
|
|
from colorama import *
|
|
import glob
|
|
from shutil import copy2, rmtree, copytree
|
|
from datetime import datetime
|
|
import pathlib
|
|
from typing import *
|
|
|
|
from pathlib import Path
|
|
|
|
init()
|
|
|
|
g_releases_path = "releases"
|
|
g_output = "bin"
|
|
g_output_folder = "" # defined at runtime
|
|
g_version = "DEV"
|
|
|
|
|
|
|
|
delphi_versions = [
|
|
{"version": "10.0", "path": "17.0", "desc": "Delphi 10 Seattle"},
|
|
{"version": "10.1", "path": "18.0", "desc": "Delphi 10.1 Berlin"},
|
|
{"version": "10.2", "path": "19.0", "desc": "Delphi 10.2 Tokyo"},
|
|
{"version": "10.3", "path": "20.0", "desc": "Delphi 10.3 Rio"},
|
|
{"version": "10.4", "path": "21.0", "desc": "Delphi 10.4 Sydney"},
|
|
{"version": "11.0", "path": "22.0", "desc": "Delphi 11 Alexandria"},
|
|
{"version": "11.1", "path": "22.0", "desc": "Delphi 11.1 Alexandria"},
|
|
{"version": "11.2", "path": "22.0", "desc": "Delphi 11.2 Alexandria"},
|
|
{"version": "11.3", "path": "22.0", "desc": "Delphi 11.3 Alexandria"},
|
|
{"version": "12.0", "path": "23.0", "desc": "Delphi 12 Athens"},
|
|
]
|
|
|
|
|
|
def get_delphi_projects_to_build(which=""):
|
|
projects = []
|
|
delphi_version, _ = get_best_delphi_version_available()
|
|
dversion = "d" + delphi_version["version"].replace(".", "")
|
|
if not which or which == "core":
|
|
projects += glob.glob(
|
|
r"packages\{dversion}\*.groupproj".format(dversion=dversion)
|
|
)
|
|
projects += glob.glob(r"tools\entitygenerator\MVCAREntitiesGenerator.dproj")
|
|
if not which or which == "tests":
|
|
projects += glob.glob(r"unittests\**\*.dproj")
|
|
if not which or which == "samples":
|
|
projects += glob.glob(r"samples\**\*.dproj")
|
|
projects += glob.glob(r"samples\**\**\*.dproj")
|
|
projects += glob.glob(r"samples\**\**\**\*.dproj")
|
|
return sorted(projects)
|
|
|
|
|
|
def get_best_delphi_version_available() -> (dict, str):
|
|
global delphi_version
|
|
found = False
|
|
rsvars_path = None
|
|
i = len(delphi_versions)
|
|
while (not found) and (i >= 0):
|
|
i-=1
|
|
delphi_version = delphi_versions[i]
|
|
version_path = delphi_version["path"]
|
|
rsvars_path = f"C:\\Program Files (x86)\\Embarcadero\\Studio\\{version_path}\\bin\\rsvars.bat"
|
|
if os.path.isfile(rsvars_path):
|
|
found = True
|
|
else:
|
|
rsvars_path = f"D:\\Program Files (x86)\\Embarcadero\\Studio\\{version_path}\\bin\\rsvars.bat"
|
|
if os.path.isfile(rsvars_path):
|
|
found = True
|
|
if found:
|
|
return delphi_version, rsvars_path
|
|
else:
|
|
raise Exception("Cannot find a Delphi compiler")
|
|
|
|
|
|
def build_delphi_project(
|
|
ctx: context.Context, project_filename, config="DEBUG", platform="Win32"
|
|
):
|
|
delphi_version, rsvars_path = get_best_delphi_version_available()
|
|
print('\nBUILD WITH: ' + delphi_version["desc"])
|
|
cmdline = (
|
|
'"'
|
|
+ rsvars_path
|
|
+ '"'
|
|
+ " & msbuild /t:Build /p:Config="
|
|
+ config
|
|
+ f' /p:Platform={platform} "'
|
|
+ project_filename
|
|
+ '"'
|
|
)
|
|
r = ctx.run(cmdline, hide=True, warn=True)
|
|
if r.failed:
|
|
print(r.stdout)
|
|
print(r.stderr)
|
|
raise Exit("Build failed for " + delphi_version["desc"])
|
|
|
|
|
|
def zip_samples(version):
|
|
global g_output_folder
|
|
cmdline = (
|
|
"7z a "
|
|
+ g_output_folder
|
|
+ f"\\..\\{version}_samples.zip -r -i@7ziplistfile.txt"
|
|
)
|
|
return subprocess.call(cmdline, shell=True) == 0
|
|
|
|
|
|
def create_zip(ctx, version):
|
|
global g_output_folder
|
|
print("CREATING ZIP")
|
|
archive_name = "..\\" + version + ".zip"
|
|
switches = ""
|
|
files_name = "*"
|
|
cmdline = f"..\\..\\7z.exe a {switches} {archive_name} *"
|
|
print(cmdline)
|
|
with ctx.cd(g_output_folder):
|
|
ctx.run(cmdline, hide=False)
|
|
|
|
|
|
def copy_sources():
|
|
global g_output_folder
|
|
os.makedirs(g_output_folder + "\\sources", exist_ok=True)
|
|
os.makedirs(g_output_folder + "\\ideexpert", exist_ok=True)
|
|
os.makedirs(g_output_folder + "\\packages", exist_ok=True)
|
|
os.makedirs(g_output_folder + "\\tools", exist_ok=True)
|
|
# copying main sources
|
|
print("Copying DMVCFramework Sources...")
|
|
src = glob.glob("sources\\*.pas") + glob.glob("sources\\*.inc")
|
|
for file in src:
|
|
print("Copying " + file + " to " + g_output_folder + "\\sources")
|
|
copy2(file, g_output_folder + "\\sources\\")
|
|
|
|
# copying tools
|
|
print("Copying tools...")
|
|
copytree("tools\\entitygenerator", g_output_folder + "\\tools\\entitygenerator")
|
|
|
|
# copying ideexperts
|
|
print("Copying DMVCFramework IDEExpert...")
|
|
src = (
|
|
glob.glob("ideexpert\\*.pas")
|
|
+ glob.glob("ideexpert\\*.dfm")
|
|
+ glob.glob("ideexpert\\*.ico")
|
|
+ glob.glob("ideexpert\\*.bmp")
|
|
)
|
|
|
|
for file in src:
|
|
print("Copying " + file + " to " + g_output_folder + "\\ideexpert")
|
|
copy2(file, g_output_folder + "\\ideexpert\\")
|
|
|
|
files = [
|
|
"dmvcframeworkDTResource.rc",
|
|
"dmvcframework_group.groupproj",
|
|
"dmvcframeworkRT.dproj",
|
|
"dmvcframeworkRT.dpk",
|
|
"dmvcframeworkDT.dproj",
|
|
"dmvcframeworkDT.dpk",
|
|
]
|
|
|
|
folders = ["d100", "d101", "d102", "d103", "d104", "d110", "d113"]
|
|
|
|
for folder in folders:
|
|
print(f"Copying DMVCFramework Delphi {folder} packages...")
|
|
for file in files:
|
|
os.makedirs(g_output_folder + f"\\packages\\{folder}", exist_ok=True)
|
|
copy2(
|
|
rf"packages\{folder}\{file}", g_output_folder + rf"\packages\{folder}"
|
|
)
|
|
# copy2(
|
|
# rf"packages\common_contains.inc", g_output_folder + rf"\packages"
|
|
# )
|
|
# copy2(
|
|
# rf"packages\common_defines.inc", g_output_folder + rf"\packages"
|
|
# )
|
|
# copy2(
|
|
# rf"packages\common_defines_design.inc", g_output_folder + rf"\packages"
|
|
# )
|
|
|
|
|
|
def copy_libs(ctx):
|
|
global g_output_folder
|
|
|
|
# swagdoc
|
|
print("Copying libraries: SwagDoc...")
|
|
curr_folder = g_output_folder + "\\lib\\swagdoc"
|
|
os.makedirs(curr_folder, exist_ok=True)
|
|
if not ctx.run(rf"xcopy lib\swagdoc\*.* {curr_folder}\*.* /E /Y /R /V /F"):
|
|
raise Exception("Cannot copy SwagDoc")
|
|
|
|
# loggerpro
|
|
print("Copying libraries: LoggerPro...")
|
|
curr_folder = g_output_folder + "\\lib\\loggerpro"
|
|
os.makedirs(curr_folder, exist_ok=True)
|
|
if not ctx.run(rf"xcopy lib\loggerpro\*.* {curr_folder}\*.* /E /Y /R /V /F"):
|
|
raise Exception("Cannot copy loggerpro")
|
|
|
|
# dmustache
|
|
print("Copying libraries: dmustache...")
|
|
curr_folder = g_output_folder + "\\lib\\dmustache"
|
|
os.makedirs(curr_folder, exist_ok=True)
|
|
if not ctx.run(rf"xcopy lib\dmustache\*.* {curr_folder}\*.* /E /Y /R /V /F"):
|
|
raise Exception("Cannot copy dmustache")
|
|
|
|
|
|
def printkv(key, value):
|
|
print(Fore.RESET + key + ": " + Fore.GREEN + value.rjust(60) + Fore.RESET)
|
|
|
|
|
|
def init_build(version):
|
|
"""Required by all tasks"""
|
|
global g_version
|
|
global g_output_folder
|
|
global g_releases_path
|
|
g_version = version
|
|
g_output_folder = g_releases_path + "\\" + g_version
|
|
print()
|
|
print(Fore.RESET + Fore.RED + "*" * 80)
|
|
print(Fore.RESET + Fore.RED + " BUILD VERSION: " + g_version + Fore.RESET)
|
|
print(Fore.RESET + Fore.RED + " OUTPUT PATH : " + g_output_folder + Fore.RESET)
|
|
print(Fore.RESET + Fore.RED + "*" * 80)
|
|
|
|
rmtree(g_output_folder, True)
|
|
os.makedirs(g_output_folder, exist_ok=True)
|
|
f = open(g_output_folder + "\\version.txt", "w")
|
|
f.write("VERSION " + g_version + "\n")
|
|
f.write("BUILD DATETIME " + datetime.now().isoformat() + "\n")
|
|
f.close()
|
|
copy2("README.md", g_output_folder)
|
|
copy2("3_0_0_breaking_changes.md", g_output_folder)
|
|
copy2("3_1_0_breaking_changes.md", g_output_folder)
|
|
copy2("3_2_0_breaking_changes.md", g_output_folder)
|
|
copy2("License.txt", g_output_folder)
|
|
|
|
|
|
def build_delphi_project_list(
|
|
ctx, projects, config="DEBUG", filter=""
|
|
):
|
|
ret = True
|
|
for delphi_project in projects:
|
|
if filter and (not filter in delphi_project):
|
|
print(f"Skipped {os.path.basename(delphi_project)}")
|
|
continue
|
|
msg = f"Building: {os.path.basename(delphi_project)} ({config})"
|
|
print(Fore.RESET + msg.ljust(90, "."), end="")
|
|
try:
|
|
build_delphi_project(ctx, delphi_project, "DEBUG")
|
|
print(Fore.GREEN + "OK" + Fore.RESET)
|
|
except Exception as e:
|
|
print(Fore.RED + "\n\nBUILD ERROR")
|
|
print(Fore.RESET)
|
|
print(e)
|
|
|
|
# if res.ok:
|
|
# print(Fore.GREEN + "OK" + Fore.RESET)
|
|
# else:
|
|
# ret = False
|
|
# print(Fore.RED + "\n\nBUILD ERROR")
|
|
# print(Fore.RESET + res.stdout)
|
|
# print("\n")
|
|
|
|
return ret
|
|
|
|
|
|
@task
|
|
def clean(ctx, folder=None):
|
|
global g_output_folder
|
|
import os
|
|
import glob
|
|
|
|
if folder is None:
|
|
folder = g_output_folder
|
|
print(f"Cleaning folder {folder}")
|
|
output = pathlib.Path(folder)
|
|
to_delete = []
|
|
to_delete += glob.glob(folder + r"\**\*.exe", recursive=True)
|
|
to_delete += glob.glob(folder + r"\**\*.dcu", recursive=True)
|
|
to_delete += glob.glob(folder + r"\**\*.stat", recursive=True)
|
|
to_delete += glob.glob(folder + r"\**\*.res", recursive=True)
|
|
to_delete += glob.glob(folder + r"\**\*.map", recursive=True)
|
|
to_delete += glob.glob(folder + r"\**\*.~*", recursive=True)
|
|
to_delete += glob.glob(folder + r"\**\*.rsm", recursive=True)
|
|
to_delete += glob.glob(folder + r"\**\*.drc", recursive=True)
|
|
to_delete += glob.glob(folder + r"\**\*.log", recursive=True)
|
|
to_delete += glob.glob(folder + r"\**\*.local", recursive=True)
|
|
to_delete += glob.glob(folder + r"\**\*.gitignore", recursive=True)
|
|
to_delete += glob.glob(folder + r"\**\*.gitattributes", recursive=True)
|
|
|
|
for f in to_delete:
|
|
print(f"Deleting {f}")
|
|
os.remove(f)
|
|
|
|
rmtree(folder + r"\lib\loggerpro\Win32", True)
|
|
rmtree(folder + r"\lib\loggerpro\packages\d100\__history", True)
|
|
rmtree(folder + r"\lib\loggerpro\packages\d100\Win32\Debug", True)
|
|
rmtree(folder + r"\lib\loggerpro\packages\d101\__history", True)
|
|
rmtree(folder + r"\lib\loggerpro\packages\d101\Win32\Debug", True)
|
|
rmtree(folder + r"\lib\loggerpro\packages\d102\__history", True)
|
|
rmtree(folder + r"\lib\loggerpro\packages\d102\Win32\Debug", True)
|
|
rmtree(folder + r"\lib\loggerpro\packages\d103\__history", True)
|
|
rmtree(folder + r"\lib\loggerpro\packages\d103\Win32\Debug", True)
|
|
rmtree(folder + r"\lib\loggerpro\packages\d104\__history", True)
|
|
rmtree(folder + r"\lib\loggerpro\packages\d104\Win32\Debug", True)
|
|
rmtree(folder + r"\lib\loggerpro\packages\d110\__history", True)
|
|
rmtree(folder + r"\lib\loggerpro\packages\d110\Win32\Debug", True)
|
|
rmtree(folder + r"\lib\loggerpro\packages\d111\__history", True)
|
|
rmtree(folder + r"\lib\loggerpro\packages\d111\Win32\Debug", True)
|
|
rmtree(folder + r"\lib\loggerpro\packages\d112\__history", True)
|
|
rmtree(folder + r"\lib\loggerpro\packages\d112\Win32\Debug", True)
|
|
rmtree(folder + r"\lib\dmustache\.git", True)
|
|
rmtree(folder + r"\lib\swagdoc\lib", True)
|
|
rmtree(folder + r"\lib\swagdoc\deploy", True)
|
|
rmtree(folder + r"\lib\swagdoc\demos", True)
|
|
|
|
|
|
@task()
|
|
def tests32(ctx):
|
|
"""Builds and execute the unit tests"""
|
|
import os
|
|
|
|
apppath = os.path.dirname(os.path.realpath(__file__))
|
|
res = True
|
|
testclient = r"unittests\general\Several\DMVCFrameworkTests.dproj"
|
|
testserver = r"unittests\general\TestServer\TestServer.dproj"
|
|
|
|
print("\nBuilding Unit Test client")
|
|
build_delphi_project(
|
|
ctx, testclient, config="CI", platform="Win32"
|
|
)
|
|
print("\nBuilding Test Server")
|
|
build_delphi_project(
|
|
ctx, testserver, config="CI", platform="Win32"
|
|
)
|
|
|
|
# import subprocess
|
|
# subprocess.run([r"unittests\general\TestServer\Win32\Debug\TestServer.exe"])
|
|
# os.spawnl(os.P_NOWAIT, r"unittests\general\TestServer\Win32\Debug\TestServer.exe")
|
|
import subprocess
|
|
|
|
print("\nExecuting tests...")
|
|
subprocess.Popen([r"unittests\general\TestServer\bin\TestServer.exe"], shell=True)
|
|
r = None
|
|
try:
|
|
r = subprocess.run([r"unittests\general\Several\bin32\DMVCFrameworkTests.exe"])
|
|
if r.returncode != 0:
|
|
return Exit("Cannot run unit test client: \n" + str(r.stdout))
|
|
finally:
|
|
subprocess.run(["taskkill", "/f", "/im", "TestServer.exe"])
|
|
if r.returncode > 0:
|
|
print(r)
|
|
print("Unit Tests Failed")
|
|
return Exit("Unit tests failed")
|
|
|
|
|
|
@task()
|
|
def tests64(ctx):
|
|
"""Builds and execute the unit tests"""
|
|
import os
|
|
|
|
apppath = os.path.dirname(os.path.realpath(__file__))
|
|
res = True
|
|
testclient = r"unittests\general\Several\DMVCFrameworkTests.dproj"
|
|
testserver = r"unittests\general\TestServer\TestServer.dproj"
|
|
|
|
print("\nBuilding Unit Test client")
|
|
build_delphi_project(
|
|
ctx, testclient, config="CI", platform="Win64"
|
|
)
|
|
print("\nBuilding Test Server")
|
|
build_delphi_project(
|
|
ctx, testserver, config="CI", platform="Win64"
|
|
)
|
|
|
|
# import subprocess
|
|
# subprocess.run([r"unittests\general\TestServer\Win32\Debug\TestServer.exe"])
|
|
# os.spawnl(os.P_NOWAIT, r"unittests\general\TestServer\Win32\Debug\TestServer.exe")
|
|
import subprocess
|
|
|
|
print("\nExecuting tests...")
|
|
subprocess.Popen([r"unittests\general\TestServer\bin\TestServer.exe"], shell=True)
|
|
r = None
|
|
try:
|
|
r = subprocess.run([r"unittests\general\Several\bin64\DMVCFrameworkTests.exe"])
|
|
if r.returncode != 0:
|
|
return Exit("Cannot run unit test client: \n" + str(r.stdout))
|
|
finally:
|
|
subprocess.run(["taskkill", "/f", "/im", "TestServer.exe"])
|
|
if r.returncode > 0:
|
|
print(r)
|
|
print("Unit Tests Failed")
|
|
return Exit("Unit tests failed")
|
|
|
|
|
|
@task(pre=[tests32, tests64])
|
|
def tests(ctx):
|
|
pass
|
|
|
|
|
|
@task()
|
|
def release(
|
|
ctx,
|
|
version="DEBUG",
|
|
skip_build=False,
|
|
skip_tests=False,
|
|
):
|
|
"""Builds all the projects, executes integration tests and prepare the release"""
|
|
init_build(version)
|
|
|
|
if not skip_tests:
|
|
tests(ctx)
|
|
if not skip_build:
|
|
delphi_projects = get_delphi_projects_to_build("")
|
|
if not build_delphi_project_list(
|
|
ctx, delphi_projects, version, ""
|
|
):
|
|
return False # fails build
|
|
print(Fore.RESET)
|
|
copy_sources()
|
|
copy_libs(ctx)
|
|
clean(ctx)
|
|
zip_samples(version)
|
|
create_zip(ctx, version)
|
|
|
|
|
|
@task
|
|
def build_samples(
|
|
ctx, version="DEBUG", filter=""
|
|
):
|
|
"""Builds samples"""
|
|
init_build(version)
|
|
delphi_projects = get_delphi_projects_to_build("samples")
|
|
return build_delphi_project_list(
|
|
ctx, delphi_projects, version, filter
|
|
)
|
|
|
|
|
|
@task(post=[])
|
|
def build_core(ctx, version="DEBUG"):
|
|
"""Builds core packages extensions"""
|
|
init_build(version)
|
|
delphi_projects = get_delphi_projects_to_build("core")
|
|
ret = build_delphi_project_list(ctx, delphi_projects, version, "")
|
|
if not ret:
|
|
raise Exit("Build failed")
|
|
|
|
|
|
def parse_template(tmpl: List[str]):
|
|
main_tmpl = []
|
|
intf_tmpl = []
|
|
impl_tmpl = []
|
|
|
|
state = "verbatim"
|
|
for row in tmpl:
|
|
if row.upper().strip() == "///INTERFACE.BEGIN":
|
|
state = "parsing.interface"
|
|
continue
|
|
if row.upper().strip() == "///IMPLEMENTATION.BEGIN":
|
|
state = "parsing.implementation"
|
|
continue
|
|
if row.upper().strip() in ["///INTERFACE.END", "///IMPLEMENTATION.END"]:
|
|
if state == "parsing.interface":
|
|
main_tmpl.append("$INTERFACE$")
|
|
if state == "parsing.implementation":
|
|
main_tmpl.append("$IMPLEMENTATION$")
|
|
state = "verbatim"
|
|
continue
|
|
|
|
if state == "parsing.interface":
|
|
intf_tmpl.append(row)
|
|
elif state == "parsing.implementation":
|
|
impl_tmpl.append(row)
|
|
elif state == "verbatim":
|
|
main_tmpl.append(row)
|
|
return main_tmpl, intf_tmpl, impl_tmpl
|
|
|
|
|
|
@task
|
|
def generate_nullables(ctx):
|
|
import pathlib
|
|
|
|
src_folder = pathlib.Path(__file__).parent.joinpath("sources")
|
|
template_unitname = src_folder.joinpath("MVCFramework.Nullables.pas.template")
|
|
output_unitname = src_folder.joinpath("MVCFramework.Nullables.pas")
|
|
|
|
with open(template_unitname, "r") as f:
|
|
rows = f.readlines()
|
|
|
|
main_tmpl, intf_tmpl, impl_tmpl = parse_template(rows)
|
|
|
|
delphi_types = [
|
|
[
|
|
"String",
|
|
"(LeftValue.IsNull and RightValue.IsNull) or ((LeftValue.HasValue and RightValue.HasValue) and \n\t (LeftValue.Value = RightValue.Value))",
|
|
],
|
|
[
|
|
"Currency",
|
|
"(LeftValue.IsNull and RightValue.IsNull) or ((LeftValue.HasValue and RightValue.HasValue) and \n\t (LeftValue.Value = RightValue.Value))",
|
|
],
|
|
[
|
|
"Boolean",
|
|
"(LeftValue.IsNull and RightValue.IsNull) or ((LeftValue.HasValue and RightValue.HasValue) and \n\t (LeftValue.Value = RightValue.Value))",
|
|
],
|
|
[
|
|
"TDate",
|
|
"(LeftValue.IsNull and RightValue.IsNull) or ((LeftValue.HasValue and RightValue.HasValue) and \n\t (DateToISODate(LeftValue.Value) = DateToISODate(RightValue.Value)))",
|
|
],
|
|
[
|
|
"TTime",
|
|
"(LeftValue.IsNull and RightValue.IsNull) or ((LeftValue.HasValue and RightValue.HasValue) and \n\t (TimeToISOTime(LeftValue.Value) = TimeToISOTime(RightValue.Value)))",
|
|
],
|
|
[
|
|
"TDateTime",
|
|
"(LeftValue.IsNull and RightValue.IsNull) or ((LeftValue.HasValue and RightValue.HasValue) and \n\t (DateTimeToISOTimeStamp(LeftValue.Value) = DateTimeToISOTimeStamp(RightValue.Value)))",
|
|
],
|
|
[
|
|
"Single",
|
|
"(LeftValue.IsNull and RightValue.IsNull) or ((LeftValue.HasValue and RightValue.HasValue) and \n\t SameValue(LeftValue.Value, RightValue.Value, 0.000001))",
|
|
],
|
|
[
|
|
"Double",
|
|
"(LeftValue.IsNull and RightValue.IsNull) or ((LeftValue.HasValue and RightValue.HasValue) and \n\t SameValue(LeftValue.Value, RightValue.Value, 0.000000001))",
|
|
],
|
|
[
|
|
"Extended",
|
|
"(LeftValue.IsNull and RightValue.IsNull) or ((LeftValue.HasValue and RightValue.HasValue) and \n\t SameValue(LeftValue.Value, RightValue.Value, 0.000000001))",
|
|
],
|
|
[
|
|
"Int16",
|
|
"(LeftValue.IsNull and RightValue.IsNull) or ((LeftValue.HasValue and RightValue.HasValue) and (LeftValue.Value = RightValue.Value))",
|
|
],
|
|
[
|
|
"UInt16",
|
|
"(LeftValue.IsNull and RightValue.IsNull) or ((LeftValue.HasValue and RightValue.HasValue) and (LeftValue.Value = RightValue.Value))",
|
|
],
|
|
[
|
|
"Int32",
|
|
"(LeftValue.IsNull and RightValue.IsNull) or ((LeftValue.HasValue and RightValue.HasValue) and (LeftValue.Value = RightValue.Value))",
|
|
],
|
|
[
|
|
"UInt32",
|
|
"(LeftValue.IsNull and RightValue.IsNull) or ((LeftValue.HasValue and RightValue.HasValue) and (LeftValue.Value = RightValue.Value))",
|
|
],
|
|
[
|
|
"Int64",
|
|
"(LeftValue.IsNull and RightValue.IsNull) or ((LeftValue.HasValue and RightValue.HasValue) and (LeftValue.Value = RightValue.Value))",
|
|
],
|
|
[
|
|
"UInt64",
|
|
"(LeftValue.IsNull and RightValue.IsNull) or ((LeftValue.HasValue and RightValue.HasValue) and (LeftValue.Value = RightValue.Value))",
|
|
],
|
|
[
|
|
"TGUID",
|
|
"(LeftValue.IsNull and RightValue.IsNull) or ((LeftValue.HasValue and RightValue.HasValue) and (LeftValue.Value = RightValue.Value))",
|
|
],
|
|
]
|
|
|
|
str_main_tmpl = "".join(main_tmpl)
|
|
str_intf_tmpl = "".join(intf_tmpl)
|
|
str_impl_tmpl = "".join(impl_tmpl)
|
|
|
|
intf_out = ""
|
|
impl_out = ""
|
|
|
|
enum_declaration = ["ntInvalidNullableType"]
|
|
enum_detect_line = []
|
|
for delphi_type, type_compare in delphi_types:
|
|
enum_declaration.append("ntNullable" + delphi_type)
|
|
enum_detect_line.append(
|
|
f" if aTypeInfo = TypeInfo(Nullable{delphi_type}) then \n Exit(ntNullable{delphi_type}); "
|
|
)
|
|
|
|
intf_out += (
|
|
f"//**************************\n// ** Nullable{delphi_type}\n//**************************\n\n"
|
|
+ str_intf_tmpl.replace("$TYPE$", delphi_type)
|
|
)
|
|
impl_out += (
|
|
str_impl_tmpl.replace("$TYPE$", delphi_type).replace(
|
|
"$COMPARE$", type_compare
|
|
)
|
|
+ "\n"
|
|
)
|
|
|
|
enum_declaration = (
|
|
" TNullableType = (\n " + "\n , ".join(enum_declaration) + ");\n\n"
|
|
)
|
|
enum_detect_function = []
|
|
enum_detect_function.append(
|
|
"function GetNullableType(const aTypeInfo: PTypeInfo): TNullableType;"
|
|
)
|
|
enum_detect_function.append("begin")
|
|
enum_detect_function.extend(enum_detect_line)
|
|
enum_detect_function.append(" Result := ntInvalidNullableType;")
|
|
enum_detect_function.append("end;")
|
|
|
|
intf_out += enum_declaration + "\n"
|
|
intf_out += enum_detect_function[0] + "\n"
|
|
impl_out += "\n".join(enum_detect_function) + "\n"
|
|
|
|
str_main_tmpl = (
|
|
str_main_tmpl.replace("$INTERFACE$", intf_out).replace(
|
|
"$IMPLEMENTATION$", impl_out
|
|
)
|
|
+ "\n"
|
|
)
|
|
|
|
with open(output_unitname, "w") as f:
|
|
f.writelines(str_main_tmpl)
|
|
|
|
with open(src_folder.joinpath("main.out.txt"), "w") as f:
|
|
f.writelines(main_tmpl)
|
|
|
|
with open(src_folder.joinpath("interface.out.txt"), "w") as f:
|
|
f.writelines(intf_tmpl)
|
|
|
|
with open(src_folder.joinpath("implementation.out.txt"), "w") as f:
|
|
f.writelines(impl_tmpl)
|