Skip to content
Snippets Groups Projects
class_maker.py 15.1 KiB
Newer Older
#!/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 cmakelists_utils
from cmakelists_utils import *
import commands
#======================================================================
def write_header(subproject, classname, filename, args):
    """Write a class header file"""
    print "Writing header file to %s" % filename
    f = open(filename, 'w')
Martyn Gigg's avatar
Martyn Gigg committed
    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
  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 = ""
#include "Mantid%s/DllConfig.h"
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>
*/
Martyn Gigg's avatar
Martyn Gigg committed
class MANTID_%s_DLL %s%s {
Martyn Gigg's avatar
Martyn Gigg committed
#endif /* %s */""" % (guard, guard, subproject,
       alg_include, subproject, classname,
Martyn Gigg's avatar
Martyn Gigg committed
       datetime.datetime.now().date().year, subproject_upper, classname, alg_class_declare,
       algorithm_header, subproject, guard)




#======================================================================
def write_source(subproject, classname, filename, args):
    """Write a class source file"""
    print "Writing source file to %s" % filename
    f = open(filename, 'w')
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(
      Kernel::make_unique<WorkspaceProperty<API::Workspace>>("InputWorkspace", "",
                                                             Direction::Input),
      "An input workspace.");
  declareProperty(
      Kernel::make_unique<WorkspaceProperty<API::Workspace>>("OutputWorkspace", "",
                                                             Direction::Output),
      "An output workspace.");
}

//----------------------------------------------------------------------------------------------
/** Execute the algorithm.
 */
void %s::exec() {
  // TODO Auto-generated execute stub
}
""" % (classname, classname, classname, classname, classname, classname, classname)
        algorithm_top = ""
        algorithm_source = ""
    # ------- Now the normal class text ------------------------------
    s = """#include "Mantid%s/%s%s.h"
namespace Mantid {
namespace %s {
} // namespace Mantid
""" % (
        subproject, args.subfolder, classname, subproject, algorithm_top,
        algorithm_source, subproject)
#======================================================================
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() )
  }
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");
Martyn Gigg's avatar
Martyn Gigg committed
  """ % (classname,classname);
#include "Mantid%s/%s%s.h"
class %sTest : 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 %sTest *createSuite() { return new %sTest(); }
  static void destroySuite( %sTest *suite ) { delete suite; }

Martyn Gigg's avatar
Martyn Gigg committed
    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)

#======================================================================
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.
..  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:


  The output workspace has ?? spectra

.. categories::

""" % (classname,classname,classname,classname)

    f.write(s)
    f.close()


#======================================================================
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 %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
    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
        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
    add_to_cmake(subproject, classname, args, args.subfolder)
Janik Zikovsky's avatar
Janik Zikovsky committed
    print "\n   Files were added to Framework/%s/CMakeLists.txt !" % (subproject)

    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)
#======================================================================
if __name__ == "__main__":
    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)