import files for platform namespace from standalone project w/o updating LAMMPS
This commit is contained in:
947
src/platform.cpp
Normal file
947
src/platform.cpp
Normal file
@ -0,0 +1,947 @@
|
||||
/* ----------------------------------------------------------------------
|
||||
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
|
||||
https://www.lammps.org/, Sandia National Laboratories
|
||||
Steve Plimpton, sjplimp@sandia.gov
|
||||
|
||||
Copyright (2003) Sandia Corporation. Under the terms of Contract
|
||||
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
|
||||
certain rights in this software. This software is distributed under
|
||||
the GNU General Public License.
|
||||
|
||||
See the README file in the top-level LAMMPS directory.
|
||||
------------------------------------------------------------------------- */
|
||||
/** \file platform.cpp
|
||||
* This file provides abstractions for a variety of platform specific
|
||||
* functionality in a namespace "platform". This is a companion to
|
||||
* the "utils" namespace with convenience and utility functions. */
|
||||
|
||||
#include "platform.h"
|
||||
#include "utils.h"
|
||||
|
||||
#if HAVE_MPI
|
||||
#include <mpi.h>
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// include system headers and tweak system settings
|
||||
#if defined(_WIN32)
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32_WINNT)
|
||||
#undef _WIN32_WINNT
|
||||
#endif
|
||||
|
||||
// target Windows version is windows 7 and later
|
||||
#define _WIN32_WINNT _WIN32_WINNT_WIN7
|
||||
#define PSAPI_VERSION 2
|
||||
|
||||
#include <direct.h>
|
||||
#include <io.h> // for _get_osfhandle()
|
||||
#include <sys/stat.h>
|
||||
#include <windows.h>
|
||||
|
||||
#else // not Windows ///////////////////////////////////////////////
|
||||
|
||||
#include <dirent.h>
|
||||
#include <dlfcn.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <fcntl.h>
|
||||
#include <sys/syslimits.h>
|
||||
#endif
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cstring>
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/// Struct for listing on-the-fly compression/decompression commands
|
||||
struct zip_info {
|
||||
/// identifier for the different compression algorithms
|
||||
enum styles { NONE, GZIP, BZIP2, ZSTD, XZ, LZMA, LZ4 };
|
||||
const std::string extension; ///< filename extension for the current algorithm
|
||||
const std::string command; ///< command to perform compression or decompression
|
||||
const std::string zipflags; ///< flags to append to compress from stdin to stdout
|
||||
const std::string unzipflags; ///< flags to decompress file to stdout
|
||||
const int style; ///< compression style flag
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
static const std::vector<zip_info> zip_styles = {
|
||||
{"", "", "", "", zip_info::NONE},
|
||||
{"gz", "gzip", " > ", " -cdf ", zip_info::GZIP},
|
||||
{"bz2", "bzip2", " > ", " -cdf ", zip_info::BZIP2},
|
||||
{"zstd", "zstd", " -q > ", " -cdf ", zip_info::ZSTD},
|
||||
{"xz", "xz", " > ", " -cdf ", zip_info::XZ},
|
||||
{"lzma", "xz", " --format=lzma > ", " --format=lzma -cdf ", zip_info::LZMA},
|
||||
{"lz4", "lz4", " > ", " -cdf ", zip_info::LZ4},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static const zip_info &find_zip_type(const std::string &file)
|
||||
{
|
||||
std::size_t dot = file.find_last_of('.');
|
||||
if (dot != std::string::npos) {
|
||||
const std::string ext = file.substr(dot + 1);
|
||||
for (const auto &i : zip_styles) {
|
||||
if (i.extension == ext) return i;
|
||||
}
|
||||
}
|
||||
return zip_styles[0];
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
using namespace LAMMPS_NS;
|
||||
|
||||
// get CPU time
|
||||
|
||||
// clang-format off
|
||||
// clang compilers are optimizing this function too aggressively returning always 0
|
||||
#if defined(__clang__)
|
||||
[[clang::optnone]]
|
||||
#endif
|
||||
double platform::cputime()
|
||||
// clang-format on
|
||||
{
|
||||
double rv = 0.0;
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
// from MSD docs.
|
||||
FILETIME ct, et, kt, ut;
|
||||
union {
|
||||
FILETIME ft;
|
||||
uint64_t ui;
|
||||
} cpu;
|
||||
if (GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) {
|
||||
cpu.ft = ut;
|
||||
rv = cpu.ui * 0.0000001;
|
||||
}
|
||||
|
||||
#else /* ! _WIN32 */
|
||||
|
||||
struct rusage ru;
|
||||
if (getrusage(RUSAGE_SELF, &ru) == 0) {
|
||||
rv = (double) ru.ru_utime.tv_sec;
|
||||
rv += (double) ru.ru_utime.tv_usec * 0.000001;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
get wall time
|
||||
------------------------------------------------------------------------ */
|
||||
double platform::walltime()
|
||||
{
|
||||
double wtime;
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
wtime = GetTickCount64() * 0.001;
|
||||
|
||||
#else
|
||||
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, nullptr);
|
||||
wtime = 1.0 * tv.tv_sec + 1.0e-6 * tv.tv_usec;
|
||||
|
||||
#endif
|
||||
|
||||
return wtime;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
get Operating system and version info
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
std::string platform::os_info()
|
||||
{
|
||||
std::string buf;
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
// Get Windows Edition name from registry
|
||||
char value[1024];
|
||||
DWORD value_length = 1024;
|
||||
const char *subkey = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
|
||||
const char *entry = "ProductName";
|
||||
RegGetValue(HKEY_LOCAL_MACHINE, subkey, entry, RRF_RT_REG_SZ, nullptr, &value,
|
||||
(LPDWORD) &value_length);
|
||||
// enforce zero termination
|
||||
value[1023] = '\0';
|
||||
buf = value;
|
||||
|
||||
DWORD fullversion, majorv, minorv, buildv = 0;
|
||||
fullversion = GetVersion();
|
||||
majorv = (DWORD) (LOBYTE(LOWORD(fullversion)));
|
||||
minorv = (DWORD) (HIBYTE(LOWORD(fullversion)));
|
||||
if (fullversion < 0x80000000) buildv = (DWORD) (HIWORD(fullversion));
|
||||
|
||||
buf += ", Windows ABI " + std::to_string(majorv) + "." + std::to_string(minorv) + " (" +
|
||||
std::to_string(buildv) + ") on ";
|
||||
|
||||
SYSTEM_INFO si;
|
||||
GetSystemInfo(&si);
|
||||
|
||||
switch (si.wProcessorArchitecture) {
|
||||
case PROCESSOR_ARCHITECTURE_AMD64:
|
||||
buf += "x86_64";
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_ARM:
|
||||
buf += "arm";
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_IA64:
|
||||
buf += "ia64";
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_INTEL:
|
||||
buf += "i386";
|
||||
break;
|
||||
default:
|
||||
buf += "(unknown)";
|
||||
}
|
||||
#else
|
||||
struct utsname ut;
|
||||
uname(&ut);
|
||||
|
||||
// try to get OS distribution name, if available
|
||||
buf = ut.sysname;
|
||||
|
||||
#if 0 // disable until this is integrated into LAMMPS and TextFileReader becomes available
|
||||
if (utils::file_is_readable("/etc/os-release")) {
|
||||
try {
|
||||
TextFileReader reader("/etc/os-release","");
|
||||
while (1) {
|
||||
auto words = reader.next_values(0,"=");
|
||||
if ((words.count() > 1) && (words.next_string() == "PRETTY_NAME")) {
|
||||
distro += " " + utils::trim(words.next_string());
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (std::exception &e) {
|
||||
; // EOF but keyword not found
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
buf += std::string(" ") + ut.release + " " + ut.machine;
|
||||
#endif
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
identify C++ standard version
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
std::string platform::cxx_standard()
|
||||
{
|
||||
#if __cplusplus > 202002L
|
||||
return "newer than C++20";
|
||||
#elif __cplusplus == 202002L
|
||||
return "C++20";
|
||||
#elif __cplusplus == 201703L
|
||||
return "C++17";
|
||||
#elif __cplusplus == 201402L
|
||||
return "C++14";
|
||||
#elif __cplusplus == 201103L
|
||||
return "C++11";
|
||||
#elif __cplusplus == 199711L
|
||||
return "C++98";
|
||||
#else
|
||||
return "unknown";
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
identify compiler and its version
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
std::string platform::compiler_info()
|
||||
{
|
||||
std::string buf = "(Unknown)";
|
||||
#if 0 // disable for now untile merged into LAMMPS and fmt:: becomes available
|
||||
#if defined(__INTEL_LLVM_COMPILER)
|
||||
double version = static_cast<double>(__INTEL_LLVM_COMPILER)*0.01;
|
||||
buf = fmt::format("Intel LLVM C++ {:.1f} / {}", version, __VERSION__);
|
||||
#elif defined(__ibmxl__)
|
||||
buf = fmt::format("IBM XL C/C++ (Clang) {}.{}.{}",
|
||||
__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__);
|
||||
#elif defined(__clang__)
|
||||
buf = fmt::format("Clang C++ {}", __VERSION__);
|
||||
#elif defined(__PGI)
|
||||
buf = fmt::format("PGI C++ {}.{}",__PGIC__,__PGIC_MINOR__);
|
||||
#elif defined(__INTEL_COMPILER)
|
||||
double version = static_cast<double>(__INTEL_COMPILER)*0.01;
|
||||
buf = fmt::format("Intel Classic C++ {:.2f}.{} / {}", version,
|
||||
__INTEL_COMPILER_UPDATE, __VERSION__);
|
||||
#elif defined(__MINGW64__)
|
||||
buf = fmt::format("MinGW-w64 64bit {}.{} / GNU C++ {}", __MINGW64_VERSION_MAJOR,
|
||||
__MINGW64_VERSION_MINOR, __VERSION__);
|
||||
#elif defined(__MINGW32__)
|
||||
buf = fmt::format("MinGW-w64 32bit {}.{} / GNU C++ {}", __MINGW32_MAJOR_VERSION,
|
||||
__MINGW32_MINOR_VERSION, __VERSION__);
|
||||
#elif defined(__GNUC__)
|
||||
buf = fmt::format("GNU C++ {}", __VERSION__);
|
||||
#elif defined(_MSC_VER) && (_MSC_VER > 1920) && (_MSC_VER < 2000)
|
||||
constexpr int major = _MSC_VER / 100;
|
||||
constexpr int minor = _MSC_VER - major *100;
|
||||
buf = "Microsoft Visual Studio 20" + std::to_string(major) + ", C/C++ " + std::to_string(major-5) + "." + std::to_string(minor);
|
||||
#else
|
||||
buf = "(Unknown)";
|
||||
#endif
|
||||
#endif
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
detect OpenMP standard
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
std::string platform::openmp_standard()
|
||||
{
|
||||
|
||||
#if !defined(_OPENMP)
|
||||
return "OpenMP not enabled";
|
||||
#else
|
||||
|
||||
// Supported OpenMP version corresponds to the release date of the
|
||||
// specifications as posted at https://www.openmp.org/specifications/
|
||||
|
||||
#if _OPENMP > 202011
|
||||
return "OpenMP newer than version 5.1";
|
||||
#elif _OPENMP == 202011
|
||||
return "OpenMP 5.1";
|
||||
#elif _OPENMP == 201811
|
||||
return "OpenMP 5.0";
|
||||
#elif _OPENMP == 201611
|
||||
return "OpenMP 5.0 preview 1";
|
||||
#elif _OPENMP == 201511
|
||||
return "OpenMP 4.5";
|
||||
#elif _OPENMP == 201307
|
||||
return "OpenMP 4.0";
|
||||
#elif _OPENMP == 201107
|
||||
return "OpenMP 3.1";
|
||||
#elif _OPENMP == 200805
|
||||
return "OpenMP 3.0";
|
||||
#elif _OPENMP == 200505
|
||||
return "OpenMP 2.5";
|
||||
#elif _OPENMP == 200203
|
||||
return "OpenMP 2.0";
|
||||
#else
|
||||
return "unknown OpenMP version";
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
identify MPI vendor from defines in the mpi.h file.
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
std::string platform::mpi_vendor()
|
||||
{
|
||||
#if defined(MPI_STUBS)
|
||||
return "MPI STUBS";
|
||||
#elif defined(OPEN_MPI)
|
||||
return "Open MPI";
|
||||
#elif defined(MPICH_NAME)
|
||||
return "MPICH";
|
||||
#elif defined(I_MPI_VERSION)
|
||||
return "Intel MPI";
|
||||
#elif defined(PLATFORM_MPI)
|
||||
return "Platform MPI";
|
||||
#elif defined(HP_MPI)
|
||||
return "HP MPI";
|
||||
#elif defined(MSMPI_VER)
|
||||
// Get Microsoft MPI version from registry
|
||||
char value[1024];
|
||||
DWORD value_length = 1024;
|
||||
const char *subkey = "SOFTWARE\\Microsoft\\MPI";
|
||||
const char *entry = "Version";
|
||||
auto rv = RegGetValueA(HKEY_LOCAL_MACHINE, subkey, entry, RRF_RT_REG_SZ, nullptr, &value,
|
||||
(LPDWORD) &value_length);
|
||||
std::string buf = "Microsoft MPI";
|
||||
if (rv == ERROR_SUCCESS) buf += std::string(" v") + value;
|
||||
return buf;
|
||||
#else
|
||||
return "Unknown MPI implementation";
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
detect MPI version info
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
std::string platform::mpi_info(int &major, int &minor)
|
||||
{
|
||||
int len = 0;
|
||||
#if (defined(MPI_VERSION) && (MPI_VERSION > 2)) || defined(MPI_STUBS)
|
||||
static char version[MPI_MAX_LIBRARY_VERSION_STRING];
|
||||
MPI_Get_library_version(version, &len);
|
||||
#else
|
||||
constexpr int MAX_VERSION_STRING = 32;
|
||||
static char version[MAX_VERSION_STRING];
|
||||
strncpy(version, mpi_vendor().c_str(), MAX_VERSION_STRING);
|
||||
#endif
|
||||
|
||||
#if defined(MPI_VERSION)
|
||||
MPI_Get_version(&major, &minor);
|
||||
if (len > 80) {
|
||||
char *ptr = strchr(version + 80, '\n');
|
||||
if (ptr) *ptr = '\0';
|
||||
}
|
||||
#else
|
||||
major = 1;
|
||||
minor = 0;
|
||||
#endif
|
||||
return std::string(version);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
set environment variable
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
int platform::putenv(const std::string &vardef)
|
||||
{
|
||||
if (vardef.size() == 0) return -1;
|
||||
|
||||
auto found = vardef.find_first_of('=');
|
||||
#ifdef _WIN32
|
||||
// must assign a value to variable with _putenv()
|
||||
if (found == std::string::npos)
|
||||
return _putenv(utils::strdup(vardef + "=1"));
|
||||
else
|
||||
return _putenv(utils::strdup(vardef));
|
||||
#else
|
||||
if (found == std::string::npos)
|
||||
return setenv(vardef.c_str(), "", 1);
|
||||
else
|
||||
return setenv(vardef.substr(0, found).c_str(), vardef.substr(found + 1).c_str(), 1);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
split a "path" environment variable into a list
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
std::vector<std::string> platform::list_pathenv(const std::string &var)
|
||||
{
|
||||
std::vector<std::string> dirs;
|
||||
const char *ptr = getenv(var.c_str());
|
||||
if (ptr == nullptr) return dirs;
|
||||
|
||||
std::string pathvar = ptr;
|
||||
std::size_t first = 0, next;
|
||||
while (true) {
|
||||
next = pathvar.find_first_of(pathvarsep, first);
|
||||
if (next == std::string::npos) {
|
||||
dirs.push_back(pathvar.substr(first));
|
||||
break;
|
||||
} else {
|
||||
dirs.push_back(pathvar.substr(first, next - first));
|
||||
first = next + 1;
|
||||
}
|
||||
}
|
||||
return dirs;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
find the full path name of an executable
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
std::string platform::find_exe_path(const std::string &cmd)
|
||||
{
|
||||
if (cmd.size() == 0) return "";
|
||||
auto pathdirs = list_pathenv("PATH");
|
||||
#ifdef _WIN32
|
||||
// windows always looks in "." and does it first
|
||||
pathdirs.insert(pathdirs.begin(), ".");
|
||||
#else
|
||||
struct stat info;
|
||||
#endif
|
||||
for (const auto &dir : pathdirs) {
|
||||
std::string exe = path_join(dir, cmd);
|
||||
#ifdef _WIN32
|
||||
const char *extensions[] = {".exe", ".com", ".bat", nullptr};
|
||||
for (auto ext = extensions; *ext != nullptr; ++ext) {
|
||||
auto exe_path = exe + *ext;
|
||||
if (file_is_readable(exe_path)) return exe_path;
|
||||
}
|
||||
#else
|
||||
memset(&info, 0, sizeof(info));
|
||||
if (stat(exe.c_str(), &info) != 0) continue;
|
||||
if ((info.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) != 0) return exe;
|
||||
#endif
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
wrapper functions for loading shared objects and libraries
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
// open a shared object file
|
||||
void *platform::dlopen(const std::string &fname)
|
||||
{
|
||||
return (void *) LoadLibrary(fname.c_str());
|
||||
}
|
||||
|
||||
// close a shared object
|
||||
int platform::dlclose(void *handle)
|
||||
{
|
||||
/* FreeLibrary returns nonzero on success unlike dlclose() */
|
||||
return (FreeLibrary((HINSTANCE) handle) == 0);
|
||||
}
|
||||
|
||||
// resolve a symbol in shared object
|
||||
void *platform::dlsym(void *handle, const std::string &symbol)
|
||||
{
|
||||
return (void *) GetProcAddress((HINSTANCE) handle, symbol.c_str());
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// open a shared object file
|
||||
void *platform::dlopen(const std::string &fname)
|
||||
{
|
||||
return ::dlopen(fname.c_str(), RTLD_NOW);
|
||||
}
|
||||
|
||||
// close a shared object
|
||||
int platform::dlclose(void *handle)
|
||||
{
|
||||
return ::dlclose(handle);
|
||||
}
|
||||
|
||||
// resolve a symbol in shared object
|
||||
void *platform::dlsym(void *handle, const std::string &symbol)
|
||||
{
|
||||
return ::dlsym(handle, symbol.c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
/** On Linux the folder /proc/self/fd holds symbolic links to the actual
|
||||
* pathnames associated with each open file descriptor of the current process.
|
||||
* On macOS the same kind of information can be obtained using ``fcntl(fd,F_GETPATH,buf)``.
|
||||
* On Windows we use ``GetFinalPathNameByHandleA()`` which is available with
|
||||
* Windows Vista and later. If the buffer is to small (< 16 bytes) a null pointer is returned.
|
||||
*
|
||||
* This function is used to provide a filename with error messages in functions
|
||||
* where the filename is not passed as an argument, but the FILE * pointer. */
|
||||
|
||||
const char *platform::guesspath(FILE *fp, char *buf, int len)
|
||||
{
|
||||
// no point in guessing a path with a short buffer or NULL pointer as buffer
|
||||
if ((buf == nullptr) || (len < 16)) return nullptr;
|
||||
|
||||
// zero buffer and reserve last character in buffer for terminating '\0'
|
||||
memset(buf, 0, len);
|
||||
len--;
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
int fd = fileno(fp);
|
||||
// get pathname from /proc or copy (unknown)
|
||||
if (readlink((std::string("/proc/self/fd/") + std::to_string(fd)).c_str(), buf, len) <= 0)
|
||||
strncpy(buf, "(unknown)", len);
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
int fd = fileno(fp);
|
||||
char filepath[PATH_MAX];
|
||||
if (fcntl(fd, F_GETPATH, filepath) != -1)
|
||||
strncpy(buf, filepath, len);
|
||||
else
|
||||
strncpy(buf, "(unknown)", len);
|
||||
|
||||
#elif defined(_WIN32)
|
||||
|
||||
char filepath[MAX_PATH];
|
||||
HANDLE h = (HANDLE) _get_osfhandle(_fileno(fp));
|
||||
if (GetFinalPathNameByHandleA(h, filepath, MAX_PATH, FILE_NAME_NORMALIZED) > 0)
|
||||
strncpy(buf, filepath, len);
|
||||
else
|
||||
strncpy(buf, "(unknown)", len);
|
||||
|
||||
#else // unsupported OS
|
||||
|
||||
strncpy(buf, "(unknown)", len);
|
||||
|
||||
#endif
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
detect terminal, e.g. for using a pager automatically
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
bool platform::is_console(FILE *fp)
|
||||
{
|
||||
if (!fp) return false;
|
||||
#if defined(_WIN32)
|
||||
return (_isatty(fileno(fp)) == 1);
|
||||
#else
|
||||
return (isatty(fileno(fp)) == 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
Get string with path to the current directory
|
||||
PATH_MAX may not be a compile time constant, so we must allocate and delete a buffer.
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
std::string platform::current_directory()
|
||||
{
|
||||
std::string cwd = "";
|
||||
|
||||
#if defined(_WIN32)
|
||||
char *buf = new char[MAX_PATH];
|
||||
if (_getcwd(buf, MAX_PATH)) { cwd = buf; }
|
||||
delete[] buf;
|
||||
#else
|
||||
char *buf = new char[PATH_MAX];
|
||||
if (::getcwd(buf, PATH_MAX)) { cwd = buf; }
|
||||
delete[] buf;
|
||||
#endif
|
||||
return cwd;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
check if a path is a directory
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
bool platform::path_is_directory(const std::string &path)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
struct _stat info;
|
||||
memset(&info, 0, sizeof(info));
|
||||
if (_stat(path.c_str(), &info) != 0) return false;
|
||||
#else
|
||||
struct stat info;
|
||||
memset(&info, 0, sizeof(info));
|
||||
if (stat(path.c_str(), &info) != 0) return false;
|
||||
#endif
|
||||
return ((info.st_mode & S_IFDIR) != 0);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
get directory listing in string vector
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
std::vector<std::string> platform::list_directory(const std::string &dir)
|
||||
{
|
||||
std::vector<std::string> files;
|
||||
if (!path_is_directory(dir)) return files;
|
||||
|
||||
#if defined(_WIN32)
|
||||
HANDLE handle;
|
||||
WIN32_FIND_DATA fd;
|
||||
std::string searchname = dir + filepathsep[0] + "*";
|
||||
handle = FindFirstFile(searchname.c_str(), &fd);
|
||||
if (handle == ((HANDLE) -1)) return files;
|
||||
while (FindNextFile(handle, &fd)) {
|
||||
std::string entry(fd.cFileName);
|
||||
if ((entry == "..") || (entry == ".")) continue;
|
||||
files.push_back(entry);
|
||||
}
|
||||
FindClose(handle);
|
||||
#else
|
||||
std::string dirname = dir + filepathsep[0];
|
||||
DIR *handle = opendir(dirname.c_str());
|
||||
if (handle == nullptr) return files;
|
||||
struct dirent *fd;
|
||||
while ((fd = readdir(handle)) != nullptr) {
|
||||
std::string entry(fd->d_name);
|
||||
if ((entry == "..") || (entry == ".")) continue;
|
||||
files.push_back(entry);
|
||||
}
|
||||
closedir(handle);
|
||||
#endif
|
||||
return files;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
Change current directory
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
int platform::chdir(const std::string &path)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
return ::_chdir(path.c_str());
|
||||
#else
|
||||
return ::chdir(path.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
Create a directory
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
int platform::mkdir(const std::string &path)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
return ::_mkdir(path.c_str());
|
||||
#else
|
||||
return ::mkdir(path.c_str(), S_IRWXU | S_IRGRP | S_IXGRP);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
Delete a directory and its contents recursively
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
int platform::rmdir(const std::string &path)
|
||||
{
|
||||
// recurse through directory tree deleting files and directories
|
||||
auto entries = list_directory(path);
|
||||
for (const auto &entry : entries) {
|
||||
const auto newpath = path_join(path, entry);
|
||||
if (path_is_directory(newpath))
|
||||
rmdir(newpath);
|
||||
else
|
||||
unlink(newpath);
|
||||
}
|
||||
#if defined(_WIN32)
|
||||
return ::_rmdir(path.c_str());
|
||||
#else
|
||||
return ::rmdir(path.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
Delete a file
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
int platform::unlink(const std::string &path)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
return ::_unlink(path.c_str());
|
||||
#else
|
||||
return ::unlink(path.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
Get current file stream position
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
bigint platform::ftell(FILE *fp)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
return (bigint)::_ftelli64(fp);
|
||||
#else
|
||||
return (bigint)::ftell(fp);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
Set current file stream position
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
int platform::fseek(FILE *fp, bigint pos)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
return ::_fseeki64(fp, (__int64) pos, SEEK_SET);
|
||||
#else
|
||||
return ::fseek(fp, (long) pos, SEEK_SET);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
Truncate opened file to given length
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
int platform::ftruncate(FILE *fp, bigint length)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
HANDLE h = (HANDLE) _get_osfhandle(_fileno(fp));
|
||||
LARGE_INTEGER li_start, li_length;
|
||||
li_start.QuadPart = (int64_t) 0;
|
||||
li_length.QuadPart = (int64_t) length;
|
||||
if (SetFilePointerEx(h, li_start, NULL, FILE_CURRENT) &&
|
||||
SetFilePointerEx(h, li_length, NULL, FILE_BEGIN) && SetEndOfFile(h)) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
platform::fseek(fp, length);
|
||||
return ::ftruncate(fileno(fp), (off_t) length);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
open pipe
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
FILE *platform::popen(const std::string &cmd, const std::string &mode)
|
||||
{
|
||||
FILE *fp = nullptr;
|
||||
#if defined(_WIN32)
|
||||
if (mode == "r")
|
||||
fp = ::_popen(cmd.c_str(), "rb");
|
||||
else if (mode == "w")
|
||||
fp = ::_popen(cmd.c_str(), "wb");
|
||||
#else
|
||||
if (mode == "r")
|
||||
fp = ::popen(cmd.c_str(), "r");
|
||||
else if (mode == "w")
|
||||
fp = ::popen(cmd.c_str(), "w");
|
||||
#endif
|
||||
return fp;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
close pipe
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
int platform::pclose(FILE *fp)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
return ::_pclose(fp);
|
||||
#else
|
||||
return ::pclose(fp);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
strip off leading part of path, return just the filename
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
std::string platform::path_basename(const std::string &path)
|
||||
{
|
||||
size_t start = path.find_last_of(platform::filepathsep);
|
||||
|
||||
if (start == std::string::npos) {
|
||||
start = 0;
|
||||
} else {
|
||||
start += 1;
|
||||
}
|
||||
|
||||
return path.substr(start);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
Return only the leading part of a path, return just the directory
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
std::string platform::path_dirname(const std::string &path)
|
||||
{
|
||||
size_t start = path.find_last_of(platform::filepathsep);
|
||||
|
||||
if (start == std::string::npos) return ".";
|
||||
|
||||
return path.substr(0, start);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
join two paths.
|
||||
if one of the two is an empty string just return the other unmodified
|
||||
if the first string ends in the separator or the second begins with one, trim them
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
std::string platform::path_join(const std::string &a, const std::string &b)
|
||||
{
|
||||
if (a.empty()) return b;
|
||||
if (b.empty()) return a;
|
||||
|
||||
// remove trailing separator(s) in first part
|
||||
std::string joined = a;
|
||||
while (joined.find_last_of(platform::filepathsep) == joined.size() - 1) {
|
||||
for (const auto &s : platform::filepathsep)
|
||||
if (joined.back() == s) joined.pop_back();
|
||||
}
|
||||
|
||||
// skip over leading separator(s) in second part
|
||||
std::size_t skip = 0;
|
||||
while (b.find_first_of(platform::filepathsep, skip) == skip) ++skip;
|
||||
|
||||
// combine and return
|
||||
joined += platform::filepathsep[0] + b.substr(skip);
|
||||
return joined;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
try to open file for reading to prove if it exists and is accessible
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
bool platform::file_is_readable(const std::string &path)
|
||||
{
|
||||
FILE *fp = fopen(path.c_str(), "r");
|
||||
if (fp) {
|
||||
fclose(fp);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
check if filename has a known compression extension
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
bool platform::has_zip_extension(const std::string &file)
|
||||
{
|
||||
return find_zip_type(file).style != zip_info::NONE;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
open pipe to read a compressed file
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
FILE *platform::zip_read(const std::string &file)
|
||||
{
|
||||
FILE *fp = nullptr;
|
||||
|
||||
#if defined(LAMMPS_GZIP)
|
||||
auto zip = find_zip_type(file);
|
||||
if (zip.style == zip_info::NONE) return nullptr;
|
||||
|
||||
if (find_exe_path(zip.command).size())
|
||||
// put quotes around file name so that they may contain blanks
|
||||
fp = popen((zip.command + zip.unzipflags + "\"" + file + "\""), "r");
|
||||
#endif
|
||||
return fp;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
open pipe to write a compressed file
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
FILE *platform::zip_write(const std::string &file)
|
||||
{
|
||||
FILE *fp = nullptr;
|
||||
|
||||
#if defined(LAMMPS_GZIP)
|
||||
auto zip = find_zip_type(file);
|
||||
if (zip.style == zip_info::NONE) return nullptr;
|
||||
|
||||
if (find_exe_path(zip.command).size())
|
||||
// put quotes around file name so that they may contain blanks
|
||||
fp = popen((zip.command + zip.zipflags + "\"" + file + "\""), "w");
|
||||
#endif
|
||||
return fp;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
350
src/platform.h
Normal file
350
src/platform.h
Normal file
@ -0,0 +1,350 @@
|
||||
/* -*- c++ -*- ----------------------------------------------------------
|
||||
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
|
||||
https://www.lammps.org/, Sandia National Laboratories
|
||||
Steve Plimpton, sjplimp@sandia.gov
|
||||
|
||||
Copyright (2003) Sandia Corporation. Under the terms of Contract
|
||||
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
|
||||
certain rights in this software. This software is distributed under
|
||||
the GNU General Public License.
|
||||
|
||||
See the README file in the top-level LAMMPS directory.
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
#ifndef LMP_PLATFORM_H
|
||||
#define LMP_PLATFORM_H
|
||||
|
||||
/*! \file platform.h */
|
||||
|
||||
#include "lmptype.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace LAMMPS_NS {
|
||||
namespace platform {
|
||||
|
||||
/*! Return the consumed CPU time for the current process in seconds
|
||||
*
|
||||
* This is a wrapper about the POSIX function getrusage() and the Windows equivalent.
|
||||
* It is to be used in a similar fashion than MPI_Wtime().
|
||||
*
|
||||
* \return used CPU time in second */
|
||||
|
||||
double cputime();
|
||||
|
||||
/*! Return the wall clock state for the current process in seconds
|
||||
*
|
||||
* This is a wrapper about XXX and its Windows equivalent.
|
||||
* It is to be used in a similar fashion than MPI_Wtime().
|
||||
*
|
||||
* \return wall clock time in second */
|
||||
|
||||
double walltime();
|
||||
|
||||
/*! Return string with the Operating system version and architecture info.
|
||||
*
|
||||
* \return string with info about the OS and the platform is is running on */
|
||||
|
||||
std::string os_info();
|
||||
|
||||
/*! Return string with C++ standard version used to compile LAMMPS.
|
||||
*
|
||||
* This function uses predefined compiler macros to identify
|
||||
* the C++ standard version used to compile LAMMPS with.
|
||||
*
|
||||
* \return string with the C++ standard version or "unknown" */
|
||||
|
||||
std::string cxx_standard();
|
||||
|
||||
/*! Return string with compiler version info
|
||||
*
|
||||
* This function uses predefined compiler macros to identify
|
||||
* Compilers and their version and configuration info.
|
||||
*
|
||||
* \return string with the compiler information text */
|
||||
|
||||
std::string compiler_info();
|
||||
|
||||
/*! Return string with OpenMP standard version info
|
||||
*
|
||||
* This function uses predefined compiler macros to identify
|
||||
* OpenMP support and the supported version of the standard.
|
||||
*
|
||||
* \return string with the openmp information text */
|
||||
|
||||
std::string openmp_standard();
|
||||
|
||||
/*! Return string with MPI vendor info
|
||||
*
|
||||
* This function uses predefined macros to identify
|
||||
* the vendor of the MPI library used.
|
||||
*
|
||||
* \return string with the MPI vendor information text */
|
||||
|
||||
std::string mpi_vendor();
|
||||
|
||||
/*! Return string with MPI version info
|
||||
*
|
||||
* This function uses predefined macros and MPI function
|
||||
* calls to identify the version of the MPI library used.
|
||||
*
|
||||
* \param major major version of the MPI standard (set on exit)
|
||||
* \param minor minor version of the MPI standard (set on exit)
|
||||
* \return string with the MPI version information text */
|
||||
|
||||
std::string mpi_info(int &major, int &minor);
|
||||
|
||||
/*! Add variable to the environment
|
||||
*
|
||||
* \param vardef variable name or variable definition (NAME=value)
|
||||
* \return -1 if failure otherwise 0 */
|
||||
|
||||
int putenv(const std::string &vardef);
|
||||
|
||||
/*! Get list of entries in a path environment variable
|
||||
*
|
||||
* This provides a list of strings of the entries in an environment
|
||||
* variable that is containing a "path" like "PATH" or "LD_LIBRARY_PATH".
|
||||
*
|
||||
* \param var name of the environment variable
|
||||
* \return vector with strings of all entries in that path variable */
|
||||
|
||||
std::vector<std::string> list_pathenv(const std::string &var);
|
||||
|
||||
/*! Open a shared object file or library
|
||||
*
|
||||
* \param fname name or path of the shared object
|
||||
* \return handle to the shared object or null */
|
||||
|
||||
void *dlopen(const std::string &fname);
|
||||
|
||||
/*! Close a shared object
|
||||
* This releases the object corresponding to the provided handle.
|
||||
* Resolved symbols associated with this handle may not be used
|
||||
* after this call
|
||||
*
|
||||
* \param handle handle to an opened shared object
|
||||
* \return 0 if succesful, non-zero of not */
|
||||
|
||||
int dlclose(void *handle);
|
||||
|
||||
/*! Resolve a symbol in shared object
|
||||
*
|
||||
* \param handle handle to an opened shared object
|
||||
* \param symbol name of the symbol to extract
|
||||
* \return pointer to the resolved symbol or null */
|
||||
|
||||
void *dlsym(void *handle, const std::string &symbol);
|
||||
|
||||
/*! File path component separators
|
||||
* These are the characters that separate directories and filename in paths on
|
||||
* a platform. If multiple are provided, the first is the preferred one. */
|
||||
|
||||
#if defined(_WIN32)
|
||||
constexpr char filepathsep[] = "\\/";
|
||||
#else
|
||||
constexpr char filepathsep[] = "/";
|
||||
#endif
|
||||
|
||||
/*! Path environment variable component separator
|
||||
* This is the character that separates entries in "PATH" environment variables. */
|
||||
|
||||
#if defined(_WIN32)
|
||||
constexpr char pathvarsep = ';';
|
||||
#else
|
||||
constexpr char pathvarsep = ':';
|
||||
#endif
|
||||
|
||||
/*! Try to detect pathname from FILE pointer.
|
||||
*
|
||||
* Currently only supported on Linux and macOS, otherwise will report "(unknown)".
|
||||
*
|
||||
* \param fp FILE pointer struct from STDIO library for which we want to detect the name
|
||||
* \param buf storage buffer for pathname. output will be truncated if not large enough
|
||||
* \param len size of storage buffer. output will be truncated to this length - 1
|
||||
* \return pointer to the storage buffer with path or a NULL pointer if buf is invalid
|
||||
* or the buffer size is too small */
|
||||
|
||||
const char *guesspath(FILE *fp, char *buf, int len);
|
||||
|
||||
/*! Check if a file pointer may be connected to a console
|
||||
*
|
||||
* \param fp file pointer
|
||||
* \return true if the file pointer is flagged as a TTY */
|
||||
|
||||
bool is_console(FILE *fp);
|
||||
|
||||
/*! Get string with path to the current directory
|
||||
*
|
||||
* \return path to the current directory or empty string */
|
||||
|
||||
std::string current_directory();
|
||||
|
||||
/*! Check if a path is a directory
|
||||
*
|
||||
* \param path directory path
|
||||
* \return true if the directory exists */
|
||||
|
||||
bool path_is_directory(const std::string &path);
|
||||
|
||||
/*! Get list of entries in a directory
|
||||
*
|
||||
* This provides a list of strings of the entries in the directory
|
||||
* without the leading path name while also skipping over ".." and ".".
|
||||
*
|
||||
* \param path path to directory
|
||||
* \return vector with strings of all directory entries */
|
||||
|
||||
std::vector<std::string> list_directory(const std::string &dir);
|
||||
|
||||
/*! Find pathname of an executable in the standard search path
|
||||
*
|
||||
* This function will traverse the list of directories in the PATH
|
||||
* environment variable and look for the executable *cmd*. If the
|
||||
* file exists and is executable the full path is returned as string,
|
||||
* otherwise and emptry string is returned.
|
||||
*
|
||||
* On Windows the *cmd* string must not include and extension as
|
||||
* this function will automatically append the extensions ".exe",
|
||||
* ".com" and ".bat" and look for those paths. On Windows also the
|
||||
* current directory is checked (and first), while otherwise not unless
|
||||
* "." exists in the PATH environment variable.
|
||||
*
|
||||
* Because of the nature of the check, this will not detect shell functions
|
||||
* built-in command or aliases.
|
||||
*
|
||||
* \param cmd name of command
|
||||
* \return vector with strings of all directory entries */
|
||||
|
||||
std::string find_exe_path(const std::string &cmd);
|
||||
|
||||
/*! Change current directory
|
||||
*
|
||||
* \param path new current working directory path
|
||||
* \return -1 if unsuccessful, otherwise >= 0 */
|
||||
|
||||
int chdir(const std::string &path);
|
||||
|
||||
/*! Create a directory
|
||||
*
|
||||
* \param path directory path
|
||||
* \return -1 if unsuccessful, otherwise >= 0 */
|
||||
|
||||
int mkdir(const std::string &path);
|
||||
|
||||
/*! Delete a directory
|
||||
*
|
||||
* \param path directory path
|
||||
* \return -1 if unsuccessful, otherwise >= 0 */
|
||||
|
||||
int rmdir(const std::string &path);
|
||||
|
||||
/*! Delete a directory and its contents
|
||||
*
|
||||
* Unlike the the ``rmdir()`` or ``_rmdir()`` function of the
|
||||
* C library, this function will check for the contents of the
|
||||
* folder and recurse into any sub-folders, if necessary and
|
||||
* delete all contained folders and their contents before
|
||||
* deleting the folder *path*.
|
||||
*
|
||||
* \param path path to file to be deleted
|
||||
* \return 0 on success, -1 on error */
|
||||
|
||||
int unlink(const std::string &path);
|
||||
|
||||
/*! Get current file position
|
||||
*
|
||||
* \param fp FILE pointer of the given file
|
||||
* \return current FILE pointer position cast to a bigint */
|
||||
|
||||
bigint ftell(FILE *fp);
|
||||
|
||||
/*! Set absolute file position
|
||||
*
|
||||
* \param fp FILE pointer of the given file
|
||||
* \param pos new position of the FILE pointer
|
||||
* \return 0 if successful, otherwise -1 */
|
||||
|
||||
int fseek(FILE *fp, bigint pos);
|
||||
|
||||
/*! Truncate file to a given length and reposition file pointer
|
||||
*
|
||||
* \param fp FILE pointer of the given file
|
||||
* \param length length to which the file is being truncated to
|
||||
* \return 0 if successful, otherwise -1 */
|
||||
|
||||
int ftruncate(FILE *fp, bigint length);
|
||||
|
||||
/*! Open a pipe to a command for reading or writing
|
||||
*
|
||||
* \param cmd command for the pipe
|
||||
* \param mode "r" for reading from *cmd* or "w" for writing to *cmd*
|
||||
* \return file pointer to the pipe if successful or null */
|
||||
|
||||
FILE *popen(const std::string &cmd, const std::string &mode);
|
||||
|
||||
/*! Close a previously opened pipe
|
||||
*
|
||||
* \param fp FILE pointer for the pipe
|
||||
* \return exit status of the pipe command or -1 in case of errors */
|
||||
|
||||
int pclose(FILE *fp);
|
||||
|
||||
/*! Strip off leading part of path, return just the filename
|
||||
*
|
||||
* \param path file path
|
||||
* \return file name */
|
||||
|
||||
std::string path_basename(const std::string &path);
|
||||
|
||||
/*! Return the directory part of a path. Return "." if empty
|
||||
*
|
||||
* \param path file path
|
||||
* \return directory name */
|
||||
|
||||
std::string path_dirname(const std::string &path);
|
||||
|
||||
/*! Join two pathname segments
|
||||
*
|
||||
* This uses the forward slash '/' character unless LAMMPS is compiled
|
||||
* for Windows where it used the equivalent backward slash '\\'.
|
||||
*
|
||||
* \param a first path
|
||||
* \param b second path
|
||||
* \return combined path */
|
||||
|
||||
std::string path_join(const std::string &a, const std::string &b);
|
||||
|
||||
/*! Check if file exists and is readable
|
||||
*
|
||||
* \param path file path
|
||||
* \return true if file exists and is readable */
|
||||
|
||||
bool file_is_readable(const std::string &path);
|
||||
|
||||
/*! Check if a file name ends in a known extension for a compressed file format
|
||||
* Currently supported extensions are: .Z, .gz, .bz2, .zstd, .xz, .lzma
|
||||
*
|
||||
* \param file name of the file to check
|
||||
* \return true if the file has a known extension, otherwise false */
|
||||
|
||||
bool has_zip_extension(const std::string &file);
|
||||
|
||||
/*! Open pipe to compressed text file for reading.
|
||||
*
|
||||
* \param file name of the file to open
|
||||
* \return FILE pointer to pipe using for reading the compressed file. */
|
||||
|
||||
FILE *zip_read(const std::string &file);
|
||||
|
||||
/*! Open pipe to compressed text file for writing.
|
||||
*
|
||||
* \param file name of the file to open
|
||||
* \return FILE pointer to pipe using for reading the compressed file. */
|
||||
|
||||
FILE *zip_write(const std::string &file);
|
||||
|
||||
} // namespace platform
|
||||
} // namespace LAMMPS_NS
|
||||
#endif
|
||||
@ -15,6 +15,22 @@ target_link_libraries(test_utils PRIVATE lammps GTest::GMockMain GTest::GMock GT
|
||||
add_test(Utils test_utils)
|
||||
set_tests_properties(Utils PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR}")
|
||||
|
||||
add_executable(test_platform test_platform.cpp)
|
||||
target_link_libraries(test_platform PRIVATE lammps GTest::GMockMain GTest::GMock GTest::GTest)
|
||||
add_test(Platform test_platform)
|
||||
set(PLATFORM_ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR}")
|
||||
if(BUILD_SHARED_LIBS)
|
||||
enable_language(C)
|
||||
target_compile_definitions(test_platform PRIVATE -DTEST_SHARED_OBJECT=1)
|
||||
add_library(testsharedobj MODULE testshared.c)
|
||||
set_target_properties(testsharedobj PROPERTIES PREFIX "" WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
|
||||
add_library(testsharedlib SHARED testshared.c)
|
||||
set_target_properties(testsharedlib PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
|
||||
add_dependencies(test_platform testsharedobj testsharedlib)
|
||||
list(APPEND PLATFORM_ENVIRONMENT "TEST_SHARED_LIB=$<TARGET_FILE:testsharedlib>" "TEST_SHARED_OBJ=$<TARGET_FILE:testsharedobj>")
|
||||
endif()
|
||||
set_tests_properties(Platform PROPERTIES ENVIRONMENT ${PLATFORM_ENVIRONMENT} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
add_executable(test_fmtlib test_fmtlib.cpp)
|
||||
target_link_libraries(test_fmtlib PRIVATE lammps GTest::GMockMain GTest::GMock GTest::GTest)
|
||||
add_test(FmtLib test_fmtlib)
|
||||
|
||||
395
unittest/utils/test_platform.cpp
Normal file
395
unittest/utils/test_platform.cpp
Normal file
@ -0,0 +1,395 @@
|
||||
|
||||
#include "platform.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
using namespace LAMMPS_NS;
|
||||
using testing::EndsWith;
|
||||
using testing::Eq;
|
||||
using testing::IsEmpty;
|
||||
using testing::StartsWith;
|
||||
using testing::StrEq;
|
||||
|
||||
TEST(Platform, clock)
|
||||
{
|
||||
const double wt_start = platform::walltime();
|
||||
const double ct_start = platform::cputime();
|
||||
|
||||
// spend some time computing pi
|
||||
constexpr double known_pi = 3.141592653589793238462643;
|
||||
constexpr int n = 10000000;
|
||||
constexpr double h = 1.0 / (double) n;
|
||||
double my_pi = 0.0, x;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
x = h * ((double) i + 0.5);
|
||||
my_pi += 4.0 / (1.0 + x * x);
|
||||
}
|
||||
my_pi *= h;
|
||||
const double wt_used = platform::walltime() - wt_start;
|
||||
const double ct_used = platform::cputime() - ct_start;
|
||||
|
||||
ASSERT_NEAR(my_pi, known_pi, 1e-12);
|
||||
ASSERT_GT(wt_used, 1e-4);
|
||||
ASSERT_GT(ct_used, 1e-4);
|
||||
}
|
||||
|
||||
TEST(Platform, putenv)
|
||||
{
|
||||
const char *var = getenv("UNITTEST_VAR1");
|
||||
ASSERT_EQ(var, nullptr);
|
||||
int rv = platform::putenv("UNITTEST_VAR1");
|
||||
var = getenv("UNITTEST_VAR1");
|
||||
ASSERT_EQ(rv, 0);
|
||||
ASSERT_NE(var, nullptr);
|
||||
// we cannot set environment variables without a value on windows with _putenv()
|
||||
#if defined(_WIN32)
|
||||
ASSERT_THAT(var, StrEq("1"));
|
||||
#else
|
||||
ASSERT_THAT(var, StrEq(""));
|
||||
#endif
|
||||
|
||||
rv = platform::putenv("UNITTEST_VAR1=one");
|
||||
var = getenv("UNITTEST_VAR1");
|
||||
ASSERT_EQ(rv, 0);
|
||||
ASSERT_NE(var, nullptr);
|
||||
ASSERT_THAT(var, StrEq("one"));
|
||||
|
||||
rv = platform::putenv("UNITTEST_VAR1=one=two");
|
||||
var = getenv("UNITTEST_VAR1");
|
||||
ASSERT_EQ(rv, 0);
|
||||
ASSERT_NE(var, nullptr);
|
||||
ASSERT_THAT(var, StrEq("one=two"));
|
||||
|
||||
ASSERT_EQ(platform::putenv(""), -1);
|
||||
}
|
||||
|
||||
TEST(Platform, list_pathenv)
|
||||
{
|
||||
auto dirs = platform::list_pathenv("PATH");
|
||||
ASSERT_GT(dirs.size(), 1);
|
||||
}
|
||||
|
||||
TEST(Platform, find_cmd_path)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
ASSERT_THAT(platform::find_exe_path("notepad"), EndsWith("\\notepad.exe"));
|
||||
ASSERT_THAT(platform::find_exe_path("cmd"), EndsWith("\\cmd.exe"));
|
||||
ASSERT_THAT(platform::find_exe_path("some_bogus_command"), IsEmpty());
|
||||
#else
|
||||
ASSERT_THAT(platform::find_exe_path("ls"), EndsWith("bin/ls"));
|
||||
ASSERT_THAT(platform::find_exe_path("sh"), EndsWith("bin/sh"));
|
||||
ASSERT_THAT(platform::find_exe_path("some_bogus_command"), IsEmpty());
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(Platform, sharedload)
|
||||
{
|
||||
const char *objs[] = {"TEST_SHARED_OBJ", "TEST_SHARED_LIB", nullptr};
|
||||
const char *envvar, **envptr;
|
||||
const int *intvar;
|
||||
const double *doublevar;
|
||||
void *handle;
|
||||
int (*intfunc)(int);
|
||||
double (*doublefunc)(double, int);
|
||||
|
||||
for (envptr = objs; *envptr != nullptr; ++envptr) {
|
||||
envvar = getenv(*envptr);
|
||||
EXPECT_NE(envvar, nullptr);
|
||||
handle = platform::dlopen(envvar);
|
||||
EXPECT_NE(handle, nullptr);
|
||||
intvar = (int *) platform::dlsym(handle, "some_int_val");
|
||||
EXPECT_NE(intvar, nullptr);
|
||||
EXPECT_EQ(*intvar, 12345);
|
||||
doublevar = (double *) platform::dlsym(handle, "some_double_val");
|
||||
EXPECT_NE(doublevar, nullptr);
|
||||
EXPECT_DOUBLE_EQ(*doublevar, 6.78e-9);
|
||||
intfunc = (int (*)(int)) platform::dlsym(handle, "some_int_function");
|
||||
EXPECT_NE(intfunc, nullptr);
|
||||
EXPECT_EQ((*intfunc)(12), 144);
|
||||
doublefunc = (double (*)(double, int)) platform::dlsym(handle, "some_double_function");
|
||||
EXPECT_NE(doublefunc, nullptr);
|
||||
EXPECT_DOUBLE_EQ((*doublefunc)(0.5, 6), 3.0);
|
||||
EXPECT_EQ(platform::dlsym(handle, "some_nonexisting_symbol"), nullptr);
|
||||
EXPECT_EQ(platform::dlclose(handle), 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Platform, guesspath)
|
||||
{
|
||||
char buf[256];
|
||||
FILE *fp = fopen("test_guesspath.txt", "w");
|
||||
#if defined(__linux__) || defined(__APPLE__) || defined(_WIN32)
|
||||
const char *path = platform::guesspath(fp, buf, sizeof(buf));
|
||||
ASSERT_THAT(path, EndsWith("test_guesspath.txt"));
|
||||
#else
|
||||
const char *path = platform::guesspath(fp, buf, sizeof(buf));
|
||||
ASSERT_THAT(path, EndsWith("(unknown)"));
|
||||
#endif
|
||||
fclose(fp);
|
||||
platform::unlink("test_guesspath.txt");
|
||||
}
|
||||
|
||||
TEST(Platform, unlink)
|
||||
{
|
||||
const char test[] = "12345678901234567890";
|
||||
platform::unlink("unlink.dat");
|
||||
ASSERT_EQ(platform::unlink("dummy.dat"), -1);
|
||||
FILE *fp = fopen("unlink.dat", "w");
|
||||
fwrite(test, sizeof(test), 1, fp);
|
||||
fclose(fp);
|
||||
ASSERT_EQ(platform::unlink("unlink.dat"), 0);
|
||||
ASSERT_EQ(platform::unlink("unlink.dat"), -1);
|
||||
fp = fopen("unlink.dat", "r");
|
||||
ASSERT_EQ(fp, nullptr);
|
||||
|
||||
platform::mkdir("unlink.dir");
|
||||
ASSERT_EQ(platform::unlink("unlink.dir"), -1);
|
||||
platform::rmdir("unlink.dir");
|
||||
}
|
||||
|
||||
TEST(Platform, fseek_ftell)
|
||||
{
|
||||
const char test[] = "12345678901234567890";
|
||||
platform::unlink("seek_tell.dat");
|
||||
FILE *fp = fopen("seek_tell.dat", "w");
|
||||
fwrite(test, sizeof(test), 1, fp);
|
||||
fflush(fp);
|
||||
ASSERT_EQ(platform::ftell(fp), sizeof(test));
|
||||
fclose(fp);
|
||||
fp = fopen("seek_tell.dat", "r+");
|
||||
ASSERT_EQ(fgetc(fp), '1');
|
||||
ASSERT_EQ(fgetc(fp), '2');
|
||||
ASSERT_EQ(platform::ftell(fp), 2);
|
||||
ASSERT_EQ(platform::fseek(fp, 15), 0);
|
||||
ASSERT_EQ(fgetc(fp), '6');
|
||||
fflush(fp);
|
||||
fseek(fp, -1, SEEK_END);
|
||||
ASSERT_EQ(fgetc(fp), 0);
|
||||
ASSERT_EQ(platform::ftell(fp), 21);
|
||||
fclose(fp);
|
||||
platform::unlink("seek_tell.dat");
|
||||
}
|
||||
|
||||
TEST(Platform, ftruncate)
|
||||
{
|
||||
platform::unlink("truncate.dat");
|
||||
FILE *fp = fopen("truncate.dat", "w");
|
||||
fputs("header one\n", fp);
|
||||
fputs("header two\n", fp);
|
||||
fflush(fp);
|
||||
bigint filepos = platform::ftell(fp);
|
||||
fputs("line one\n", fp);
|
||||
fputs("line two\n", fp);
|
||||
fputs("line three\n", fp);
|
||||
fflush(fp);
|
||||
ASSERT_EQ(platform::ftruncate(fp, filepos), 0);
|
||||
fputs("line four\n", fp);
|
||||
ASSERT_GT(platform::ftell(fp), filepos);
|
||||
fputs("line five\n", fp);
|
||||
fflush(fp);
|
||||
fclose(fp);
|
||||
|
||||
// check file
|
||||
fp = fopen("truncate.dat", "r");
|
||||
char buf[128];
|
||||
char *ptr = fgets(buf, 127, fp);
|
||||
ASSERT_THAT(ptr, StartsWith("header one"));
|
||||
ptr = fgets(buf, 127, fp);
|
||||
ASSERT_THAT(ptr, StartsWith("header two"));
|
||||
ptr = fgets(buf, 127, fp);
|
||||
ASSERT_THAT(ptr, StartsWith("line four"));
|
||||
ptr = fgets(buf, 127, fp);
|
||||
ASSERT_THAT(ptr, StartsWith("line five"));
|
||||
ptr = fgets(buf, 127, fp);
|
||||
ASSERT_EQ(ptr, nullptr);
|
||||
fclose(fp);
|
||||
platform::unlink("truncate.dat");
|
||||
}
|
||||
|
||||
TEST(Platform, path_basename)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
ASSERT_THAT(platform::path_basename("c:\\parent\\folder\\filename"), Eq("filename"));
|
||||
ASSERT_THAT(platform::path_basename("folder\\"), Eq(""));
|
||||
ASSERT_THAT(platform::path_basename("c:/parent/folder/filename"), Eq("filename"));
|
||||
#else
|
||||
ASSERT_THAT(platform::path_basename("/parent/folder/filename"), Eq("filename"));
|
||||
ASSERT_THAT(platform::path_basename("/parent/folder/"), Eq(""));
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(Platform, path_dirname)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
ASSERT_THAT(platform::path_dirname("c:/parent/folder/filename"), Eq("c:/parent/folder"));
|
||||
ASSERT_THAT(platform::path_dirname("c:\\parent\\folder\\filename"), Eq("c:\\parent\\folder"));
|
||||
ASSERT_THAT(platform::path_dirname("c:filename"), Eq("."));
|
||||
#else
|
||||
ASSERT_THAT(platform::path_dirname("/parent/folder/filename"), Eq("/parent/folder"));
|
||||
#endif
|
||||
ASSERT_THAT(platform::path_dirname("filename"), Eq("."));
|
||||
}
|
||||
|
||||
TEST(Platform, path_join)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
ASSERT_THAT(platform::path_join("c:\\folder", "filename"), Eq("c:\\folder\\filename"));
|
||||
ASSERT_THAT(platform::path_join("c:\\folder\\", "filename"), Eq("c:\\folder\\filename"));
|
||||
ASSERT_THAT(platform::path_join("c:\\folder", "\\filename"), Eq("c:\\folder\\filename"));
|
||||
ASSERT_THAT(platform::path_join("c:\\folder\\", "\\filename"), Eq("c:\\folder\\filename"));
|
||||
ASSERT_THAT(platform::path_join("c:\\folder", "/filename"), Eq("c:\\folder\\filename"));
|
||||
ASSERT_THAT(platform::path_join("c:\\folder\\\\", "\\filename"), Eq("c:\\folder\\filename"));
|
||||
ASSERT_THAT(platform::path_join("c:\\folder\\", "\\\\filename"), Eq("c:\\folder\\filename"));
|
||||
ASSERT_THAT(platform::path_join("c:\\folder/\\", "/\\filename"), Eq("c:\\folder\\filename"));
|
||||
ASSERT_THAT(platform::path_join("c:\\folder\\/", "\\/filename"), Eq("c:\\folder\\filename"));
|
||||
ASSERT_THAT(platform::path_join("c:\\folder", ""), Eq("c:\\folder"));
|
||||
ASSERT_THAT(platform::path_join("", "\\/filename"), Eq("\\/filename"));
|
||||
#else
|
||||
ASSERT_THAT(platform::path_join("/parent/folder", "filename"), Eq("/parent/folder/filename"));
|
||||
ASSERT_THAT(platform::path_join("/parent/folder/", "filename"), Eq("/parent/folder/filename"));
|
||||
ASSERT_THAT(platform::path_join("/parent/folder", "/filename"), Eq("/parent/folder/filename"));
|
||||
ASSERT_THAT(platform::path_join("/parent/folder/", "/filename"), Eq("/parent/folder/filename"));
|
||||
ASSERT_THAT(platform::path_join("/parent/folder//", "filename"), Eq("/parent/folder/filename"));
|
||||
ASSERT_THAT(platform::path_join("/parent/folder", "//filename"), Eq("/parent/folder/filename"));
|
||||
ASSERT_THAT(platform::path_join("/parent/folder///", "/filename"), Eq("/parent/folder/filename"));
|
||||
ASSERT_THAT(platform::path_join("/parent/folder/", "///filename"), Eq("/parent/folder/filename"));
|
||||
ASSERT_THAT(platform::path_join("/parent/folder/", ""), Eq("/parent/folder/"));
|
||||
ASSERT_THAT(platform::path_join("", "\\/filename"), Eq("\\/filename"));
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(Platform, is_console)
|
||||
{
|
||||
platform::unlink("file_is_no_console.txt");
|
||||
FILE *fp = fopen("file_is_no_console.txt", "w");
|
||||
fputs("some text\n", fp);
|
||||
EXPECT_FALSE(platform::is_console(fp));
|
||||
fclose(fp);
|
||||
platform::unlink("file_is_no_console.txt");
|
||||
}
|
||||
|
||||
TEST(Platform, path_and_directory)
|
||||
{
|
||||
platform::unlink("path_is_directory");
|
||||
platform::rmdir("path_is_directory");
|
||||
platform::unlink("path_is_file");
|
||||
platform::mkdir("path_is_directory");
|
||||
FILE *fp = fopen("path_is_file", "w");
|
||||
fputs("some text\n", fp);
|
||||
fclose(fp);
|
||||
|
||||
ASSERT_TRUE(platform::path_is_directory("path_is_directory"));
|
||||
ASSERT_FALSE(platform::path_is_directory("path_is_file"));
|
||||
ASSERT_FALSE(platform::path_is_directory("path_does_not_exist"));
|
||||
platform::unlink("path_is_file");
|
||||
|
||||
#if defined(_WIN32)
|
||||
fp = fopen("path_is_directory\\path_is_file", "w");
|
||||
#else
|
||||
fp = fopen("path_is_directory/path_is_file", "w");
|
||||
#endif
|
||||
fputs("some text\n", fp);
|
||||
fclose(fp);
|
||||
#if defined(_WIN32)
|
||||
platform::mkdir("path_is_directory\\path_is_directory");
|
||||
fp = fopen("path_is_directory\\path_is_other_file", "w");
|
||||
#else
|
||||
platform::mkdir("path_is_directory/path_is_directory");
|
||||
fp = fopen("path_is_directory/path_is_other_file", "w");
|
||||
#endif
|
||||
fputs("some text\n", fp);
|
||||
fclose(fp);
|
||||
auto dirs = platform::list_directory("path_is_directory");
|
||||
ASSERT_EQ(dirs.size(), 3);
|
||||
platform::rmdir("path_is_directory");
|
||||
ASSERT_FALSE(platform::path_is_directory("path_is_directory"));
|
||||
}
|
||||
|
||||
TEST(Platform, get_change_directory)
|
||||
{
|
||||
platform::unlink("working_directory");
|
||||
platform::rmdir("working_directory");
|
||||
|
||||
auto cwd = platform::current_directory();
|
||||
ASSERT_GT(cwd.size(), 0);
|
||||
|
||||
platform::mkdir("working_directory");
|
||||
ASSERT_EQ(platform::chdir("working_directory"), 0);
|
||||
ASSERT_THAT(platform::current_directory(), EndsWith("working_directory"));
|
||||
|
||||
ASSERT_EQ(platform::chdir(".."), 0);
|
||||
ASSERT_THAT(platform::current_directory(), StrEq(cwd));
|
||||
platform::rmdir("working_directory");
|
||||
}
|
||||
|
||||
TEST(Platform, file_is_readable)
|
||||
{
|
||||
platform::unlink("file_is_readable.txt");
|
||||
FILE *fp = fopen("file_is_readable.txt", "w");
|
||||
fputs("some text\n", fp);
|
||||
fclose(fp);
|
||||
|
||||
ASSERT_TRUE(platform::file_is_readable("file_is_readable.txt"));
|
||||
ASSERT_FALSE(platform::file_is_readable("file_does_not_exist.txt"));
|
||||
platform::unlink("file_is_readable.txt");
|
||||
|
||||
// windows does not have permission flags
|
||||
#if !defined(_WIN32)
|
||||
platform::unlink("file_is_not_readable.txt");
|
||||
fp = fopen("file_is_not_readable.txt", "w");
|
||||
fputs("some text\n", fp);
|
||||
fclose(fp);
|
||||
chmod("file_is_not_readable.txt", 0);
|
||||
ASSERT_FALSE(platform::file_is_readable("file_is_not_readable.txt"));
|
||||
platform::unlink("file_is_not_readable.txt");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(Platform, has_zip_extension)
|
||||
{
|
||||
ASSERT_FALSE(platform::has_zip_extension("dummy"));
|
||||
ASSERT_FALSE(platform::has_zip_extension("dum.my"));
|
||||
ASSERT_TRUE(platform::has_zip_extension("dummy.gz"));
|
||||
ASSERT_TRUE(platform::has_zip_extension("dummy.bz2"));
|
||||
ASSERT_TRUE(platform::has_zip_extension("dummy.zstd"));
|
||||
ASSERT_TRUE(platform::has_zip_extension("dummy.xz"));
|
||||
ASSERT_TRUE(platform::has_zip_extension("dummy.lzma"));
|
||||
ASSERT_TRUE(platform::has_zip_extension("dummy.lz4"));
|
||||
}
|
||||
|
||||
TEST(Platform, zip_read_write)
|
||||
{
|
||||
const std::vector<std::string> test_files = {"zip_test.zip", "zip_test.gz", "zip_test.bz2",
|
||||
"zip_test.zstd", "zip_test.xz", "zip_test.lzma",
|
||||
"zip_test.lz4", "zip_test.unk", "zip test.gz"};
|
||||
for (const auto &file : test_files) {
|
||||
platform::unlink(file);
|
||||
FILE *fp = platform::zip_write(file);
|
||||
if (!fp) {
|
||||
platform::unlink(file);
|
||||
continue;
|
||||
}
|
||||
|
||||
clearerr(fp);
|
||||
fputs("line one\n", fp);
|
||||
fputs("line two\n", fp);
|
||||
ASSERT_EQ(ferror(fp), 0);
|
||||
fflush(fp);
|
||||
platform::pclose(fp);
|
||||
|
||||
fp = platform::zip_read(file);
|
||||
ASSERT_NE(fp, nullptr);
|
||||
char buf[128];
|
||||
char *ptr = fgets(buf, 128, fp);
|
||||
EXPECT_THAT(ptr, StartsWith("line one"));
|
||||
ptr = fgets(buf, 128, fp);
|
||||
EXPECT_THAT(ptr, StartsWith("line two"));
|
||||
ASSERT_EQ(ferror(fp), 0);
|
||||
platform::pclose(fp);
|
||||
platform::unlink(file);
|
||||
}
|
||||
}
|
||||
20
unittest/utils/testshared.c
Normal file
20
unittest/utils/testshared.c
Normal file
@ -0,0 +1,20 @@
|
||||
/* file for testing loading of shared objects and libraries */
|
||||
|
||||
int some_int_val = 12345;
|
||||
double some_double_val = 6.78e-9;
|
||||
|
||||
int some_int_function(int arg)
|
||||
{
|
||||
return arg*arg;
|
||||
}
|
||||
|
||||
double some_double_function(double arg1, int arg2)
|
||||
{
|
||||
double sum = 0;
|
||||
for (int i = 0; i < arg2; ++i)
|
||||
sum += arg1;
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user