Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "inchi-gem",
"image": "ruby:3.3.10",
"features": {
"ghcr.io/devcontainers/features/common-utils:2": {
"installZsh": true,
"configureZshAsDefaultShell": true,
"installOhMyZsh": true,
"installOhMyZshConfig": true,
"upgradePackages": true,
"username": "automatic",
"userUid": "automatic",
"userGid": "automatic"
}
},
"customizations": {
"vscode": {
"extensions": [
"eamodio.gitlens"
]
}
},
"postCreateCommand": "apt-get update && apt-get install cmake -y"
}
7 changes: 3 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ Gemfile.lock
pkg/*

# ignore build artifacts
ext/inchi-gem/Makefile
ext/inchi-gem/inchi/
ext/inchi-gem/inchi.o
ext/inchi-gem/inchi_wrap.*
ext/inchi-gem/*

!ext/inchi-gem/extconf.rb
!ext/inchi-gem/inchi.c

.tool-versions
163 changes: 19 additions & 144 deletions ext/inchi-gem/extconf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,153 +2,28 @@
require 'rbconfig'
require 'mkmf'

# current dir
main_dir = File.expand_path(File.join(File.dirname(__FILE__)))
inchi_src = File.join(main_dir, "inchi_src")
libinchi_src = File.join(inchi_src, "INCHI-1-SRC/INCHI_API/libinchi/src")
libinchi_build = File.join(inchi_src, "build")
inchi_version = "d124ee52b4de0ed3d608d2a3d522b939f01f2549"

# ext/inchi-gem/inchi
inchi_src_dir = File.join(main_dir, "inchi")

FileUtils.rm_rf inchi_src_dir

### Download InChI source
FileUtils.rm_rf inchi_src
Dir.chdir main_dir
puts "Downloading InChI sources"
# ext/inchi-gem/inchi/
system "git clone https://github.com/CamAnNguyen/inchi" or abort

# ext/inchi-gem/inchi/INCHI_BASE/src -> inchi-gem/build/inchi/INCHI_API/libinchi/src
FileUtils.cp_r("#{inchi_src_dir}/INCHI_BASE/src/.", "#{inchi_src_dir}/INCHI_API/libinchi/src")

inc_dirs = '-I. -I./inchi/INCHI_API/libinchi/src'
system("git clone --no-checkout --filter=tree:0 https://github.com/IUPAC-InChI/InChI.git #{inchi_src}") or abort
Dir.chdir inchi_src
system("git fetch --depth 1 origin #{inchi_version}") or abort
system("git sparse-checkout set --no-cone /INCHI-1-SRC") or abort
system("git checkout #{inchi_version}") or abort

system("swig #{inc_dirs} -c++ -ruby inchi.i") or abort
### Compile InChI
system("cmake -B #{libinchi_build} -S #{libinchi_src}") or abort
system("cmake --build #{libinchi_build}") or abort

$INCFLAGS << inc_dirs
$CFLAGS << " -DTARGET_API_LIB"
### Configure Ruby extension
find_library('inchi', "MakeINCHIFromMolfileText", File.join(libinchi_build, "lib"))
find_header('inchi_api.h', File.join(inchi_src, "/INCHI-1-SRC/INCHI_BASE/src"))

$srcs = [
'inchi.cpp',
'inchi_wrap.cxx',
'./inchi/INCHI_API/libinchi/src/ichi_bns.c',
'./inchi/INCHI_API/libinchi/src/ichi_io.c',
'./inchi/INCHI_API/libinchi/src/ichican2.c',
'./inchi/INCHI_API/libinchi/src/ichicano.c',
'./inchi/INCHI_API/libinchi/src/ichicans.c',
'./inchi/INCHI_API/libinchi/src/ichierr.c',
'./inchi/INCHI_API/libinchi/src/ichiisot.c',
'./inchi/INCHI_API/libinchi/src/ichilnct.c',
'./inchi/INCHI_API/libinchi/src/ichimak2.c',
'./inchi/INCHI_API/libinchi/src/ichimake.c',
'./inchi/INCHI_API/libinchi/src/ichimap1.c',
'./inchi/INCHI_API/libinchi/src/ichimap2.c',
'./inchi/INCHI_API/libinchi/src/ichimap4.c',
'./inchi/INCHI_API/libinchi/src/ichinorm.c',
'./inchi/INCHI_API/libinchi/src/ichiparm.c',
'./inchi/INCHI_API/libinchi/src/ichiprt1.c',
'./inchi/INCHI_API/libinchi/src/ichiprt2.c',
'./inchi/INCHI_API/libinchi/src/ichiprt3.c',
'./inchi/INCHI_API/libinchi/src/ichiqueu.c',
'./inchi/INCHI_API/libinchi/src/ichiread.c',
'./inchi/INCHI_API/libinchi/src/ichiring.c',
'./inchi/INCHI_API/libinchi/src/ichirvr1.c',
'./inchi/INCHI_API/libinchi/src/ichirvr2.c',
'./inchi/INCHI_API/libinchi/src/ichirvr3.c',
'./inchi/INCHI_API/libinchi/src/ichirvr4.c',
'./inchi/INCHI_API/libinchi/src/ichirvr5.c',
'./inchi/INCHI_API/libinchi/src/ichirvr6.c',
'./inchi/INCHI_API/libinchi/src/ichirvr7.c',
'./inchi/INCHI_API/libinchi/src/ichisort.c',
'./inchi/INCHI_API/libinchi/src/ichister.c',
'./inchi/INCHI_API/libinchi/src/ichitaut.c',
'./inchi/INCHI_API/libinchi/src/ikey_base26.c',
'./inchi/INCHI_API/libinchi/src/ikey_dll.c',
'./inchi/INCHI_API/libinchi/src/inchi_dll.c',
'./inchi/INCHI_API/libinchi/src/inchi_dll_a.c',
'./inchi/INCHI_API/libinchi/src/inchi_dll_a2.c',
'./inchi/INCHI_API/libinchi/src/inchi_dll_b.c',
'./inchi/INCHI_API/libinchi/src/inchi_dll_main.c',
'./inchi/INCHI_API/libinchi/src/inchi_gui.c',
'./inchi/INCHI_API/libinchi/src/mol2atom.c',
'./inchi/INCHI_API/libinchi/src/mol_fmt1.c',
'./inchi/INCHI_API/libinchi/src/mol_fmt2.c',
'./inchi/INCHI_API/libinchi/src/mol_fmt3.c',
'./inchi/INCHI_API/libinchi/src/mol_fmt4.c',
'./inchi/INCHI_API/libinchi/src/readinch.c',
'./inchi/INCHI_API/libinchi/src/runichi.c',
'./inchi/INCHI_API/libinchi/src/runichi2.c',
'./inchi/INCHI_API/libinchi/src/runichi3.c',
'./inchi/INCHI_API/libinchi/src/runichi4.c',
'./inchi/INCHI_API/libinchi/src/sha2.c',
'./inchi/INCHI_API/libinchi/src/strutil.c',
'./inchi/INCHI_API/libinchi/src/util.c',
'./inchi/INCHI_API/libinchi/src/ixa/ixa_builder.c',
'./inchi/INCHI_API/libinchi/src/ixa/ixa_inchikey_builder.c',
'./inchi/INCHI_API/libinchi/src/ixa/ixa_mol.c',
'./inchi/INCHI_API/libinchi/src/ixa/ixa_read_inchi.c',
'./inchi/INCHI_API/libinchi/src/ixa/ixa_read_mol.c',
'./inchi/INCHI_API/libinchi/src/ixa/ixa_status.c',
]

$objs = [
'inchi.o',
'inchi_wrap.o',
'./inchi/INCHI_API/libinchi/src/ichi_bns.o',
'./inchi/INCHI_API/libinchi/src/ichi_io.o',
'./inchi/INCHI_API/libinchi/src/ichican2.o',
'./inchi/INCHI_API/libinchi/src/ichicano.o',
'./inchi/INCHI_API/libinchi/src/ichicans.o',
'./inchi/INCHI_API/libinchi/src/ichierr.o',
'./inchi/INCHI_API/libinchi/src/ichiisot.o',
'./inchi/INCHI_API/libinchi/src/ichilnct.o',
'./inchi/INCHI_API/libinchi/src/ichimak2.o',
'./inchi/INCHI_API/libinchi/src/ichimake.o',
'./inchi/INCHI_API/libinchi/src/ichimap1.o',
'./inchi/INCHI_API/libinchi/src/ichimap2.o',
'./inchi/INCHI_API/libinchi/src/ichimap4.o',
'./inchi/INCHI_API/libinchi/src/ichinorm.o',
'./inchi/INCHI_API/libinchi/src/ichiparm.o',
'./inchi/INCHI_API/libinchi/src/ichiprt1.o',
'./inchi/INCHI_API/libinchi/src/ichiprt2.o',
'./inchi/INCHI_API/libinchi/src/ichiprt3.o',
'./inchi/INCHI_API/libinchi/src/ichiqueu.o',
'./inchi/INCHI_API/libinchi/src/ichiread.o',
'./inchi/INCHI_API/libinchi/src/ichiring.o',
'./inchi/INCHI_API/libinchi/src/ichirvr1.o',
'./inchi/INCHI_API/libinchi/src/ichirvr2.o',
'./inchi/INCHI_API/libinchi/src/ichirvr3.o',
'./inchi/INCHI_API/libinchi/src/ichirvr4.o',
'./inchi/INCHI_API/libinchi/src/ichirvr5.o',
'./inchi/INCHI_API/libinchi/src/ichirvr6.o',
'./inchi/INCHI_API/libinchi/src/ichirvr7.o',
'./inchi/INCHI_API/libinchi/src/ichisort.o',
'./inchi/INCHI_API/libinchi/src/ichister.o',
'./inchi/INCHI_API/libinchi/src/ichitaut.o',
'./inchi/INCHI_API/libinchi/src/ikey_base26.o',
'./inchi/INCHI_API/libinchi/src/ikey_dll.o',
'./inchi/INCHI_API/libinchi/src/inchi_dll.o',
'./inchi/INCHI_API/libinchi/src/inchi_dll_a.o',
'./inchi/INCHI_API/libinchi/src/inchi_dll_a2.o',
'./inchi/INCHI_API/libinchi/src/inchi_dll_b.o',
'./inchi/INCHI_API/libinchi/src/inchi_dll_main.o',
'./inchi/INCHI_API/libinchi/src/inchi_gui.o',
'./inchi/INCHI_API/libinchi/src/mol2atom.o',
'./inchi/INCHI_API/libinchi/src/mol_fmt1.o',
'./inchi/INCHI_API/libinchi/src/mol_fmt2.o',
'./inchi/INCHI_API/libinchi/src/mol_fmt3.o',
'./inchi/INCHI_API/libinchi/src/mol_fmt4.o',
'./inchi/INCHI_API/libinchi/src/readinch.o',
'./inchi/INCHI_API/libinchi/src/runichi.o',
'./inchi/INCHI_API/libinchi/src/runichi2.o',
'./inchi/INCHI_API/libinchi/src/runichi3.o',
'./inchi/INCHI_API/libinchi/src/runichi4.o',
'./inchi/INCHI_API/libinchi/src/sha2.o',
'./inchi/INCHI_API/libinchi/src/strutil.o',
'./inchi/INCHI_API/libinchi/src/util.o',
'./inchi/INCHI_API/libinchi/src/ixa/ixa_builder.o',
'./inchi/INCHI_API/libinchi/src/ixa/ixa_inchikey_builder.o',
'./inchi/INCHI_API/libinchi/src/ixa/ixa_mol.o',
'./inchi/INCHI_API/libinchi/src/ixa/ixa_read_inchi.o',
'./inchi/INCHI_API/libinchi/src/ixa/ixa_read_mol.o',
'./inchi/INCHI_API/libinchi/src/ixa/ixa_status.o',
]

create_makefile('inchi')
Dir.chdir main_dir
create_makefile("inchi")
122 changes: 122 additions & 0 deletions ext/inchi-gem/inchi.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#include <ruby.h>
#include <inchi_api.h>
#include <string.h>


typedef struct {
int returnCode;
VALUE message;
VALUE log;
VALUE auxInfo;
} rb_ExtraInchiReturnValues;

static void
rb_eirv_free(void *ptr)
{
ruby_xfree(ptr);
}

static const rb_data_type_t rb_eirv_type = {
"ExtraInchiReturnValues",
{0, rb_eirv_free, 0},
0, 0,
RUBY_TYPED_FREE_IMMEDIATELY
};

static VALUE
rb_eirv_alloc(VALUE klass)
{
rb_ExtraInchiReturnValues *p;
VALUE obj = TypedData_Make_Struct(klass, rb_ExtraInchiReturnValues,
&rb_eirv_type, p);
p->returnCode = 0;
p->message = Qnil;
p->log = Qnil;
p->auxInfo = Qnil;
return obj;
}

static void
fix_option_symbols(const char *in, char *out)
{
const char *s = in;
char *d = out;
while ((*d = *s)) {
#ifdef _WIN32
if (*d == '-') *d = '/';
#else
if (*d == '/') *d = '-';
#endif
++d; ++s;
}
}

static VALUE
rb_molfile_to_inchi(int argc, VALUE *argv, VALUE self)
{
VALUE rb_mol, rb_rv, rb_opts;
rb_scan_args(argc, argv, "21", &rb_mol, &rb_rv, &rb_opts);
if (NIL_P(rb_opts)) rb_opts = rb_str_new2("");

Check_Type(rb_mol, T_STRING);
Check_Type(rb_opts, T_STRING);

rb_ExtraInchiReturnValues *rv;
TypedData_Get_Struct(rb_rv, rb_ExtraInchiReturnValues,
&rb_eirv_type, rv);

const char *mol_text = RSTRING_PTR(rb_mol);
const char *opts_in = RSTRING_LEN(rb_opts) ? RSTRING_PTR(rb_opts) : NULL;

char *opts_copy = NULL;
if (opts_in) {
opts_copy = ALLOCA_N(char, RSTRING_LEN(rb_opts) + 1);
fix_option_symbols(opts_in, opts_copy);
}

inchi_Output out;
memset(&out, 0, sizeof(out));

int ret = MakeINCHIFromMolfileText(mol_text, opts_copy, &out);

rv->returnCode = ret;
rv->message = (out.szMessage ? rb_str_new2(out.szMessage) : Qnil);
rv->log = (out.szLog ? rb_str_new2(out.szLog) : Qnil);
rv->auxInfo = (out.szAuxInfo ? rb_str_new2(out.szAuxInfo) : Qnil);
VALUE result = out.szInChI ? rb_str_new2(out.szInChI) : rb_str_new2("");
FreeINCHI(&out);
return result;
}

static VALUE
rb_inchi_to_inchi_key(VALUE self, VALUE rb_inchi)
{
Check_Type(rb_inchi, T_STRING);
const char *inchi = RSTRING_PTR(rb_inchi);

char inchi_key[29], xtra1[65], xtra2[65];
int rc = GetINCHIKeyFromINCHI(inchi, 0, 0, inchi_key, xtra1, xtra2);

if (rc == INCHIKEY_OK)
return rb_str_new2(inchi_key);

return Qnil;
}

void
Init_inchi(void)
{
VALUE mInchi = rb_define_module("Inchi");

VALUE cRV = rb_define_class_under(mInchi, "ExtraInchiReturnValues", rb_cObject);
rb_define_alloc_func(cRV, rb_eirv_alloc);
rb_define_attr(cRV, "returnCode", 1, 1);
rb_define_attr(cRV, "message", 1, 1);
rb_define_attr(cRV, "log", 1, 1);
rb_define_attr(cRV, "auxInfo", 1, 1);

rb_define_module_function(mInchi, "molfileToInchi",
rb_molfile_to_inchi, -1);
rb_define_module_function(mInchi, "InchiToInchiKey",
rb_inchi_to_inchi_key, 1);
}
Loading