Improve ProgramInfo and use it everywhere.
authorTeemu Murtola <teemu.murtola@gmail.com>
Mon, 4 Jun 2012 09:03:50 +0000 (12:03 +0300)
committerGerrit Code Review <gerrit@gerrit.gromacs.org>
Tue, 3 Jul 2012 23:25:56 +0000 (01:25 +0200)
- The ProgramInfo class now stores the full command line.
  Moved functionality to add quotes to arguments with spaces from
  gmx::test::CommandLine to ProgramInfo.
- ProgramInfo initialization is now protected by a mutex for
  completeness.
- oenv.c and statutil.c now use ProgramInfo internally to store/return
  the binary name and command line.  Removes duplicate implementations
  of this functionality.  Required changing the return values of
  ProgramInfo methods from std::string values to const references.
- Removed many unnecessary #include directives from these two files.
- Fixed warnings that were produced from these files when switching them
  to C++ compilation (one cppcheck warning suppressed for now).
- Add a temporary hack for Windows to try to make get_libdir() work for
  tests that are run through CTest.

Helps with #950.

Change-Id: I1bfd4231b8b7055d0a014b41be67a7c1c99e36b0

cmake/legacy_and_external.supp
src/gromacs/gmxlib/oenv.cpp [moved from src/gromacs/gmxlib/oenv.c with 72% similarity]
src/gromacs/gmxlib/pargs.c
src/gromacs/gmxlib/statutil.cpp [moved from src/gromacs/gmxlib/statutil.c with 83% similarity]
src/gromacs/legacyheaders/oenv.h
src/gromacs/legacyheaders/statutil.h
src/gromacs/legacyheaders/types/oenv.h
src/gromacs/utility/CMakeLists.txt
src/gromacs/utility/programinfo.cpp
src/gromacs/utility/programinfo.h
src/testutils/cmdlinetest.cpp

index 0e18ccd0921c794c2b3644fa96b7020722fccfee..6788ba24b8a7e2cf5150d3e65729ef021be99b76 100644 (file)
    fun:inflateInit2_
 }
 
-{
-   set_program_name
-   Memcheck:Leak
-   ...
-   fun:set_program_name
-}
-
-{
-   set_command_line
-   Memcheck:Leak
-   ...
-   fun:set_command_line
-}
-
 {
    read_tps_conf
    Memcheck:Leak
similarity index 72%
rename from src/gromacs/gmxlib/oenv.c
rename to src/gromacs/gmxlib/oenv.cpp
index 015b25b94d6bbdc67f6f4dd4b3a3a0def4d372d7..9a88904c6499d2c2139d41c0ad0243fb2460f724 100644 (file)
  * And Hey:
  * GROningen Mixture of Alchemy and Childrens' Stories
  */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
+#include "oenv.h"
 
-#include <ctype.h>
-#include <assert.h>
-#include "sysstuff.h"
-#include "macros.h"
-#include "string2.h"
 #include "smalloc.h"
-#include "pbc.h"
-#include "statutil.h"
-#include "names.h"
-#include "vec.h"
-#include "futil.h"
-#include "wman.h"
-#include "tpxio.h"
-#include "gmx_fatal.h"
-#include "network.h"
-#include "vec.h"
-#include "mtop_util.h"
-#include "gmxfio.h"
-#include "oenv.h"
 
-#ifdef GMX_THREAD_MPI
-#include "thread_mpi.h"
-#endif
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/programinfo.h"
 
 struct output_env
 {
+    output_env()
+    {
+        setDefaults();
+    }
+    output_env(int argc, const char *const argv[])
+        : programInfo(argc, argv)
+    {
+        setDefaults();
+    }
+
+    void setDefaults()
+    {
+        time_unit   = time_ps;
+        view        = FALSE;
+        xvg_format  = exvgNONE;
+        verbosity   = 0;
+        debug_level = 0;
+    }
+
+    gmx::ProgramInfo programInfo;
+
     time_unit_t time_unit; /* the time unit, enum defined in oenv.h */
     gmx_bool view;  /* view of file requested */
     xvg_format_t xvg_format; /* xvg output format, enum defined in oenv.h */
     int  verbosity; /* The level of verbosity for this program */
     int debug_level; /* the debug level */
-
-    char *program_name; /* the program name */
-    char *cmd_line; /* the re-assembled command line */
 };
 
 /* The source code in this file should be thread-safe. 
@@ -93,78 +89,41 @@ static const char *time_units_xvgr[] = { NULL, "fs", "ps", "ns",
                                         "ms", "s", NULL };
 
 
-
 /***** OUTPUT_ENV MEMBER FUNCTIONS ******/
 
 void output_env_init(output_env_t *oenvp, int argc, char *argv[],
                      time_unit_t tmu, gmx_bool view, xvg_format_t xvg_format,
                      int verbosity, int debug_level)
 {
-    int i;
-    int cmdlength=0;
-    char *argvzero=NULL;
-    output_env_t oenv;
-
-    snew(oenv, 1);
-    *oenvp = oenv;
-    oenv->time_unit  = tmu;
-    oenv->view=view;
-    oenv->xvg_format = xvg_format;
-    oenv->verbosity=verbosity;
-    oenv->debug_level=debug_level;
-    oenv->program_name=NULL;
-
-    if (argv)
-    {
-        argvzero=argv[0];
-        assert(argvzero);
-    }
-    /* set program name */
-    if (argvzero)
-    {
-        oenv->program_name=strdup(argvzero);
-    }
-    if (oenv->program_name == NULL)
-        oenv->program_name = strdup("GROMACS");
-   
-    /* copy command line */ 
-    if (argv) 
+    try
     {
-        cmdlength = strlen(argvzero);
-        for (i=1; i<argc; i++) 
-        {
-            cmdlength += strlen(argv[i]);
-        }
-    }
-        
-    /* Fill the cmdline string */
-    snew(oenv->cmd_line,cmdlength+argc+1);
-    if (argv)
-    {
-        for (i=0; i<argc; i++)
-        {
-            strcat(oenv->cmd_line,argv[i]);
-            strcat(oenv->cmd_line," ");
-        }
+        output_env_t oenv = new output_env(argc, argv);
+        *oenvp = oenv;
+        oenv->time_unit   = tmu;
+        oenv->view        = view;
+        oenv->xvg_format  = xvg_format;
+        oenv->verbosity   = verbosity;
+        oenv->debug_level = debug_level;
     }
+    GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
 }
 
-
 void output_env_init_default(output_env_t *oenvp)
 {
-    output_env_init(oenvp, 0, NULL, time_ps, FALSE, exvgNONE, 0, 0);
+    try
+    {
+        output_env_t oenv = new output_env();
+        *oenvp = oenv;
+    }
+    GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
 }
 
-
 void output_env_done(output_env_t oenv)
 {
-    sfree(oenv->program_name);
-    sfree(oenv->cmd_line);
-    sfree(oenv);
+    delete oenv;
 }
 
 
-
 int output_env_get_verbosity(const output_env_t oenv)
 {
     return oenv->verbosity;
@@ -175,7 +134,6 @@ int output_env_get_debug_level(const output_env_t oenv)
     return oenv->debug_level;
 }
 
-
 const char *output_env_get_time_unit(const output_env_t oenv)
 {
     return time_units_str[oenv->time_unit];
@@ -203,7 +161,6 @@ const char *output_env_get_xvgr_tlabel(const output_env_t oenv)
     return label;
 }
 
-
 real output_env_get_time_factor(const output_env_t oenv)
 {
     return timefactors[oenv->time_unit];
@@ -219,7 +176,6 @@ real output_env_conv_time(const output_env_t oenv, real time)
     return time*timefactors[oenv->time_unit];
 }
 
-
 void output_env_conv_times(const output_env_t oenv, int n, real *time)
 {
     int i;
@@ -242,23 +198,27 @@ xvg_format_t output_env_get_xvg_format(const output_env_t oenv)
 
 const char *output_env_get_program_name(const output_env_t oenv)
 {
-    return oenv->program_name;
+    try
+    {
+        return oenv->programInfo.programNameWithPath().c_str();
+    }
+    GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
 }
 
 const char *output_env_get_short_program_name(const output_env_t oenv)
 {
-    const char *pr,*ret;
-    pr=ret=oenv->program_name; 
-    if ((pr=strrchr(ret,DIR_SEPARATOR)) != NULL)
-        ret=pr+1;
-    return ret;
+    try
+    {
+        return oenv->programInfo.programName().c_str();
+    }
+    GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
 }
 
-
-
 const char *output_env_get_cmd_line(const output_env_t oenv)
 {
-    return oenv->cmd_line;
+    try
+    {
+        return oenv->programInfo.commandLine().c_str();
+    }
+    GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
 }
-
-
index dd244cada4da37b3eee6e6b7685e901e5d5826c5..d8c0a95624446e9e6488cb559c086ee8032434f1 100644 (file)
@@ -49,6 +49,7 @@
 #include "string2.h"
 #include "vec.h"
 #include "macros.h"
+#include "wman.h"
 
 /* The source code in this file should be thread-safe. 
       Please keep it that way. */
similarity index 83%
rename from src/gromacs/gmxlib/statutil.c
rename to src/gromacs/gmxlib/statutil.cpp
index ae3691139bc39ad4cc123c34c9338b028dc008cc..efbba7284e8177268c955c8b6a6e3a6a3c7f1f59 100644 (file)
 #include <config.h>
 #endif
 
-#include <ctype.h>
-#include <assert.h>
+#include <cctype>
+#include <cmath>
+#include <cstdlib>
+
 #include "copyrite.h"
 #include "sysstuff.h"
 #include "macros.h"
 #include "string2.h"
 #include "smalloc.h"
-#include "pbc.h"
 #include "statutil.h"
-#include "names.h"
-#include "vec.h"
-#include "futil.h"
 #include "wman.h"
 #include "tpxio.h"
 #include "gmx_fatal.h"
 #include "network.h"
-#include "vec.h"
-#include "mtop_util.h"
 #include "gmxfio.h"
 
-#ifdef GMX_THREAD_MPI
-#include "thread_mpi.h"
-#endif
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/programinfo.h"
+
+#include "thread_mpi/threads.h"
 
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
  *
  ******************************************************************/
 
-/* inherently globally shared names: */
-static const char *program_name=NULL;
-static char *cmd_line=NULL;
-
-#ifdef GMX_THREAD_MPI
-/* For now, some things here are simply not re-entrant, so
-   we have to actively lock them. */
-static tMPI_Thread_mutex_t init_mutex=TMPI_THREAD_MUTEX_INITIALIZER;
-#endif
-
-
 /****************************************************************
  *
  *            E X P O R T E D   F U N C T I O N S
@@ -102,92 +89,36 @@ static tMPI_Thread_mutex_t init_mutex=TMPI_THREAD_MUTEX_INITIALIZER;
 
 const char *ShortProgram(void)
 {
-    const char *pr,*ret;
-#ifdef GMX_THREAD_MPI
-    tMPI_Thread_mutex_lock(&init_mutex);
-#endif
-    pr=ret=program_name; 
-#ifdef GMX_THREAD_MPI
-    tMPI_Thread_mutex_unlock(&init_mutex);
-#endif
-    if (ret == NULL)
-        return "GROMACS";
-    if ((pr=strrchr(ret,DIR_SEPARATOR)) != NULL)
-        ret=pr+1;
-    return ret;
+    try
+    {
+        return gmx::ProgramInfo::getInstance().programName().c_str();
+    }
+    GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
 }
 
 const char *Program(void)
 {
-    const char *ret;
-#ifdef GMX_THREAD_MPI
-    tMPI_Thread_mutex_lock(&init_mutex);
-#endif
-    ret=program_name; 
-#ifdef GMX_THREAD_MPI
-    tMPI_Thread_mutex_unlock(&init_mutex);
-#endif
-    return ret;
+    try
+    {
+        return gmx::ProgramInfo::getInstance().programNameWithPath().c_str();
+    }
+    GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
 }
 
 const char *command_line(void)
 {
-    const char *ret;
-#ifdef GMX_THREAD_MPI
-    tMPI_Thread_mutex_lock(&init_mutex);
-#endif
-    ret=cmd_line; 
-#ifdef GMX_THREAD_MPI
-    tMPI_Thread_mutex_unlock(&init_mutex);
-#endif
-    return ret;
-}
-
-void set_program_name(const char *argvzero)
-{
-#ifdef GMX_THREAD_MPI
-    tMPI_Thread_mutex_lock(&init_mutex);
-#endif
-    if (program_name == NULL)
+    try
     {
-        program_name = strdup(argvzero);
+        return gmx::ProgramInfo::getInstance().commandLine().c_str();
     }
-    if (program_name == NULL)
-        program_name="GROMACS";
-#ifdef GMX_THREAD_MPI
-    tMPI_Thread_mutex_unlock(&init_mutex);
-#endif
+    GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
 }
 
-
-void set_command_line(int argc, char *argv[])
+void set_program_name(const char *argvzero)
 {
-    int i;
-    size_t cmdlength;
-
-#ifdef GMX_THREAD_MPI
-    tMPI_Thread_mutex_lock(&init_mutex);
-#endif
-    if (cmd_line==NULL)
-    {
-        cmdlength = strlen(argv[0]);
-        for (i=1; i<argc; i++) 
-        {
-            cmdlength += strlen(argv[i]);
-        }
-        
-        /* Fill the cmdline string */
-        snew(cmd_line,cmdlength+argc+1);
-        for (i=0; i<argc; i++) 
-        {
-            strcat(cmd_line,argv[i]);
-            strcat(cmd_line," ");
-        }
-    }
-#ifdef GMX_THREAD_MPI
-    tMPI_Thread_mutex_unlock(&init_mutex);
-#endif
-
+    // The negative argc is a hack to make the ProgramInfo overridable in
+    // parse_common_args(), where the full command-line is known.
+    gmx::ProgramInfo::init(-1, &argvzero);
 }
 
 /* utility functions */
@@ -199,9 +130,9 @@ gmx_bool bRmod_fd(double a, double b, double c, gmx_bool bDouble)
     
     tol = 2*(bDouble ? GMX_DOUBLE_EPS : GMX_FLOAT_EPS);
     
-    iq = (a - b + tol*a)/c;
+    iq = static_cast<int>((a - b + tol*a)/c);
     
-    if (fabs(a - b - c*iq) <= tol*fabs(a))
+    if (std::fabs(a - b - c*iq) <= tol*std::fabs(a))
         return TRUE;
     else
         return FALSE;
@@ -210,18 +141,12 @@ gmx_bool bRmod_fd(double a, double b, double c, gmx_bool bDouble)
 int check_times2(real t,real t0,real tp, real tpp, gmx_bool bDouble)
 {
     int  r;
-    real margin;
     
 #ifndef GMX_DOUBLE
     /* since t is float, we can not use double precision for bRmod */
     bDouble = FALSE;
 #endif
     
-    if (t-tp>0 && tp-tpp>0)
-        margin = 0.1*min(t-tp,tp-tpp);
-    else
-        margin = 0;
-    
     r=-1;
     if ((!bTimeSet(TBEGIN) || (t >= rTimeValue(TBEGIN)))  &&
         (!bTimeSet(TEND)   || (t <= rTimeValue(TEND)))) {
@@ -248,7 +173,7 @@ int check_times(real t)
 
 static void set_default_time_unit(const char *time_list[], gmx_bool bCanTime)
 {
-    int i=0,j;
+    int i=0;
     const char *select;
 
     if (bCanTime)
@@ -280,8 +205,8 @@ static void set_default_time_unit(const char *time_list[], gmx_bool bCanTime)
 
 static void set_default_xvg_format(const char *xvg_list[])
 {
-    int i,j;
-    const char *select,*tmp;
+    int i;
+    const char *select;
 
     select = getenv("GMX_VIEW_XVG");
     if (select == NULL)
@@ -331,46 +256,58 @@ t_topology *read_top(const char *fn,int *ePBC)
 
 static void usage(const char *type,const char *arg)
 {
-    assert(arg);
+    GMX_ASSERT(arg != NULL, "NULL command-line argument should not occur");
     gmx_fatal(FARGS,"Expected %s argument for option %s\n",type,arg);
 }
 
 int iscan(int argc,char *argv[],int *i)
 {
-    int var;
-    
-    if (argc > (*i)+1) {
-        if (!sscanf(argv[++(*i)],"%d",&var))
-            usage("an integer",argv[(*i)-1]);
-    } else
-        usage("an integer",argv[*i]);
-    
+    const char *const arg = argv[*i];
+    if (argc <= (*i)+1)
+    {
+        usage("an integer", arg);
+    }
+    const char *const value = argv[++(*i)];
+    char *endptr;
+    int var = std::strtol(value, &endptr, 10);
+    if (*value == '\0' || *endptr != '\0')
+    {
+        usage("an integer", arg);
+    }
     return var;
 }
 
 gmx_large_int_t istepscan(int argc,char *argv[],int *i)
 {
-    gmx_large_int_t var;
-    
-    if (argc > (*i)+1) {
-        if (!sscanf(argv[++(*i)],gmx_large_int_pfmt,&var))
-            usage("an integer",argv[(*i)-1]);
-    } else
-        usage("an integer",argv[*i]);
-    
+    const char *const arg = argv[*i];
+    if (argc <= (*i)+1)
+    {
+        usage("an integer", arg);
+    }
+    const char *const value = argv[++(*i)];
+    char *endptr;
+    gmx_large_int_t var = str_to_large_int_t(value, &endptr);
+    if (*value == '\0' || *endptr != '\0')
+    {
+        usage("an integer", arg);
+    }
     return var;
 }
 
 double dscan(int argc,char *argv[],int *i)
 {
-    double var;
-    
-    if (argc > (*i)+1) {
-        if (!sscanf(argv[++(*i)],"%lf",&var))
-            usage("a real",argv[(*i)-1]);
-    } else
-        usage("a real",argv[*i]);
-    
+    const char *const arg = argv[*i];
+    if (argc <= (*i)+1)
+    {
+        usage("a real", arg);
+    }
+    const char *const value = argv[++(*i)];
+    char *endptr;
+    double var = std::strtod(value, &endptr);
+    if (*value == '\0' || *endptr != '\0')
+    {
+        usage("a real", arg);
+    }
     return var;
 }
 
@@ -411,7 +348,7 @@ static void pdesc(char *desc)
     if ((int)strlen(ptr) < 70)
         fprintf(stderr,"\t%s\n",ptr);
     else {
-        for(nptr=ptr+70; (nptr != ptr) && (!isspace(*nptr)); nptr--)
+        for(nptr=ptr+70; (nptr != ptr) && (!std::isspace(*nptr)); nptr--)
             ;
         if (nptr == ptr)
             fprintf(stderr,"\t%s\n",ptr);
@@ -523,15 +460,18 @@ void parse_common_args(int *argc,char *argv[],unsigned long Flags,
     /* This array should match the order of the enum in oenv.h */
     const char *time_units[] = { NULL, "fs", "ps", "ns", "us", "ms", "s", 
                                 NULL };
-    int  nicelevel=0,mantp=0,npri=0,debug_level=0,verbose_level=0;
+    int  nicelevel=0,debug_level=0,verbose_level=0;
     char *deffnm=NULL;
     real tbegin=0,tend=0,tdelta=0;
     gmx_bool bView=FALSE;
     
     t_pargs *all_pa=NULL;
     
+#ifdef __sgi
+    int npri=0;
     t_pargs npri_pa   = { "-npri", FALSE, etINT,   {&npri},
     "HIDDEN Set non blocking priority (try 128)" };
+#endif
     t_pargs nice_pa   = { "-nice", FALSE, etINT,   {&nicelevel}, 
     "Set the nicelevel" };
     t_pargs deffnm_pa = { "-deffnm", FALSE, etSTR, {&deffnm}, 
@@ -570,18 +510,14 @@ void parse_common_args(int *argc,char *argv[],unsigned long Flags,
 #define NPCA_PA asize(pca_pa)
     FILE *fp;  
     gmx_bool bPrint,bExit,bXvgr;
-    int  i,j,k,npall,max_pa,cmdlength;
-    char *ptr,*newdesc;
-    const char *envstr;
+    int  i,j,k,npall,max_pa;
     
 #define FF(arg) ((Flags & arg)==arg)
 
-    cmdlength = strlen(argv[0]);
     /* Check for double arguments */
     for (i=1; (i<*argc); i++) 
     {
-        cmdlength += strlen(argv[i]);
-        if (argv[i] && (strlen(argv[i]) > 1) && (!isdigit(argv[i][1]))) 
+        if (argv[i] && (strlen(argv[i]) > 1) && (!std::isdigit(argv[i][1])))
         {
             for (j=i+1; (j<*argc); j++) 
             {
@@ -599,8 +535,7 @@ void parse_common_args(int *argc,char *argv[],unsigned long Flags,
         }
     }
     debug_gmx();
-    set_program_name(argv[0]);
-    set_command_line(*argc, argv);
+    gmx::ProgramInfo::init(*argc, argv);
       
     /* Handle the flags argument, which is a bit field 
      * The FF macro returns whether or not the bit is set
@@ -611,11 +546,11 @@ void parse_common_args(int *argc,char *argv[],unsigned long Flags,
     max_pa = NPCA_PA + EXTRA_PA + npargs+1;
     snew(all_pa,max_pa);
     
-    for(i=npall=0; (i<NPCA_PA); i++)
+    for(i=npall=0; (i<static_cast<int>(NPCA_PA)); i++)
         npall = add_parg(npall,all_pa,&(pca_pa[i]));
     
 #ifdef __sgi
-    envstr = getenv("GMXNPRIALL");
+    const char *envstr = getenv("GMXNPRIALL");
     if (envstr)
         npri=strtol(envstr,NULL,10);
     if (FF(PCA_BE_NICE)) {
@@ -727,23 +662,22 @@ void parse_common_args(int *argc,char *argv[],unsigned long Flags,
 #endif 
     
 #ifdef HAVE_UNISTD_H
-    
 #ifndef GMX_NO_NICE
     /* The some system, e.g. the catamount kernel on cray xt3 do not have nice(2). */
     if (nicelevel != 0 && !bExit)
     {
-#ifdef GMX_THREAD_MPI
         static gmx_bool nice_set=FALSE; /* only set it once */
+        static tMPI_Thread_mutex_t init_mutex = TMPI_THREAD_MUTEX_INITIALIZER;
         tMPI_Thread_mutex_lock(&init_mutex);
         if (!nice_set)
         {
-#endif
-            i=nice(nicelevel); /* assign ret value to avoid warnings */
-#ifdef GMX_THREAD_MPI
+            if (nice(nicelevel) == -1)
+            {
+                /* Do nothing, but use the return value to avoid warnings. */
+            }
             nice_set=TRUE;
         }
         tMPI_Thread_mutex_unlock(&init_mutex);
-#endif
     }
 #endif
 #endif
index 9559080f1abf4973b6475c3f74acaa430d060263..42107fbfc85af8d5d373b1a4200cc516dc8d7607 100644 (file)
@@ -36,7 +36,8 @@
 #ifndef _oenv_h
 #define _oenv_h
 
-#include "typedefs.h"
+#include "types/simple.h"
+#include "types/oenv.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -110,7 +111,6 @@ void output_env_conv_times(const output_env_t oenv, int n, real *time);
 gmx_bool output_env_get_view(const output_env_t oenv);
 /* Return TRUE when user requested viewing of the file */
 
-
 xvg_format_t output_env_get_xvg_format(const output_env_t oenv);
 /* Returns enum (see above) for xvg output formatting */
 
@@ -123,8 +123,6 @@ const char *output_env_get_cmd_line(const output_env_t oenv);
 const char *output_env_get_short_program_name(const output_env_t oenv);
 /* get the short version (without path component) of the program name */
 
-
-
 #ifdef __cplusplus
 }
 #endif
index d66f4fcc85fba7fc8d6c0622669dc5c9113eab22..7ff7dfc5e417f8529528bfbd814ae5c0a5890485 100644 (file)
 #ifndef _statutil_h
 #define _statutil_h
 
-#include <stdio.h>
 #include "typedefs.h"
 #include "filenm.h"
 #include "readinp.h"
-#include "wman.h"
 #include "pdbio.h"
 #include "oenv.h"
 #include "gmxfio.h"
 
-
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -90,7 +87,6 @@ typedef gmx_bool t_next_x(t_trxstatus *status,real *t,int natoms,rvec x[],
     
 /* Return the name of the program */
 const char *command_line(void);
-void set_command_line(int argc, char *argv[]);
 
 /* set the program name to the provided string, but note
  * that it must be a real file - we determine the library
index 8ce72f5047126a75023651b9a16a165bd9b91023..4887b4eab2dd8d71cb52be6656df4a7e1d89bf43 100644 (file)
  * And Hey:
  * GRoups of Organic Molecules in ACtion for Science
  */
+#ifndef GMX_TYPES_OENV_H
+#define GMX_TYPES_OENV_H
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-/* output options opaque type, for functions in statutil.c */
+/* output options opaque type, for functions in statutil.h and oenv.h */
 typedef struct output_env *output_env_t;
 
 #ifdef __cplusplus
 }
 #endif
 
+#endif
index b770d13d0764c199f6f177c3274a2b4f25b768f3..737ab4e91f27508f9de8a65058df82b7c23d4a52 100644 (file)
@@ -15,6 +15,7 @@ set(UTILITY_PUBLIC_HEADERS
     flags.h
     gmx_header_config.h
     gmxassert.h
+    programinfo.h
     stringutil.h
     uniqueptr.h)
 install(FILES ${UTILITY_PUBLIC_HEADERS}
index b1ef191d5a435553e78af7f1ee583d08a3caab36..d850cf939b8fad91e8178dc9d4bb50942f7fe05d 100644 (file)
 #include <cstdlib>
 #include <cstring>
 
+#include <algorithm>
 #include <string>
 
 #include <boost/scoped_ptr.hpp>
 
-#include "gromacs/legacyheaders/statutil.h"
+#include "gromacs/legacyheaders/futil.h"
+#include "gromacs/legacyheaders/thread_mpi/mutex.h"
 
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/path.h"
@@ -60,6 +62,9 @@ namespace gmx
 
 namespace
 {
+tMPI::mutex g_programInfoMutex;
+//! Partially filled program info, needed to support set_program_name().
+boost::scoped_ptr<ProgramInfo> g_partialProgramInfo;
 boost::scoped_ptr<ProgramInfo> g_programInfo;
 } // namespace
 
@@ -77,20 +82,32 @@ class ProgramInfo::Impl
         std::string fullInvokedProgram_;
         std::string programName_;
         std::string invariantProgramName_;
+        std::string commandLine_;
 };
 
 ProgramInfo::Impl::Impl()
-    : realBinaryName_("GROMACS"), fullInvokedProgram_("GROMACS")
+    : realBinaryName_("GROMACS"), fullInvokedProgram_("GROMACS"),
+      programName_("GROMACS"), invariantProgramName_("GROMACS")
 {
 }
 
 ProgramInfo::Impl::Impl(const char *realBinaryName,
                         int argc, const char *const argv[])
     : realBinaryName_(realBinaryName != NULL ? realBinaryName : ""),
-      fullInvokedProgram_(argv[0]),
+      fullInvokedProgram_(argc != 0 ? argv[0] : ""),
       programName_(Path::splitToPathAndFilename(fullInvokedProgram_).second),
       invariantProgramName_(programName_)
 {
+    // Temporary hack to make things work on Windows while waiting for #950.
+    // Some places in the existing code expect to have DIR_SEPARATOR in all
+    // input paths, but Windows may also give '/' (and does that, e.g., for
+    // tests invoked through CTest).
+    // When removing this, remove also the #include "futil.h".
+    if (DIR_SEPARATOR == '\\')
+    {
+        std::replace(fullInvokedProgram_.begin(), fullInvokedProgram_.end(),
+                     '/', '\\');
+    }
     invariantProgramName_ = stripSuffixIfPresent(invariantProgramName_, ".exe");
 #ifdef GMX_BINARY_SUFFIX
     invariantProgramName_ =
@@ -100,6 +117,25 @@ ProgramInfo::Impl::Impl(const char *realBinaryName,
     {
         realBinaryName_ = invariantProgramName_;
     }
+
+    for (int i = 0; i < argc; ++i)
+    {
+        if (i > 0)
+        {
+            commandLine_.append(" ");
+        }
+        const char *arg = argv[i];
+        bool bSpaces = (std::strchr(arg, ' ') != NULL);
+        if (bSpaces)
+        {
+            commandLine_.append("'");
+        }
+        commandLine_.append(arg);
+        if (bSpaces)
+        {
+            commandLine_.append("'");
+        }
+    }
 }
 
 /********************************************************************
@@ -109,10 +145,15 @@ ProgramInfo::Impl::Impl(const char *realBinaryName,
 // static
 const ProgramInfo &ProgramInfo::getInstance()
 {
-    // TODO: For completeness, this should be thread-safe.
+    tMPI::lock_guard<tMPI::mutex> lock(g_programInfoMutex);
     if (g_programInfo.get() == NULL)
     {
-        g_programInfo.reset(new ProgramInfo());
+        if (g_partialProgramInfo.get() != NULL)
+        {
+            return *g_partialProgramInfo;
+        }
+        static ProgramInfo fallbackInfo;
+        return fallbackInfo;
     }
     return *g_programInfo;
 }
@@ -129,11 +170,22 @@ const ProgramInfo &ProgramInfo::init(const char *realBinaryName,
 {
     try
     {
-        set_program_name(argv[0]);
-        // TODO: Constness should not be cast away.
-        set_command_line(argc, const_cast<char **>(argv));
-        // TODO: For completeness, this should be thread-safe.
-        g_programInfo.reset(new ProgramInfo(realBinaryName, argc, argv));
+        tMPI::lock_guard<tMPI::mutex> lock(g_programInfoMutex);
+        if (g_programInfo.get() == NULL)
+        {
+            // TODO: Remove this hack with negative argc once there is no need for
+            // set_program_name().
+            if (argc < 0)
+            {
+                if (g_partialProgramInfo.get() == NULL)
+                {
+                    g_partialProgramInfo.reset(
+                            new ProgramInfo(realBinaryName, -argc, argv));
+                }
+                return *g_partialProgramInfo;
+            }
+            g_programInfo.reset(new ProgramInfo(realBinaryName, argc, argv));
+        }
         return *g_programInfo;
     }
     catch (const std::exception &ex)
@@ -168,24 +220,29 @@ ProgramInfo::~ProgramInfo()
 {
 }
 
-std::string ProgramInfo::realBinaryName() const
+const std::string &ProgramInfo::realBinaryName() const
 {
     return impl_->realBinaryName_;
 }
 
-std::string ProgramInfo::programNameWithPath() const
+const std::string &ProgramInfo::programNameWithPath() const
 {
     return impl_->fullInvokedProgram_;
 }
 
-std::string ProgramInfo::programName() const
+const std::string &ProgramInfo::programName() const
 {
     return impl_->programName_;
 }
 
-std::string ProgramInfo::invariantProgramName() const
+const std::string &ProgramInfo::invariantProgramName() const
 {
     return impl_->invariantProgramName_;
 }
 
+const std::string &ProgramInfo::commandLine() const
+{
+    return impl_->commandLine_;
+}
+
 } // namespace gmx
index 001ec731679bdd608c192c59e1cdbb7096ab78f3..8b85b1ce0b9b0541db192df0104b989a74bd951b 100644 (file)
@@ -77,6 +77,7 @@ class ProgramInfo
          * \returns The same object as initialized with the last call to init().
          * \throws  std::bad_alloc if out of memory (only if this is the first
          *      call and init() has not been called either).
+         * \throws  tMPI::system_error on thread synchronization errors.
          */
         static const ProgramInfo &getInstance();
         /*! \brief
@@ -152,23 +153,37 @@ class ProgramInfo
          *
          * The returned value is comparable to invariantProgramName(), i.e., it
          * has suffixes and OS-specific extensions removed.
+         *
+         * Does not throw.
          */
-        std::string realBinaryName() const;
+        const std::string &realBinaryName() const;
         /*! \brief
          * Returns the path and name of the binary as it was invoked.
+         *
+         * Does not throw.
          */
-        std::string programNameWithPath() const;
+        const std::string &programNameWithPath() const;
         /*! \brief
          * Returns the name of the binary as it was invoked without any path.
+         *
+         * Does not throw.
          */
-        std::string programName() const;
+        const std::string &programName() const;
         /*! \brief
          * Returns an invariant name of the binary.
          *
          * The returned value has OS-specific extensions (.exe on Windows)
          * removed, as well as any binary suffix that was configured.
+         *
+         * Does not throw.
+         */
+        const std::string &invariantProgramName() const;
+        /*! \brief
+         * Returns the full command line used to invoke the binary.
+         *
+         * Does not throw.
          */
-        std::string invariantProgramName() const;
+        const std::string &commandLine() const;
 
     private:
         class Impl;
index e75064987d1fcfcb4a9942bc238a0e447044ebc5..a4604e11e99dbc763037b47a70140525470fa5ab 100644 (file)
@@ -43,6 +43,8 @@
 #include <new>
 #include <vector>
 
+#include "gromacs/utility/programinfo.h"
+
 namespace gmx
 {
 namespace test
@@ -134,26 +136,7 @@ const char *CommandLine::arg(int i) const { return impl_->argv_[i]; }
 
 std::string CommandLine::toString() const
 {
-    std::string result;
-    for (size_t i = 0; i < impl_->args_.size(); ++i)
-    {
-        if (i > 0)
-        {
-            result.append(" ");
-        }
-        const char *arg = impl_->args_[i];
-        bool bSpaces = (std::strchr(arg, ' ') != NULL);
-        if (bSpaces)
-        {
-            result.append("'");
-        }
-        result.append(arg);
-        if (bSpaces)
-        {
-            result.append("'");
-        }
-    }
-    return result;
+    return ProgramInfo(argc(), argv()).commandLine();
 }
 
 } // namespace test