23 #include "casadi_os.hpp"
24 #include "exception.hpp"
25 #include "global_options.hpp"
29 #ifdef CASADI_WITH_THREAD
30 #ifdef CASADI_WITH_THREAD_MINGW
31 #include <mingw.mutex.h>
44 extern char **environ;
60 #define STRING(ITEMS) \
61 ((dynamic_cast<std::ostringstream &>(std::ostringstream() \
62 . seekp(0, std::ios_base::cur) << (ITEMS))) . str())
82 std::vector<std::string> search_paths;
87 char* pPLUGIN = getenv(
"CASADI_PLUGIN_SEARCH_PATH");
88 if (pPLUGIN!=
nullptr) {
89 std::stringstream pluginpaths(pPLUGIN);
90 std::string pluginpath;
91 while (std::getline(pluginpaths, pluginpath,
pathsep())) {
92 search_paths.push_back(pluginpath);
98 std::string casadipath;
99 while (std::getline(casadipaths, casadipath,
pathsep())) {
100 search_paths.push_back(casadipath);
105 pLIBDIR = getenv(
"CASADIPATH");
107 if (pLIBDIR!=
nullptr) {
108 std::stringstream casadipaths(pLIBDIR);
109 std::string casadipath;
110 while (std::getline(casadipaths, casadipath,
pathsep())) {
111 search_paths.push_back(casadipath);
116 search_paths.push_back(
"");
119 #ifdef PLUGIN_EXTRA_SEARCH_PATH
120 search_paths.push_back(
121 std::string(
"") + PLUGIN_EXTRA_SEARCH_PATH);
125 search_paths.push_back(
".");
132 std::wstring utf8_to_utf16(
const std::string& s);
139 #if !defined(__APPLE__) && !defined(__EMSCRIPTEN__)
157 char** environ_snapshot =
nullptr;
158 std::size_t environ_snapshot_n = 0;
159 #ifdef CASADI_WITH_THREAD
160 std::mutex environ_snapshot_mutex;
163 void publish_environ_snapshot() {
164 char*** slot =
reinterpret_cast<char***
>(dlsym(RTLD_NEXT,
"environ"));
165 if (!slot || slot == &environ)
return;
166 #ifdef CASADI_WITH_THREAD
167 std::lock_guard<std::mutex> lock(environ_snapshot_mutex);
170 if (environ)
while (environ[n]) ++n;
171 if (environ_snapshot && n == environ_snapshot_n &&
172 std::memcmp(environ_snapshot, environ, n *
sizeof(
char*)) == 0) {
173 *slot = environ_snapshot;
176 char** fresh =
static_cast<char**
>(std::malloc((n + 1) *
sizeof(
char*)));
178 if (n) std::memcpy(fresh, environ, n *
sizeof(
char*));
180 environ_snapshot = fresh;
181 environ_snapshot_n = n;
182 *slot = environ_snapshot;
190 handle_t open_shared_library(
const std::string& lib,
const std::vector<std::string> &search_paths,
191 const std::string& caller,
bool global) {
192 std::string resultpath;
193 return open_shared_library(lib, search_paths, resultpath, caller, global);
196 int close_shared_library(
handle_t handle) {
198 return !FreeLibrary(handle);
200 return dlclose(handle);
204 handle_t open_shared_library(
const std::string& lib,
const std::vector<std::string> &search_paths,
205 std::string& resultpath,
const std::string& caller,
bool global) {
213 flag = RTLD_NOW | RTLD_GLOBAL;
215 flag = RTLD_LAZY | RTLD_LOCAL;
218 #if !defined(__APPLE__) && !defined(__EMSCRIPTEN__)
219 flag |= RTLD_DEEPBIND;
224 publish_environ_snapshot();
232 std::stringstream errors;
233 errors << caller <<
": Cannot load shared library '"
234 << lib <<
"': " << std::endl;
236 <<
" Searched directories: 1. CASADI_PLUGIN_SEARCH_PATH env var\n"
237 <<
" 2. casadipath from GlobalOptions\n"
238 <<
" 3. CASADIPATH env var\n"
239 <<
" 4. PATH env var (Windows)\n"
240 <<
" 5. LD_LIBRARY_PATH env var (Linux)\n"
241 <<
" 6. DYLD_LIBRARY_PATH env var (osx)\n"
242 <<
" A library may be 'not found' even if the file exists:\n"
243 <<
" * library is not ABI-compatible (different compiler/bitness)\n"
244 <<
" * the dependencies are not found\n"
245 <<
" * the dependencies are found but have an ABI-incompatible version/compiler/bitness\n"
248 std::string searchpath;
263 std::wstring libW = utf8_to_utf16(lib);
264 for (
const std::string& sp : search_paths) {
265 if (sp.empty())
continue;
266 std::wstring spW = utf8_to_utf16(sp);
267 DLL_DIRECTORY_COOKIE cookie = AddDllDirectory(spW.c_str());
268 handle = LoadLibraryExW(libW.c_str(), NULL,
269 LOAD_LIBRARY_SEARCH_USER_DIRS |
270 LOAD_LIBRARY_SEARCH_DEFAULT_DIRS |
271 LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR);
272 if (cookie) RemoveDllDirectory(cookie);
285 #ifdef __EMSCRIPTEN__
292 std::set<std::string> em_tried;
294 for (casadi_int i=0;i<search_paths.size();++i) {
295 searchpath = search_paths[i];
297 SetDllDirectory(TEXT(searchpath.c_str()));
298 handle = LoadLibrary(TEXT(lib.c_str()));
299 SetDllDirectory(NULL);
301 std::string libname = searchpath.empty() ? lib : searchpath +
filesep() + lib;
302 #ifdef __EMSCRIPTEN__
303 if (libname.rfind(
"./", 0) == 0) libname.erase(0, 2);
304 if (!em_tried.insert(libname).second)
continue;
306 handle = dlopen(libname.c_str(), flag);
309 resultpath = searchpath;
312 errors << std::endl <<
" Tried '" << searchpath <<
"' :";
314 errors << std::endl <<
" Error code (WIN32): " << STRING(GetLastError());
316 errors << std::endl <<
" Error code: " << dlerror();
324 #if !defined(__APPLE__) && !defined(__EMSCRIPTEN__)
329 publish_environ_snapshot();
335 casadi_assert(handle!=
nullptr, errors.str());
345 std::wstring utf8_to_utf16(
const std::string& s) {
346 int wlen = MultiByteToWideChar(CP_UTF8, 0, s.data(),
static_cast<int>(s.size()),
nullptr, 0);
347 if (wlen == 0)
return {};
348 std::wstring ws(wlen, 0);
349 MultiByteToWideChar(CP_UTF8, 0, s.data(),
static_cast<int>(s.size()), &ws[0], wlen);
355 class FdStreamBuf :
public std::streambuf {
357 explicit FdStreamBuf(
int fd,
size_t bufsize = 4096)
358 : fd_(fd), buffer_(bufsize) {
359 setg(buffer_.data(), buffer_.data(), buffer_.data());
362 ~FdStreamBuf()
override {
369 int_type underflow()
override {
370 if (gptr() < egptr()) {
371 return traits_type::to_int_type(*gptr());
374 int n = _read(fd_, buffer_.data(),
static_cast<unsigned int>(buffer_.size()));
376 return traits_type::eof();
379 setg(buffer_.data(), buffer_.data(), buffer_.data() + n);
380 return traits_type::to_int_type(*gptr());
383 std::streampos seekoff(std::streamoff off, std::ios_base::seekdir dir,
384 std::ios_base::openmode which = std::ios_base::in)
override {
385 if (!(which & std::ios_base::in))
return -1;
389 case std::ios_base::beg: whence = SEEK_SET;
break;
390 case std::ios_base::cur:
392 off -= egptr() - gptr();
395 case std::ios_base::end: whence = SEEK_END;
break;
399 __int64 result = _lseeki64(fd_, off,
static_cast<int>(whence));
405 setg(buffer_.data(), buffer_.data(), buffer_.data());
409 std::streampos seekpos(std::streampos pos,
410 std::ios_base::openmode which = std::ios_base::in)
override {
411 return seekoff(
static_cast<std::streamoff
>(pos), std::ios_base::beg, which);
417 std::vector<char> buffer_;
420 class FdOStreamBuf :
public std::streambuf {
422 explicit FdOStreamBuf(
int fd,
size_t bufsize = 4096)
423 : fd_(fd), buffer_(bufsize) {
424 setp(buffer_.data(), buffer_.data() + buffer_.size());
427 ~FdOStreamBuf()
override {
435 int_type overflow(int_type ch)
override {
436 if (flush_buffer() == -1)
return traits_type::eof();
438 if (ch != traits_type::eof()) {
439 *pptr() =
static_cast<char>(ch);
446 int sync()
override {
447 return flush_buffer() == -1 ? -1 : 0;
452 int len =
static_cast<int>(pptr() - pbase());
454 int written = _write(fd_, pbase(), len);
455 if (written != len)
return -1;
462 std::vector<char> buffer_;
465 struct OwnedIStream {
466 std::unique_ptr<FdStreamBuf> buffer;
467 std::unique_ptr<std::istream> stream;
469 explicit OwnedIStream(
int fd)
470 : buffer(
std::make_unique<FdStreamBuf>(fd)),
471 stream(
std::make_unique<
std::istream>(buffer.get())) {}
474 struct StreamWithOwnedBuffer :
public std::istream {
475 std::shared_ptr<OwnedIStream> owned;
476 explicit StreamWithOwnedBuffer(std::shared_ptr<OwnedIStream> o)
477 :
std::istream(o->buffer.get()), owned(
std::move(o)) {}
480 struct OwnedOStream {
481 std::unique_ptr<FdOStreamBuf> buffer;
482 std::unique_ptr<std::ostream> stream;
484 explicit OwnedOStream(
int fd)
485 : buffer(
std::make_unique<FdOStreamBuf>(fd)),
486 stream(
std::make_unique<
std::ostream>(buffer.get())) {}
489 struct StreamWithOwnedOBuffer :
public std::ostream {
490 std::shared_ptr<OwnedOStream> owned;
491 explicit StreamWithOwnedOBuffer(std::shared_ptr<OwnedOStream> o)
492 :
std::ostream(o->buffer.get()), owned(
std::move(o)) {}
498 std::ios::openmode mode) {
500 std::wstring utf16_path = utf8_to_utf16(utf8_path);
502 access |= GENERIC_READ;
503 if (mode & std::ios::out) access |= GENERIC_WRITE;
505 HANDLE h = CreateFileW(utf16_path.c_str(), access,
506 FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr,
507 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
nullptr);
508 if (h == INVALID_HANDLE_VALUE)
return {};
510 int flags = (mode & std::ios::out) ? _O_RDWR : _O_RDONLY;
511 if (mode & std::ios::binary) flags |= _O_BINARY;
513 int fd = _open_osfhandle(
reinterpret_cast<intptr_t
>(h), flags);
518 auto owned = std::make_shared<OwnedIStream>(fd);
519 return std::unique_ptr<StreamWithOwnedBuffer>(
new StreamWithOwnedBuffer(std::move(owned)));
521 auto ifs = std::unique_ptr<std::ifstream>(
new std::ifstream(utf8_path, mode));
522 if (!*ifs)
return {};
523 return std::unique_ptr<std::istream>(std::move(ifs));
528 std::ios::openmode mode) {
530 std::wstring utf16_path = utf8_to_utf16(utf8_path);
533 access |= GENERIC_WRITE;
534 if (mode & std::ios::in) access |= GENERIC_READ;
536 DWORD creation = (mode & std::ios::app) ? OPEN_ALWAYS : CREATE_ALWAYS;
538 HANDLE h = CreateFileW(utf16_path.c_str(), access,
539 FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr,
540 creation, FILE_ATTRIBUTE_NORMAL,
nullptr);
541 if (h == INVALID_HANDLE_VALUE)
return {};
543 int flags = (mode & std::ios::in) ? _O_RDWR : _O_WRONLY;
544 if (mode & std::ios::app) flags |= _O_APPEND;
545 if (mode & std::ios::binary) {
551 int fd = _open_osfhandle(
reinterpret_cast<intptr_t
>(h), flags);
557 auto owned = std::make_shared<OwnedOStream>(fd);
558 return std::unique_ptr<StreamWithOwnedOBuffer>(
new StreamWithOwnedOBuffer(std::move(owned)));
560 auto ofs = std::unique_ptr<std::ofstream>(
new std::ofstream(utf8_path, mode));
561 if (!*ofs)
return {};
562 return std::unique_ptr<std::ostream>(std::move(ofs));
static std::string getCasadiPath()
std::unique_ptr< std::istream > ifstream_compat(const std::string &utf8_path, std::ios::openmode mode)
std::unique_ptr< std::ostream > ofstream_compat(const std::string &utf8_path, std::ios::openmode mode)
std::vector< std::string > get_search_paths()