refactor: refactor visual studio build file generation script.

- refactoring VS build used file generation script, but not finished. only correct some bad Python syntax and etc.
- remove gamepiaynmo virtools SDK pre-build project. we no longer need it.
- rename some folders name.
This commit is contained in:
2024-05-22 22:05:55 +08:00
parent 656db23902
commit 4ce7021054
11 changed files with 283 additions and 799 deletions

412
scripts/mk_mtl_cfg.py Normal file
View File

@ -0,0 +1,412 @@
import vs_props_writer, vs_vcxproj_modifier
import os, enum, sys
#region Constant Declarations
class BuildType(enum.Enum):
Standalone: str = "standalone"
Plugin: str = "plugin"
class VirtoolsVersion(enum.Enum):
V21 = '21'
V25 = '25'
V30 = '30'
V35 = '35'
V40 = '40'
V50 = '50'
VT_STANDALONE_ATTACHED_LIBS: dict[VirtoolsVersion, str] = {
VirtoolsVersion.V21: "VxMath.lib;CK2.lib",
VirtoolsVersion.V25: "VxMath.lib;CK2.lib",
VirtoolsVersion.V30: "VxMath.lib;CK2.lib",
VirtoolsVersion.V35: "VxMath.lib;CK2.lib",
VirtoolsVersion.V40: "VxMath.lib;CK2.lib",
VirtoolsVersion.V50: "VxMath.lib;CK2.lib"
}
VT_PLUGIN_ATTACHED_LIBS: dict[VirtoolsVersion, str] = {
VirtoolsVersion.V21: "",
VirtoolsVersion.V25: "",
VirtoolsVersion.V30: "VxMath.lib;DllEditor.lib;CK2.lib;InterfaceControls.lib;CKControls.lib",
VirtoolsVersion.V35: "VxMath.lib;DllEditor.lib;CK2.lib;InterfaceControls.lib;CKControls.lib",
VirtoolsVersion.V40: "VxMath.lib;DllEditor.lib;CK2.lib;InterfaceControls.lib;CKControls.lib",
VirtoolsVersion.V50: "VxMath.lib;DllEditor.lib;CK2.lib;InterfaceControls.lib;CKControls.lib"
}
VT_PLUGIN_MACROS: dict[VirtoolsVersion, str] = {
VirtoolsVersion.V21: "_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE",
VirtoolsVersion.V25: "_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE",
VirtoolsVersion.V30: "_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE",
VirtoolsVersion.V35: "_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE",
VirtoolsVersion.V40: "_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE",
VirtoolsVersion.V50: "_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE"
}
VT_STANDALONE_MACROS: dict[VirtoolsVersion, str] = {
VirtoolsVersion.V21: "_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_DEBUG",
VirtoolsVersion.V25: "_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_DEBUG",
VirtoolsVersion.V30: "_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_DEBUG",
VirtoolsVersion.V35: "_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_DEBUG",
VirtoolsVersion.V40: "_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_DEBUG",
VirtoolsVersion.V50: "_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_DEBUG;VIRTOOLS_USER_SDK"
}
VT_EXECUTABLE_DEV: dict[VirtoolsVersion, str] = {
VirtoolsVersion.V21: "Dev.exe",
VirtoolsVersion.V25: "Dev.exe",
VirtoolsVersion.V30: "devr.exe",
VirtoolsVersion.V35: "devr.exe",
VirtoolsVersion.V40: "devr.exe",
VirtoolsVersion.V50: "devr.exe"
}
#endregion
#region Assist Functions
def get_project_root() -> str:
# build project root path
ret: str = os.path.dirname(os.path.dirname(__file__))
# check whether have readme file
if not os.path.isfile(os.path.join(ret, 'README.md')):
print('Fail to get project root folder. This script may be placed at wrong location.')
sys.exit(1)
# return value
return ret
def get_attached_libs(build_type: BuildType, vt_version: VirtoolsVersion) -> str:
match(build_type):
case BuildType.Standalone:
return VT_STANDALONE_ATTACHED_LIBS[vt_version]
case BuildType.Plugin:
return VT_PLUGIN_ATTACHED_LIBS[vt_version]
case _:
raise Exception('invalid build type')
def get_macros(build_type: BuildType, vt_version: VirtoolsVersion) -> str:
match(build_type):
case BuildType.Standalone:
return VT_STANDALONE_MACROS[vt_version]
case BuildType.Plugin:
return VT_PLUGIN_MACROS[vt_version]
case _:
raise Exception('invalid build type')
def get_executable_dev(vt_version: VirtoolsVersion) -> str:
"""
Return the path to executable Virtools Dev according to given Virtools version.
Usually it is `Dev.exe` or `devr.exe`.
:param vt_version The version of Virtools.
"""
pass
def get_output_path(build_type: BuildType, vt_root: str) -> str:
# fetch output path by build type
ret: str
match(build_type):
case BuildType.Standalone:
ret = vt_root
case BuildType.Plugin:
ret = os.path.join(vt_root, 'InterfacePlugins')
case _:
raise Exception('invalid build type')
# make sure return value is end with slash or backslash
if ret[-1] != '\\' or ret[-1] != '/':
ret += '\\'
# return value
return ret
#endregion
# =========== check work dir ===========
if not os.path.isfile(os.path.join(os.getcwd(), 'README.md')):
print('Error! Please run this script at the root of this repository.')
sys.exit(1)
# =========== const define ===========
build_type_standalone = "standalone"
build_type_plugin = "plugin"
valid_build_type = (
build_type_standalone,
build_type_plugin
)
virtools_attached_lib_plugin_dict = {
"21": "",
"25": "",
"35": "vxmath.lib;DllEditor.lib;ck2.lib;InterfaceControls.lib;CKControls.lib",
"40": "vxmath.lib;DllEditor.lib;ck2.lib;InterfaceControls.lib;CKControls.lib",
"50": "vxmath.lib;DllEditor.lib;ck2.lib;InterfaceControls.lib;CKControls.lib"
}
virtools_attached_lib_standalone_dict = {
"21": "VxMath.lib;CK2.lib",
"25": "VxMath.lib;CK2.lib",
"35": "vxmath.lib;ck2.lib",
"40": "vxmath.lib;ck2.lib",
"50": "vxmath.lib;ck2.lib"
}
virtools_gp_static_proj = 'GPVirtoolsStatic'
virtools_std_macro_plugin_dict = {
"21": "_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE",
"25": "_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE",
"35": "_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE",
"40": "_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE",
"50": "_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE"
}
virtools_std_macro_standalone_dict = {
"21": "_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_DEBUG",
"25": "_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_DEBUG",
"35": "_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_DEBUG",
"40": "_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_DEBUG",
"50": "_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;VIRTOOLS_USER_SDK;_DEBUG"
}
executable_virtools = {
"21": "Dev.exe",
"25": "Dev.exe",
"35": "devr.exe",
"40": "devr.exe",
"50": "devr.exe"
}
valid_virtools_standalone_ver = (
"21",
"25",
"35",
"40",
"50"
)
valid_virtools_plugin_ver = (
"35",
"40",
"50"
)
valid_vt21_reverse_work_type = (
'gamepiaynmo',
'doyagu'
)
# =========== assist func ===========
def get_executable_virtools(vt_ver):
if vt_ver == '21':
return 'Dev.exe'
elif vt_ver == '25':
return 'Dev.exe'
elif vt_ver == '35':
return 'devr.exe'
elif vt_ver == '40':
return 'devr.exe'
elif vt_ver == '50':
return 'devr.exe'
# =========== requirement get ===========
# get basic cfg, such as build type, and vt version
while True:
input_build_type = input('Choose build type(plugin, standalone): ')
if input_build_type not in valid_build_type:
print("Invalid build type!")
else:
break
valid_vtver_for_this_type = valid_virtools_plugin_ver if input_build_type == build_type_plugin else valid_virtools_standalone_ver
while True:
input_virtools_version = input('Choose virtools version({}): '.format(', '.join(valid_vtver_for_this_type)))
if input_virtools_version not in valid_vtver_for_this_type:
print("Invalid virtools version!")
else:
break
# collect sqlite library data
while True:
input_sqlite_header_path = input('SQLite header folder path: ')
if not os.path.isdir(input_sqlite_header_path):
print("Invalid SQLite header folder!")
else:
break
while True:
input_sqlite_lib_path = input('SQLite lib file path: ')
if not os.path.isfile(input_sqlite_lib_path):
print("Invalid SQLite lib file!")
else:
break
# collect virtools sdk data
if input_virtools_version != '21':
# if we do not use virtools 21, we order get original virtools SDK
while True:
input_virtools_root_path = input('Virtools root path: ')
if not os.path.isdir(input_virtools_root_path):
print("Invalid virtools root path!")
else:
break
else:
# if we are in virtools 21 environment, we have 2 choose aboud used virtools sdk
# one is gamepiaynmo and another one is doyagu
# allow user choose a proper one from them and input their corresponding path about cloned repository.
# also order a proper runtime environment for debug
while True:
input_vt21_reverse_work_type = input('Choose Virtools 2.1 reverse work source(gamepiaynmo, doyagu): ')
if input_vt21_reverse_work_type not in valid_vt21_reverse_work_type:
print("Invalid Virtools 2.1 reverse work source!")
else:
break
while True:
input_vt21_reverse_work_path = input('Virtools 2.1 reverse work root path: ')
if not os.path.isdir(input_vt21_reverse_work_path):
print("Invalid Virtools 2.1 reverse work root path!")
else:
break
while True:
input_vt21_runtime_path = input('Virtools 2.1 runtime path: ')
if not os.path.isdir(input_vt21_runtime_path):
print("Invalid Virtools 2.1 runtime path!")
else:
break
# =========== construct some path ===========
# build sqlite related data
sqlite_header_path = input_sqlite_header_path
(sqlite_lib_path, sqlite_lib_filename) = os.path.split(input_sqlite_lib_path)
# virtools version macro
virtools_ver = 'VIRTOOLS_' + input_virtools_version
# build type macro, and some essential build macros, linked lib
if input_build_type == build_type_plugin:
virtools_build_type = 'VIRTOOLS_PLUGIN'
virtools_build_suffix = 'dll'
virtools_module_define = 'SuperScriptMaterializer.def'
virtools_std_macro = virtools_std_macro_plugin_dict[input_virtools_version]
virtools_attached_lib = virtools_attached_lib_plugin_dict[input_virtools_version]
elif input_build_type == build_type_standalone:
virtools_build_type = 'VIRTOOLS_STANDALONE'
virtools_build_suffix = 'exe'
virtools_module_define = ''
virtools_std_macro = virtools_std_macro_standalone_dict[input_virtools_version]
# gamepiaynmo linked lib need special lib name
if input_virtools_version == '21' and input_vt21_reverse_work_type == 'gamepiaynmo':
virtools_attached_lib = virtools_gp_static_proj + '.lib'
else:
virtools_attached_lib = virtools_attached_lib_standalone_dict[input_virtools_version]
# debug configuration and output path
if input_virtools_version == '21':
# virtools 21 onlt allow standalone build type
# we copy it and specific some field
virtools_debug_root = input_vt21_runtime_path
virtools_debug_commandline = 'test.nmo test_script.db test_env.db'
virtools_debug_target = os.path.join(input_vt21_runtime_path, 'SuperScriptMaterializer.exe')
virtools_output_path = input_vt21_runtime_path
else:
# in original virtools sdk environment
# output file according to build type
virtools_debug_root = input_virtools_root_path
if input_build_type == build_type_plugin:
virtools_debug_commandline = ''
virtools_debug_target = os.path.join(input_virtools_root_path, executable_virtools[input_virtools_version])
virtools_output_path = os.path.join(input_virtools_root_path, 'InterfacePlugins')
else:
virtools_debug_commandline = 'test.nmo test_script.db test_env.db'
virtools_debug_target = os.path.join(input_virtools_root_path, 'SuperScriptMaterializer.exe')
virtools_output_path = input_virtools_root_path
# make sure the last char of output_path is slash
if virtools_output_path[-1] != '\\' or virtools_output_path[-1] != '/':
virtools_output_path = virtools_output_path + '\\'
# virtools compile and link options
# we need do different strategy for virtools 2.1 and anything else virtools version
if input_virtools_version == '21':
# the reverse work of doyagu and gamepiaynmo is different, so we need to
# use them differently
if input_vt21_reverse_work_type == 'doyagu':
# doyagu do not need any extra macro
virtools_extra_macro = ''
virtools_header_path = os.path.join(input_vt21_reverse_work_path, 'Include')
virtools_lib_path = os.path.join(input_vt21_reverse_work_path, 'Lib')
else:
# gamepiaynmo need a special macro but his proj do not need any link,
# instead, we need compile it fully which will be implemented in following code
virtools_extra_macro = 'BML_EXPORT='
virtools_header_path = os.path.join(input_vt21_reverse_work_path, 'virtools')
virtools_lib_path = '$(SolutionDir)out\\$(Platform)\\$(Configuration)\\' + virtools_gp_static_proj
else:
virtools_extra_macro = ''
if input_virtools_version == '25':
virtools_header_path = os.path.join(input_virtools_root_path, 'Virtools_SDK/Includes')
virtools_lib_path = os.path.join(input_virtools_root_path, 'Virtools_SDK/Lib')
else:
virtools_header_path = os.path.join(input_virtools_root_path, 'Sdk/Includes')
virtools_lib_path = os.path.join(input_virtools_root_path, 'Sdk/Lib/Win32/Release')
# =========== create props ===========
props = vs_props_writer.VsPropsWriter()
vcxproj = vs_vcxproj_modifier.VsVcxprojModifier(
'./SuperScriptMaterializer/SuperScriptMaterializer.vcxproj'
)
# write build type
if input_build_type == build_type_standalone:
vcxproj.SetBuildType(vcxproj.BUILDTYPE_EXE)
elif input_build_type == build_type_plugin:
vcxproj.SetBuildType(vcxproj.BUILDTYPE_DLL)
# write subsystem
if input_build_type == build_type_standalone:
props.SetSubSystem(vs_props_writer.VsSubSystem.Console)
elif input_build_type == build_type_plugin:
props.SetSubSystem(vs_props_writer.VsSubSystem.Windows)
# write macro and misc
# build type distinguish macro
props.AddMacro('VIRTOOLS_VER', virtools_ver)
props.AddMacro('VIRTOOLS_BUILD_TYPE', virtools_build_type)
# header and libs
props.AddMacro('VIRTOOLS_HEADER_PATH', virtools_header_path)
props.AddMacro('VIRTOOLS_LIB_PATH', virtools_lib_path)
props.AddMacro('VIRTOOLS_LIB_FILENAME', virtools_attached_lib)
props.AddMacro('SQLITE_HEADER_PATH', sqlite_header_path)
props.AddMacro('SQLITE_LIB_PATH', sqlite_lib_path)
props.AddMacro('SQLITE_LIB_FILENAME', sqlite_lib_filename)
# output and debug
props.AddMacro('VIRTOOLS_OUTPUT_PATH', virtools_output_path)
props.AddMacro('VIRTOOLS_DEBUG_TARGET', virtools_debug_target)
props.AddMacro('VIRTOOLS_DEBUG_ROOT', virtools_debug_root)
props.AddMacro('VIRTOOLS_DEBUG_COMMANDLINE', virtools_debug_commandline)
# essential build macro
props.AddMacro('VIRTOOLS_STD_MACRO', virtools_std_macro)
props.AddMacro('VIRTOOLS_EXTRA_MACRO', virtools_extra_macro)
# misc macro
props.AddMacro('VIRTOOLS_BUILD_SUFFIX', virtools_build_suffix)
props.AddMacro('VIRTOOLS_MODULE_DEFINE', virtools_module_define)
# output
props.Write2File('./SuperScriptMaterializer/Virtools.props')
vcxproj.Write2File()
# =========== create vt21 props ===========
# if we are using virtools 2.1. and we use gamepiaynmo as our
# reverse library. we need enable project GPVirtoolsStatic and
# add some macro for it
if input_virtools_version == '21' and input_vt21_reverse_work_type == 'gamepiaynmo':
gp_props = VSProp.VSPropWriter()
gp_props.AddMacro('BML_REPOSITORY', input_vt21_reverse_work_path)
gp_props.Write2File('./GPVirtoolsStatic/Virtools.props')
print("OK!")
if __name__ == '__main__':
pass

View File

@ -0,0 +1,80 @@
import xml.dom.minidom as minidom
import enum, os, sys
class VsSubSystem(enum.Enum):
Windows = 'Windows'
Console = 'Console'
class VsPropsWriter():
__mMacroList: dict[str, str]
__mSubSystem: VsSubSystem
def __init__(self):
self.__mMacroList = {}
self.__mSubSystem = VsSubSystem.Windows
def AddMacro(self, key: str, value: str):
if key in self.__mMacroList:
raise Exception(f'Duplicated Set Macro "{key}".')
self.__mMacroList[key] = value
def SetSubSystem(self, subsys: VsSubSystem):
self.__mSubSystem = subsys
def Generate(self, filename: str):
# create some header
document = minidom.getDOMImplementation().createDocument(None, 'Project', None)
root: minidom.Element = document.documentElement
root.setAttribute('ToolsVersion', '4.0')
root.setAttribute('xmlns', 'http://schemas.microsoft.com/developer/msbuild/2003')
cache = document.createElement('ImportGroup')
cache.setAttribute('Label', 'PropertySheets')
root.appendChild(cache)
# write sub system
for bt in ('Debug', 'Release'):
node_IDG = document.createElement('ItemDefinitionGroup')
node_IDG.setAttribute('Condition', f"'$(Configuration)|$(Platform)'=='{bt}|Win32'")
node_link = document.createElement('Link')
node_sub_system = document.createElement('SubSystem')
node_sub_system.appendChild(document.createTextNode(self.__mSubSystem.value))
node_link.appendChild(node_sub_system)
node_IDG.appendChild(node_link)
root.appendChild(node_IDG)
# write macro
node_PG = document.createElement('PropertyGroup') # macro node 1
node_PG.setAttribute('Label', 'UserMacros')
root.appendChild(node_PG)
cache = document.createElement('PropertyGroup') # dummy structure
root.appendChild(cache)
cache = document.createElement('ItemDefinitionGroup') #dummy structure
root.appendChild(cache)
node_IG = document.createElement('ItemGroup') # macro node 2
root.appendChild(node_IG)
for key, value in self.__mMacroList.items():
# create for PropertyGroup
node_macro_decl = document.createElement(key)
if value != '': # check whether data is empty.
node_macro_decl.appendChild(document.createTextNode(value))
node_PG.appendChild(node_macro_decl)
# create for ItemGroup
node_macro_ref = document.createElement("BuildMacro")
node_macro_ref.setAttribute('Include', key)
node_inner_macro_ref = document.createElement('Value')
node_inner_macro_ref.appendChild(document.createTextNode("$({})".format(key)))
node_macro_ref.appendChild(node_inner_macro_ref)
node_IG.appendChild(node_macro_ref)
# write to file
with open(filename, 'w', encoding='utf-8') as f:
document.writexml(f, addindent='\t', newl='\n', encoding='utf-8')

View File

@ -0,0 +1,51 @@
import xml.dom.minidom as minidom
import xml.dom
import os
import sys
class VsVcxprojModifier():
BUILDTYPE_EXE: str = 'Application'
BUILDTYPE_DLL: str = 'DynamicLibrary'
def __init__(self, vcfile: str):
self.__Dom = minidom.parse(vcfile)
self.__FileName: str = vcfile
self.__BuildType: str = None
def SetBuildType(self, bt: str):
self.__BuildType = bt
def Write2File(self):
# if no spec build type, do not modify
if self.__BuildType is None:
return
# get corresponding entry
dom = self.__Dom
node_project = dom.documentElement
for node_PG in node_project.getElementsByTagName('PropertyGroup'):
attr_label = node_PG.getAttribute('Label')
attr_condition = node_PG.getAttribute('Condition')
# skip invalid node
if attr_label != 'Configuration':
continue
if attr_condition != "'$(Configuration)|$(Platform)'=='Debug|Win32'" and attr_condition != "'$(Configuration)|$(Platform)'=='Release|Win32'":
continue
# this is valid node, process it
node_CT = node_PG.getElementsByTagName('ConfigurationType')
if len(node_CT) != 0:
# have node, change it
node_CT[0].childNodes[0].nodeValue = self.__BuildType
else:
# don't have node, add one
node_CT = dom.createElement('ConfigurationType')
node_CT.appendChild(dom.createTextNode(self.__BuildType))
node_PG.appendChild(node_CT)
# write file
with open(self.__FileName, 'w', encoding='utf-8') as f:
dom.writexml(f, encoding='utf-8')