Skip to content
Snippets Groups Projects
class_maker.py 13.4 KiB
Newer Older
#!/usr/bin/env python
""" Utility for generating a class file, header, and test file """
from __future__ import (absolute_import, division, print_function, unicode_literals)
import argparse
import cmakelists_utils
from cmakelists_utils import *
#======================================================================
def write_header(subproject, classname, filename, args):
    print("Writing header file to", filename)
Martyn Gigg's avatar
Martyn Gigg committed
    subproject_upper = subproject.upper()
    guard = "MANTID_{}_{}_H_".format(subproject_upper, classname.upper())
    # Create an Algorithm header; will not use it if not an algo
  const std::string name() const override;
  int version() const override;
  const std::string category() const override;
  const std::string summary() const override;
  void init() override;
  void exec() override;
    alg_class_declare = " : public API::Algorithm"
    alg_include = '#include "MantidAPI/Algorithm.h"'
        algorithm_header = ""
        alg_class_declare = ""
        alg_include = ""
    s = r"""#ifndef {guard}
#define {guard}
#include "Mantid{subproject}/DllConfig.h"
{alg_include}
namespace {subproject} {{
/** {classname} : TODO: DESCRIPTION
  Copyright © {today} 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_{subproject_upper}_DLL {classname}{alg_class_declare} {{
public:{algorithm_header}}};
}} // namespace {subproject}
}} // namespace Mantid
#endif /* {guard} */""".format(guard=guard, subproject=subproject,
       alg_include=alg_include, classname=classname,
       today=datetime.datetime.now().date().year, subproject_upper=subproject_upper,
       alg_class_declare=alg_class_declare, algorithm_header=algorithm_header)

#======================================================================
def write_source(subproject, classname, filename, args):
    print("Writing source file to", filename)
using Mantid::Kernel::Direction;
using Mantid::API::WorkspaceProperty;
// Register the algorithm into the AlgorithmFactory
DECLARE_ALGORITHM({})
""".format(classname)
//----------------------------------------------------------------------------------------------

/// Algorithms name for identification. @see Algorithm::name
const std::string {algname}::name() const {{ return "{algname}"; }}

/// Algorithm's version for identification. @see Algorithm::version
int {algname}::version() const {{ return 1; }}

/// Algorithm's category for identification. @see Algorithm::category
const std::string {algname}::category() const {{
  return "TODO: FILL IN A CATEGORY";

/// Algorithm's summary for use in the GUI and help. @see Algorithm::summary
const std::string {algname}::summary() const {{
  return "TODO: FILL IN A SUMMARY";

//----------------------------------------------------------------------------------------------
/** Initialize the algorithm's properties.
 */
void {algname}::init() {{
      Kernel::make_unique<WorkspaceProperty<API::Workspace>>("InputWorkspace", "",
                                                             Direction::Input),
      "An input workspace.");
  declareProperty(
      Kernel::make_unique<WorkspaceProperty<API::Workspace>>("OutputWorkspace", "",
                                                             Direction::Output),

//----------------------------------------------------------------------------------------------
/** Execute the algorithm.
 */
void {algname}::exec() {{
  // TODO Auto-generated execute stub
}}
""".format(algname=classname)
        algorithm_top = ""
        algorithm_source = ""
    # ------- Now the normal class text ------------------------------
    s = """#include "Mantid{subproject}/{subfolder}{classname}.h"
namespace {subproject} {{
{algorithm_top}{algorithm_source}
}} // namespace {subproject}
}} // namespace Mantid
""".format(
        subproject=subproject, subfolder=args.subfolder, classname=classname, algorithm_top=algorithm_top,
        algorithm_source=algorithm_source)
#======================================================================
def write_test(subproject, classname, filename, args):
Antti Soininen's avatar
Antti Soininen committed
    print("Writing test file to", filename)
    guard = "MANTID_{}_{}TEST_H_".format(subproject.upper(), classname.upper())
    algorithm_test = """
  void test_Init()
    TS_ASSERT_THROWS_NOTHING( alg.initialize() )
    TS_ASSERT( alg.isInitialized() )
Martyn Gigg's avatar
Martyn Gigg committed
    // Create test input if necessary
    MatrixWorkspace_sptr inputWS = //-- Fill in appropriate code. Consider using TestHelpers/WorkspaceCreationHelpers.h --
Martyn Gigg's avatar
Martyn Gigg committed
    // Don't put output in ADS by default
    alg.setChild(true);
    TS_ASSERT_THROWS_NOTHING( alg.initialize() )
    TS_ASSERT( alg.isInitialized() )
Martyn Gigg's avatar
Martyn Gigg committed
    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() );
Martyn Gigg's avatar
Martyn Gigg committed
    // 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");
  }}
  """.format(algname=classname);
    s = """#ifndef {guard}
#define {guard}
#include "Mantid{subproject}/{subfolder}{classname}.h"
using Mantid::{subproject}::{classname};
class {classname}Test : public CxxTest::TestSuite {{
  // This pair of boilerplate methods prevent the suite being created statically
  // This means the constructor isn't called when running other tests
  static {classname}Test *createSuite() {{ return new {classname}Test(); }}
  static void destroySuite( {classname}Test *suite ) {{ delete suite; }}
Martyn Gigg's avatar
Martyn Gigg committed
    TS_FAIL( "You forgot to write a test!");
#endif /* {guard} */""".format(
          guard=guard, subproject=subproject, subfolder=args.subfolder, classname=classname,
          algorithm_test=algorithm_test)

#======================================================================
def write_rst(subproject, classname, filename, args):
    """Write an algorithm rst documentation file"""
    print("Writing rst file to", filename)
.. seeAlso::
TODO: Enter a full rst-markup description of your algorithm here.
..  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 - {algname}**
.. testcode:: {algname}Example

   # Create a host workspace
   ws = CreateWorkspace(DataX=range(0,3), DataY=(0,2))
   or
   ws = CreateSampleWorkspace()


   # Print the result
   print "The output workspace has %%i spectra" %% wsOut.getNumberHistograms()

Output:

.. testoutput:: {algname}Example

  The output workspace has ?? spectra

.. categories::

""".format(algname=classname)
#======================================================================
def generate(subproject, classname, overwrite, args):
    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 {} already exists. Use --force to overwrite.\n".format(headerfile))
    if args.cpp and not overwrite and os.path.exists(sourcefile):
        print("\nError! Source file {} already exists. Use --force to overwrite.\n".format(sourcefile))
    if args.test and not overwrite and os.path.exists(testfile):
        print("\nError! Test file {} already exists. Use --force to overwrite.\n".format(testfile))
    if args.rst and args.alg and not overwrite and os.path.exists(rstfile):
        print("\nError! Rst documentation file {} already exists. Use --force to overwrite.\n".format(rstfile))
        write_header(subproject, classname, headerfile, args)
        write_source(subproject, classname, sourcefile, args)
        write_test(subproject, classname, testfile, args)
    if args.rst and args.alg:
        write_rst(subproject, classname, rstfile, args)
    # Insert into the cmake list if required
    if args.cmake:
        add_to_cmake(subproject, classname, args, args.subfolder)
        print("\n   Files were added to Framework/{}/CMakeLists.txt !".format(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 __name__ == "__main__":
    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('--no-cmake', dest='cmake', action='store_const',
                        const=False, default=True,
                        help="Don't modify cmake files")
    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')

    args = parser.parse_args()
    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)