[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