import files for platform namespace from standalone project w/o updating LAMMPS

This commit is contained in:
Axel Kohlmeyer
2021-09-29 16:29:25 -04:00
parent 9e49a934c2
commit 15b3e875d5
5 changed files with 1728 additions and 0 deletions

947
src/platform.cpp Normal file
View 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
View 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

View File

@ -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)

View 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);
}
}

View 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;
}