convert plugin functionality into a package

This commit is contained in:
Axel Kohlmeyer 2021-03-16 22:43:13 -04:00
parent 11894f83b9
commit 125ae33ccf
No known key found for this signature in database
GPG Key ID: D9B44E93BF0C375A
16 changed files with 198 additions and 51 deletions

View File

@ -113,7 +113,7 @@ option(CMAKE_VERBOSE_MAKEFILE "Generate verbose Makefiles" OFF)
set(STANDARD_PACKAGES ASPHERE BODY CLASS2 COLLOID COMPRESS DIPOLE set(STANDARD_PACKAGES ASPHERE BODY CLASS2 COLLOID COMPRESS DIPOLE
GRANULAR KSPACE LATTE MANYBODY MC MESSAGE MISC MLIAP MOLECULE PERI POEMS GRANULAR KSPACE LATTE MANYBODY MC MESSAGE MISC MLIAP MOLECULE PERI POEMS
QEQ REPLICA RIGID SHOCK SPIN SNAP SRD KIM PYTHON MSCG MPIIO VORONOI PLUGIN QEQ REPLICA RIGID SHOCK SPIN SNAP SRD KIM PYTHON MSCG MPIIO VORONOI
USER-ADIOS USER-ATC USER-AWPMD USER-BOCS USER-CGDNA USER-MESODPD USER-CGSDK USER-ADIOS USER-ATC USER-AWPMD USER-BOCS USER-CGDNA USER-MESODPD USER-CGSDK
USER-COLVARS USER-DIFFRACTION USER-DPD USER-DRUDE USER-EFF USER-FEP USER-H5MD USER-COLVARS USER-DIFFRACTION USER-DPD USER-DRUDE USER-EFF USER-FEP USER-H5MD
USER-LB USER-MANIFOLD USER-MEAMC USER-MESONT USER-MGPT USER-MISC USER-MOFFF USER-LB USER-MANIFOLD USER-MEAMC USER-MESONT USER-MGPT USER-MISC USER-MOFFF
@ -240,11 +240,6 @@ if(BUILD_OMP)
target_link_libraries(lammps PRIVATE OpenMP::OpenMP_CXX) target_link_libraries(lammps PRIVATE OpenMP::OpenMP_CXX)
endif() endif()
# link with -ldl or equivalent for plugin loading; except on Windows
if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
target_link_libraries(lammps PRIVATE ${CMAKE_DL_LIBS})
endif()
# Compiler specific features for testing # Compiler specific features for testing
if(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") if(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")
option(ENABLE_COVERAGE "Enable collecting code coverage data" OFF) option(ENABLE_COVERAGE "Enable collecting code coverage data" OFF)
@ -538,6 +533,18 @@ foreach(PKG_WITH_INCL CORESHELL QEQ USER-OMP USER-SDPD KOKKOS OPT USER-INTEL GPU
endif() endif()
endforeach() endforeach()
if(PKG_PLUGIN)
if(BUILD_SHARED_LIBS)
target_compile_definitions(lammps PRIVATE -DLMP_PLUGIN)
else()
message(WARNING "Plugin loading will not work unless BUILD_SHARED_LIBS is enabled")
endif()
# link with -ldl or equivalent for plugin loading; except on Windows
if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
target_link_libraries(lammps PRIVATE ${CMAKE_DL_LIBS})
endif()
endif()
###################################################################### ######################################################################
# the windows version of LAMMPS requires a couple extra libraries # the windows version of LAMMPS requires a couple extra libraries
# and the MPI library - if use - has to be linked right before those # and the MPI library - if use - has to be linked right before those

View File

@ -4,7 +4,7 @@
set(ALL_PACKAGES ASPHERE BODY CLASS2 COLLOID COMPRESS CORESHELL DIPOLE set(ALL_PACKAGES ASPHERE BODY CLASS2 COLLOID COMPRESS CORESHELL DIPOLE
GRANULAR KSPACE MANYBODY MC MISC MLIAP MOLECULE OPT PERI GRANULAR KSPACE MANYBODY MC MISC MLIAP MOLECULE OPT PERI
POEMS PYTHON QEQ REPLICA RIGID SHOCK SNAP SPIN SRD VORONOI PLUGIN POEMS PYTHON QEQ REPLICA RIGID SHOCK SNAP SPIN SRD VORONOI
USER-BOCS USER-CGDNA USER-CGSDK USER-COLVARS USER-DIFFRACTION USER-BOCS USER-CGDNA USER-CGSDK USER-COLVARS USER-DIFFRACTION
USER-DPD USER-DRUDE USER-EFF USER-FEP USER-MEAMC USER-MESODPD USER-DPD USER-DRUDE USER-EFF USER-FEP USER-MEAMC USER-MESODPD
USER-MISC USER-MOFFF USER-OMP USER-PHONON USER-REACTION USER-MISC USER-MOFFF USER-OMP USER-PHONON USER-REACTION

View File

@ -19,12 +19,12 @@ atc atomistic-to-continuum methods, USER-ATC package
from Reese Jones, Jeremy Templeton, Jon Zimmerman (Sandia) from Reese Jones, Jeremy Templeton, Jon Zimmerman (Sandia)
awpmd antisymmetrized wave packet molecular dynamics, AWPMD package awpmd antisymmetrized wave packet molecular dynamics, AWPMD package
from Ilya Valuev (JIHT RAS) from Ilya Valuev (JIHT RAS)
colvars collective variable module (Metadynamics, ABF and more) colvars collective variable module (Metadynamics, ABF and more)
from Giacomo Fiorin and Jerome Henin (ICMS, Temple U) from Giacomo Fiorin and Jerome Henin (ICMS, Temple U)
compress hook to system lib for performing I/O compression, COMPRESS pkg compress hook to system lib for performing I/O compression, COMPRESS pkg
from Axel Kohlmeyer (Temple U) from Axel Kohlmeyer (Temple U)
gpu general GPU routines, GPU package gpu general GPU routines, GPU package
from Mike Brown (ORNL) from Mike Brown (ORNL)
h5md ch5md library for output of MD data in HDF5 format h5md ch5md library for output of MD data in HDF5 format
from Pierre de Buyl (KU Leuven) from Pierre de Buyl (KU Leuven)
kim hooks to the KIM library, used by KIM package kim hooks to the KIM library, used by KIM package
@ -32,26 +32,28 @@ kim hooks to the KIM library, used by KIM package
kokkos Kokkos package for GPU and many-core acceleration kokkos Kokkos package for GPU and many-core acceleration
from Kokkos development team (Sandia) from Kokkos development team (Sandia)
linalg set of BLAS and LAPACK routines needed by USER-ATC package linalg set of BLAS and LAPACK routines needed by USER-ATC package
from Axel Kohlmeyer (Temple U) from Axel Kohlmeyer (Temple U)
message client/server communication library via MPI, sockets, files message client/server communication library via MPI, sockets, files
from Steve Plimpton (Sandia) from Steve Plimpton (Sandia)
molfile hooks to VMD molfile plugins, used by the USER-MOLFILE package molfile hooks to VMD molfile plugins, used by the USER-MOLFILE package
from Axel Kohlmeyer (Temple U) and the VMD development team from Axel Kohlmeyer (Temple U) and the VMD development team
mscg hooks to the MSCG library, used by fix_mscg command mscg hooks to the MSCG library, used by fix_mscg command
from Jacob Wagner and Greg Voth group (U Chicago) from Jacob Wagner and Greg Voth group (U Chicago)
netcdf hooks to a NetCDF library installed on your system netcdf hooks to a NetCDF library installed on your system
from Lars Pastewka (Karlsruhe Institute of Technology) from Lars Pastewka (Karlsruhe Institute of Technology)
poems POEMS rigid-body integration package, POEMS package plugin settings to load styles into LAMMPS from plugins
from Axel Kohlmeyer (Temple U)
poems POEMS rigid-body integration package, POEMS package
from Rudranarayan Mukherjee (RPI) from Rudranarayan Mukherjee (RPI)
python hooks to the system Python library, used by the PYTHON package python hooks to the system Python library, used by the PYTHON package
from the LAMMPS development team from the LAMMPS development team
qmmm quantum mechanics/molecular mechanics coupling interface qmmm quantum mechanics/molecular mechanics coupling interface
from Axel Kohlmeyer (Temple U) from Axel Kohlmeyer (Temple U)
quip interface to QUIP/libAtoms framework, USER-QUIP package quip interface to QUIP/libAtoms framework, USER-QUIP package
from Albert Bartok-Partay and Gabor Csanyi (U Cambridge) from Albert Bartok-Partay and Gabor Csanyi (U Cambridge)
smd hooks to Eigen library, used by USER-SMD package smd hooks to Eigen library, used by USER-SMD package
from Georg Ganzenmueller (Ernst Mach Institute, Germany) from Georg Ganzenmueller (Ernst Mach Institute, Germany)
voronoi hooks to the Voro++ library, used by compute voronoi/atom command voronoi hooks to the Voro++ library, used by compute voronoi/atom command
from Daniel Schwen (LANL) from Daniel Schwen (LANL)
vtk hooks to the VTK library, used by dump custom/vtk command vtk hooks to the VTK library, used by dump custom/vtk command
from Richard Berger (JKU) from Richard Berger (JKU)

16
lib/plugin/README Normal file
View File

@ -0,0 +1,16 @@
This directory has a Makefile.lammps file with settings that allows
LAMMPS to dynamically link LAMMPS plugins. More details about this
are in the manual.
In order to be able to dynamically load and execute the plugins from
inside LAMMPS, you need to link with a system library containing functions
like dlopen(), dlsym() and so on for dynamic linking of executable code
into an executable. This library is defined by setting the plugin_SYSLIB
variable in the Makefile.lammps file in this dir. For this mechanism
to work, LAMMPS must be built as a shared library (i.e. with mode=shared).
For Linux and most current unix-like operating systems, this can be
kept at the default setting of "-ldl" (on some platforms this library
is called "-ldld"). The Windows platform is currently not supported.
See the header of Makefile.lammps for more info.

4
src/.gitignore vendored
View File

@ -172,6 +172,10 @@
/pair_lj_charmmfsw_coul_long.cpp /pair_lj_charmmfsw_coul_long.cpp
/pair_lj_charmmfsw_coul_long.h /pair_lj_charmmfsw_coul_long.h
/plugin.cpp
/plugin.h
/lammpsplugin.h
/atom_vec_spin.cpp /atom_vec_spin.cpp
/atom_vec_spin.h /atom_vec_spin.h
/compute_spin.cpp /compute_spin.cpp

View File

@ -13,7 +13,7 @@ DEPFLAGS = -M
LINK = mpicxx LINK = mpicxx
LINKFLAGS = -g -O3 LINKFLAGS = -g -O3
LIB = -ldl LIB =
SIZE = size SIZE = size
ARCHIVE = ar ARCHIVE = ar

View File

@ -13,7 +13,7 @@ DEPFLAGS = -M
LINK = g++ LINK = g++
LINKFLAGS = -g -O LINKFLAGS = -g -O
LIB = -ldl LIB =
SIZE = size SIZE = size
ARCHIVE = ar ARCHIVE = ar

View File

@ -48,7 +48,7 @@ endif
PACKAGE = asphere body class2 colloid compress coreshell dipole gpu \ PACKAGE = asphere body class2 colloid compress coreshell dipole gpu \
granular kim kokkos kspace latte manybody mc message misc \ granular kim kokkos kspace latte manybody mc message misc \
mliap molecule mpiio mscg opt peri poems \ mliap molecule mpiio mscg opt peri plugin poems \
python qeq replica rigid shock snap spin srd voronoi python qeq replica rigid shock snap spin srd voronoi
PACKUSER = user-adios user-atc user-awpmd user-bocs user-cgdna user-cgsdk user-colvars \ PACKUSER = user-adios user-atc user-awpmd user-bocs user-cgdna user-cgsdk user-colvars \

64
src/PLUGIN/Install.sh Executable file
View File

@ -0,0 +1,64 @@
# Install/unInstall package files in LAMMPS
# mode = 0/1/2 for uninstall/install/update
mode=$1
# enforce using portable C locale
LC_ALL=C
export LC_ALL
# arg1 = file, arg2 = file it depends on
action () {
if (test $mode = 0) then
rm -f ../$1
elif (! cmp -s $1 ../$1) then
if (test -z "$2" || test -e ../$2) then
cp $1 ..
if (test $mode = 2) then
echo " updating src/$1"
fi
fi
elif (test -n "$2") then
if (test ! -e ../$2) then
rm -f ../$1
fi
fi
}
# all package files with no dependencies
for file in *.cpp *.h; do
test -f ${file} && action $file
done
# edit 2 Makefile.package files to include/exclude package info
if (test $1 = 1) then
if (test -e ../Makefile.package) then
sed -i -e 's/[^ \t]*plugin[^ \t]* //' ../Makefile.package
sed -i -e 's|^PKG_SYSINC =[ \t]*|&$(plugin_SYSINC) |' ../Makefile.package
sed -i -e 's|^PKG_SYSLIB =[ \t]*|&$(plugin_SYSLIB) |' ../Makefile.package
sed -i -e 's|^PKG_SYSPATH =[ \t]*|&$(plugin_SYSPATH) |' ../Makefile.package
fi
if (test -e ../Makefile.package.settings) then
sed -i -e '/^include.*plugin.*$/d' ../Makefile.package.settings
# multiline form needed for BSD sed on Macs
sed -i -e '4 i \
include ..\/..\/lib\/plugin\/Makefile.lammps
' ../Makefile.package.settings
fi
elif (test $1 = 0) then
if (test -e ../Makefile.package) then
sed -i -e 's/[^ \t]*plugin[^ \t]* //' ../Makefile.package
fi
if (test -e ../Makefile.package.settings) then
sed -i -e '/^include.*plugin.*$/d' ../Makefile.package.settings
fi
fi

12
src/PLUGIN/README Normal file
View File

@ -0,0 +1,12 @@
This package provides a plugin command that can load LAMMPS
styles linked into shared object files into a LAMMPS binary
at run time without having to recompile and relink LAMMPS.
For more details please see the LAMMPS manual.
To be able to dynamically load and execute the plugins from inside
LAMMPS, you need to link with an appropriate system library, which
is done using the settings in lib/plugin/Makefile.lammps. See
that file and the lib/plugin/README file for more details.
The person who created this package is Axel Kohlmeyer at Temple U
(akohlmey at gmail.com). Contact him directly if you have questions.

View File

@ -38,9 +38,53 @@ namespace LAMMPS_NS
// map for counting references to dso handles // map for counting references to dso handles
static std::map<void *, int> dso_refcounter; static std::map<void *, int> dso_refcounter;
/* ---------------------------------------------------------------------- */
Plugin::Plugin(LAMMPS *lmp) : Pointers(lmp) {}
/* ---------------------------------------------------------------------- */
void Plugin::command(int narg, char **arg)
{
if (narg < 1) error->all(FLERR,"Illegal plugin command");
#if defined(LMP_PLUGIN)
std::string cmd = arg[0];
if (cmd == "load") {
if (narg < 2) error->all(FLERR,"Illegal plugin load command");
for (int i=1; i < narg; ++i)
plugin_load(arg[i],lmp);
} else if (cmd == "unload") {
if (narg != 3) error->all(FLERR,"Illegal plugin unload command");
plugin_unload(arg[1],arg[2],lmp);
} else if (cmd == "clear") {
plugin_clear(lmp);
} else if (cmd == "list") {
if (comm->me == 0) {
int num = plugin_get_num_plugins();
utils::logmesg(lmp,"Currently loaded plugins\n");
for (int i=0; i < num; ++i) {
auto entry = plugin_get_info(i);
utils::logmesg(lmp,fmt::format("{:4}: {} style plugin {}\n",
i+1,entry->style,entry->name));
}
}
} else error->all(FLERR,"Illegal plugin command");
#else
if (comm->me == 0)
error->warning(FLERR,"Ignoring plugin command. LAMMPS must be built as "
"a shared library for it to work.");
#endif
}
// load DSO and call included registration function // load DSO and call included registration function
void plugin_load(const char *file, LAMMPS *lmp) void plugin_load(const char *file, LAMMPS *lmp)
{ {
#if defined(LMP_PLUGIN)
int me = lmp->comm->me; int me = lmp->comm->me;
#if defined(WIN32) #if defined(WIN32)
lmp->error->all(FLERR,"Loading of plugins on Windows is not supported\n"); lmp->error->all(FLERR,"Loading of plugins on Windows is not supported\n");
@ -77,6 +121,7 @@ namespace LAMMPS_NS
(*(lammpsplugin_initfunc)(initfunc))((void *)lmp, dso, (*(lammpsplugin_initfunc)(initfunc))((void *)lmp, dso,
(void *)&plugin_register); (void *)&plugin_register);
#endif
#endif #endif
} }
@ -89,6 +134,7 @@ namespace LAMMPS_NS
void plugin_register(lammpsplugin_t *plugin, void *ptr) void plugin_register(lammpsplugin_t *plugin, void *ptr)
{ {
#if defined(LMP_PLUGIN)
LAMMPS *lmp = (LAMMPS *)ptr; LAMMPS *lmp = (LAMMPS *)ptr;
int me = lmp->comm->me; int me = lmp->comm->me;
@ -158,6 +204,7 @@ namespace LAMMPS_NS
"yet implemented\n",pstyle)); "yet implemented\n",pstyle));
pluginlist.pop_back(); pluginlist.pop_back();
} }
#endif
} }
/* -------------------------------------------------------------------- /* --------------------------------------------------------------------
@ -168,6 +215,7 @@ namespace LAMMPS_NS
void plugin_unload(const char *style, const char *name, LAMMPS *lmp) void plugin_unload(const char *style, const char *name, LAMMPS *lmp)
{ {
#if defined(LMP_PLUGIN)
int me = lmp->comm->me; int me = lmp->comm->me;
// ignore unload request from unsupported style categories // ignore unload request from unsupported style categories
@ -246,6 +294,7 @@ namespace LAMMPS_NS
dlclose(handle); dlclose(handle);
#endif #endif
} }
#endif
} }
/* -------------------------------------------------------------------- /* --------------------------------------------------------------------

View File

@ -11,14 +11,26 @@
See the README file in the top-level LAMMPS directory. See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */ ------------------------------------------------------------------------- */
#ifdef COMMAND_CLASS
CommandStyle(plugin,Plugin)
#else
#ifndef LMP_PLUGIN_H #ifndef LMP_PLUGIN_H
#define LMP_PLUGIN_H #define LMP_PLUGIN_H
#include "lammpsplugin.h" #include "lammpsplugin.h"
#include "pointers.h"
namespace LAMMPS_NS namespace LAMMPS_NS
{ {
class LAMMPS;
class Plugin : protected Pointers {
public:
Plugin(class LAMMPS *);
void command(int, char **);
};
void plugin_load(const char *, LAMMPS *); void plugin_load(const char *, LAMMPS *);
void plugin_register(lammpsplugin_t *, void *); void plugin_register(lammpsplugin_t *, void *);
@ -33,3 +45,4 @@ namespace LAMMPS_NS
} }
#endif #endif
#endif

View File

@ -35,7 +35,6 @@
#include "neighbor.h" #include "neighbor.h"
#include "output.h" #include "output.h"
#include "pair.h" #include "pair.h"
#include "plugin.h"
#include "special.h" #include "special.h"
#include "style_command.h" #include "style_command.h"
#include "thermo.h" #include "thermo.h"
@ -718,7 +717,6 @@ int Input::execute_command()
else if (!strcmp(command,"log")) log(); else if (!strcmp(command,"log")) log();
else if (!strcmp(command,"next")) next_command(); else if (!strcmp(command,"next")) next_command();
else if (!strcmp(command,"partition")) partition(); else if (!strcmp(command,"partition")) partition();
else if (!strcmp(command,"plugin")) plugin();
else if (!strcmp(command,"print")) print(); else if (!strcmp(command,"print")) print();
else if (!strcmp(command,"python")) python(); else if (!strcmp(command,"python")) python();
else if (!strcmp(command,"quit")) quit(); else if (!strcmp(command,"quit")) quit();
@ -1097,34 +1095,6 @@ void Input::partition()
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
void Input::plugin()
{
if (narg < 1) error->all(FLERR,"Illegal plugin command");
std::string cmd = arg[0];
if (cmd == "load") {
if (narg < 2) error->all(FLERR,"Illegal plugin load command");
for (int i=1; i < narg; ++i)
plugin_load(arg[i],lmp);
} else if (cmd == "unload") {
if (narg != 3) error->all(FLERR,"Illegal plugin unload command");
plugin_unload(arg[1],arg[2],lmp);
} else if (cmd == "clear") {
plugin_clear(lmp);
} else if (cmd == "list") {
if (comm->me == 0) {
int num = plugin_get_num_plugins();
utils::logmesg(lmp,"Currently loaded plugins\n");
for (int i=0; i < num; ++i) {
auto entry = plugin_get_info(i);
utils::logmesg(lmp,fmt::format("{:4}: {} style plugin {}\n",
i+1,entry->style,entry->name));
}
}
} else error->all(FLERR,"Illegal plugin command");
}
/* ---------------------------------------------------------------------- */
void Input::print() void Input::print()
{ {
if (narg < 1) error->all(FLERR,"Illegal print command"); if (narg < 1) error->all(FLERR,"Illegal print command");

View File

@ -38,7 +38,9 @@
#include "neighbor.h" #include "neighbor.h"
#include "region.h" #include "region.h"
#include "output.h" #include "output.h"
#if defined(LMP_PLUGIN)
#include "plugin.h" #include "plugin.h"
#endif
#include "thermo.h" #include "thermo.h"
#include "timer.h" #include "timer.h"
#include "universe.h" #include "universe.h"
@ -4590,7 +4592,11 @@ This function counts how many plugins are currently loaded.
*/ */
int lammps_plugin_count() int lammps_plugin_count()
{ {
#if defined(LMP_PLUGIN)
return plugin_get_num_plugins(); return plugin_get_num_plugins();
#else
return 0;
#endif
} }
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
@ -4617,6 +4623,7 @@ set to an empty string, otherwise 1.
*/ */
int lammps_plugin_name(int idx, char *stylebuf, char *namebuf, int buf_size) int lammps_plugin_name(int idx, char *stylebuf, char *namebuf, int buf_size)
{ {
#if defined(LMP_PLUGIN)
stylebuf[0] = namebuf[0] = '\0'; stylebuf[0] = namebuf[0] = '\0';
const lammpsplugin_t *plugin = plugin_get_info(idx); const lammpsplugin_t *plugin = plugin_get_info(idx);
@ -4625,6 +4632,7 @@ int lammps_plugin_name(int idx, char *stylebuf, char *namebuf, int buf_size)
strncpy(namebuf,plugin->name,buf_size); strncpy(namebuf,plugin->name,buf_size);
return 1; return 1;
} }
#endif
return 0; return 0;
} }

View File

@ -1,6 +1,6 @@
# build LAMMPS plugins, but not on Windows # build LAMMPS plugins, but not on Windows
if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows") if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows" AND PKG_PLUGIN)
ExternalProject_Add(plugins ExternalProject_Add(plugins
SOURCE_DIR "${LAMMPS_DIR}/examples/plugins" SOURCE_DIR "${LAMMPS_DIR}/examples/plugins"
BINARY_DIR ${CMAKE_BINARY_DIR}/build-plugins BINARY_DIR ${CMAKE_BINARY_DIR}/build-plugins

View File

@ -367,6 +367,7 @@ TEST_F(SimpleCommandsTest, Units)
TEST_FAILURE(".*ERROR: Illegal units command.*", lmp->input->one("units unknown");); TEST_FAILURE(".*ERROR: Illegal units command.*", lmp->input->one("units unknown"););
} }
#if defined(LMP_PLUGIN)
TEST_F(SimpleCommandsTest, Plugin) TEST_F(SimpleCommandsTest, Plugin)
{ {
#if defined(__APPLE__) #if defined(__APPLE__)
@ -437,6 +438,7 @@ TEST_F(SimpleCommandsTest, Plugin)
if (verbose) std::cout << text; if (verbose) std::cout << text;
ASSERT_THAT(text, MatchesRegex(".*Currently loaded plugins.*")); ASSERT_THAT(text, MatchesRegex(".*Currently loaded plugins.*"));
} }
#endif
TEST_F(SimpleCommandsTest, Shell) TEST_F(SimpleCommandsTest, Shell)
{ {