-
Campbell, Stuart authoredCampbell, Stuart authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
class_maker.py 15.28 KiB
#!/usr/bin/env python
""" Utility for generating a class file, header, and test file """
import sys
import os
try:
import argparse
useArgparse = True
except ImportError, e:
import optparse # deprecated in v2.7
useArgparse = False
import datetime
import re
import cmakelists_utils
from cmakelists_utils import *
import commands
VERSION = "1.0"
#======================================================================
def write_header(subproject, classname, filename, args):
"""Write a class header file"""
print "Writing header file to %s" % filename
f = open(filename, 'w')
subproject_upper = subproject.upper()
guard = "MANTID_%s_%s_H_" % (subproject_upper, classname.upper())
# Create an Algorithm header; will not use it if not an algo
algorithm_header = """
virtual const std::string name() const;
virtual int version() const;
virtual const std::string category() const;
virtual const std::string summary() const;
private:
void init();
void exec();
"""
alg_class_declare = " : public API::Algorithm"
alg_include = '#include "MantidAPI/Algorithm.h"'
if not args.alg:
algorithm_header = ""
alg_class_declare = ""
alg_include = ""
# The full text
s = """#ifndef %s
#define %s
#include "Mantid%s/DllConfig.h"
%s
namespace Mantid {
namespace %s {
/** %s : TODO: DESCRIPTION
Copyright © %s ISIS Rutherford Appleton Laboratory, NScD Oak Ridge
National Laboratory & European Spallation Source
This file is part of Mantid.
Mantid 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.
Mantid 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/>.
File change history is stored at: <https://github.com/mantidproject/mantid>
Code Documentation is available at: <http://doxygen.mantidproject.org>
*/
class MANTID_%s_DLL %s%s {
public:
%s();
virtual ~%s();
%s};
} // namespace %s
} // namespace Mantid
#endif /* %s */""" % (guard, guard, subproject,
alg_include, subproject, classname,
datetime.datetime.now().date().year, subproject_upper, classname, alg_class_declare,
classname, classname, algorithm_header, subproject, guard)
f.write(s)
f.close()
#======================================================================
def write_source(subproject, classname, filename, args):
"""Write a class source file"""
print "Writing source file to %s" % filename
f = open(filename, 'w')
algorithm_top = """
using Mantid::Kernel::Direction;
using Mantid::API::WorkspaceProperty;
// Register the algorithm into the AlgorithmFactory
DECLARE_ALGORITHM(%s)
""" % (classname)
algorithm_source = """
//----------------------------------------------------------------------------------------------
/// Algorithms name for identification. @see Algorithm::name
const std::string %s::name() const { return "%s"; }
/// Algorithm's version for identification. @see Algorithm::version
int %s::version() const { return 1; }
/// Algorithm's category for identification. @see Algorithm::category
const std::string %s::category() const {
return "TODO: FILL IN A CATEGORY";
}
/// Algorithm's summary for use in the GUI and help. @see Algorithm::summary
const std::string %s::summary() const {
return "TODO: FILL IN A SUMMARY";
}
//----------------------------------------------------------------------------------------------
/** Initialize the algorithm's properties.
*/
void %s::init() {
declareProperty(
new WorkspaceProperty<>("InputWorkspace", "", Direction::Input),
"An input workspace.");
declareProperty(
new WorkspaceProperty<>("OutputWorkspace", "", Direction::Output),
"An output workspace.");
}
//----------------------------------------------------------------------------------------------
/** Execute the algorithm.
*/
void %s::exec() {
// TODO Auto-generated execute stub
}
""" % (classname, classname, classname, classname, classname, classname, classname)
if not args.alg:
algorithm_top = ""
algorithm_source = ""
# ------- Now the normal class text ------------------------------
s = """#include "Mantid%s/%s%s.h"
namespace Mantid {
namespace %s {
%s
//----------------------------------------------------------------------------------------------
/** Constructor
*/
%s::%s() {}
//----------------------------------------------------------------------------------------------
/** Destructor
*/
%s::~%s() {}
%s
} // namespace %s
} // namespace Mantid
""" % (
subproject, args.subfolder, classname, subproject, algorithm_top,
classname, classname, classname, classname, algorithm_source, subproject)
f.write(s)
f.close()
#======================================================================
def write_test(subproject, classname, filename, args):
"""Write a class test file"""
print "Writing test file to %s" % filename
f = open(filename, 'w')
guard = "MANTID_%s_%sTEST_H_" % (subproject.upper(), classname.upper())
algorithm_test = """
void test_Init()
{
%s alg;
TS_ASSERT_THROWS_NOTHING( alg.initialize() )
TS_ASSERT( alg.isInitialized() )
}
void test_exec()
{
// Create test input if necessary
MatrixWorkspace_sptr inputWS = //-- Fill in appropriate code. Consider using TestHelpers/WorkspaceCreationHelpers.h --
%s alg;
// Don't put output in ADS by default
alg.setChild(true);
TS_ASSERT_THROWS_NOTHING( alg.initialize() )
TS_ASSERT( alg.isInitialized() )
TS_ASSERT_THROWS_NOTHING( alg.setProperty("InputWorkspace", inputWS) );
TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("OutputWorkspace", "_unused_for_child") );
TS_ASSERT_THROWS_NOTHING( alg.execute(); );
TS_ASSERT( alg.isExecuted() );
// Retrieve the workspace from the algorithm. The type here will probably need to change. It should
// be the type using in declareProperty for the "OutputWorkspace" type.
// We can't use auto as it's an implicit conversion.
Workspace_sptr outputWS = alg.getProperty("OutputWorkspace");
TS_ASSERT(outputWS);
TS_FAIL("TODO: Check the results and remove this line");
}
""" % (classname,classname);
if not args.alg:
algorithm_test = ""
s = """#ifndef %s
#define %s
#include <cxxtest/TestSuite.h>
#include "Mantid%s/%s%s.h"
using Mantid::%s::%s;
class %sTest : public CxxTest::TestSuite {
public:
// This pair of boilerplate methods prevent the suite being created statically
// This means the constructor isn't called when running other tests
static %sTest *createSuite() { return new %sTest(); }
static void destroySuite( %sTest *suite ) { delete suite; }
%s
void test_Something()
{
TS_FAIL( "You forgot to write a test!");
}
};
#endif /* %s */""" % (
guard, guard, subproject, args.subfolder, classname,
subproject, classname, classname, classname, classname, classname,
algorithm_test, guard)
f.write(s)
f.close()
#======================================================================
def write_rst(subproject, classname, filename, args):
"""Write an algorithm rst documentation file"""
print "Writing rst file to %s" % filename
f = open(filename, 'w')
s = """
.. algorithm::
.. summary::
.. alias::
.. properties::
Description
-----------
TODO: Enter a full rst-markup description of your algorithm here.
Usage
-----
.. Try not to use files in your examples,
but if you cannot avoid it then the (small) files must be added to
autotestdata\UsageData and the following tag unindented
.. include:: ../usagedata-note.txt
**Example - %s**
.. testcode:: %sExample
# Create a host workspace
ws = CreateWorkspace(DataX=range(0,3), DataY=(0,2))
or
ws = CreateSampleWorkspace()
wsOut = %s()
# Print the result
print "The output workspace has %%i spectra" %% wsOut.getNumberHistograms()
Output:
.. testoutput:: %sExample
The output workspace has ?? spectra
.. categories::
.. sourcelink::
""" % (classname,classname,classname,classname)
f.write(s)
f.close()
#======================================================================
def generate(subproject, classname, overwrite, args):
# Directory at base of subproject
basedir, header_folder = find_basedir(args.project, subproject)
headerfile = os.path.join(basedir, "inc", header_folder, args.subfolder + classname + ".h")
sourcefile = os.path.join(basedir, "src", args.subfolder + classname + ".cpp")
testfile = os.path.join(basedir, "test", classname + "Test.h")
#up two from the subproject basedir and then docs\source\algorithms
mantiddir = os.path.dirname(os.path.dirname(basedir))
rstfile = os.path.join(mantiddir, "docs", "source", "algorithms", classname + "-v1.rst")
if args.header and not overwrite and os.path.exists(headerfile):
print "\nError! Header file %s already exists. Use --force to overwrite.\n" % headerfile
return
if args.cpp and not overwrite and os.path.exists(sourcefile):
print "\nError! Source file %s already exists. Use --force to overwrite.\n" % sourcefile
return
if args.test and not overwrite and os.path.exists(testfile):
print "\nError! Test file %s already exists. Use --force to overwrite.\n" % testfile
return
if args.rst and args.alg and not overwrite and os.path.exists(rstfile):
print "\nError! Rst documentation file %s already exists. Use --force to overwrite.\n" % rstfile
return
print
if args.header:
write_header(subproject, classname, headerfile, args)
if args.cpp:
write_source(subproject, classname, sourcefile, args)
if args.test:
write_test(subproject, classname, testfile, args)
if args.rst and args.alg:
write_rst(subproject, classname, rstfile, args)
# Insert into the cmake list
add_to_cmake(subproject, classname, args, args.subfolder)
print "\n Files were added to Framework/%s/CMakeLists.txt !" % (subproject)
print
if args.alg:
print "Note: if this is a WorkflowAlgorithm, please subclass DataProcessorAlgorithm"
print "Note: if this algorithm operates on a WorkspaceGroup, please override processGroups()"
print
# if not test_only:
# print "\tsrc/%s.cpp" % (classname)
# print "\tinc/Mantid%s/%s.h" % (subproject, classname)
# print "\ttest/%sTest.h" % (classname)
# print
#======================================================================
if __name__ == "__main__":
parser = None
if useArgparse:
parser = argparse.ArgumentParser(description='Utility to create Mantid class files: header, source and test. version ' + VERSION)
parser.add_argument('subproject', metavar='SUBPROJECT', type=str,
help='The subproject under Framework/; e.g. Kernel')
parser.add_argument('classname', metavar='CLASSNAME', type=str,
help='Name of the class to create')
parser.add_argument('--force', dest='force', action='store_const',
const=True, default=False,
help='Force overwriting existing files. Use with caution!')
parser.add_argument('--no-header', dest='header', action='store_const',
const=False, default=True,
help="Don't create the header file")
parser.add_argument('--no-test', dest='test', action='store_const',
const=False, default=True,
help="Don't create the test file")
parser.add_argument('--no-cpp', dest='cpp', action='store_const',
const=False, default=True,
help="Don't create the cpp file")
parser.add_argument('--no-rst', dest='rst', action='store_const',
const=False, default=True,
help="Don't create the rst file")
parser.add_argument('--alg', dest='alg', action='store_const',
const=True, default=False,
help='Create an Algorithm stub. This adds some methods common to algorithms.')
parser.add_argument('--subfolder', dest='subfolder',
default="",
help='Put the source under a subfolder below the main part of the project, e.g. Geometry/Instrument.')
parser.add_argument('--project', dest='project',
default="Framework",
help='The project in which this goes. Default: Framework. Can be MantidQt, Vates')
else:
parser = optparse.OptionParser("Usage: %prog SUBPROJECT CLASSNAME [options]", None,
optparse.Option, VERSION, 'error', 'Utility to create Mantid class files: header, source and test.')
parser.add_option('--force', dest='force', action='store_const',
const=True, default=False,
help='Force overwriting existing files. Use with caution!')
parser.add_option('--no-header', dest='header', action='store_const',
const=False, default=True,
help="Don't create the header file")
parser.add_option('--no-test', dest='test', action='store_const',
const=False, default=True,
help="Don't create the test file")
parser.add_option('--no-cpp', dest='cpp', action='store_const',
const=False, default=True,
help="Don't create the cpp file")
parser.add_option('--no-rst', dest='rst', action='store_const',
const=False, default=True,
help="Don't create the rst file")
parser.add_option('--alg', dest='alg', action='store_const',
const=True, default=False,
help='Create an Algorithm stub. This adds some methods common to algorithms.')
parser.add_option('--subfolder', dest='subfolder',
default="",
help='Put the source under a subfolder below the main part of the project, e.g. Geometry/Instrument.')
parser.add_option('--project', dest='project',
default="Framework",
help='The project in which this goes. Default: Framework. Can be MantidQt, Vates')
args = None
if useArgparse:
args = parser.parse_args()
else:
(options, myargs) = parser.parse_args()
args = options
args.subproject = myargs[0]
args.classname = myargs[1]
subproject = args.subproject
classname = args.classname
overwrite = args.force
# Make sure the subfolders end with a /
if args.subfolder != "":
if args.subfolder[-1:] != "/":
args.subfolder += "/"
generate(subproject, classname, overwrite, args)