diff --git a/ChangeLog b/ChangeLog index 41aa46e..a1153e1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,8 +1,9 @@ -2018-06-13 +2018-06-14 * Version 1.6.0 released ======================== + Support application specific system parameter files (#2513) Add methods and operators to handle additional columns in GPha Made GApplicationPar::query() method public Fix handling of q-mode parameters (#2495) diff --git a/NEWS b/NEWS index b016343..780b23c 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,6 @@ New Features and Important Changes in GammaLib 1.6.0 -13 June 2018 +14 June 2018 1. Introduction @@ -27,6 +27,7 @@ The following methods have been added: - GSkyDir::cos_dist() - GApplicationPar::query() - GApplicationPar::was_queried() +- GApplicationPars::syspfiles() - operator==(GSkyPixel&, GSkyPixel&) - operator!=(GSkyPixel&, GSkyPixel&) - GPha::operator(int&, int&) @@ -84,6 +85,14 @@ Keep existing library path in installation script on Mac OS X (#2420) 4. Application module --------------------- +A GApplicationPars::syspfiles() setter and getter method was added to allow the +specification of an application specific system parameter file path. If such a +file path is specified, the loading of the application parameters needs to be +redone by the client. The GApplicationPars::load() methods now gives priority +to a parameter file found in the syspfiles folder and updates parameter values +using the parameter file found in the user's pfiles folder. In that way, any +changes in the parameter file structure should be handled transparently (#2513). + The GApplicationPar::query() method was made public. Add GApplicationPar::was_queried() method and do no longer change the mode diff --git a/doc/source/admin/release_history/1.6.rst b/doc/source/admin/release_history/1.6.rst index 19dcf9b..02afd53 100644 --- a/doc/source/admin/release_history/1.6.rst +++ b/doc/source/admin/release_history/1.6.rst @@ -30,6 +30,8 @@ Bug fixes Improvements ------------ +* [`2513 `_] - + Support application specific system parameter files * Add methods and operators to handle additional columns in ``GPha`` * Made ``GApplicationPar::query()`` method public * [`2466 `_] - diff --git a/include/GApplicationPars.hpp b/include/GApplicationPars.hpp index c567f59..db1e91c 100644 --- a/include/GApplicationPars.hpp +++ b/include/GApplicationPars.hpp @@ -1,7 +1,7 @@ /*************************************************************************** * GApplicationPars.hpp - Application parameters * * ----------------------------------------------------------------------- * - * copyright (C) 2010-2016 by Juergen Knoedlseder * + * copyright (C) 2010-2018 by Juergen Knoedlseder * * ----------------------------------------------------------------------- * * * * This program is free software: you can redistribute it and/or modify * @@ -83,6 +83,8 @@ public: void reserve(const int& num); void extend(const GApplicationPars& pars); bool contains(const std::string& name) const; + void syspfiles(const std::string& syspfiles); + const std::string& syspfiles(void) const; void load(const GFilename& filename); void load(const GFilename& filename, const std::vector& args); @@ -95,11 +97,14 @@ protected: void copy_members(const GApplicationPars& pars); void free_members(void); std::string inpath(const std::string& filename) const; + std::string pfiles_path(const std::string& filename) const; + std::string syspfiles_path(const std::string& filename) const; std::string outpath(const std::string& filename) const; void read(const std::string& filename); void write(const std::string& filename) const; void parse(void); void update(void); + void synchronise(const std::string& filename); int get_index(const std::string& name) const; std::string parline(GApplicationPar& par, size_t* start, size_t* stop) const; @@ -110,6 +115,7 @@ protected: std::vector m_vstart; //!< Column of value start std::vector m_vstop; //!< Column of value stop std::string m_mode; //!< Effective mode + std::string m_syspfiles; //!< Optional location of syspfiles }; @@ -184,7 +190,7 @@ bool GApplicationPars::is_empty(void) const /***********************************************************************//** * @brief Reserves space for parameters in container * - * @param[in] num Number of parameter + * @param[in] num Number of parameters. * * Reserves space for @p num parameters in the container. ***************************************************************************/ @@ -195,4 +201,33 @@ void GApplicationPars::reserve(const int& num) return; } + +/***********************************************************************//** + * @brief Set path to system pfiles + * + * @param[in] syspfiles Path to system pfiles. + * + * Sets the path to system parameter files. + ***************************************************************************/ +inline +void GApplicationPars::syspfiles(const std::string& syspfiles) +{ + m_syspfiles = syspfiles; + return; +} + + +/***********************************************************************//** + * @brief Return path to system pfiles + * + * @return Path to system pfiles. + * + * Returns path to system parameter files. + ***************************************************************************/ +inline +const std::string& GApplicationPars::syspfiles(void) const +{ + return (m_syspfiles); +} + #endif /* GAPPLICATIONPARS_HPP */ diff --git a/pyext/GApplicationPars.i b/pyext/GApplicationPars.i index 4983d64..2871a1f 100644 --- a/pyext/GApplicationPars.i +++ b/pyext/GApplicationPars.i @@ -1,7 +1,7 @@ /*************************************************************************** * GApplicationPars.i - Application parameter container * * ----------------------------------------------------------------------- * - * copyright (C) 2010-2017 by Juergen Knoedlseder * + * copyright (C) 2010-2018 by Juergen Knoedlseder * * ----------------------------------------------------------------------- * * * * This program is free software: you can redistribute it and/or modify * @@ -48,26 +48,28 @@ public: virtual ~GApplicationPars(void); // Methods - void clear(void); - GApplicationPars* clone(void) const; - std::string classname(void) const; - GApplicationPar& at(const int& index); - int size(void) const; - bool is_empty(void) const; - GApplicationPar& append(const GApplicationPar& par); - void append_standard(void); - GApplicationPar& insert(const int& index, const GApplicationPar& par); - GApplicationPar& insert(const std::string& name, - const GApplicationPar& par); - void remove(const int& index); - void remove(const std::string& name); - void reserve(const int& num); - void extend(const GApplicationPars& pars); - bool contains(const std::string& name) const; - void load(const GFilename& filename); - void load(const GFilename& filename, - const std::vector& args); - void save(const GFilename& filename); + void clear(void); + GApplicationPars* clone(void) const; + std::string classname(void) const; + GApplicationPar& at(const int& index); + int size(void) const; + bool is_empty(void) const; + GApplicationPar& append(const GApplicationPar& par); + void append_standard(void); + GApplicationPar& insert(const int& index, const GApplicationPar& par); + GApplicationPar& insert(const std::string& name, + const GApplicationPar& par); + void remove(const int& index); + void remove(const std::string& name); + void reserve(const int& num); + void extend(const GApplicationPars& pars); + bool contains(const std::string& name) const; + void syspfiles(const std::string& syspfiles); + const std::string& syspfiles(void) const; + void load(const GFilename& filename); + void load(const GFilename& filename, + const std::vector& args); + void save(const GFilename& filename); }; diff --git a/src/app/GApplication.cpp b/src/app/GApplication.cpp index 9204436..f5613ac 100644 --- a/src/app/GApplication.cpp +++ b/src/app/GApplication.cpp @@ -91,7 +91,7 @@ GApplication::GApplication(const std::string& name, const std::string& version) m_parfile = name+".par"; m_logfile = name+".log"; - // Initialise application parameters + // Load application parameters m_pars.load(par_filename()); // Signal that application parameters have been loaded diff --git a/src/app/GApplicationPars.cpp b/src/app/GApplicationPars.cpp index 41b58d7..a9840b4 100644 --- a/src/app/GApplicationPars.cpp +++ b/src/app/GApplicationPars.cpp @@ -1,7 +1,7 @@ /*************************************************************************** * GApplicationPars.cpp - Application parameters * * ----------------------------------------------------------------------- * - * copyright (C) 2010-2016 by Juergen Knoedlseder * + * copyright (C) 2010-2018 by Juergen Knoedlseder * * ----------------------------------------------------------------------- * * * * This program is free software: you can redistribute it and/or modify * @@ -632,23 +632,58 @@ bool GApplicationPars::contains(const std::string& name) const * Parameter file not found. * * Loads all parameters from parameter file. + * + * If the syspfiles path is set then load the parameter file from that + * location and update the parameters using a paramater file that is found + * in the users "pfiles" directory. + * + * Otherwise, search the parameter file in the usual location (see the + * GApplicationPars::inpath method for more information). ***************************************************************************/ void GApplicationPars::load(const GFilename& filename) { // Reset parameters m_parfile.clear(); - // Get path to parameter file for input - std::string path = inpath(filename.url()); - if (path.length() == 0) { - throw GException::par_file_not_found(G_LOAD1, filename.url()); + // If a syspfiles path was set then get parfile from this path and + // update the parameters using a copy found in the users "pfiles" + // directory + std::string path = syspfiles_path(filename.url()); + if (!path.empty()) { + + // Read parfile + read(path); + + // Parse parfile + parse(); + + // Synchronize parfile using a copy found in the users "pfiles" + // directory + path = pfiles_path(filename.url()); + if (!path.empty()) { + synchronise(path); + } + } - // Read parfile - read(path); + // ... otherwise get path to parameter file for input + else { - // Parse parfile - parse(); + // Get parfile file path + path = inpath(filename.url()); + + // If file path is empty then throw an exception + if (path.empty()) { + throw GException::par_file_not_found(G_LOAD1, filename.url()); + } + + // Read parfile + read(path); + + // Parse parfile + parse(); + + } // Return return; @@ -661,8 +696,6 @@ void GApplicationPars::load(const GFilename& filename) * @param[in] filename Parameter filename. * @param[in] args Command line arguments. * - * @exception GException::par_file_not_found - * Parameter file not found. * @exception GException::bad_cmdline_argument * Invalid command line argument encountered. * @@ -672,20 +705,8 @@ void GApplicationPars::load(const GFilename& filename) void GApplicationPars::load(const GFilename& filename, const std::vector& args) { - // Reset parameters - m_parfile.clear(); - - // Get path to parameter file for input - std::string path = inpath(filename.url()); - if (path.length() == 0) { - throw GException::par_file_not_found(G_LOAD2, filename.url()); - } - - // Read parfile - read(path); - - // Parse parfile - parse(); + // Load parameters + load(filename); // Overwrite parameter values that are specified in the command line for (int i = 1; i < args.size(); ++i) { @@ -809,6 +830,7 @@ void GApplicationPars::init_members(void) m_vstart.clear(); m_vstop.clear(); m_mode = "h"; + m_syspfiles.clear(); // Return return; @@ -823,12 +845,13 @@ void GApplicationPars::init_members(void) void GApplicationPars::copy_members(const GApplicationPars& pars) { // Copy attributes - m_parfile = pars.m_parfile; - m_pars = pars.m_pars; - m_line = pars.m_line; - m_vstart = pars.m_vstart; - m_vstop = pars.m_vstop; - m_mode = pars.m_mode; + m_parfile = pars.m_parfile; + m_pars = pars.m_pars; + m_line = pars.m_line; + m_vstart = pars.m_vstart; + m_vstop = pars.m_vstop; + m_mode = pars.m_mode; + m_syspfiles = pars.m_syspfiles; // Return return; @@ -937,6 +960,98 @@ std::string GApplicationPars::inpath(const std::string& filename) const /***********************************************************************//** + * @brief Return path to parfile in $PFILES or $HOME/pfiles folder + * + * @param[in] filename Parameter filename to search for. + * @return Full parameter filename, including absolute access path. + * + * Locates parameter file in the directories that are listed in the PFILES + * environment variable. Directories may be separated by : or by ; in the + * PFILES environment variable. + * + * If the PFILES environment variable is not set the parameter file is + * searched in the users pfiles directory. + * + * If parameter file was not found, or the parameter file is not + * read-accessible, an empty string is returned. + ***************************************************************************/ +std::string GApplicationPars::pfiles_path(const std::string& filename) const +{ + // Allocate result path + std::string path; + + // Search for parameter file in PFILES directories if the PFILES + // environment variable has been set + char* ptr = std::getenv("PFILES"); + if (ptr != NULL) { + + // Extract directories from PFILES environment variable + std::string pfiles = ptr; + std::vector dirs = gammalib::split(pfiles, ":;"); + + // Search for first occurence of parameter file + for (int i = 0; i < dirs.size(); ++i) { + + // Build filename + std::string fname = dirs[i] + "/" + filename; + + // If file is accessible for reading then exit loop + if (access(fname.c_str(), R_OK) == 0) { + path = fname; + break; + } + + } // endfor: searched all directories given in PFILES + + } // endif: PFILES environment variable has been set + + // ... otherwise, if no PFILES environment variable has been set + // then search in users pfiles directory + else { + uid_t uid = geteuid(); + struct passwd* pw = getpwuid(uid); + if (pw != NULL) { + std::string fname = std::string(pw->pw_dir) + "/pfiles/" + filename; + if (access(fname.c_str(), R_OK) == 0) { + path = fname; + } + } + } // endif: searched in users pfiles directory + + // Return path + return path; +} + + +/***********************************************************************//** + * @brief Return path to parfile in m_syspfiles folder + * + * @param[in] filename Parameter filename to search for. + * @return Full parameter filename, including absolute access path. + * + * Locates parameter file in the directory that is specified by the + * m_syspfiles string. If the string is empty, the specified file is not + * found or read accessible, an empty string is returned. + ***************************************************************************/ +std::string GApplicationPars::syspfiles_path(const std::string& filename) const +{ + // Allocate result path + std::string path; + + // Continue only if m_syspfiles is not empty + if (!m_syspfiles.empty()) { + std::string fname = m_syspfiles + "/" + filename; + if (access(fname.c_str(), R_OK) == 0) { + path = fname; + } + } + + // Return path + return path; +} + + +/***********************************************************************//** * @brief Determine filepath for parameter file output * * @param[in] filename Parameter filename. @@ -1347,6 +1462,54 @@ void GApplicationPars::update(void) /***********************************************************************//** + * @brief Synchronise parameter file with the parameter values in a another + * parameter file + * + * @param[in] filename File name of other parameter file. + * + * Copies over all values from another parameter file that correspond to + * parameters with the same name and type, and that are signaled as "learn" + * in the other parameter file. + ***************************************************************************/ +void GApplicationPars::synchronise(const std::string& filename) +{ + // Allocate other application parameters + GApplicationPars pars; + + // Read other application parameters + pars.read(filename); + + // Parse other parameters + pars.parse(); + + // Loop over all parameters in parameter file + for (int i = 0; i < m_pars.size(); ++i) { + + // If a parameter with the same name and type exists in the other + // parameter file + int inx = pars.get_index(m_pars[i].name()); + if (inx != -1) { + + // If the parameter types match, the mode in the other parameter + // file is "learn", and the parameter values differ, then copy + // over the value and signal value updating + if ((m_pars[i].type() == pars[inx].type()) && + pars[inx].is_learn() && + m_pars[i].m_value != pars[inx].m_value) { + m_pars[i].m_value = pars[inx].m_value; + m_pars[i].m_update = true; + } + + } // endif: parameter with same name existed + + } // endfor: looped over all parameters + + // Return + return; +} + + +/***********************************************************************//** * @brief Return parameter index by name * * @param[in] name Parameter name. diff --git a/src/gammalib-setup.in b/src/gammalib-setup.in index 473c5ee..9f1c951 100644 --- a/src/gammalib-setup.in +++ b/src/gammalib-setup.in @@ -74,19 +74,6 @@ if [ -d $prefix/syspfiles/. ]; then syspfiles="$prefix/syspfiles$syspfiles" fi - # updates any existing PFILES if syspfiles are newer - for filename in $HOME/pfiles/*.par - do - parfile=`basename $filename` - sys_parfile=$prefix/syspfiles/$parfile - if [ -e "$sys_parfile" ]; then - if test "$sys_parfile" -nt "$filename" - then - cp -f "$sys_parfile" "$filename" - fi - fi - done - # Set PFILES and PFCLOBBER environment variable PFILES="$locpfiles;$syspfiles" PFCLOBBER=1