Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:17:18

0001 #!/usr/bin/env python3
0002 from sys import argv
0003 import subprocess
0004 import sys
0005 import os
0006 
0007 disapatch_table = {}
0008 
0009 
0010 def copy_from_source_dir(files_to_copy):
0011     # For files that are copied from a JANA source directory we need to know
0012     # that directory. Preference is given to a path derived from the location
0013     # of the currently executing script since that is most the likely to
0014     # succeed and most likely what the user is intending.
0015     # This is complicated by the fact that someone could be running this script
0016     # from the install directory or from the source directory OR from a copy
0017     # they placed somewhere else. For this last case we must rely on the
0018     # JANA_HOME environment variable.
0019     #
0020     # This is also complicated by the relative path to files (e.g. catch.hpp)
0021     # being different in the JANA source directory tree than where it is
0022     # installed. Thus, in order to handle all cases, we need to make a list of
0023     # the source files along with various relative paths. We do this using a
0024     # dictionary where each key is the source filename and the value is itself
0025     # a dictionary containing the relative paths is the source and install
0026     # directories as well as a destination name for the file (in case we ever
0027     # add one that needs to be renamed).
0028 
0029     jana_scripts_dir = os.path.dirname(os.path.realpath(__file__)) # Directory hold current script
0030     jana_dir = os.path.dirname(jana_scripts_dir)                   # Parent directory of above dir
0031 
0032     # Guess whether this script is being run from install or source directory
0033     script_in_source  = (os.path.basename(jana_scripts_dir) == "scripts")
0034     script_in_install = (os.path.basename(jana_scripts_dir) == "bin")
0035 
0036     # Check for files relative to this script
0037     Nfile_in_source  = sum([1 for f,d in files_to_copy.items() if os.path.exists(jana_dir+"/"+d["sourcedir" ]+"/"+f)])
0038     Nfile_in_install = sum([1 for f,d in files_to_copy.items() if os.path.exists(jana_dir+"/"+d["installdir"]+"/"+f)])
0039     all_files_in_source  = (Nfile_in_source  == len(files_to_copy))
0040     all_files_in_install = (Nfile_in_install == len(files_to_copy))
0041 
0042     # This more complicated than it should be, but is done this way to try and
0043     # handle all ways a user may have setup their install and source dirs.
0044     Nerrs = 0
0045     use_install = script_in_install and all_files_in_install
0046     use_source  = script_in_source  and all_files_in_source
0047     if not (use_install or use_source):
0048         use_install = script_in_source  and all_files_in_install
0049         use_source  = script_in_install and all_files_in_source
0050 
0051     # Make a new entry in each file's dictionary of full path to actual source file
0052     if use_install:
0053         for f,d in files_to_copy.items():
0054             files_to_copy[f]["sourcefile"] = jana_dir+"/"+d["installdir"]+"/"+f
0055     if use_source:
0056         for f,d in files_to_copy.items():
0057             files_to_copy[f]["sourcefile"] = jana_dir+"/"+d["sourcedir"]+"/"+f
0058     else:
0059         # We only get here if neither the install nor source directory contain
0060         # all of the files we need to install (or they don't exist). Try JANA_HOME
0061         jana_home = os.getenv("JANA_HOME")
0062         if jana_home is None:
0063             print("ERROR: This script does not look like it is being run from a known")
0064             print("       location and JANA_HOME is also not set. Please set your")
0065             print("       JANA_HOME environment variable to a valid JANA installation.")
0066             sys.exit(-1)
0067         for f,d in files_to_copy.items():
0068             files_to_copy[f]["sourcefile"] = jana_home +"/"+d["installdir"]+"/"+f
0069 
0070     # OK, finally we can copy the files
0071     Nerrs = 0
0072     for f,d in files_to_copy.items():
0073         try:
0074             cmd = ["cp", d["sourcefile"], d["destname"]]
0075             print(" ".join(cmd))
0076             subprocess.check_call(cmd)
0077         except subprocess.CalledProcessError as cpe:
0078             print("ERROR: Copy failed of " + d["sourcefile"] + " -> " + d["destname"])
0079             Nerrs += 1
0080     if Nerrs > 0:
0081         print("")
0082         print("Errors encountered while copying files to plugin directory. Please")
0083         print("check your permissions, your JANA_HOME environment variable, and")
0084         print("you JANA installation. Continuing now though this may leave you with")
0085         print("a broken plugin.")
0086 
0087 
0088 def boolify(x, name):
0089     if x==True or x=="True" or x=="true" or x=="1":
0090         return True
0091     elif x==False or x=="False" or x=="false" or x=="0":
0092         return False
0093     else:
0094         raise Exception("Argument "+name+ " must be either 'true' or 'false'")
0095 
0096 
0097 copyright_notice = """//
0098 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
0099 // Jefferson Science Associates LLC Copyright Notice:
0100 // Copyright 251 2014 Jefferson Science Associates LLC All Rights Reserved. Redistribution
0101 // and use in source and binary forms, with or without modification, are permitted as a
0102 // licensed user provided that the following conditions are met:
0103 //
0104 // 1. Redistributions of source code must retain the above copyright notice, this
0105 //    list of conditions and the following disclaimer.
0106 // 2. Redistributions in binary form must reproduce the above copyright notice, this
0107 //    list of conditions and the following disclaimer in the documentation and/or other
0108 //    materials provided with the distribution.
0109 // 3. The name of the author may not be used to endorse or promote products derived
0110 //    from this software without specific prior written permission.
0111 //
0112 // This material resulted from work developed under a United States Government Contract.
0113 // The Government retains a paid-up, nonexclusive, irrevocable worldwide license in such
0114 // copyrighted data to reproduce, distribute copies to the public, prepare derivative works,
0115 // perform publicly and display publicly and to permit others to do so.
0116 // THIS SOFTWARE IS PROVIDED BY JEFFERSON SCIENCE ASSOCIATES LLC "AS IS" AND ANY EXPRESS
0117 // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
0118 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
0119 // JEFFERSON SCIENCE ASSOCIATES, LLC OR THE U.S. GOVERNMENT BE LIABLE TO LICENSEE OR ANY
0120 // THIRD PARTES FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
0121 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
0122 // OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
0123 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
0124 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0125 // POSSIBILITY OF SUCH DAMAGE.
0126 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
0127 //"""
0128 
0129 
0130 jobject_template_h = """
0131 
0132 #ifndef _{name}_h_
0133 #define _{name}_h_
0134 
0135 #include <JANA/JObject.h>
0136 
0137 /// JObjects are plain-old data containers for inputs, intermediate results, and outputs.
0138 /// They have member functions for introspection and maintaining associations with other JObjects, but
0139 /// all of the numerical code which goes into their creation should live in a JFactory instead.
0140 /// You are allowed to include STL containers and pointers to non-POD datatypes inside your JObjects,
0141 /// however, it is highly encouraged to keep them flat and include only primitive datatypes if possible.
0142 /// Think of a JObject as being a row in a database table, with event number as an implicit foreign key.
0143 
0144 struct {name} : public JObject {{
0145     JOBJECT_PUBLIC({name})
0146 
0147     int x;     // Pixel coordinates centered around 0,0
0148     int y;     // Pixel coordinates centered around 0,0
0149     double E;  // Energy loss in GeV
0150     double t;  // Time in ms
0151 
0152 
0153     /// Make it convenient to construct one of these things
0154     {name}(int x, int y, double E, double t) : x(x), y(y), E(E), t(t) {{}};
0155 
0156 
0157     /// Override Summarize to tell JANA how to produce a convenient string representation for our JObject.
0158     /// This can be called from user code, but also lets JANA automatically inspect its own data. See the
0159     /// CsvWriter example. Warning: Because this is slow, it should be used for debugging and monitoring 
0160     /// but not inside the performance critical code paths.
0161 
0162     void Summarize(JObjectSummary& summary) const override {{
0163         summary.add(x, NAME_OF(x), "%d", "Pixel coordinates centered around 0,0");
0164         summary.add(y, NAME_OF(y), "%d", "Pixel coordinates centered around 0,0");
0165         summary.add(E, NAME_OF(E), "%f", "Energy loss in GeV");
0166         summary.add(t, NAME_OF(t), "%f", "Time in ms");
0167     }}
0168 }};
0169 
0170 
0171 #endif // _{name}_h_
0172 
0173 """
0174 
0175 jeventsource_template_h = """
0176 
0177 #ifndef _{name}_h_
0178 #define  _{name}_h_
0179 
0180 #include <JANA/JEventSource.h>
0181 #include <JANA/JEventSourceGeneratorT.h>
0182 
0183 class {name} : public JEventSource {{
0184 
0185     /// Add member variables here
0186 
0187 public:
0188     {name}();
0189 
0190     virtual ~{name}() = default;
0191 
0192     void Open() override;
0193 
0194     void Close() override;
0195 
0196     Result Emit(JEvent& event) override;
0197     
0198     static std::string GetDescription();
0199 
0200 }};
0201 
0202 template <>
0203 double JEventSourceGeneratorT<{name}>::CheckOpenable(std::string);
0204 
0205 #endif // _{name}_h_
0206 
0207 """
0208 
0209 jeventsource_template_cc = """
0210 
0211 #include "{name}.h"
0212 
0213 #include <JANA/JApplication.h>
0214 #include <JANA/JEvent.h>
0215 
0216 /// Include headers to any JObjects you wish to associate with each event
0217 // #include "Hit.h"
0218 
0219 /// There are two different ways of instantiating JEventSources
0220 /// 1. Creating them manually and registering them with the JApplication
0221 /// 2. Creating a corresponding JEventSourceGenerator and registering that instead
0222 ///    If you have a list of files as command line args, JANA will use the JEventSourceGenerator
0223 ///    to find the most appropriate JEventSource corresponding to that filename, instantiate and register it.
0224 ///    For this to work, the JEventSource constructor has to have the following constructor arguments:
0225 
0226 {name}::{name}() : JEventSource() {{
0227     SetTypeName(NAME_OF_THIS); // Provide JANA with class name
0228     SetCallbackStyle(CallbackStyle::ExpertMode);
0229 }}
0230 
0231 void {name}::Open() {{
0232 
0233     /// Open is called exactly once when processing begins.
0234     
0235     /// Get any configuration parameters from the JApplication
0236     // GetApplication()->SetDefaultParameter("{name}:random_seed", m_seed, "Random seed");
0237 
0238     /// For opening a file, get the filename via:
0239     // std::string resource_name = GetResourceName();
0240     /// Open the file here!
0241 }}
0242 
0243 void {name}::Close() {{
0244 
0245     /// Close is called exactly once when processing ends. This is where you should close your files or sockets.
0246     /// It is important to do that here instead of in Emit() because we want everything to be cleanly closed 
0247     /// even when JANA is terminated via Ctrl-C or via a timeout.
0248 
0249 }}
0250 
0251 JEventSource::Result {name}::Emit(JEvent& event) {{
0252 
0253     /// Calls to GetEvent are synchronized with each other, which means they can
0254     /// read and write state on the JEventSource without causing race conditions.
0255     
0256     /// Configure event and run numbers
0257     static size_t current_event_number = 1;
0258     event.SetEventNumber(current_event_number++);
0259     event.SetRunNumber(22);
0260 
0261     /// Insert whatever data was read into the event
0262     // std::vector<Hit*> hits;
0263     // hits.push_back(new Hit(0,0,1.0,0));
0264     // event.Insert(hits);
0265 
0266     /// If you are reading a file of events and have reached the end, terminate the stream like this:
0267     /// Note that you should close any file handles or sockets in Close(), not here!
0268     // return Result::FailureFinished;
0269 
0270     /// If you are streaming events and there are no new events in the message queue,
0271     /// tell JANA that Emit() was temporarily unsuccessful like this:
0272     // return Result::FailureTryAgain;
0273 
0274     return Result::Success;
0275 }}
0276 
0277 std::string {name}::GetDescription() {{
0278 
0279     /// GetDescription() helps JANA explain to the user what is going on
0280     return "";
0281 }}
0282 
0283 
0284 template <>
0285 double JEventSourceGeneratorT<{name}>::CheckOpenable(std::string resource_name) {{
0286 
0287     /// CheckOpenable() decides how confident we are that this EventSource can handle this resource.
0288     ///    0.0        -> 'Cannot handle'
0289     ///    (0.0, 1.0] -> 'Can handle, with this confidence level'
0290     
0291     /// To determine confidence level, feel free to open up the file and check for magic bytes or metadata.
0292     /// Returning a confidence <- {{0.0, 1.0}} is perfectly OK!
0293     
0294     return (resource_name == "{name}") ? 1.0 : 0.0;
0295 }}
0296 """
0297 
0298 plugin_main = """
0299 
0300 #include <JANA/JApplication.h>
0301 #include <JANA/JFactoryGenerator.h>
0302 
0303 #include "{name}Processor.h"
0304 
0305 extern "C" {{
0306 void InitPlugin(JApplication* app) {{
0307 
0308     // This code is executed when the plugin is attached.
0309     // It should always call InitJANAPlugin(app) first, and then do one or more of:
0310     //   - Read configuration parameters
0311     //   - Register JFactoryGenerators
0312     //   - Register JEventProcessors
0313     //   - Register JEventSourceGenerators (or JEventSources directly)
0314     //   - Register JServices
0315 
0316     InitJANAPlugin(app);
0317 
0318     LOG << "Loading {name}" << LOG_END;
0319     app->Add(new {name}Processor);
0320     // Add any additional components as needed
0321 }}
0322 }}
0323 
0324 """
0325 
0326 project_cmakelists_txt = """
0327 cmake_minimum_required(VERSION 3.9)
0328 project({name}_project)
0329 
0330 if(NOT "${{CMAKE_CXX_STANDARD}}")
0331   set(CMAKE_CXX_STANDARD 14)
0332 endif()
0333 set(CMAKE_POSITION_INDEPENDENT_CODE ON)   # Enable -fPIC for all targets
0334 
0335 # Expose custom cmake modules
0336 list(APPEND CMAKE_MODULE_PATH "${{CMAKE_CURRENT_LIST_DIR}}/cmake")
0337 
0338 # Set install directory to $JANA_HOME
0339 set(CMAKE_INSTALL_PREFIX $ENV{{JANA_HOME}} CACHE PATH "magic incantation" FORCE)
0340 
0341 # Find dependencies
0342 find_package(JANA REQUIRED)
0343 
0344 """
0345 
0346 
0347 project_src_cmakelists_txt = """
0348 add_subdirectory(libraries)
0349 add_subdirectory(plugins)
0350 add_subdirectory(programs)
0351 """
0352 
0353 
0354 plugin_cmakelists_txt = """
0355 {extra_find_packages}
0356 
0357 # According to the internet, CMake authors discourage the use
0358 # of GLOB for identifying source files. IMHO, this is due to
0359 # the flawed use of cache files in CMake itself. Here, GLOB
0360 # is used as the default. What this means is you can add source
0361 # files and re-run cmake (after clearing the cache file) and
0362 # they will be found without needing to modify this file.
0363 # You also have the option of switching the following to "false"
0364 # and managing the source file list manually the way they recommend.
0365 if(true)
0366   # Automatically determine source file list.
0367   file(GLOB mysourcefiles *.cpp *.cc *.c  *.hpp *.hh *.h)
0368   set( JANAGPUTest_PLUGIN_SOURCES ${{mysourcefiles}} )    
0369 else()
0370   # Manually manage source file list
0371   set ({name}_PLUGIN_SOURCES
0372         {name}.cc
0373         {name}Processor.cc
0374         {name}Processor.h
0375   )
0376 endif()
0377 
0378 add_library({name}_plugin SHARED ${{{name}_PLUGIN_SOURCES}})
0379 
0380 target_include_directories({name}_plugin PUBLIC  ${{CMAKE_SOURCE_DIR}} ${{JANA_INCLUDE_DIR}} {extra_includes})
0381 target_link_libraries({name}_plugin ${{JANA_LIBRARY}} {extra_libraries})
0382 set_target_properties({name}_plugin PROPERTIES PREFIX "" OUTPUT_NAME "{name}" SUFFIX ".so")
0383 
0384 install(TARGETS {name}_plugin DESTINATION plugins)
0385 
0386 file(GLOB my_headers "*.h*")
0387 install(FILES ${{my_headers}} DESTINATION include/{name})
0388 
0389 # For root dictionaries
0390 file(GLOB my_pcms "${{CMAKE_CURRENT_BINARY_DIR}}/*.pcm")
0391 install(FILES ${{my_pcms}} DESTINATION plugins)
0392 
0393 """
0394 
0395 
0396 mini_plugin_cmakelists_txt = """
0397 {extra_find_packages}
0398 
0399 # According to the internet, CMake authors discourage the use
0400 # of GLOB for identifying source files. IMHO, this is due to
0401 # the flawed use of cache files in CMake itself. Here, GLOB
0402 # is used as the default. What this means is you can add source
0403 # files and re-run cmake (after clearing the cache file) and
0404 # they will be found without needing to modify this file.
0405 # You also have the option of switching the following to "false"
0406 # and managing the source file list manually the way they recommend.
0407 if(true)
0408   # Automatically determine source file list.
0409   file(GLOB mysourcefiles *.cpp *.cc *.c  *.hpp *.hh *.h)
0410   set( {name}_PLUGIN_SOURCES ${{mysourcefiles}} )    
0411 else()
0412   # Manually manage source file list
0413   set ({name}_PLUGIN_SOURCES
0414         {name}.cc
0415   )
0416 endif()
0417 
0418 add_library({name}_plugin SHARED ${{{name}_PLUGIN_SOURCES}})
0419 
0420 target_include_directories({name}_plugin PUBLIC ${{CMAKE_SOURCE_DIR}} ${{JANA_INCLUDE_DIR}} {extra_includes})
0421 target_link_libraries({name}_plugin ${{JANA_LIB}} {extra_libraries})
0422 set_target_properties({name}_plugin PROPERTIES PREFIX "" OUTPUT_NAME "{name}" SUFFIX ".so")
0423 
0424 install(TARGETS {name}_plugin DESTINATION plugins)
0425 
0426 file(GLOB my_headers "*.h*")
0427 install(FILES ${{my_headers}} DESTINATION include/{name})
0428 
0429 # For root dictionaries
0430 file(GLOB my_pcms "${{CMAKE_CURRENT_BINARY_DIR}}/*.pcm")
0431 install(FILES ${{my_pcms}} DESTINATION plugins)
0432 
0433 """
0434 
0435 
0436 plugin_tests_cmakelists_txt = """
0437 
0438 set ({name}_PLUGIN_TESTS_SOURCES
0439         catch.hpp
0440         TestsMain.cc
0441         IntegrationTests.cc
0442         # Add component tests here
0443         )
0444 
0445 add_executable({name}_plugin_tests ${{{name}_PLUGIN_TESTS_SOURCES}})
0446 
0447 find_package(JANA REQUIRED)
0448 
0449 target_include_directories({name}_plugin_tests PUBLIC ../src)
0450 target_include_directories({name}_plugin_tests PUBLIC ${{JANA_INCLUDE_DIR}})
0451 
0452 target_link_libraries({name}_plugin_tests {name}_plugin)
0453 target_link_libraries({name}_plugin_tests ${{JANA_LIBRARY}})
0454 
0455 install(TARGETS {name}_plugin_tests DESTINATION bin)
0456 
0457 """
0458 
0459 
0460 plugin_integration_tests_cc = """
0461 #include "catch.hpp"
0462 #include "{name}Processor.h"
0463 
0464 #include <JANA/JApplication.h>
0465 #include <JANA/JFactoryGenerator.h>
0466 
0467 
0468 // This is where you can assemble various components and verify that when put together, they
0469 // do what you'd expect. This means you can skip the laborious mixing and matching of plugins and configurations,
0470 // and have everything run automatically inside one executable.
0471 
0472 TEST_CASE("{name}IntegrationTests") {{
0473 
0474     auto app = new JApplication;
0475     
0476     // Create and register components
0477     // app->Add(new {name}Processor);
0478     
0479     // TODO: Add (mocked) event source
0480     // app->Add(new MockEventSource);
0481 
0482     // Set test parameters
0483     app->SetParameterValue("nevents", 10);
0484 
0485     // Run everything, blocking until finished
0486     app->Run();
0487 
0488     // Verify the results you'd expect
0489     REQUIRE(app->GetNEventsProcessed() == 10);
0490 
0491 }}
0492 
0493 """
0494 
0495 
0496 plugin_tests_main_cc = """
0497 
0498 // This is the entry point for our test suite executable.
0499 // Catch2 will take over from here.
0500 
0501 #define CATCH_CONFIG_MAIN
0502 #include "catch.hpp"
0503 
0504 """
0505 
0506 
0507 jeventprocessor_template_h = """
0508 #ifndef _{name}_h_
0509 #define _{name}_h_
0510 
0511 #include <JANA/JEventProcessor.h>
0512 
0513 class {name} : public JEventProcessor {{
0514 
0515     // Shared state (e.g. histograms, TTrees, TFiles) live
0516     std::mutex m_mutex;
0517     
0518 public:
0519 
0520     {name}();
0521     virtual ~{name}() = default;
0522 
0523     void Init() override;
0524     void Process(const std::shared_ptr<const JEvent>& event) override;
0525     void Finish() override;
0526 
0527 }};
0528 
0529 
0530 #endif // _{name}_h_
0531 
0532 """
0533 
0534 
0535 jeventprocessor_template_cc = """
0536 #include "{name}.h"
0537 #include <JANA/JLogger.h>
0538 
0539 {name}::{name}() {{
0540     SetTypeName(NAME_OF_THIS); // Provide JANA with this class's name
0541 }}
0542 
0543 void {name}::Init() {{
0544     LOG << "{name}::Init" << LOG_END;
0545     // Open TFiles, set up TTree branches, etc
0546 }}
0547 
0548 void {name}::Process(const std::shared_ptr<const JEvent> &event) {{
0549     LOG << "{name}::Process, Event #" << event->GetEventNumber() << LOG_END;
0550     
0551     /// Do everything we can in parallel
0552     /// Warning: We are only allowed to use local variables and `event` here
0553     //auto hits = event->Get<Hit>();
0554 
0555     /// Lock mutex
0556     std::lock_guard<std::mutex>lock(m_mutex);
0557 
0558     /// Do the rest sequentially
0559     /// Now we are free to access shared state such as m_heatmap
0560     //for (const Hit* hit : hits) {{
0561         /// Update shared state
0562     //}}
0563 }}
0564 
0565 void {name}::Finish() {{
0566     // Close any resources
0567     LOG << "{name}::Finish" << LOG_END;
0568 }}
0569 
0570 """
0571 
0572 
0573 jeventprocessor_template_tests = """
0574 """
0575 
0576 
0577 jfactory_template_cc = """
0578 #include "{name}.h"
0579 
0580 #include <JANA/JEvent.h>
0581 
0582 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
0583 // Please add the following lines to your InitPlugin or similar routine
0584 // in order to register this factory with the system.
0585 //
0586 // #include "{name}.h"
0587 //
0588 //     app->Add( new JFactoryGeneratorT<{name}>() );
0589 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
0590 
0591 
0592 //------------------------
0593 // Constructor
0594 //------------------------
0595 {name}::{name}(){{
0596     SetTag("{tag}");
0597 }}
0598 
0599 //------------------------
0600 // Init
0601 //------------------------
0602 void {name}::Init() {{
0603     auto app = GetApplication();
0604     
0605     /// Acquire any parameters
0606     // app->GetParameter("parameter_name", m_destination);
0607     
0608     /// Acquire any services
0609     // m_service = app->GetService<ServiceT>();
0610     
0611     /// Set any factory flags
0612     // SetFactoryFlag(JFactory_Flags_t::NOT_OBJECT_OWNER);
0613 }}
0614 
0615 //------------------------
0616 // ChangeRun
0617 //------------------------
0618 void {name}::ChangeRun(const std::shared_ptr<const JEvent> &event) {{
0619     /// This is automatically run before Process, when a new run number is seen
0620     /// Usually we update our calibration constants by asking a JService
0621     /// to give us the latest data for this run number
0622     
0623     auto run_nr = event->GetRunNumber();
0624     // m_calibration = m_service->GetCalibrationsForRun(run_nr);
0625 }}
0626 
0627 //------------------------
0628 // Process
0629 //------------------------
0630 void {name}::Process(const std::shared_ptr<const JEvent> &event) {{
0631 
0632     /// JFactories are local to a thread, so we are free to access and modify
0633     /// member variables here. However, be aware that events are _scattered_ to
0634     /// different JFactory instances, not _broadcast_: this means that JFactory 
0635     /// instances only see _some_ of the events. 
0636     
0637     /// Acquire inputs (This may recursively call other JFactories)
0638     // auto inputs = event->Get<...>();
0639     
0640     /// Do some computation
0641     
0642     /// Publish outputs
0643     // std::vector<{jobject_name}*> results;
0644     // results.push_back(new {jobject_name}(...));
0645     // Set(results);
0646 }}
0647 """
0648 
0649 
0650 jfactory_template_h = """
0651 #ifndef _{name}_h_
0652 #define _{name}_h_
0653 
0654 #include <JANA/JFactoryT.h>
0655 
0656 #include "{jobject_name}.h"
0657 
0658 class {name} : public JFactoryT<{jobject_name}> {{
0659 
0660     // Insert any member variables here
0661 
0662 public:
0663     {name}();
0664     void Init() override;
0665     void ChangeRun(const std::shared_ptr<const JEvent> &event) override;
0666     void Process(const std::shared_ptr<const JEvent> &event) override;
0667 
0668 }};
0669 
0670 #endif // _{name}_h_
0671 """
0672 
0673 
0674 jroot_output_processor_h = """
0675 #include <JANA/JEventProcessor.h>
0676 #include <JANA/Services/JGlobalRootLock.h>
0677 #include <TH1D.h>
0678 #include <TFile.h>
0679 
0680 class {processor_name}: public JEventProcessor {{
0681 
0682 private:
0683     std::string m_tracking_alg = "genfit";
0684     std::shared_ptr<JGlobalRootLock> m_lock;
0685     TH1D* h1d_pt_reco = nullptr;
0686     TDirectory* dest_file = nullptr;
0687     TDirectory* dest_dir=nullptr; // Virtual subfolder inside dest_file used for this specific processor
0688 
0689 public:
0690     {processor_name}();
0691     
0692     void Init() override;
0693 
0694     void Process(const std::shared_ptr<const JEvent>& event) override;
0695 
0696     void Finish() override;
0697 }};
0698 
0699 """
0700 
0701 
0702 jroot_output_processor_cc = """
0703 #include "{processor_name}.h"
0704 
0705 {processor_name}::{processor_name}() {{
0706     SetTypeName(NAME_OF_THIS); // Provide JANA with this class's name
0707 }}
0708     
0709 void {processor_name}::Init() {{
0710     auto app = GetApplication();
0711     m_lock = app->GetService<JGlobalRootLock>();
0712 
0713     /// Set parameters to control which JFactories you use
0714     app->SetDefaultParameter("tracking_alg", m_tracking_alg);
0715 
0716     /// Set up histograms
0717     m_lock->acquire_write_lock();
0718     
0719     if( dest_file == nullptr ){{
0720         dest_file = new TFile("{processor_name}.root", "recreate");  /// TODO: Acquire dest_file via either a JService or a JParameter
0721     }}
0722     dest_dir = dest_file->mkdir("{dir_name}"); // Create a subdir inside dest_file for these results
0723     h1d_pt_reco = new TH1D("pt_reco", "reco pt", 100,0,10);
0724     h1d_pt_reco->SetDirectory(dest_dir);
0725     m_lock->release_lock();
0726 }}
0727 
0728 void {processor_name}::Process(const std::shared_ptr<const JEvent>& event) {{
0729 
0730     /// Acquire any results you need for your analysis
0731     //auto reco_tracks = event->Get<RecoTrack>(m_tracking_alg);
0732 
0733     m_lock->acquire_write_lock();
0734     /// Inside the global root lock, update histograms
0735     // for (auto reco_track : reco_tracks) {{
0736     //    h1d_pt_reco->Fill(reco_track->p.Pt());
0737     // }}
0738     m_lock->release_lock();
0739 }}
0740 
0741 void {processor_name}::Finish() {{
0742     // TODO: If we did not create this file then we should not delete it
0743     dest_file->Write();
0744     delete dest_file;
0745     dest_file = nullptr;
0746 }};
0747 
0748 """
0749 
0750 
0751 mini_plugin_cc_noroot = """
0752 #include <JANA/JEventProcessor.h>
0753 
0754 class {name}Processor: public JEventProcessor {{
0755 private:
0756     std::string m_tracking_alg = "genfit";
0757     std::mutex m_mutex;
0758 
0759 public:
0760     {name}Processor() {{
0761         SetTypeName(NAME_OF_THIS); // Provide JANA with this class's name
0762     }}
0763     
0764     void Init() override {{
0765         auto app = GetApplication();
0766 
0767         /// Set parameters to control which JFactories you use
0768         app->SetDefaultParameter("tracking_alg", m_tracking_alg);
0769     }}
0770 
0771     void Process(const std::shared_ptr<const JEvent>& event) override {{
0772 
0773         /// Acquire any per-event results you need for your analysis
0774         // auto reco_tracks = event->Get<RecoTrack>(m_tracking_alg);
0775 
0776         /// Inside the lock, update any shared state, e.g. histograms
0777         std::lock_guard<std::mutex> lock(m_mutex);
0778         
0779         // for (auto reco_track : reco_tracks) {{
0780         //    histogram->Fill(reco_track->p.Pt());
0781         // }}
0782     }}
0783 
0784     void Finish() override {{
0785         // Write data and close resources
0786     }}
0787 }};
0788     
0789 extern "C" {{
0790     void InitPlugin(JApplication *app) {{
0791         InitJANAPlugin(app);
0792         app->Add(new {name}Processor);
0793     }}
0794 }}
0795     
0796 """
0797 
0798 
0799 mini_plugin_cc = """
0800 #include <JANA/JEventProcessor.h>
0801 #include <JANA/Services/JGlobalRootLock.h>
0802 #include <TH1D.h>
0803 #include <TFile.h>
0804 
0805 class {name}Processor: public JEventProcessor {{
0806 private:
0807     std::string m_tracking_alg = "genfit";
0808     std::shared_ptr<JGlobalRootLock> m_lock;
0809     TFile* dest_file;
0810     TDirectory* dest_dir; // Virtual subfolder inside dest_file used for this specific processor
0811     
0812     /// Declare histograms here
0813     TH1D* h1d_pt_reco;
0814 
0815 public:
0816     {name}Processor() {{
0817         SetTypeName(NAME_OF_THIS); // Provide JANA with this class's name
0818     }}
0819     
0820     void Init() override {{
0821         auto app = GetApplication();
0822         m_lock = app->GetService<JGlobalRootLock>();
0823 
0824         /// Set parameters to control which JFactories you use
0825         app->SetDefaultParameter("tracking_alg", m_tracking_alg);
0826 
0827         /// Set up histograms
0828         m_lock->acquire_write_lock();
0829         dest_file = new TFile("{name}.root", "recreate");  /// TODO: Acquire dest_file via either a JService or a JParameter
0830         dest_dir = dest_file->mkdir("{name}"); // Create a subdir inside dest_file for these results
0831         dest_file->cd();
0832         h1d_pt_reco = new TH1D("pt_reco", "reco pt", 100,0,10);
0833         h1d_pt_reco->SetDirectory(dest_dir);
0834         m_lock->release_lock();
0835     }}
0836 
0837     void Process(const std::shared_ptr<const JEvent>& event) override {{
0838 
0839         /// Acquire any results you need for your analysis
0840         //auto reco_tracks = event->Get<RecoTrack>(m_tracking_alg);
0841 
0842         m_lock->acquire_write_lock();
0843         /// Inside the global root lock, update histograms
0844         // for (auto reco_track : reco_tracks) {{
0845         //    h1d_pt_reco->Fill(reco_track->p.Pt());
0846         // }}
0847         m_lock->release_lock();
0848     }}
0849 
0850     void Finish() override {{
0851         // TODO: If we did not create this file then we should not delete it
0852         dest_file->Write();
0853         delete dest_file;
0854         dest_file = nullptr;
0855     }}
0856 }};
0857     
0858 extern "C" {{
0859     void InitPlugin(JApplication *app) {{
0860         InitJANAPlugin(app);
0861         app->Add(new {name}Processor);
0862     }}
0863 }}
0864     
0865 """
0866 
0867 
0868 def create_jobject(name):
0869     """Create a JObject code skeleton in the current directory. Requires one argument:
0870        name:  The name of the JObject, e.g. "RecoTrack"
0871     """
0872     filename = name + ".h"
0873     text = jobject_template_h.format(copyright_notice=copyright_notice, name=name)
0874     print(f"Creating {name}:public JObject")
0875     with open(filename, 'w') as f:
0876         f.write(text)
0877 
0878 
0879 def create_jeventsource(name):
0880     """Create a JEventSource code skeleton in the current directory. Requires one argument:
0881        name:  The name of the JEventSource, e.g. "CsvFileSource"
0882     """
0883 
0884     with open(name + ".h", 'w') as f:
0885         text = jeventsource_template_h.format(copyright_notice=copyright_notice, name=name)
0886         f.write(text)
0887 
0888     with open(name + ".cc", 'w') as f:
0889         text = jeventsource_template_cc.format(copyright_notice=copyright_notice, name=name)
0890         f.write(text)
0891 
0892 
0893 def create_jeventprocessor(name):
0894     """Create a JEventProcessor code skeleton in the current directory. Requires one argument:
0895        name:  The name of the JEventProcessor, e.g. "TrackingEfficiencyProcessor"
0896     """
0897 
0898     with open(name + ".h", 'w') as f:
0899         text = jeventprocessor_template_h.format(copyright_notice=copyright_notice, name=name)
0900         f.write(text)
0901 
0902     with open(name + ".cc", 'w') as f:
0903         text = jeventprocessor_template_cc.format(copyright_notice=copyright_notice, name=name)
0904         f.write(text)
0905 
0906     with open(name + "Tests.cc", 'w') as f:
0907         text = jeventprocessor_template_tests.format(copyright_notice=copyright_notice, name=name)
0908         f.write(text)
0909 
0910 
0911 def create_root_eventprocessor(processor_name, dir_name):
0912     """Create a ROOT-aware JEventProcessor code skeleton in the current directory. Requires two positional arguments:
0913        [processor_name]  The name of the JEventProcessor, e.g. "TrackingEfficiencyProcessor"
0914        [dir_name]        The name of the virtual directory in the ROOT file where everything goes, e.g. "trk_eff"
0915     """
0916     with open(processor_name + ".h", 'w') as f:
0917         text = jroot_output_processor_h.format(processor_name=processor_name, dir_name=dir_name)
0918         f.write(text)
0919 
0920 
0921 def build_plugin_test(plugin_name, copy_catch_hpp=True, dir="."):
0922 
0923     if copy_catch_hpp:
0924         files_to_copy = {"catch.hpp": {"sourcedir": "src/external",
0925                                        "installdir": "include/JANA/external",
0926                                        "destname": dir+"/catch.hpp"}}
0927         copy_from_source_dir(files_to_copy)
0928 
0929     cmakelists_path = dir + "/" + "CMakeLists.txt"
0930     with open(cmakelists_path) as f:
0931         text = plugin_tests_cmakelists_txt.format(name=plugin_name)
0932         f.write(text)
0933 
0934     integration_test_cc_path = dir + "/IntegrationTests.cc"
0935     with open(integration_test_cc_path) as f:
0936         text = plugin_integration_tests_cc.format(name=plugin_name)
0937         f.write(text)
0938 
0939     tests_main_path = dir + "/TestsMain.cc"
0940     with open(tests_main_path) as f:
0941         text = plugin_tests_main_cc
0942         f.write(text)
0943 
0944 
0945 def build_jeventprocessor_test(processor_name, is_mini=True, copy_catch_hpp=True, dir="."):
0946 
0947     if copy_catch_hpp:
0948         files_to_copy = {"catch.hpp": {"sourcedir": "src/external",
0949                                        "installdir": "include/JANA/external",
0950                                        "destname": dir+"/catch.hpp"}}
0951         copy_from_source_dir(files_to_copy)
0952 
0953     cmakelists_path = dir + "/" + "CMakeLists.txt"
0954     with open(cmakelists_path) as f:
0955         text = plugin_tests_cmakelists_txt.format(name=name)
0956         f.write(text)
0957 
0958     if is_mini:
0959         integration_test_cc_path = dir + "/IntegrationTests.cc"
0960         with open(integration_test_cc_path) as f:
0961             text = plugin_integration_tests_cc.format(name=processor_name)
0962             f.write(text)
0963     else:
0964         tests_main_path = dir + "/TestsMain.cc"
0965         with open(tests_main_path) as f:
0966             text = plugin_tests_main_cc
0967             f.write(text)
0968 
0969 
0970 def create_jeventprocessor_test(processor_name):
0971     """Create a JEventProcessor unit test skeleton in the current directory. Requires two arguments:
0972        processor_name:  The name of the JEventProcessor under test
0973     """
0974     build_jeventprocessor_test()
0975 
0976 
0977 def create_jfactory(jobject_name, tag=''):
0978     """Create a JFactory code skeleton in the current directory. One required argument and one optional argument:
0979        jobject_name  The name of the JObject this factory creates, e.g. "RecoTrack"
0980        [tag]         Optional tag for the factory (defaults to empty string)
0981        
0982        n.b. This will also automatically create a JObject header in the current
0983        directory if the file does not already exist. You may need to delete this
0984        manually if you already have this class defined elsewhere in the source tree.
0985     """
0986 
0987     factory_name = f'JFactory_{jobject_name}'
0988     if tag != '' : factory_name += f'_{tag}'
0989 
0990     print(f"Creating {factory_name}:public JFactoryT<{jobject_name}>")
0991     with open(factory_name + ".cc", 'w') as f:
0992         text = jfactory_template_cc.format(name=factory_name, jobject_name=jobject_name, tag=tag)
0993         f.write(text)
0994 
0995     with open(factory_name + ".h", 'w') as f:
0996         text = jfactory_template_h.format(name=factory_name, jobject_name=jobject_name, tag=tag)
0997         f.write(text)
0998     
0999     # Check if a header for the JObject already exists. If not, create it.
1000     # The more likely scenario is that someone creating a factory will also
1001     # need to create this.
1002     jobject_header_filename = jobject_name + ".h"
1003     if not os.path.exists( jobject_header_filename ):
1004         create_jobject( jobject_name )
1005 
1006 
1007 def create_jfactory_test(factory_name, jobject_name):
1008     with open(factory_name + "Test.cc", 'w') as f:
1009         text = jfactory_test_cc.format(factory_name=factory_name, jobject_name=jobject_name);
1010         f.write(text)
1011 
1012 
1013 def create_executable(name):
1014     """Create a code skeleton for a project executable in the current directory. Requires one positional argument:
1015        name:  The name of the executable, e.g. "escalate" or "halld_recon"
1016     """
1017     pass
1018 
1019 
1020 def create_plugin(name, is_standalone=True, is_mini=True, include_root=False, include_tests=False):
1021     """Create a code skeleton for a plugin in its own directory. Takes the following positional arguments:
1022           name            The name of the plugin, e.g. "trk_eff" or "TrackingEfficiency"
1023          [is_standalone]  Is this a new project, or are we inside the source tree of an existing CMake project? (default=True)
1024          [is_mini]        Reduce boilerplate and put everything in a single file? (default=True)
1025          [include_root]   Include a ROOT dependency and stubs for filling a ROOT histogram? (default=False)
1026 
1027        Example: `jana_generate.py Plugin TrackingEfficiency 1 0 0`
1028     """
1029 
1030     is_standalone = boolify(is_standalone, "is_standalone")
1031     is_mini = boolify(is_mini, "is_mini")
1032     include_root = boolify(include_root, "include_root")
1033     include_tests = boolify(include_tests, "include_tests")
1034 
1035     os.mkdir(name)
1036     if not is_standalone:
1037         with open("CMakeLists.txt", 'a') as f:
1038             f.write("add_subdirectory("+name+")\n")
1039             # If CMakeLists doesn't already exist, this will create it
1040             # TODO: Probably want to auto detect is_standalone based on existence of parent CMakeLists
1041 
1042     cmakelists = ""
1043     if include_root:
1044         extra_find_packages = "find_package(ROOT REQUIRED)"
1045         extra_includes = "${ROOT_INCLUDE_DIRS}"
1046         extra_libraries = "${ROOT_LIBRARIES}"
1047     else:
1048         extra_find_packages = ""
1049         extra_includes = ""
1050         extra_libraries = ""
1051 
1052     if is_standalone:
1053         cmakelists += project_cmakelists_txt.format(name=name)
1054 
1055     if not is_mini:
1056         with open(name + "/" + name + ".cc", 'w') as f:
1057             text = plugin_main.format(name=name)
1058             f.write(text)
1059 
1060         # Otherwise InitPlugin goes into the processor file
1061 
1062     if include_root and is_mini:
1063         cmakelists += mini_plugin_cmakelists_txt.format(name=name,
1064                                                         extra_find_packages=extra_find_packages,
1065                                                         extra_includes=extra_includes,
1066                                                         extra_libraries=extra_libraries)
1067         with open(name + "/" + name + ".cc", 'w') as f:
1068             text = mini_plugin_cc.format(name=name)
1069             f.write(text)
1070 
1071     elif include_root and not is_mini:
1072         with open(name + "/" + name + "Processor.h", 'w') as f:
1073             text = jroot_output_processor_h.format(processor_name=name+"Processor", dir_name=name)
1074             f.write(text)
1075 
1076         with open(name + "/" + name + "Processor.cc", 'w') as f:
1077             text = jroot_output_processor_cc.format(processor_name=name+"Processor", dir_name=name)
1078             f.write(text)
1079 
1080         cmakelists += plugin_cmakelists_txt.format(name=name,
1081                                                    extra_find_packages=extra_find_packages,
1082                                                    extra_includes=extra_includes,
1083                                                    extra_libraries=extra_libraries)
1084 
1085     elif not include_root and is_mini:
1086         cmakelists += mini_plugin_cmakelists_txt.format(name=name,
1087                                                         extra_find_packages=extra_find_packages,
1088                                                         extra_includes=extra_includes,
1089                                                         extra_libraries=extra_libraries)
1090         with open(name + "/" + name + ".cc", 'w') as f:
1091             text = mini_plugin_cc_noroot.format(name=name)
1092             f.write(text)
1093 
1094     else:
1095         # not include_root and not is_mini:
1096         cmakelists += plugin_cmakelists_txt.format(name=name,
1097                                                    extra_find_packages=extra_find_packages,
1098                                                    extra_includes=extra_includes,
1099                                                    extra_libraries=extra_libraries)
1100 
1101         with open(name + "/" + name + "Processor.cc", 'w') as f:
1102             text = jeventprocessor_template_cc.format(name=name+"Processor")
1103             f.write(text)
1104 
1105         with open(name + "/" + name + "Processor.h", 'w') as f:
1106             text = jeventprocessor_template_h.format(name=name+"Processor")
1107             f.write(text)
1108 
1109     if include_tests:
1110         with open(name + "/" + name + "ProcessorTest.cc", 'w') as f:
1111             text = jeventprocessor_template_tests.format(name=name+"Processor")
1112             f.write(text)
1113 
1114         # Copy tests CMakeLists stub
1115         # Copy catch2
1116         # Copy tests for processor
1117 
1118     # Write CMakeLists body
1119     with open(name + "/CMakeLists.txt", 'w') as f:
1120         f.write(cmakelists)
1121 
1122 
1123 def create_project(name):
1124     """Create a code skeleton for a complete project in its own directory. Requires one argument:
1125        name:  The name of the project, e.g. "escalate" or "halld_recon"
1126     """
1127     os.mkdir(name)
1128     os.mkdir(name + "/cmake")
1129     os.mkdir(name + "/external")
1130     os.mkdir(name + "/src")
1131     os.mkdir(name + "/src/libraries")
1132     os.mkdir(name + "/src/plugins")
1133     os.mkdir(name + "/src/programs")
1134     os.mkdir(name + "/src/programs/cli")
1135     # os.mkdir(name + "/tests")
1136 
1137     with open(name + "/CMakeLists.txt", 'w') as f:
1138         text = project_cmakelists_txt.format(name=name)
1139         f.write(text)
1140 
1141     with open(name + "/src/CMakeLists.txt", 'w') as f:
1142         text = project_src_cmakelists_txt
1143         f.write(text)
1144 
1145     # with open(name + "/tests/CMakeLists.txt", 'w') as f:
1146     #     f.write("")
1147 
1148     with open(name + "/src/libraries/CMakeLists.txt", 'w') as f:
1149         f.write("")
1150 
1151     with open(name + "/src/plugins/CMakeLists.txt", 'w') as f:
1152         f.write("\n")
1153 
1154     with open(name + "/src/programs/CMakeLists.txt", 'w') as f:
1155         f.write("")
1156 
1157 def print_usage():
1158     print("Usage: jana-generate.py [-h|--help] [type] [args...]")
1159     print("  type: " + ' '.join(dispatch_table.keys()))
1160 
1161 def print_help():
1162     print("""
1163 jana-generate.py : Generate skeleton code template for various JANA code constructs.
1164   
1165 Usage: jana-generate.py [-h|--help] [type] [args...]
1166 
1167   This is used to generate a small piece of skeleton code that can then be easily
1168 edited to implement a specific class or plugin in JANA. The type argument specifies
1169 the type of construct to generate. Optional arguments vary depending on the type.
1170 The type argument should be one of:
1171 """)
1172     print('\n'.join(dispatch_table.keys()))
1173 
1174     print('\n--------------------------------------------------------------------')
1175     print('Details:\n')
1176    
1177     for (name,proc) in dispatch_table.items():
1178         print( name )
1179         print( proc.__doc__ )
1180 
1181 
1182 #========================================================================================
1183 if __name__ == '__main__':
1184 
1185     if sys.version_info < (3, 6):
1186         print('Please upgrade your Python version to 3.6.0 or higher')
1187         sys.exit()
1188 
1189     # dispatch_table must be defined here since print_usage uses it
1190     dispatch_table = {'JObject': create_jobject,
1191                       'JEventSource': create_jeventsource,
1192                       'JEventProcessor': create_jeventprocessor,
1193                       'RootEventProcessor': create_root_eventprocessor,
1194                       'JEventProcessorTest': create_jeventprocessor_test,
1195                       'JFactory': create_jfactory,
1196                       # 'JFactoryTest': create_jfactory_test,
1197                       # 'Executable': create_executable,
1198                       'Plugin': create_plugin,
1199                       'Project': create_project,
1200                       #'Test': run_tests
1201                       }
1202 
1203     # Print short usage statement if no arguments
1204     if len(argv) < 2:
1205         print_usage()
1206         exit()
1207     
1208     # Print longer help if user specifies -h or --help
1209     if len(argv) == 2:
1210         if argv[1]=='-h' or argv[1]=='--help':
1211             print_help()
1212             exit()
1213 
1214     # Run generator for user specified type
1215     option = argv[1]
1216     if option in dispatch_table:
1217         try:
1218             dispatch_table[option](*argv[2:])
1219         except TypeError:
1220             print('\n'+dispatch_table[option].__doc__)
1221     else:
1222         print("Error: Invalid option!")
1223         print_usage()
1224         exit()
1225 
1226