Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Homogenize finding our components. #1336

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
90 changes: 86 additions & 4 deletions src/config/config.cpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,27 @@

#include "config/config.h"

#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <sstream>

#if defined(_WIN32)
#include <stdio.h>
#elif defined(__APPLE__)
#include <limits.h>
#include <mach-o/dyld.h>
#else
#endif

namespace fs = std::filesystem;

/// Git version of the project
const std::string nmodl::Version::GIT_REVISION = "@NMODL_GIT_REVISION@";

/// NMODL version
const std::string nmodl::Version::NMODL_VERSION = "@PROJECT_VERSION@";

const std::string nmodl::CMakeInfo::SHARED_LIBRARY_SUFFIX = "@CMAKE_SHARED_LIBRARY_SUFFIX@";

/**
* \brief Path of nrnutils.lib file
*
Expand All @@ -23,5 +36,74 @@ const std::string nmodl::CMakeInfo::SHARED_LIBRARY_SUFFIX = "@CMAKE_SHARED_LIBRA
* from CMAKE_INSTALL_PREFIX. Note that this use of NMODL_PROJECT_BINARY_DIR
* will cause ccache misses when the build prefix is changed.
*/
std::vector<std::string> nmodl::NrnUnitsLib::NRNUNITSLIB_PATH =
{"@CMAKE_INSTALL_PREFIX@/share/nmodl/nrnunits.lib", "@NMODL_PROJECT_BINARY_DIR@/share/nmodl/nrnunits.lib"};
const std::vector<std::string> nmodl::PathHelper::BASE_SEARCH_PATHS =
{"@CMAKE_INSTALL_PREFIX@", "@NMODL_PROJECT_BINARY_DIR@"};

const std::string nmodl::PathHelper::SHARED_LIBRARY_SUFFIX = "@CMAKE_SHARED_LIBRARY_SUFFIX@";

namespace {

std::string maybe_from_env(const std::string& varname) {
const auto value = std::getenv(varname.c_str());
if (value != nullptr) {
return value;
}

#if defined(_WIN32)
std::vector<wchar_t> buffer;
DWORD copied = 0;
do {
buffer.resize(buffer.size() + MAX_PATH);
copied = GetModuleFileName(0, &buffer.at(0), buffer.size());
} while (copied >= buffer.size());
buffer.resize(copied);
fs::path executable(std::wstring(buffer.begin(), buffer.end()));
pramodk marked this conversation as resolved.
Show resolved Hide resolved
#elif defined(__APPLE__)
char buffer[PATH_MAX + 1];
uint32_t bufsize = PATH_MAX + 1;
if( _NSGetExecutablePath(buffer, &bufsize) != 0) {
return "";
}
auto executable = fs::path(buffer);
#else
auto executable = fs::read_symlink("/proc/self/exe");
#endif

auto executable_dir = fs::weakly_canonical(executable).parent_path();
if (executable_dir.filename() == "bin") {
return executable_dir.parent_path();
} else {
// On Windows, we may find ourselves in the top-level directory without a bin/
return executable_dir;
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the past, I didn't go via this route. But if you see that this is a relatively stable approach that peopl use and works on windows/Mac/Linux then I would say this is fine as well!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that way. All my searches more or less indicated that this is the way to go. Mac OS/Windows could use some more testing though. It at least goes through the CI so far.

}

const std::string nmodl::PathHelper::NMODL_HOME = maybe_from_env("NMODL_HOME");

std::string nmodl::PathHelper::get_path(const std::string& what, bool add_library_suffix) {
std::vector<std::string> search_paths = BASE_SEARCH_PATHS;
if (!NMODL_HOME.empty()) {
search_paths.emplace(search_paths.begin(), NMODL_HOME);
}

// check paths in order and return if found
for (const auto& path: search_paths) {
auto full_path = fs::path(path) / fs::path(what);
if (add_library_suffix) {
full_path += SHARED_LIBRARY_SUFFIX;
}
std::ifstream f(full_path);
if (f.good()) {
return full_path;
}
}
std::ostringstream err_msg;
err_msg << "Could not find '" << what << "' in any of:\n";
for (const auto& path: search_paths) {
err_msg << "\t" << path << "\n";
}
err_msg << "Please try setting the NMODLHOME environment variable\n";
throw std::runtime_error(err_msg.str());
}
54 changes: 24 additions & 30 deletions src/config/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@
* \brief Version information and units file path
*/

#include <cstdlib>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>

Expand All @@ -44,38 +41,35 @@
/**
* \brief Information of units database i.e. `nrnunits.lib`
*/
struct NrnUnitsLib {
/// paths where nrnunits.lib can be found
static std::vector<std::string> NRNUNITSLIB_PATH;
class PathHelper {
/// pre-defined paths to search for files
const static std::vector<std::string> BASE_SEARCH_PATHS;

/// suffix to use when looking for libraries
const static std::string SHARED_LIBRARY_SUFFIX;

/// base directory of the NMODL installation
const static std::string NMODL_HOME;

/**
* Return path of units database file
* Search for a given relative file path
*/
static std::string get_path() {
// first look for NMODLHOME env variable
if (const char* nmodl_home = std::getenv("NMODLHOME")) {
auto path = std::string(nmodl_home) + "/share/nmodl/nrnunits.lib";
NRNUNITSLIB_PATH.emplace(NRNUNITSLIB_PATH.begin(), path);
}
static std::string get_path(const std::string& what, bool add_library_suffix = false);

// check paths in order and return if found
for (const auto& path: NRNUNITSLIB_PATH) {
std::ifstream f(path.c_str());
if (f.good()) {
return path;
}
}
std::ostringstream err_msg;
err_msg << "Could not find nrnunits.lib in any of:\n";
for (const auto& path: NRNUNITSLIB_PATH) {
err_msg << path << "\n";
}
throw std::runtime_error(err_msg.str());
}
};
public:
/**
* Return path of units database file
*/
static std::string get_units_path() {
return get_path("share/nmodl/nrnunits.lib");
};

struct CMakeInfo {
static const std::string SHARED_LIBRARY_SUFFIX;
/**
* Return path of the python wrapper library
*/
static std::string get_wrapper_path() {
return get_path("lib/libpywrapper", true);

Check warning on line 71 in src/config/config.h

View check run for this annotation

Codecov / codecov/patch

src/config/config.h#L70-L71

Added lines #L70 - L71 were not covered by tests
};
};

} // namespace nmodl
2 changes: 1 addition & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ int main(int argc, const char* argv[]) {
std::string scratch_dir("tmp");

/// directory where units lib file is located
std::string units_dir(NrnUnitsLib::get_path());
std::string units_dir(PathHelper::get_units_path());

/// true if ast should be converted to json
bool json_ast(false);
Expand Down
2 changes: 1 addition & 1 deletion src/parser/main_units.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
fmt::format("Unit-Parser : Standalone Parser for Units({})", Version::to_string())};

std::vector<std::string> units_files;
units_files.push_back(NrnUnitsLib::get_path());
units_files.push_back(PathHelper::get_units_path());

Check warning on line 31 in src/parser/main_units.cpp

View check run for this annotation

Codecov / codecov/patch

src/parser/main_units.cpp#L31

Added line #L31 was not covered by tests
app.add_option("units_files", units_files, "One or more Units files to process");

CLI11_PARSE(app, argc, argv);
Expand Down
14 changes: 2 additions & 12 deletions src/pybind/pyembed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,21 +81,11 @@

assert_compatible_python_versions();

if (std::getenv("NMODLHOME") == nullptr) {
logger->critical("NMODLHOME environment variable must be set to load embedded python");
throw std::runtime_error("NMODLHOME not set");
}
auto pybind_wraplib_env = fs::path(std::getenv("NMODLHOME")) / "lib" / "libpywrapper";
pybind_wraplib_env.concat(CMakeInfo::SHARED_LIBRARY_SUFFIX);
if (!fs::exists(pybind_wraplib_env)) {
logger->critical("NMODLHOME doesn't contain libpywrapper{} library",
CMakeInfo::SHARED_LIBRARY_SUFFIX);
throw std::runtime_error("NMODLHOME doesn't have lib/libpywrapper library");
}
auto pybind_wraplib_env = PathHelper::get_wrapper_path();

Check warning on line 84 in src/pybind/pyembed.cpp

View check run for this annotation

Codecov / codecov/patch

src/pybind/pyembed.cpp#L84

Added line #L84 was not covered by tests
pybind_wrapper_handle = dlopen(pybind_wraplib_env.c_str(), dlopen_opts);
if (!pybind_wrapper_handle) {
const auto errstr = dlerror();
logger->critical("Tried but failed to load {}", pybind_wraplib_env.string());
logger->critical("Tried but failed to load {}", pybind_wraplib_env);

Check warning on line 88 in src/pybind/pyembed.cpp

View check run for this annotation

Codecov / codecov/patch

src/pybind/pyembed.cpp#L88

Added line #L88 was not covered by tests
logger->critical(errstr);
throw std::runtime_error("Failed to dlopen");
}
Expand Down
2 changes: 1 addition & 1 deletion src/visitors/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
"SympyConductanceVisitor"},
{std::make_shared<SympySolverVisitor>(), "sympy-solve", "SympySolverVisitor"},
{std::make_shared<NeuronSolveVisitor>(), "neuron-solve", "NeuronSolveVisitor"},
{std::make_shared<UnitsVisitor>(NrnUnitsLib::get_path()), "units", "UnitsVisitor"},
{std::make_shared<UnitsVisitor>(PathHelper::get_units_path()), "units", "UnitsVisitor"},

Check warning on line 98 in src/visitors/main.cpp

View check run for this annotation

Codecov / codecov/patch

src/visitors/main.cpp#L98

Added line #L98 was not covered by tests
};

const std::vector<ConstVisitorInfo> const_visitors = {
Expand Down
2 changes: 1 addition & 1 deletion test/unit/units/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ bool is_valid_construct(const std::string& construct) {

std::string parse_string(const std::string& unit_definition) {
nmodl::parser::UnitDriver correctness_driver;
correctness_driver.parse_file(nmodl::NrnUnitsLib::get_path());
correctness_driver.parse_file(nmodl::PathHelper::get_units_path());
correctness_driver.parse_string(unit_definition);
std::stringstream ss;
correctness_driver.table->print_units_sorted(ss);
Expand Down
2 changes: 1 addition & 1 deletion test/unit/visitor/units.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ std::tuple<std::shared_ptr<ast::Program>, std::shared_ptr<units::UnitTable>> run
const auto& ast = driver.get_ast();

// Parse nrnunits.lib file and the UNITS block of the mod file
const std::string units_lib_path(NrnUnitsLib::get_path());
const std::string units_lib_path(PathHelper::get_units_path());
UnitsVisitor units_visitor = UnitsVisitor(units_lib_path);

units_visitor.visit_program(*ast);
Expand Down
Loading