[Scummvm-git-logs] scummvm master -> 72bc43d8f2843c5b0d409de090d659cdde7100ce
mduggan
noreply at scummvm.org
Fri Aug 12 05:52:30 UTC 2022
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
72bc43d8f2 DEVTOOLS: Add a helper script for adding a class to an engine
Commit: 72bc43d8f2843c5b0d409de090d659cdde7100ce
https://github.com/scummvm/scummvm/commit/72bc43d8f2843c5b0d409de090d659cdde7100ce
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2022-08-12T15:52:28+10:00
Commit Message:
DEVTOOLS: Add a helper script for adding a class to an engine
When working on new parts of an engine, the boilerplate needed to add a new
class can be a bit of a chore. Created a simple script to save some work.
Changed paths:
A devtools/make_class.py
devtools/README
diff --git a/devtools/README b/devtools/README
index a1fc928bfe3..b8a258e3d96 100644
--- a/devtools/README
+++ b/devtools/README
@@ -174,6 +174,21 @@ ______________________________
Achievements lists.
+make_class.py
+-------------------
+ Tool that adds all the boilerplate for a new C++ class inside an engine
+ Examples:
+
+ $ make_class.py scumm . LeChuck
+ Make new class `LeChuck` in the scumm engine root (engines/scumm/). Creates
+ boilerplate le_chuck.cpp and le_chuck.h containing class Scumm::LeChuck and
+ adds corresponding .o file to module.mk
+
+ $ make_class.py -n BladeRunner bladerunner ui Scores
+ Make a new class BladeRunner::Scores in the engines/bladerunner/ui/
+ directory.
+
+
make-scumm-fontdata (eriktorbjorn)
-------------------
Tool that generates compressed font data used in SCUMM: To get rid of
diff --git a/devtools/make_class.py b/devtools/make_class.py
new file mode 100755
index 00000000000..73408a04570
--- /dev/null
+++ b/devtools/make_class.py
@@ -0,0 +1,232 @@
+#!/usr/bin/env python
+
+import argparse
+import os
+import re
+import sys
+
+HELPTEXT = \
+'''A script to add a new C++ class to an engine. Creates the .cpp and .h
+with copyright notices, header guards, and namespace, and updates module.mk.'''
+
+# This copyright notice is used by the script but also applies to this file.
+COPYRIGHT = \
+'''/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */'''
+
+def get_engine_path(engname):
+ engname = engname.lower()
+ curpath = os.path.realpath(os.path.curdir)
+ pathelems = curpath.split(os.path.sep)
+
+ result = None
+
+ if 'engines' in pathelems:
+ # already in engines
+ result = os.path.join(*(pathelems[:pathelems.index('engines') + 1] + [engname]))
+ elif 'devtools' in pathelems:
+ result = os.path.join(*(pathelems[:pathelems.index('devtools')] + ['engines', engname]))
+ elif 'scummvm' in pathelems:
+ result = os.path.join(*(pathelems[:pathelems.index('scummvm') + 1] + ['engines', engname]))
+
+ if result and curpath.startswith('/'):
+ # put the / back on the start
+ result = '/' + result
+
+ if not result or not os.path.isdir(result):
+ result = None
+
+ return result
+
+CPP_TEMPLATE = \
+'''{copyright}
+
+#include "{headerpath}/{headerfilename}"
+
+namespace {namespace} {{
+
+{classname}::{classname}() {{
+}}
+
+// TODO: Add more functions here.
+
+}} // end namespace {namespace}
+'''
+
+H_TEMPLATE = \
+'''{copyright}
+
+#ifndef {headername}
+#define {headername}
+
+namespace {namespace} {{
+
+class {classname} {{
+public:
+ {classname}();
+
+ // TODO add public members
+
+private:
+ // TODO add private members
+
+}};
+
+}} // end namespace {namespace}
+
+#endif // {headername}
+'''
+
+def class_to_fbase(classname):
+ '''Create class_file_pattern from camelcase classname'''
+ return re.sub('([A-Z])', '_\\1', classname).lower()[1:]
+
+
+def create_files(destpath, classname, namespace, enginename):
+ '''Create the cpp and h files from the templates'''
+ fbase = class_to_fbase(classname)
+ fname_cpp = os.path.join(destpath, fbase + '.cpp')
+ fname_h = os.path.join(destpath, fbase + '.h')
+ headerpath = destpath[destpath.index(enginename):]
+
+ headername = headerpath.upper().replace('/', '_') + '_' + fbase.upper() + '_H'
+
+ if os.path.exists(fname_cpp):
+ print('ERROR: cpp file %s already exists' % fname_cpp, file=sys.stderr)
+ return False
+ if os.path.exists(fname_h):
+ print('ERROR: header file %s already exists' % fname_h, file=sys.stderr)
+ return False
+
+ cppstr = CPP_TEMPLATE.format(copyright=COPYRIGHT, classname=classname,
+ namespace=namespace, headerpath=headerpath,
+ headerfilename=(fbase+'.h'))
+
+ hstr = H_TEMPLATE.format(copyright=COPYRIGHT, classname=classname,
+ namespace=namespace, headername=headername)
+
+ print("Creating %s" % fname_cpp)
+ with open(fname_cpp, 'w') as cppfile:
+ cppfile.write(cppstr)
+ print("Creating %s" % fname_h)
+ with open(fname_h, 'w') as hfile:
+ hfile.write(hstr)
+
+ return True
+
+high_sort_key = chr(0x7e)
+
+def module_sort_key(mod):
+ '''
+ Sort key for .o files in module.mk
+
+ Files in a directory always before sub-dir contents.
+ Alphabetical order otherwise.
+ '''
+ parts = mod.split('/')
+ nparts = len(parts)
+ key = ''
+ for i in range(nparts):
+ if i < nparts - 1:
+ key += high_sort_key + parts[i] + '/'
+ else:
+ key += parts[i]
+ return key
+
+def update_module_mk(module_mk_path, classname, subpath):
+ '''Add the new object file to module.mk's obj file list'''
+ fbase = class_to_fbase(classname)
+
+ o_file_path = fbase + '.o'
+ if subpath != '.':
+ o_file_path = os.path.join(subpath, o_file_path)
+
+ module_mk_contents = open(module_mk_path).read()
+ module_mk_contents = module_mk_contents.split('\n')
+
+ mod_obs_start = -1
+ mod_obs_end = -1
+ for lineno, line in enumerate(module_mk_contents):
+ if line.startswith('MODULE_OBJS := '):
+ mod_obs_start = lineno
+ break
+ if mod_obs_start == -1:
+ print('ERROR: MODULE_OBJS line not found in %s?' % module_mk_path)
+ return False
+
+ for lineno, line in enumerate(module_mk_contents[mod_obs_start:]):
+ if not line:
+ mod_obs_end = lineno
+ break
+ if mod_obs_end == -1:
+ print('ERROR: MODULE_OBJS never ends in %s?' % module_mk_path)
+ return False
+
+ before = module_mk_contents[:mod_obs_start+1]
+ modules = module_mk_contents[mod_obs_start+1:mod_obs_end]
+ after = module_mk_contents[mod_obs_end:]
+
+ modules.append('\t%s \\' % o_file_path)
+ modules.sort(key=module_sort_key)
+
+ print("Adding %s to module.mk" % o_file_path)
+ open(module_mk_path, 'w').write('\n'.join(before + modules + after))
+
+ return True
+
+
+def main():
+ parser = argparse.ArgumentParser(description=HELPTEXT)
+ parser.add_argument('engine', help='name of the engine')
+ parser.add_argument('subpath', help='subpath within the engine (use \'.\' to add to the engine root)')
+ parser.add_argument('classname', help='CamelCase name of the class to add')
+ parser.add_argument('--namespace', '-n', help='Namespace tp use (default is name of engine with a capital letter start)')
+
+ args = parser.parse_args()
+
+ engpath = get_engine_path(args.engine)
+ if not engpath:
+ parser.error("Can't find path for engine %s. Run inside scummvm checkout and ensure engine exists" % (args.engine,))
+
+ module_mk = os.path.join(engpath, 'module.mk')
+ if not os.path.exists(module_mk):
+ parser.error("Can't find module.mk inside engine %s, help!" % (args.engine,))
+
+ destpath = engpath
+ if not args.subpath == '.':
+ destpath = destpath + os.path.sep + args.subpath
+ if not os.path.isdir(destpath):
+ parser.error("Subdir %s doesn't exist in engine %s, try making it first." % (args.subpath, args.engine,))
+
+ if not args.classname[0].isupper():
+ parser.error("Class name %s should be CamelCase" % (args.classname,))
+
+ namespace = args.namespace or args.engine.capitalize()
+ if not create_files(destpath, args.classname, namespace, args.engine):
+ return 1
+
+ if not update_module_mk(module_mk, args.classname, args.subpath):
+ return 1
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
More information about the Scummvm-git-logs
mailing list