Windowsでoxを使えるようにした軌跡

##動機
`nokogiri`以外のXMLパーサーないかなー。
[GemのいろいろXML/HTMLparserをまとめたい(Oga特集)](https://qiita.com/gravitonMain/items/720f441713b1378fe55c)を発見。
ふむ、`ox` を使ってみるか。

##流れ
- `gem i ox --platform=mswin64`して上手くいかなかったので、MAKEFILEを編集し、`ox.so` を作る。
- `gem`に登録するのは諦め、`require "ox"`できるよう`site_ruby`に設置する。


## 手順

###環境
- OS: Windows 10

```powershell
> ruby -v
ruby 2.5.0p0 (2017-12-25 revision 61468) [x64-mingw32]
```



### ox.soを作る

- まずは普通に `gem i ox --platform=mswin64`を実行。

```
> gem i ox --platform=mswin64
Temporarily enhancing PATH for MSYS/MINGW...
Building native extensions. This could take a while...
ERROR:  Error installing ox:
        ERROR: Failed to build gem native extension.

    current directory: C:/tools/ruby25/lib/ruby/gems/2.5.0/gems/ox-2.9.3/ext/ox
C:/tools/ruby25/bin/ruby.exe -r ./siteconf20180715-13724-4vbaft.rb extconf.rb
>>>>> Creating Makefile for ruby version 2.5.0 on x64-mingw32 <<<<<
creating Makefile

current directory: C:/tools/ruby25/lib/ruby/gems/2.5.0/gems/ox-2.9.3/ext/ox
make "DESTDIR=" clean

current directory: C:/tools/ruby25/lib/ruby/gems/2.5.0/gems/ox-2.9.3/ext/ox
make "DESTDIR="
generating ox-x64-mingw32.def
make: *** `base64.o' に必要なターゲット `/C/tools/ruby25/include/ruby-2.5.0/ruby.h' を make するルールがありません.  中 止.

make failed, exit code 2

Gem files will remain installed in C:/tools/ruby25/lib/ruby/gems/2.5.0/gems/ox-2.9.3 for inspection.
Results logged to C:/tools/ruby25/lib/ruby/gems/2.5.0/extensions/x64-mingw32/2.5.0/ox-2.9.3/gem_make.out

```

- makeでエラー。
- `make: *** base64.o に必要なターゲット /C/tools/ruby25/include/ruby-2.5.0/ruby.h を make するルールがありません.  中 止.`が怪しい。
- `C:\tools\ruby25\lib\ruby\gems\2.5.0\gems\ox-2.9.3\ext\ox\Makefile`をチェックすると、パスがダメに感じたのでいじる。


```diff:MAKEFILE
- topdir = /C/tools/ruby25/include/ruby-2.5.0
+ topdir = C:/tools/ruby25/include/ruby-2.5.0
```

- cdしてmakeしてみる。

```
> cd C:/tools/ruby25/lib/ruby/gems/2.5.0/gems/ox-2.9.3/ext/ox
> make
compiling base64.c
compiling builder.c
compiling cache.c
compiling cache8.c
compiling dump.c
compiling err.c
compiling gen_load.c
compiling hash_load.c
compiling obj_load.c
compiling ox.c
compiling parse.c
compiling sax.c
compiling sax_as.c
compiling sax_buf.c
compiling sax_hint.c
compiling special.c
linking shared-object ox.so
process_begin: CreateProcess(NULL, rm -f ox.so, ...) failed.
make (e=2): 指定されたファイルが見つかりません。
make: [ox.so] エラー 2 (無視されました)
C:/tools/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/5.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find -lx64-msvcrt-ruby250
C:/tools/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/5.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find -lgmp
collect2.exe: error: ld returned 1 exit status
make: *** [ox.so] エラー 1
```

- `rm -f` がエラーしてるようなので、該当行を削除。
- `C:/tools/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/5.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find -lx64-msvcrt-ruby250`：分からないから放置。
- `（略）: cannot find -lgmp` ：gmpなしでいいや。

```diff:MAKEFILE
- 	-$(Q)$(RM) $(@)
- LIBS = $(LIBRUBYARG_SHARED)  -lgmp -lshell32 -lws2_32 -liphlpapi -limagehlp -lshlwapi
+ LIBS = $(LIBRUBYARG_SHARED) -lshell32 -lws2_32 -liphlpapi -limagehlp -lshlwapi
```

- とりあえず`make`を実行。

```
> make
linking shared-object ox.so
C:/tools/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/5.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find -lx64-msvcrt-ruby250
collect2.exe: error: ld returned 1 exit status
make: *** [ox.so] エラー 1
```

- MAKEFILEを眺めてたら、もう一箇所パスが怪しかったのでいじる。

```diff:MAKEFILE
- prefix = $(DESTDIR)/C/tools/ruby25
+ prefix = C:/tools/ruby25
```

- もう一回`make`を実行。

```
> make
linking shared-object ox.so
```

- とりあえず `ox.so` は作れた。
- ここで奮闘するが、gemとして登録するのを諦める。

###site_rubyにファイルを移動する

- 諸々の`require`をチェック。

```ox.c
// ox.c → ox.so
(略)
    rb_require("ox/version");
    rb_require("ox/error");
    rb_require("ox/hasattrs");
    rb_require("ox/node");
    rb_require("ox/comment");
    rb_require("ox/instruct");
    rb_require("ox/cdata");
    rb_require("ox/doctype");
    rb_require("ox/element");
    rb_require("ox/document");
    rb_require("ox/bag");
    rb_require("ox/sax");
(略)
```

```ox.rb
(略)
require 'ox/version'
require 'ox/error'
require 'ox/hasattrs'
require 'ox/node'
require 'ox/comment'
require 'ox/raw'
require 'ox/instruct'
require 'ox/cdata'
require 'ox/doctype'
require 'ox/element'
require 'ox/document'
require 'ox/bag'
require 'ox/sax'

require 'ox/ox' # C extension
```

- こんな感じに配置すれば良さそう。

```
site_ruby
├── ox.rb
└── ox
    ├── ox.so
    ├── bag.rb
    ├── cdata.rb
    ├── comment.rb
    ├── doctype.rb
    ├── document.rb
    ├── element.rb
    ├── error.rb
    ├── hasattrs.rb
    ├── instruct.rb
    ├── node.rb
    ├── raw.rb
    ├── sax.rb
    ├── version.rb
    └── xmlrpc_adapter.rb
```

- `C:/tools/ruby25/lib/ruby/site_ruby`にファイルを突っ込む。

###oxを実行する

- [GemのいろいろXML/HTMLparserをまとめたい(Oga特集)](https://qiita.com/gravitonMain/items/720f441713b1378fe55c)さんのサンプルをいじって実行。

```irb
irb(main):001:0> require "open-uri"
=> true
irb(main):002:0>
irb(main):003:0> xml = ""
=> ""
irb(main):004:0> open("http://www.nicovideo.jp/ranking/fav/hourly/all?rss=2.0") do |f|
irb(main):005:1*   xml = f.read
irb(main):006:1>   nil
irb(main):007:1> end
=> nil
irb(main):008:0> require 'ox'
=> true
irb(main):009:0> titles = Ox.parse(xml).locate("rss/channel/item/title").map{|n| n.nodes[0]}[0,2]
=> ["\u7B2C1\u4F4D\uFF1A\u3010\u7B2C10\u56DE\u6771\u65B9\u30CB\u30B3\u7AE5\u796D\u3011Pizuya's Cell / \u30E6\u30D3\u30AD\u30BF\u30B9\u30FB\u30B0\u30EA\u30CB\u30C3\u30B8", "\u7B2C2\u4F4D\uFF1A\u3010DbD\u3011\u30B5\u30D0\u30A4\u30D0\u30EB\u30FB\u30AA\u30D6\u30FB\u5B87\u90FD\u5BAE14.mp36"]
irb(main):010:0> puts titles
第1位：【第10回東方ニコ童祭】Pizuya's Cell / ユビキタス・グリニッジ
第2位：【DbD】サバイバル・オブ・宇都宮14.mp36
=> nil
```

- **できた！！**



##コメント
- `gmp`はどこで使われてるのか？
- issues を見てみたら参考事例があった → [Error installing gem: No rule to make target · Issue #165 · ohler55/ox](https://github.com/ohler55/ox/issues/165)
    - 普通にインストールできたのでは？