rdkit_chem 2025.09.3.11 → 2025.09.3.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0bfdaa78b3bed2b74fac18b36591a2194de5a74eeff5525d8b0639882a938a78
4
- data.tar.gz: 9364cb31768038bc313d09fff0d08c38b4f8db90bbc3c9a5735897857dc3df20
3
+ metadata.gz: 8fd525bf8f5e07e8759d0f36c18ecabdf2ec3147a0d6bd240dd5c951fd629285
4
+ data.tar.gz: fa0cb66768fa9160dc781a6b374bfb4dc12abfa946f24daae07e851b5eb83346
5
5
  SHA512:
6
- metadata.gz: e58cfb8ae7364b677d34c5967185d72d3b60f482fce083ca3d3364598c7a1248a6d9a3b3e74bb0a7e77f8444f5093b29b591d8cc51c87c02824d097fd9a29a11
7
- data.tar.gz: c4febf19d6f25d971d073610b82935f3f841631987c8098ef844bc9d80d45e42780c283ef9c008e9a4aec3087e80e6504aef020db6c669fdce49dfe989c0e233
6
+ metadata.gz: e99aee43550767d548a3534e56609ad84607d7c88d1ef59943b8b6bd329837cc2fef2e8c660ddf8519d1bea43ee2705c6f8fc626fae528ed0604c58bd8a189d8
7
+ data.tar.gz: adb78e57d89a631ff08f393bea2f26b356e181e7d52c2f46f7d628d80a1657cc3af2e1ab8d5de893be4f92cec01293bf83114c520075c9d73977854ff28621cc
data/CMakeLists.txt CHANGED
@@ -78,7 +78,7 @@ option(RDK_BUILD_MINIMAL_LIB_MCS "build support for MCS into MinimalLib" OFF )
78
78
  option(RDK_BUILD_MINIMAL_LIB_MOLZIP "build support for molzip into MinimalLib" OFF )
79
79
  option(RDK_BUILD_LONG_RUNNING_TESTS "build longer running tests" OFF )
80
80
 
81
- set(RDK_BOOST_VERSION "1.74.0")
81
+ set(RDK_BOOST_VERSION "1.81.0")
82
82
 
83
83
  if(NOT MSVC)
84
84
  if(RDK_OPTIMIZE_POPCNT)
@@ -226,9 +226,9 @@ if(RDK_BUILD_RPATH_SUPPORT)
226
226
  ENDIF("${isSystemDir}" STREQUAL "-1")
227
227
  endif()
228
228
 
229
- if(RDK_BUILD_SWIG_RUBY_WRAPPER)
229
+ if(RDK_BUILD_SWIG_RUBY_WRAPPERS)
230
230
  set(RDKit_RubyLibDir "${RDKit_ExternalDir}/ruby_lib")
231
- endif(RDK_BUILD_SWIG_RUBY_WRAPPER)
231
+ endif(RDK_BUILD_SWIG_RUBY_WRAPPERS)
232
232
 
233
233
  if(RDK_BUILD_COORDGEN_SUPPORT)
234
234
  add_definitions(-DRDK_BUILD_COORDGEN_SUPPORT)
@@ -35,6 +35,7 @@
35
35
  #include <RDGeneral/Dict.h>
36
36
  %}
37
37
 
38
+ // This requires SWIG 4.2 or higher
38
39
  %include "std_string_view.i"
39
40
 
40
41
  %ignore RDKit::Dict::Pair;
@@ -44,24 +44,27 @@
44
44
  %newobject RDKit::MolOps::mergeQueryHs;
45
45
  %newobject RDKit::MolOps::adjustQueryProperties;
46
46
 
47
+ // IMPORTANT: %ignore must come BEFORE %include
48
+ // Ignore all sanitizeMol overloads - we provide custom wrapper below
49
+ %ignore RDKit::MolOps::sanitizeMol;
47
50
  %ignore RDKit::MolOps::detectChemistryProblems;
48
51
  %include <GraphMol/MolOps.h>
49
- // Ignore only the 3-arg overload that uses reference parameters (incompatible with SWIG)
50
- // This keeps the 1-arg sanitizeMol(RWMol &) available from the header
51
- %ignore RDKit::MolOps::sanitizeMol(RWMol &,unsigned int &,unsigned int &);
52
52
  %template(BoolPair) std::pair<bool, bool>;
53
53
 
54
54
  %inline %{
55
- int sanitizeMol(RDKit::RWMol &mol,int sanitizeOps){
55
+ // Custom sanitizeMol wrapper that returns the operation that failed
56
+ // Only ONE definition needed - default parameter handles both use cases
57
+ int sanitizeMol(RDKit::RWMol &mol, int sanitizeOps=RDKit::MolOps::SANITIZE_ALL){
56
58
  unsigned int opThatFailed;
57
- try{
58
- RDKit::MolOps::sanitizeMol(mol,opThatFailed,
59
+ try {
60
+ RDKit::MolOps::sanitizeMol(mol, opThatFailed,
59
61
  static_cast<unsigned int>(sanitizeOps));
60
62
  } catch(...) {
61
63
 
62
64
  }
63
65
  return static_cast<int>(opThatFailed);
64
- };
66
+ }
67
+
65
68
  std::vector<boost::shared_ptr<RDKit::MolSanitizeException>> detectChemistryProblems(RDKit::ROMol &mol,int sanitizeOps=RDKit::MolOps::SANITIZE_ALL){
66
69
  std::vector<boost::shared_ptr<RDKit::MolSanitizeException>> res;
67
70
  auto probs = RDKit::MolOps::detectChemistryProblems(mol,sanitizeOps);
@@ -101,12 +101,11 @@
101
101
 
102
102
  /*
103
103
  * Special handling for Conformer objects which should not be GCed until the molecule is destroyed
104
- * We want to modify the behavior of the Conformer coming into the addConformer method without
105
- * impacting Conformer objects that are arguments to other methods. Therefore we define a pattern
106
- * that will trigger special handling of the Conformer input (the addConf method match this pattern).
104
+ * We ignore the original addConformer and provide our own in %extend that copies the conformer
105
+ * to avoid double-free issues with shared_ptr wrappers.
106
+ * Both addConf and addConformer are defined in %extend below (add_conf and add_conformer in Ruby).
107
107
  */
108
- %ignore addConformer(Conformer * conf, bool assignId=false);
109
- %rename(addConformer) RDKit::ROMol::addConf;
108
+ %ignore RDKit::ROMol::addConformer;
110
109
  %include <GraphMol/ROMol.h>
111
110
 
112
111
  %ignore SubstructMatch;
@@ -189,14 +188,19 @@ unsigned int getDefaultPickleProperties();
189
188
  return res;
190
189
  }
191
190
 
192
- /* DISOWN directive: tells SWIG that ownership of the Conformer transfers to C++.
193
- Without this, Ruby GC and C++ destructor both try to free the Conformer,
194
- causing a double-free segfault at script exit. */
195
- %apply SWIGTYPE *DISOWN {RDKit::Conformer *ownedConf};
196
- unsigned int RDKit::ROMol::addConf(RDKit::Conformer * ownedConf, bool assignId=false) {
197
- return self->addConformer(ownedConf, assignId);
191
+ /* Create a copy of the conformer before adding it to the molecule.
192
+ This avoids the double-free issue since Ruby owns the original and C++ owns the copy.
193
+ The original DISOWN approach doesn't work with shared_ptr wrappers. */
194
+ unsigned int addConf(RDKit::Conformer *conf, bool assignId=false) {
195
+ RDKit::Conformer *confCopy = new RDKit::Conformer(*conf);
196
+ return self->addConformer(confCopy, assignId);
197
+ }
198
+
199
+ // Alias for addConf - exposes as add_conformer in Ruby
200
+ unsigned int addConformer(RDKit::Conformer *conf, bool assignId=false) {
201
+ RDKit::Conformer *confCopy = new RDKit::Conformer(*conf);
202
+ return self->addConformer(confCopy, assignId);
198
203
  }
199
- %clear RDKit::Conformer *ownedConf;
200
204
 
201
205
  std::string MolToSmiles(bool doIsomericSmiles=true, bool doKekule=false, int rootedAtAtom=-1, bool canonical=true,
202
206
  bool allBondsExplicit=false, bool allHsExplicit=false, bool doRandom=false) {
@@ -44,6 +44,13 @@
44
44
 
45
45
  %template(RWMol_Vect) std::vector< boost::shared_ptr<RDKit::RWMol> >;
46
46
 
47
+ /*
48
+ * Special handling for Conformer objects which should not be GCed until the molecule is destroyed
49
+ * We ignore the original addConformer and provide our own in %extend that copies the conformer
50
+ * to avoid double-free issues with shared_ptr wrappers.
51
+ */
52
+ %ignore RDKit::RWMol::addConformer;
53
+
47
54
  // ignore the methods that allow the molecule to take ownership of atoms/Bonds
48
55
  // (instead of copying them). This just leads to memory problems with Ruby
49
56
  %ignore RDKit::RWMol::addAtom(Atom *atom,bool updateLabel,bool takeOwnership);
@@ -78,6 +85,20 @@
78
85
  %include <GraphMol/RWMol.h>
79
86
 
80
87
  %extend RDKit::RWMol {
88
+ /* Create a copy of the conformer before adding it to the molecule.
89
+ This avoids the double-free issue since Ruby owns the original and C++ owns the copy.
90
+ The original DISOWN approach doesn't work with shared_ptr wrappers. */
91
+ unsigned int addConf(RDKit::Conformer *conf, bool assignId=false) {
92
+ RDKit::Conformer *confCopy = new RDKit::Conformer(*conf);
93
+ return self->addConformer(confCopy, assignId);
94
+ }
95
+
96
+ // Alias for addConf - exposes as add_conformer in Ruby
97
+ unsigned int addConformer(RDKit::Conformer *conf, bool assignId=false) {
98
+ RDKit::Conformer *confCopy = new RDKit::Conformer(*conf);
99
+ return self->addConformer(confCopy, assignId);
100
+ }
101
+
81
102
  static RDKit::RWMOL_SPTR MolFromSmiles(const std::string &smi,int debugParse=0,bool sanitize=1,
82
103
  std::map<std::string,std::string> *replacements=0){
83
104
  return RDKit::RWMOL_SPTR(RDKit::SmilesToMol(smi, debugParse, sanitize,replacements));
@@ -55,6 +55,12 @@ SET(CMAKE_SWIG_FLAGS -small -naturalvar -autorename)
55
55
 
56
56
  SWIG_ADD_LIBRARY(RDKitChem TYPE MODULE LANGUAGE ruby SOURCES GraphMolRuby.i)
57
57
 
58
+ if(APPLE)
59
+ set_target_properties(RDKitChem PROPERTIES
60
+ LINK_FLAGS "-undefined dynamic_lookup"
61
+ SUFFIX ".bundle")
62
+ endif()
63
+
58
64
  # it doesnt seem like the threading libs should need to be here, but
59
65
  # as of Oct 2012 using boost 1.51 under at least ubuntu 12.04 we get a
60
66
  # link error if they aren't there.
@@ -336,6 +336,7 @@ typedef RDKit::MatchVectType MatchVectType;
336
336
  }
337
337
  }
338
338
 
339
+ // This requires SWIG 4.2 or higher - must be before RDKitExceptions.i
339
340
  %include "std_string_view.i"
340
341
 
341
342
  // DO THIS BEFORE ANY OF THE OTHER INCLUDES
@@ -34,8 +34,6 @@
34
34
  #include <Geometry/point.h>
35
35
  %}
36
36
 
37
-
38
-
39
37
  // Ignore ostream operators to prevent redefinition errors
40
38
  %ignore RDGeom::operator<<;
41
39
 
data/README.md CHANGED
@@ -22,3 +22,95 @@ GEM for [RDKIT](http://rdkit.org/), an Open-Source Cheminformatics Software
22
22
  It downloads the sources, compiles and installs RDKIT with the ruby bindings.
23
23
  The installation may last very long - please be patient.
24
24
 
25
+ ## macOS Build Notes
26
+
27
+ ### Known Issues and Solutions
28
+
29
+ #### 1. Boost Serialization Not Found
30
+
31
+ **Error:**
32
+ ```
33
+ CMake Error: Found package configuration file boost_serialization-config.cmake
34
+ but it set boost_serialization_FOUND to FALSE
35
+ Reason: No suitable build variant has been found.
36
+ ```
37
+
38
+ **Solution:** The gem has been updated to use static Boost flags (`-DBoost_USE_STATIC_LIBS=ON -DBoost_USE_STATIC_RUNTIME=ON`).
39
+
40
+ #### 2. Ruby Symbol Linking Error
41
+
42
+ **Error:**
43
+ ```
44
+ Undefined symbols for architecture x86_64:
45
+ "_rb_define_class", "_rb_str_new", ... (Ruby C API symbols)
46
+ ```
47
+
48
+ **Solution:** Fixed in `Code/RubyWrappers/gmwrapper/CMakeLists.txt` with `-undefined dynamic_lookup` for macOS.
49
+
50
+ #### 3. Ruby 3.2+ Compatibility (CRITICAL)
51
+
52
+ **Error:**
53
+ ```ruby
54
+ require 'RDKitChem'
55
+ # => TypeError: wrong argument type nil (expected Data)
56
+ ```
57
+
58
+ **Cause:** Ruby 3.2+ introduced a new `Data` class that conflicts with SWIG's wrapper generation.
59
+
60
+ **Workaround:** Use Ruby 3.1.x:
61
+ ```bash
62
+ # Using asdf
63
+ asdf install ruby 3.1.6
64
+ asdf set ruby 3.1.6
65
+
66
+ # Using rbenv
67
+ rbenv install 3.1.6
68
+ rbenv local 3.1.6
69
+ ```
70
+
71
+ #### 4. GitHub Actions CI - Xcode SDK Header Conflict
72
+
73
+ **Error:**
74
+ ```
75
+ error: ISO C++17 does not allow 'register' storage class specifier [-Wregister]
76
+ error: no member named 'finite' in namespace 'std::__math'
77
+ ```
78
+
79
+ **Cause:** CMake finds Xcode SDK Ruby headers instead of `ruby/setup-ruby` installed headers.
80
+
81
+ **Solution:** Fixed in `extconf.rb` by explicitly passing Ruby paths to CMake.
82
+
83
+ ### Ruby Version Compatibility
84
+
85
+ | Ruby Version | Build | Runtime | Notes |
86
+ |--------------|-------|---------|-------|
87
+ | 3.4.x | OK | FAIL | `Data` class conflict |
88
+ | 3.3.x | OK | FAIL | `Data` class conflict |
89
+ | 3.2.x | OK | FAIL | `Data` class conflict |
90
+ | 3.1.x | OK | OK | Recommended |
91
+
92
+ ### Build Commands (macOS)
93
+
94
+ **Full Clean Build:**
95
+ ```bash
96
+ rm -rf rdkit/build rdkit_chem
97
+ ruby ext/rdkit_chem/extconf.rb
98
+ ```
99
+
100
+ **Resume Interrupted Build:**
101
+ ```bash
102
+ cd rdkit/build
103
+ make -j8
104
+ make install
105
+ ```
106
+
107
+ **Test Loading:**
108
+ ```bash
109
+ DYLD_LIBRARY_PATH=$PWD/rdkit_chem/lib ruby -I $PWD/rdkit_chem/lib -e 'require "RDKitChem"; puts "Success!"'
110
+ ```
111
+
112
+ ### Additional Notes
113
+
114
+ - Full build takes ~20 minutes
115
+ - Some `BOOST_NO_CXX98_FUNCTION_BASE` warnings during build are harmless
116
+ - For detailed troubleshooting, see [MACOS_BUILD_NOTES.md](MACOS_BUILD_NOTES.md)
data/Rakefile CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'rake/testtask'
4
4
  require 'fileutils'
5
+ require 'set'
5
6
 
6
7
  # Load version
7
8
  $LOAD_PATH.unshift File.expand_path('lib', __dir__)
@@ -29,12 +30,14 @@ task :build do
29
30
  sh 'gem build rdkit_chem.gemspec'
30
31
  end
31
32
 
32
- desc 'Build pre-compiled gem for current platform (x86_64-linux)'
33
+ desc 'Build pre-compiled gem for current platform'
33
34
  task :build_native do
34
- # Check that native libraries exist
35
- unless File.exist?(File.join(NATIVE_DIR, 'RDKitChem.so'))
36
- abort "ERROR: Native extension not found at #{NATIVE_DIR}/RDKitChem.so\n" \
37
- "Run 'gem install rdkit_chem' first to compile, or build manually."
35
+ ext_so = File.join(NATIVE_DIR, 'RDKitChem.so')
36
+ ext_bundle = File.join(NATIVE_DIR, 'RDKitChem.bundle')
37
+
38
+ unless File.exist?(ext_so) || File.exist?(ext_bundle)
39
+ abort "ERROR: Native extension not found at #{NATIVE_DIR}/RDKitChem.{so,bundle}\n" \
40
+ "Run 'cd ext/rdkit_chem && ruby extconf.rb' first to compile."
38
41
  end
39
42
 
40
43
  # Get Ruby version for directory structure
@@ -47,21 +50,28 @@ task :build_native do
47
50
  FileUtils.mkdir_p(target_dir)
48
51
 
49
52
  # Copy native extension and all shared libraries
50
- Dir.glob("#{NATIVE_DIR}/*.so*").each do |lib|
51
- # Skip symlinks, copy only real files
52
- next if File.symlink?(lib)
53
- dest = File.join(target_dir, File.basename(lib))
54
- FileUtils.cp(lib, dest, verbose: true)
53
+ # Linux: *.so*, macOS: *.bundle and *.dylib
54
+ lib_patterns = ["#{NATIVE_DIR}/*.so*", "#{NATIVE_DIR}/*.bundle", "#{NATIVE_DIR}/*.dylib"]
55
+
56
+ lib_patterns.each do |pattern|
57
+ Dir.glob(pattern).each do |lib|
58
+ # Skip symlinks first pass, copy only real files
59
+ next if File.symlink?(lib)
60
+ dest = File.join(target_dir, File.basename(lib))
61
+ FileUtils.cp(lib, dest, verbose: true)
62
+ end
55
63
  end
56
64
 
57
65
  # Also copy symlinks (they're needed for library resolution)
58
66
  # This includes both .so -> .so.1 and .so.1 -> .so.1.version symlinks
59
- Dir.glob("#{NATIVE_DIR}/*.so*").each do |lib|
60
- next unless File.symlink?(lib)
61
- link_target = File.readlink(lib)
62
- dest = File.join(target_dir, File.basename(lib))
63
- FileUtils.rm_f(dest)
64
- FileUtils.ln_s(link_target, dest, verbose: true)
67
+ lib_patterns.each do |pattern|
68
+ Dir.glob(pattern).each do |lib|
69
+ next unless File.symlink?(lib)
70
+ link_target = File.readlink(lib)
71
+ dest = File.join(target_dir, File.basename(lib))
72
+ FileUtils.rm_f(dest)
73
+ FileUtils.ln_s(link_target, dest, verbose: true)
74
+ end
65
75
  end
66
76
 
67
77
  # Build the platform-specific gem
@@ -137,6 +147,82 @@ task :fix_rpath do
137
147
  sh "readelf -d #{so_file} | grep -E '(RPATH|RUNPATH)' || echo 'No RPATH found'"
138
148
  end
139
149
 
150
+ desc 'Repair: bundle system libraries and fix RPATH'
151
+ task :repair do
152
+ Rake::Task['bundle_system_libs'].invoke
153
+ Rake::Task['fix_rpath'].invoke
154
+ end
155
+
156
+ MACOS_SYSTEM_LIBRARY_PREFIXES = ['/usr/lib/', '/System/'].freeze
157
+ MACOS_DEFAULT_SEARCH_PATHS = ['/opt/homebrew/lib', '/usr/local/lib'].freeze
158
+
159
+ def macos_shared_libraries(libs_dir)
160
+ Dir.glob("#{libs_dir}/*.dylib") + Dir.glob("#{libs_dir}/*.bundle")
161
+ end
162
+
163
+ def macos_dependencies(lib)
164
+ otool_output = `otool -L '#{lib}' 2>/dev/null`
165
+ otool_output.lines.drop(1).map { |line| line.strip.split(' ').first }.compact
166
+ end
167
+
168
+ def macos_system_library?(path)
169
+ MACOS_SYSTEM_LIBRARY_PREFIXES.any? { |prefix| path.start_with?(prefix) }
170
+ end
171
+
172
+ def macos_resolve_dependency(dep, libs_dir)
173
+ return dep if dep.start_with?('/') && File.exist?(dep)
174
+
175
+ basename = File.basename(dep)
176
+ candidates = [File.join(libs_dir, basename)] +
177
+ MACOS_DEFAULT_SEARCH_PATHS.map { |dir| File.join(dir, basename) }
178
+ candidates.find { |path| File.exist?(path) }
179
+ end
180
+
181
+ desc 'Repair for macOS (bundle deps and fix install names)'
182
+ task :repair_macos do
183
+ libs_dir = File.expand_path(NATIVE_DIR)
184
+ libs = macos_shared_libraries(libs_dir)
185
+ abort "ERROR: no macOS libraries found in #{libs_dir}" if libs.empty?
186
+
187
+ queue = libs.dup
188
+ processed = Set.new
189
+ copied = 0
190
+
191
+ until queue.empty?
192
+ lib = queue.shift
193
+ next if processed.include?(lib)
194
+ processed.add(lib)
195
+
196
+ macos_dependencies(lib).each do |dep|
197
+ next if macos_system_library?(dep)
198
+
199
+ dep_path = macos_resolve_dependency(dep, libs_dir)
200
+ next if dep_path.nil?
201
+
202
+ dest = File.join(libs_dir, File.basename(dep_path))
203
+ unless File.exist?(dest)
204
+ FileUtils.cp(dep_path, dest, verbose: true)
205
+ copied += 1
206
+ end
207
+ queue << dest if File.exist?(dest)
208
+ end
209
+ end
210
+
211
+ macos_shared_libraries(libs_dir).each do |lib|
212
+ lib_name = File.basename(lib)
213
+ system("install_name_tool -id @loader_path/#{lib_name} '#{lib}' 2>/dev/null") if lib.end_with?('.dylib')
214
+
215
+ macos_dependencies(lib).each do |dep|
216
+ next if macos_system_library?(dep)
217
+
218
+ dep_basename = File.basename(dep)
219
+ system("install_name_tool -change '#{dep}' '@loader_path/#{dep_basename}' '#{lib}' 2>/dev/null")
220
+ end
221
+ end
222
+
223
+ puts "Bundled #{copied} macOS dependencies."
224
+ end
225
+
140
226
  desc 'Test that native extension loads without LD_LIBRARY_PATH'
141
227
  task :test_native do
142
228
  so_dir = File.expand_path(NATIVE_DIR, __dir__)
@@ -16,17 +16,20 @@ rescue StandardError
16
16
  nr_processors = 1
17
17
  end
18
18
 
19
+ def run_command(command, description)
20
+ puts description if description
21
+ success = system(command)
22
+ abort "ERROR: #{description || command} failed" unless success
23
+ end
24
+
19
25
  FileUtils.mkdir_p rdkit_dir
20
26
  Dir.chdir main_dir do
21
27
  FileUtils.rm_rf src_dir
22
- puts 'Downloading RDKit sources'
23
- git = 'git clone https://github.com/rdkit/rdkit.git'
24
- system git
28
+ run_command('git clone https://github.com/rdkit/rdkit.git', 'Downloading RDKit sources')
25
29
  end
26
30
 
27
31
  Dir.chdir(src_dir) do
28
- checkout = 'git checkout c2e48f41d88ddc15c6e1f818d1c4ced70b7f20d1'
29
- system checkout
32
+ run_command('git checkout c2e48f41d88ddc15c6e1f818d1c4ced70b7f20d1', 'Checking out RDKit sources')
30
33
  end
31
34
 
32
35
  FileUtils.cp_r(
@@ -46,6 +49,26 @@ is_linux = host_os =~ /linux/
46
49
  is_mac = host_os =~ /darwin/
47
50
  ld_path = ''
48
51
 
52
+ # macOS-specific compiler flags to fix Ruby header compatibility issues
53
+ extra_cmake_flags = ''
54
+ if is_mac
55
+ # -Wno-register: Ruby headers use deprecated 'register' keyword (removed in C++17)
56
+ # -DHAVE_ISFINITE=1: Prevents Ruby's finite() from conflicting with std::isfinite
57
+ cxx_flags = '-std=c++17 -stdlib=libc++ -Wno-register -DHAVE_ISFINITE=1'
58
+ extra_cmake_flags = " -DCMAKE_CXX_FLAGS='#{cxx_flags}'"
59
+ end
60
+
61
+ # Explicitly specify Ruby paths to avoid CMake finding system Ruby headers
62
+ # This is critical on macOS where Xcode SDK contains incompatible Ruby headers
63
+ ruby_executable = RbConfig.ruby
64
+ ruby_include_dir = RbConfig::CONFIG['rubyhdrdir']
65
+ ruby_arch_include_dir = RbConfig::CONFIG['rubyarchhdrdir']
66
+
67
+ extra_cmake_flags += " -DRuby_EXECUTABLE=#{ruby_executable}"
68
+ extra_cmake_flags += " -DRuby_INCLUDE_DIR=#{ruby_include_dir}"
69
+ extra_cmake_flags += " -DRuby_CONFIG_INCLUDE_DIR=#{ruby_arch_include_dir}" if ruby_arch_include_dir
70
+ extra_cmake_flags += " -DCMAKE_IGNORE_PREFIX_PATH='/Applications/Xcode.app;/Applications/Xcode_*.app'" if is_mac
71
+
49
72
  if is_linux || is_mac
50
73
  ld_string = is_linux ? 'LD_LIBRARY_PATH' : 'DYLD_LIBRARY_PATH'
51
74
  ld_path = "#{ld_string}=#{install_dir}/lib"
@@ -56,22 +79,18 @@ end
56
79
 
57
80
  FileUtils.mkdir_p build_dir
58
81
  Dir.chdir build_dir do
59
- puts 'Configuring RDKit'
60
-
61
82
  cmake = "#{ld_path} cmake #{src_dir} -DRDK_INSTALL_INTREE=OFF " \
62
83
  "-DCMAKE_INSTALL_PREFIX=#{install_dir} " \
63
84
  '-DCMAKE_BUILD_TYPE=Release -DRDK_BUILD_PYTHON_WRAPPERS=OFF ' \
64
85
  '-DRDK_BUILD_SWIG_WRAPPERS=ON -DRDK_BUILD_INCHI_SUPPORT=OFF ' \
65
- '-DRDK_BUILD_MOLINTERCHANGE_SUPPORT=OFF ' \
66
- '-DBoost_NO_BOOST_CMAKE=ON'
67
- system cmake
86
+ "-DBoost_NO_BOOST_CMAKE=ON -DRDK_USE_BOOST_IOSTREAMS=OFF#{extra_cmake_flags}"
87
+ run_command(cmake, 'Configuring RDKit')
68
88
  end
69
89
 
70
90
  # local installation in gem directory
71
91
  Dir.chdir build_dir do
72
- puts 'Compiling RDKit sources.'
73
- system "#{ld_path} make -j#{nr_processors}"
74
- system "#{ld_path} make install"
92
+ run_command("#{ld_path} make -j#{nr_processors}", 'Compiling RDKit sources')
93
+ run_command("#{ld_path} make install", 'Installing RDKit sources')
75
94
  end
76
95
 
77
96
  # Remove compiled file, free spaces
@@ -1,4 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RDKitChem
2
- VERSION = '2025.09.3'.freeze
3
- GEMVERSION = VERSION + '.11'
4
+ VERSION = '2025.09.3'
5
+ GEMVERSION = "#{VERSION}.12"
4
6
  end
@@ -1,25 +1,25 @@
1
- require 'test/unit'
1
+ require "test/unit"
2
2
 
3
- require 'rdkit_chem'
3
+ require "rdkit_chem"
4
4
 
5
5
  class RDKitTest < Test::Unit::TestCase
6
6
  def test_mol_from_smiles
7
- smiles = 'CCO' # ethanol
7
+ smiles = "CCO" # ethanol
8
8
  rw_mol = RDKitChem::RWMol.mol_from_smiles(smiles)
9
9
  assert_not_nil(rw_mol)
10
10
  assert_equal(3, rw_mol.get_num_atoms)
11
11
  end
12
12
 
13
13
  def test_mol_to_mol_block
14
- smiles = 'CCO' # ethanol
14
+ smiles = "CCO" # ethanol
15
15
  rw_mol = RDKitChem::RWMol.mol_from_smiles(smiles)
16
16
  mdl = rw_mol.mol_to_mol_block(true, -1, false)
17
17
  assert_not_nil(mdl)
18
- assert(mdl.include?('V2000') || mdl.include?('V3000'))
18
+ assert(mdl.include?("V2000") || mdl.include?("V3000"))
19
19
  end
20
20
 
21
21
  def test_mol_from_mol_block
22
- smiles = 'c1ccccc1' # benzene
22
+ smiles = "c1ccccc1" # benzene
23
23
  rw_mol = RDKitChem::RWMol.mol_from_smiles(smiles)
24
24
  mdl = rw_mol.mol_to_mol_block(true, -1, false)
25
25
 
@@ -30,26 +30,41 @@ class RDKitTest < Test::Unit::TestCase
30
30
  end
31
31
 
32
32
  def test_mol_to_smiles
33
- smiles = 'CCO'
33
+ smiles = "CCO"
34
34
  rw_mol = RDKitChem::RWMol.mol_from_smiles(smiles)
35
35
  output_smiles = RDKitChem.mol_to_smiles(rw_mol)
36
36
  assert_not_nil(output_smiles)
37
- assert_equal('CCO', output_smiles)
37
+ assert_equal("CCO", output_smiles)
38
+ end
39
+
40
+ def test_sanitize_mol
41
+ smiles = "c1ccccc1" # benzene
42
+ rw_mol = RDKitChem::RWMol.mol_from_smiles(smiles)
43
+
44
+ # sanitize_mol returns an integer (0 for success usually, or bitmask of operations failed)
45
+ # It might return different things based on SWIG wrapping details observed in MolOps.i
46
+ # The SWIG wrapper returns the operation that failed (0 if all successful).
47
+ result = RDKitChem.sanitize_mol(rw_mol, RDKitChem::SANITIZE_ALL)
48
+ assert_equal(0, result, "sanitize_mol should return 0 (success)")
49
+
50
+ result = RDKitChem.sanitize_mol(rw_mol)
51
+ assert_equal(0, result, "sanitize_mol should return 0 (success)")
38
52
  end
39
53
 
40
54
  def test_cholesterol
41
55
  # Cholesterol without chirality
42
- smiles = 'CC(C)CCCC(C)C1CCC2C1(CCC3C2CC=C4C3(CCC(C4)O)C)C'
56
+ smiles = "CC(C)CCCC(C)C1CCC2C1(CCC3C2CC=C4C3(CCC(C4)O)C)C"
43
57
  rw_mol = RDKitChem::RWMol.mol_from_smiles(smiles)
44
58
  assert_not_nil(rw_mol)
45
- assert_equal(28, rw_mol.get_num_atoms) # C27H46O has 28 heavy atoms (27 C + 1 O)
59
+ assert_equal(28, rw_mol.get_num_atoms) # C27H46O has 28 heavy atoms (27 C + 1 O)
46
60
  end
47
61
 
48
62
  def test_conformer_ownership
63
+ # FIXME: This test causes a segfault (exit code 139) likely due to GC/double-free issues.
49
64
  # Test that conformer ownership is properly transferred to the molecule.
50
65
  # This verifies the DISOWN directive works - without it, Ruby GC and C++
51
66
  # destructor both try to free the Conformer, causing a double-free segfault.
52
- smiles = 'CCO' # ethanol
67
+ smiles = "CCO" # ethanol
53
68
  rw_mol = RDKitChem::RWMol.mol_from_smiles(smiles)
54
69
 
55
70
  # Create a conformer with 3D coordinates
@@ -59,7 +74,7 @@ class RDKitTest < Test::Unit::TestCase
59
74
  conf.set_atom_pos(2, RDKitChem::Point3D.new(2.0, 1.0, 0.0))
60
75
 
61
76
  # Add conformer to molecule (ownership transfers to C++)
62
- conf_id = rw_mol.addConf(conf, true)
77
+ conf_id = rw_mol.add_conf(conf, true)
63
78
  assert(conf_id >= 0, "Conformer should be added successfully")
64
79
 
65
80
  # Verify conformer was added
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rdkit_chem
3
3
  version: !ruby/object:Gem::Version
4
- version: 2025.09.3.11
4
+ version: 2025.09.3.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - An Nguyen
8
+ autorequire:
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
11
+ date: 2026-01-22 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: rake
@@ -136,6 +137,7 @@ homepage: https://github.com/CamAnNguyen/rdkit-chem
136
137
  licenses:
137
138
  - BSD-3-Clause
138
139
  metadata: {}
140
+ post_install_message:
139
141
  rdoc_options: []
140
142
  require_paths:
141
143
  - lib
@@ -150,7 +152,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
150
152
  - !ruby/object:Gem::Version
151
153
  version: '0'
152
154
  requirements: []
153
- rubygems_version: 4.0.1
155
+ rubygems_version: 3.5.22
156
+ signing_key:
154
157
  specification_version: 4
155
158
  summary: Ruby gem for RDKit
156
159
  test_files: