• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

openmc-dev / openmc / 13686156298

05 Mar 2025 09:45PM UTC coverage: 85.27% (-0.002%) from 85.272%
13686156298

push

github

web-flow
NCrystal becomes runtime rather than buildtime dependency (#3328)

Co-authored-by: Paul Romano <paul.k.romano@gmail.com>

86 of 98 new or added lines in 6 files covered. (87.76%)

1 existing line in 1 file now uncovered.

51313 of 60177 relevant lines covered (85.27%)

31549565.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.71
/src/ncrystal_load.cpp
1
#include "openmc/ncrystal_load.h"
2

3
#include <cctype>  // for isspace
4
#include <cstdlib> // for strtoul
5
#include <memory>  // for shared_ptr
6
#include <mutex>   // for mutex, lock_guard
7
#include <string>
8

9
#include <fmt/core.h>
10
#include <stdio.h> // for popen, pclose
11

12
#include "openmc/error.h"
13

14
#ifdef _WIN32
15
#ifndef WIN32_LEAN_AND_MEAN
16
#define WIN32_LEAN_AND_MEAN
17
#endif
18
#include <windows.h> // for LoadLibrary, GetProcAddress
19
#else
20
#include <dlfcn.h> // for dlopen, dlsym, dlerror
21
#endif
22

23
namespace openmc {
24
namespace {
25

26
struct NCrystalConfig {
27
  std::string shlibpath;
28
  unsigned long intversion = 0;
29
  std::string symbol_namespace;
30
};
31

32
NCrystalConfig query_ncrystal_config()
16✔
33
{
34
#ifdef _WIN32
35
  FILE* pipe = _popen("ncrystal-config --show "
36
                      "intversion shlibpath namespace",
37
    "r");
38
#else
39
  FILE* pipe = popen("ncrystal-config --show "
16✔
40
                     "intversion shlibpath namespace 2>/dev/null",
41
    "r");
42
#endif
43
  if (!pipe)
16✔
NEW
44
    return {}; // failure
×
45
  auto readLine = [pipe](std::string& tgt) -> bool {
48✔
46
    // Read line and discard trailing whitespace (including newline chars).
47
    char buffer[4096];
48
    if (fgets(buffer, sizeof(buffer), pipe) == NULL)
48✔
NEW
49
      return false;
×
50
    tgt = buffer;
48✔
51
    while (!tgt.empty() && std::isspace(tgt.back()))
96✔
52
      tgt.pop_back();
48✔
53
    return true;
48✔
54
  };
16✔
55
  auto parseIntVersion = [](const std::string& s) {
16✔
56
    char* str_end = nullptr;
16✔
57
    unsigned long v = std::strtoul(s.c_str(), &str_end, 10);
16✔
58
    return (v >= 2002000 && v < 999999999 && str_end == s.c_str() + s.size())
16✔
59
             ? v
32✔
60
             : 0;
16✔
61
  };
62

63
  NCrystalConfig res;
16✔
64
  if (!readLine(res.shlibpath) ||
16✔
65
      !(res.intversion = parseIntVersion(res.shlibpath)) ||
16✔
66
      !readLine(res.shlibpath) || res.shlibpath.empty() ||
48✔
67
      !readLine(res.symbol_namespace)) {
16✔
NEW
68
    res.intversion = 0; // failure
×
69
  }
70

71
#ifdef _WIN32
72
  auto returnCode = _pclose(pipe);
73
#else
74
  auto returnCode = pclose(pipe);
16✔
75
#endif
76
  if (returnCode == 0 && res.intversion >= 2002000)
16✔
77
    return res;
16✔
NEW
78
  return {}; // failure
×
79
}
16✔
80

81
struct NCrystalAPIDB {
82
  std::mutex mtx;
83
  std::shared_ptr<const NCrystalAPI> api;
84
  using FctSignature = void* (*)(int);
85
  FctSignature ncrystal_access_virtapi_fct = nullptr;
86
};
87

88
void* load_virtapi_raw(unsigned interface_id, NCrystalAPIDB& db)
16✔
89
{
90
  if (!db.ncrystal_access_virtapi_fct) {
16✔
91
    auto cfg = query_ncrystal_config();
16✔
92
    if (!(cfg.intversion >= 4001000)) {
16✔
93
      // This is the most likely error message people will see:
NEW
94
      fatal_error("Could not locate a functioning and recent enough"
×
95
                  " NCrystal installation (required since geometry"
96
                  " contains NCrystal materials).");
97
    }
98
#ifdef _WIN32
99
    auto handle = LoadLibrary(cfg.shlibpath.c_str());
100
#else
101
    dlerror(); // clear previous errors
16✔
102
    void* handle = dlopen(cfg.shlibpath.c_str(), RTLD_LOCAL | RTLD_LAZY);
16✔
103
#endif
104
    if (!handle)
16✔
NEW
105
      fatal_error("Loading of the NCrystal library failed");
×
106

107
    std::string symbol =
108
      fmt::format("ncrystal{}_access_virtual_api", cfg.symbol_namespace);
13✔
109

110
#ifdef _WIN32
111
    void* addr = (void*)(intptr_t)GetProcAddress(handle, symbol.c_str());
112
    if (!addr)
113
      fatal_error("GetProcAddress("
114
                  "ncrystal_access_virtual_api) failed");
115
#else
116
    dlerror(); // clear previous errors
16✔
117
    void* addr = dlsym(handle, symbol.c_str());
16✔
118
    if (!addr)
16✔
NEW
119
      fatal_error("dlsym(ncrystal_access_virtual_api) failed");
×
120
#endif
121
    db.ncrystal_access_virtapi_fct =
16✔
122
      reinterpret_cast<NCrystalAPIDB::FctSignature>(addr);
16✔
123
  }
16✔
124

125
  void* result = (*db.ncrystal_access_virtapi_fct)(interface_id);
16✔
126
  if (!result)
16✔
NEW
127
    fatal_error("NCrystal installation does not support required interface.");
×
128

129
  return result;
16✔
130
}
131

132
NCrystalAPIDB& get_ncrystal_api_db()
16✔
133
{
134
  static NCrystalAPIDB db;
16✔
135
  return db;
16✔
136
}
137
} // namespace
138

139
std::shared_ptr<const NCrystalAPI> load_ncrystal_api()
16✔
140
{
141
  auto& db = get_ncrystal_api_db();
16✔
142
  std::lock_guard<std::mutex> lock(db.mtx);
16✔
143
  if (!db.api) {
16✔
144
    void* raw_api = load_virtapi_raw(NCrystalAPI::interface_id, db);
16✔
145
    if (!raw_api)
16✔
NEW
146
      fatal_error("Problems loading NCrystal.");
×
147
    db.api = *reinterpret_cast<std::shared_ptr<const NCrystalAPI>*>(raw_api);
16✔
148
  }
149
  return db.api;
32✔
150
}
16✔
151
} // namespace openmc
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc