.. SPDX-FileCopyrightText: 2024 The IceTray Contributors .. .. SPDX-License-Identifier: BSD-2-Clause .. highlight:: text How to make a project ===================== .. warning:: This document is old and crufty. While the `Implementation`_ section below is generally useful, this document as a whole should be taken as nothing more than a *rough* guide. CMake ----- - A makefile generator - Like autoconf, but better - Uses it's own private language (bad, but not horrible) Project Layout -------------- :: / CMakeLists.txt python/ __init__.py private/ modulename/ Module.cxx pybindings/ (optional) converter/ (optional) public/ (optional) modulename/ PublicInterface.h IceCube CMake macros -------------------- i3_project ^^^^^^^^^^ :: i3_project(project_name DOCS_DIR resources/docs PYTHON_DIR python ) i3_add_library ^^^^^^^^^^^^^^ :(First): Name of library :(N args): Names of files :USE_TOOLS: external tools :USE_PROJECTS: icetray projects :: i3_add_library(lib_name private/modulename/file1.cxx private/modulename/file2.cxx USE_TOOLS boost gsl root USE_PROJECTS icetray dataio dataclasses ) i3_add_pybindings ^^^^^^^^^^^^^^^^^ :(First): Name of project (should match directory name) :(N args): Names of files :USE_TOOLS: external tools :USE_PROJECTS: icetray projects :: i3_add_pybindings(project_name private/pybindings/file1.cxx private/pybindings/file2.cxx USE_TOOLS boost gsl root USE_PROJECTS icetray dataio dataclasses ) i3_test_scripts ^^^^^^^^^^^^^^^ This registers scripts with the test system and automatically runs them on the build bots. Check https://github.com/icecube/icetray/actions/ after every commit to see if you broke anything on other platforms. It's common to include the following in your CMakeLists.txt file. This will automatically register every python script in 'resources/test'. :: i3_test_scripts(resources/test/*.py) CMake syntax ------------ sh-like syntax: - SET(VARIABLE_NAME "VALUE") - message(STATUS "A status message: ${VARIABLE_NAME}") - colormsg(CYAN " +- Project is not found. Skipping") Conditional Code ^^^^^^^^^^^^^^^^ If you can, optionally include parts of a module that require extra things. Use AND and OR for boolean logic. ex: .. code-block:: cmake if(SUITESPARSE_FOUND) # do things that require SuiteSparse else(SUITESPARSE_FOUND) colormsg(CYAN " +- SuiteSparse not found") endif(SUITESPARSE_FOUND) Trivial Pybindings ------------------ If you don't have real pybindings, use this. .. note:: Don't mix this form with real pybindings. In ``python/__init__.py``: .. code-block:: python from icecube._project_name import * Documentation ------------- .. note:: Please document your project. Besides being a good idea, it is a requirement for any serious project. Doxygen documentation ^^^^^^^^^^^^^^^^^^^^^ Documents the C++ code. Decent out of the box, but we can help it along. Note: most comments are more appropriate for implementation files. We'd actually like to read the header files instead of lines of comments. (no one really reads the implementation files though). The basic comment: .. code-block:: c++ /** * This is a function that finds the sqrt of a number */ double Sqrt(double n) { return sqrt(n); } Build the docs: .. code-block:: console $ make docs This will also make sphinx docs. Sphinx ^^^^^^ Documents the Python code. Better styling. Use restructured text (.rst). Sphinx interprets this to make html. Modify CMakeLists.txt:: i3_project(advanced_bootcamp PYTHON_DIR python DOCS_DIR resources/docs ) Build only the Sphinx docs: .. code-block:: console $ make html Section Syntax:: # with overline, for parts * with overline, for chapters =, for sections -, for subsections ^, for subsubsections ", for paragraphs https://www.sphinx-doc.org/ Implementation -------------- .. highlight:: c++ Let's implement the basic framework of a module: http://code.icecube.wisc.edu/svn/sandbox/advanced_bootcamp Public Header ^^^^^^^^^^^^^ public/advanced_bootcamp/I3Bootcamp.h:: // include guard #ifndef BOOTCAMP_H #define BOOTCAMP_H #include #include #include // subclass I3FrameObject so we can insert this class into the frame class I3Bootcamp : public I3FrameObject { public: // interface goes here std::string text; int number; float number2; private: // basic boost serialization friend boost::serialization::access; template void serialize(Archive &ar, unsigned version); }; // icetray macro to make pointer typedefs for I3BootCampPtr, etc I3_POINTER_TYPEDEFS(I3Bootcamp); #endif // BOOTCAMP_H Private Modules ^^^^^^^^^^^^^^^ private/advanced_bootcamp/I3Bootcamp.cxx:: // the module interface #include // do serialization (write to/read from an i3 file) // the version number can be used to establish version formats template void I3Bootcamp::serialize(Archive &ar, unsigned version) { // convince the serializer that we are an I3FrameObject // the & operator is both read and write // make_nvp (name,value pair) allows both binary and xml output ar & make_nvp("I3FrameObject", base_object(*this)); // now actually serialize our contents // all default types and I3 types are serializable ar & make_nvp("text", text); ar & make_nvp("number", number); ar & make_nvp("number2", number2); } // another icetray macro to do most of the heavy lifting // for serialization I3_SERIALIZABLE(I3Bootcamp); private/advanced_bootcamp/I3BootcampModule.cxx:: // some basic includes #include // the module interface #include // let's make a private module class I3BootcampModule : public I3ConditionalModule { public: // the constructor just calls the parent I3BootcampModule(const I3Context &ctx) : I3ConditionalModule(ctx) {} virtual ~I3BootcampModule() {} // process physics frames void Physics(I3FramePtr frame); } // use an icetray macro to make this work with icetray I3_MODULE(I3BootcampModule); void I3BootcampModule::Physics(I3FramePtr frame) { // make an I3Bootcamp object I3BootcampPtr output(new I3Bootcamp); output->number = 6; output->text = "Some text"; // add the I3Bootcamp object to the frame frame->Put("BootcampStuff", output); // push the frame to the next module PushFrame(frame); } Pybindings ^^^^^^^^^^ private/pybindings/module.cxx:: #include #include // register function for the interface class void register_I3Bootcamp() { // use an alias instead of "using boost::python" // saves us from really strange errors namespace bp = boost::python; // make a boost::python class // the I3BootcampPtr came from I3_POINTER_TYPEDEFS bp::class_ >("I3Bootcamp") // make a python value (read/write) .def_readwrite("text",&I3Bootcamp::text) .def_readwrite("number",&I3Bootcamp::number) .def_readwrite("number2",&I3Bootcamp::number2) ; } // an icetray macro around the boost::python messiness I3_PYTHON_MODULE(advanced_bootcamp) { // load the c++ library // second argument is false to be quiet load_project("advanced_bootcamp", false); register_I3Bootcamp(); } .. highlight:: python python/__init__.py:: # load the c++ pybindings from icecube._import advanced_bootcamp CMakeLists.txt ^^^^^^^^^^^^^^ .. code-block:: text i3_project(advanced_bootcamp) i3_add_library(advanced_bootcamp private/advanced_bootcamp/I3Bootcamp.cxx private/advanced_bootcamp/I3BootcampModule.cxx USE_TOOLS boost python USE_PROJECTS icetray dataclasses ) i3_add_pybindings(advanced_bootcamp private/pybindings/module.cxx USE_TOOLS boost python USE_PROJECTS icetray dataclasses ) Usage ^^^^^ .. highlight:: python Let's use this module to do something:: from icecube.icetray import I3Tray from icecube import icetray, dataio, dataclasses, advanced_bootcamp tray = I3Tray() tray.AddModule('I3InfiniteSource', Stream=icetray.I3Frame.Physics) tray.AddModule('I3BootcampModule') def foo(frame): bootcamp = frame['BootcampStuff'] print(bootcamp) tray.Add(foo) tray.AddModule('Dump') tray.AddModule('I3Writer', filename='foo.i3') tray.Execute()