diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..e59eeec --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,109 @@ +cmake_minimum_required(VERSION 3.1) +project(whereami VERSION 1.0.0 LANGUAGES C CXX) +include(GNUInstallDirs) + +option(BUILD_SHARED_LIBS "Build shared libs" ON) +option(BUILD_STATIC_LIBS "Build static libs" ON) +option(BUILD_EXAMPLES "Build examples" ON) + +find_package(PkgConfig) + +# whereami +set(CMAKE_C_STANDARD 99) +add_library(whereami OBJECT ${CMAKE_SOURCE_DIR}/src/whereami.c) +target_include_directories(whereami + PUBLIC $ + $) +set_target_properties(whereami PROPERTIES POSITION_INDEPENDENT_CODE ON) + +if(BUILD_SHARED_LIBS) + add_library(whereami_shared SHARED $) + set_target_properties(whereami_shared + PROPERTIES VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} + OUTPUT_NAME whereami + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_INSTALL_LIBDIR}) + install(TARGETS whereami_shared LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) +endif() + +if(BUILD_STATIC_LIBS) + add_library(whereami_static STATIC $) + set_target_properties(whereami_static + PROPERTIES OUTPUT_NAME whereami + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_INSTALL_LIBDIR}) + install(TARGETS whereami_static ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) +endif() + +if(BUILD_EXAMPLES) + add_executable(executable ${CMAKE_SOURCE_DIR}/example/executable.c + ${CMAKE_SOURCE_DIR}/src/whereami.c) + target_include_directories(executable PUBLIC $) + if(CMAKE_DL_LIBS) + target_link_libraries(executable ${CMAKE_DL_LIBS}) + endif() + + add_library(library SHARED ${CMAKE_SOURCE_DIR}/example/library.c + ${CMAKE_SOURCE_DIR}/src/whereami.c) + target_include_directories(library PUBLIC $) + if(CMAKE_DL_LIBS) + target_link_libraries(library ${CMAKE_DL_LIBS}) + endif() +endif() + +if(PKG_CONFIG_FOUND) + configure_file(${CMAKE_SOURCE_DIR}/pkgconfig/whereami.pc.in ${CMAKE_BINARY_DIR}/whereami.pc) + install(FILES ${CMAKE_BINARY_DIR}/whereami.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) +endif() + + +# whereamipp +if(CMAKE_CXX_COMPILER) + set(CMAKE_CXX_STANDARD 11) + add_library(whereamipp OBJECT ${CMAKE_SOURCE_DIR}/src/whereami++.cpp) + target_include_directories(whereamipp + PUBLIC $ + $) + set_target_properties(whereamipp PROPERTIES POSITION_INDEPENDENT_CODE ON) + + if(BUILD_SHARED_LIBS) + add_library(whereamipp_shared SHARED $) + set_target_properties(whereamipp_shared + PROPERTIES VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} + OUTPUT_NAME whereamipp + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_INSTALL_LIBDIR}) + install(TARGETS whereamipp_shared LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif() + + if(BUILD_STATIC_LIBS) + add_library(whereamipp_static STATIC $) + set_target_properties(whereamipp_static + PROPERTIES OUTPUT_NAME whereamipp + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_INSTALL_LIBDIR}) + install(TARGETS whereamipp_static ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif() + + if(BUILD_EXAMPLES) + add_executable(executable-as-cxx ${CMAKE_SOURCE_DIR}/example/executable.cpp + ${CMAKE_SOURCE_DIR}/src/whereami++.cpp + ${CMAKE_SOURCE_DIR}/src/whereami.c) + target_include_directories(executable-as-cxx PUBLIC $) + if(CMAKE_DL_LIBS) + target_link_libraries(executable-as-cxx ${CMAKE_DL_LIBS}) + endif() + + add_library(library-as-cxx SHARED ${CMAKE_SOURCE_DIR}/example/library.cpp + ${CMAKE_SOURCE_DIR}/src/whereami++.cpp + ${CMAKE_SOURCE_DIR}/src/whereami.c) + target_include_directories(library-as-cxx PUBLIC $) + if(CMAKE_DL_LIBS) + target_link_libraries(library-as-cxx ${CMAKE_DL_LIBS}) + endif() + + endif() + + if(PKG_CONFIG_FOUND) + configure_file(${CMAKE_SOURCE_DIR}/pkgconfig/whereamipp.pc.in ${CMAKE_BINARY_DIR}/whereamipp.pc) + install(FILES ${CMAKE_BINARY_DIR}/whereamipp.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) + endif() +endif() diff --git a/_gnu-make/Makefile b/_gnu-make/Makefile index 2167a3e..ce2edb3 100644 --- a/_gnu-make/Makefile +++ b/_gnu-make/Makefile @@ -84,28 +84,48 @@ endif .PHONY: build-executable build: build-executable -build-executable: $(bindir)/executable $(bindir)/executable-cpp +build-executable: $(bindir)/executable $(bindir)/executable-as-cxx $(bindir)/executable-as-cxx11 $(bindir)/executable-cxx $(bindir)/executable-cxx11 $(bindir)/executable: $(srcdir)/whereami.c $(srcdir)/whereami.h $(exampledir)/executable.c mkdir -p $(@D) $(CC) -I $(srcdir) $(CPPFLAGS) $(CFLAGS) $(filter-out %.h,$^) $(LDFLAGS) -o $@ $(if $(postbuild),$(postbuild) $@) -$(bindir)/executable-cpp: $(srcdir)/whereami.c $(srcdir)/whereami.h $(exampledir)/executable.c +$(bindir)/executable-as-cxx: $(srcdir)/whereami.c $(srcdir)/whereami.h $(exampledir)/executable.c mkdir -p $(@D) - $(CXX) -x c++ -I $(srcdir) $(CPPFLAGS) $(CXXFLAGS) $(filter-out %.h,$^) $(LDFLAGS) -o $@ + $(CXX) -std=c++03 -x c++ -I $(srcdir) $(CPPFLAGS) $(CXXFLAGS) $(filter-out %.h,$^) $(LDFLAGS) -o $@ + $(if $(postbuild),$(postbuild) $@) + +$(bindir)/executable-as-cxx11: $(srcdir)/whereami.c $(srcdir)/whereami.h $(exampledir)/executable.c + mkdir -p $(@D) + $(CXX) -std=c++11 -x c++ -I $(srcdir) $(CPPFLAGS) $(CXXFLAGS) $(filter-out %.h,$^) $(LDFLAGS) -o $@ + $(if $(postbuild),$(postbuild) $@) + +$(bindir)/executable-cxx: $(srcdir)/whereami++.cpp $(srcdir)/whereami++.h $(exampledir)/executable.cpp + mkdir -p $(@D) + $(CXX) -std=c++03 -x c++ -I $(srcdir) $(CPPFLAGS) $(CXXFLAGS) $(filter-out %.h,$^) $(LDFLAGS) -o $@ + $(if $(postbuild),$(postbuild) $@) + +$(bindir)/executable-cxx11: $(srcdir)/whereami++.cpp $(srcdir)/whereami++.h $(exampledir)/executable.cpp + mkdir -p $(@D) + $(CXX) -std=c++11 -x c++ -I $(srcdir) $(CPPFLAGS) $(CXXFLAGS) $(filter-out %.h,$^) $(LDFLAGS) -o $@ $(if $(postbuild),$(postbuild) $@) .PHONY: build-library build: build-library -build-library: $(bindir)/library$(libsuffix) $(bindir)/library-cpp$(libsuffix) +build-library: $(bindir)/library$(libsuffix) $(bindir)/library-as-cxx$(libsuffix) $(bindir)/library-cxx$(libsuffix) $(bindir)/library$(libsuffix): $(srcdir)/whereami.c $(srcdir)/whereami.h $(exampledir)/library.c mkdir -p $(@D) $(CC) -I $(srcdir) $(CPPFLAGS) $(CFLAGS) $(filter-out %.h,$^) $(LDFLAGS) -shared -o $@ $(if $(postbuild),$(postbuild) $@) -$(bindir)/library-cpp$(libsuffix): $(srcdir)/whereami.c $(srcdir)/whereami.h $(exampledir)/library.c +$(bindir)/library-as-cxx$(libsuffix): $(srcdir)/whereami.c $(srcdir)/whereami.h $(exampledir)/library.c + mkdir -p $(@D) + $(CXX) -x c++ -I $(srcdir) $(CPPFLAGS) $(CXXFLAGS) $(filter-out %.h,$^) $(LDFLAGS) -shared -o $@ + $(if $(postbuild),$(postbuild) $@) + +$(bindir)/library-cxx$(libsuffix): $(srcdir)/whereami++.cpp $(srcdir)/whereami++.h $(exampledir)/library.cpp mkdir -p $(@D) $(CXX) -x c++ -I $(srcdir) $(CPPFLAGS) $(CXXFLAGS) $(filter-out %.h,$^) $(LDFLAGS) -shared -o $@ $(if $(postbuild),$(postbuild) $@) diff --git a/example/executable.cpp b/example/executable.cpp new file mode 100644 index 0000000..ff07707 --- /dev/null +++ b/example/executable.cpp @@ -0,0 +1,101 @@ +#include + +#include + +#if defined(_WIN32) + +#define WIN32_LEAN_AND_MEAN +#if defined(_MSC_VER) +#pragma warning(push, 3) +#endif +#include + +#define RTLD_LAZY 1 +#define RTLD_NOW 2 +#define RTLD_GLOBAL 4 +#define RTLD_LOCAL 8 + +static void* dlopen(const char* fileName, int mode) +{ + wchar_t buffer[MAX_PATH]; + + if (MultiByteToWideChar(CP_UTF8, 0, fileName, -1, buffer, sizeof(buffer) / sizeof(*buffer))) + { + wchar_t buffer_[MAX_PATH]; + + GetFullPathNameW(buffer, sizeof(buffer_) / sizeof(*buffer_), buffer_, NULL); + + return (void*)LoadLibraryW(buffer_); + } + + return NULL; +} + +static int dlclose(void* handle) +{ + return FreeLibrary((HMODULE)handle) ? 0 : -1; +} + +static const char* dlerror(void) +{ + DWORD error; + + error = GetLastError(); + + if (error) + { + static char message[1024]; + + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), message, sizeof(message), NULL); + + return message; + } + + return "no error"; +} + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#else + +#include + +#endif + +int main(int argc, char** argv) +{ + std::cout << "executable path: " << whereami::getExecutablePath() << std::endl; + whereami::whereami_path_t executablePath = whereami::getExecutablePath(); + std::cout << " dirname: " << executablePath.dirname() << std::endl; + std::cout << " basename: " << executablePath.basename() << std::endl; + + std::cout << "module path: " << whereami::getModulePath() << std::endl; + whereami::whereami_path_t modulePath = whereami::getModulePath(); + std::cout << " dirname: " << modulePath.dirname() << std::endl; + std::cout << " basename: " << modulePath.basename() << std::endl; + + for (int i = 1; i < argc; ++i) + { + std::string prefix = "--load-library="; + std::string arg = std::string(argv[i]); + + if (arg.compare(0, prefix.length(), prefix) == 0) + { + std::string name = arg.substr(prefix.length()); + void* handle; + + std::cout << std::endl; + + handle = dlopen(name.c_str(), RTLD_NOW); + if (!handle) + std::cout << "failed to load library: " << dlerror() << std::endl; + + if (handle) + dlclose(handle); + } + } + + return 0; +} diff --git a/example/library.cpp b/example/library.cpp new file mode 100644 index 0000000..ce0c253 --- /dev/null +++ b/example/library.cpp @@ -0,0 +1,61 @@ +#include + +#include + +#if defined(__GNUC__) && !defined(_WIN32) +__attribute__((constructor)) +#endif +static void load() +{ + std::cout << "library loaded" << std::endl; + + std::cout << "executable path: " << whereami::getExecutablePath() << std::endl; + whereami::whereami_path_t executablePath = whereami::getExecutablePath(); + std::cout << " dirname: " << executablePath.dirname() << std::endl; + std::cout << " basename: " << executablePath.basename() << std::endl; + + std::cout << "module path: " << whereami::getModulePath() << std::endl; + whereami::whereami_path_t modulePath = whereami::getModulePath(); + std::cout << " dirname: " << modulePath.dirname() << std::endl; + std::cout << " basename: " << modulePath.basename() << std::endl; +} + +#if defined(__GNUC__) && !defined(_WIN32) +__attribute__((destructor)) +#endif +static void unload() +{ + std::cout << "library unloaded" << std::endl; +} + +#if defined(_WIN32) + +#define WIN32_LEAN_AND_MEAN +#if defined(_MSC_VER) +#pragma warning(push, 3) +#endif +#include + +BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + load(); + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + break; + case DLL_PROCESS_DETACH: + unload(); + break; + } + return TRUE; +} + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#endif diff --git a/pkgconfig/whereami.pc.in b/pkgconfig/whereami.pc.in new file mode 100644 index 0000000..c6e02f3 --- /dev/null +++ b/pkgconfig/whereami.pc.in @@ -0,0 +1,13 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=@CMAKE_INSTALL_PREFIX@ +libdir=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@ +includedir=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_INCLUDEDIR@ + +Name: whereami +Description: Whereami C library +Version: @PROJECT_VERSION@ + +Requires: +Libs: -L@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@ -lwhereami +Cflags: -I@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_INCLUDEDIR@ + diff --git a/pkgconfig/whereamipp.pc.in b/pkgconfig/whereamipp.pc.in new file mode 100644 index 0000000..b301720 --- /dev/null +++ b/pkgconfig/whereamipp.pc.in @@ -0,0 +1,13 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=@CMAKE_INSTALL_PREFIX@ +libdir=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@ +includedir=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_INCLUDEDIR@ + +Name: whereamipp +Description: Whereami library C++ bindings +Version: @PROJECT_VERSION@ + +Requires: +Libs: -L@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@ -lwhereamipp -whereami +Cflags: -I@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_INCLUDEDIR@ + diff --git a/src/whereami++.cpp b/src/whereami++.cpp new file mode 100644 index 0000000..8334ed9 --- /dev/null +++ b/src/whereami++.cpp @@ -0,0 +1,104 @@ +// (‑●‑●)> released under the WTFPL v2 license, by Gregory Pakosz (@gpakosz) +// https://github.com/gpakosz/whereami + +// in case you want to #include "whereami++.cpp" in a larger compilation unit +#if !defined(WHEREAMIPP_H) +#include +#endif + +namespace whereami { + +namespace { + #define WHEREAMI_H + #define WAI_FUNCSPEC + #define WAI_PREFIX(function) function + #include "whereami.c" +} + + whereami_string_t whereami_path_t::dirname() const + { + return path.substr(0, dirname_length); + } + + whereami_string_t whereami_path_t::basename() const + { + return path.substr(dirname_length + 1); + } + +#if defined(WHEREAMI_CXX11) + whereami_path_t::operator whereami_string_t() && + { + return std::move(path); + } + + whereami_path_t::operator whereami_string_t() const & + { + return path; + } +#else + whereami_path_t::operator const whereami_string_t&() const + { + return path; + } +#endif + +#if defined(WHEREAMI_CXX11) + whereami_path_t::whereami_path_t(whereami_string_t&& path, int dirname_length) noexcept + : path(std::move(path)), dirname_length(dirname_length) + { + } +#else + whereami_path_t::whereami_path_t(whereami_string_t& path, int dirname_length) + : path(path), dirname_length(dirname_length) + { + } +#endif + +#if !defined(WHEREAMI_DISABLE_OSTREAM) + std::ostream& operator<<(std::ostream& os, const whereami_path_t& path) + { + return os << path.path; + } + +#endif + WAI_FUNCSPEC + whereami_path_t getExecutablePath() + { + whereami_string_t path; + int dirname_length = -1; + + int length = getExecutablePath(0, 0, 0); + if (length != -1) + { + path.resize(length); + getExecutablePath(&path[0], length, &dirname_length); + } + +#if defined(WHEREAMI_CXX11) + return whereami_path_t(std::move(path), dirname_length); +#else + return whereami_path_t(path, dirname_length); +#endif + } + + WAI_FUNCSPEC + whereami_path_t getModulePath() + { + whereami_string_t path; + int dirname_length = -1; + + int length = getModulePath(0, 0, 0); + if (length != -1) + { + path.resize(length); + getModulePath(&path[0], length, &dirname_length); + } + +#if defined(WHEREAMI_CXX11) + return whereami_path_t(std::move(path), dirname_length); +#else + return whereami_path_t(path, dirname_length); +#endif + } + +} // namespace whereami diff --git a/src/whereami++.h b/src/whereami++.h new file mode 100644 index 0000000..928a19b --- /dev/null +++ b/src/whereami++.h @@ -0,0 +1,67 @@ +// (‑●‑●)> released under the WTFPL v2 license, by Gregory Pakosz (@gpakosz) +// https://github.com/gpakosz/whereami + +#ifndef WHEREAMIPP_H +#define WHEREAMIPP_H + +#if !defined(WHEREAMI_STRING_T) +#include +typedef std::string whereami_string_t; +#else +typedef WHEREAMI_STRING_T whereami_string_t; +#endif +#if !defined(WHEREAMI_DISABLE_OSTREAM) +#include +#endif + +#if (defined (__cplusplus) && (__cplusplus > 199711L)) || (defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 150020706)) + #define WHEREAMI_CXX11 +#endif + +namespace whereami { + + class whereami_path_t + { + public: +#if defined(WHEREAMI_CXX11) + operator whereami_string_t() &&; + operator whereami_string_t() const &; +#else + operator const whereami_string_t&() const; +#endif + whereami_string_t dirname() const; + whereami_string_t basename() const; + + private: + whereami_path_t(); + +#if defined(WHEREAMI_CXX11) + whereami_path_t(whereami_string_t&& path, int dirname_length) noexcept; +#else + whereami_path_t(whereami_string_t& path, int dirname_length); +#endif + + friend whereami_path_t getExecutablePath(); + friend whereami_path_t getModulePath(); +#if !defined(WHEREAMI_DISABLE_OSTREAM) + friend std::ostream& operator<<(std::ostream& os, const whereami_path_t& path); +#endif + + whereami_string_t path; + int dirname_length; + }; + + /** + * Returns the path to the current executable. + */ + whereami_path_t getExecutablePath(); + + /** + * Returns the path to the current module. + */ + whereami_path_t getModulePath(); + +} // namespace whereami + +#endif // #ifndef WHEREAMIPP_H + diff --git a/src/whereami.c b/src/whereami.c index 48d9af3..d2295e5 100644 --- a/src/whereami.c +++ b/src/whereami.c @@ -12,6 +12,13 @@ extern "C" { #endif +#ifndef WAI_FUNCSPEC + #define WAI_FUNCSPEC +#endif +#ifndef WAI_PREFIX +#define WAI_PREFIX(function) wai_##function +#endif + #if !defined(WAI_MALLOC) || !defined(WAI_FREE) || !defined(WAI_REALLOC) #include #endif diff --git a/src/whereami.h b/src/whereami.h index 670db54..c0d1bcd 100644 --- a/src/whereami.h +++ b/src/whereami.h @@ -40,7 +40,7 @@ WAI_FUNCSPEC int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length); /** - * Returns the path to the current module + * Returns the path to the current module. * * Usage: * - first call `int length = wai_getModulePath(NULL, 0, NULL);` to retrieve