こんにちは。(ほ)です。

 

最近、RubyのScriptを配布する必要があったのですが、対象者がRuby開発者ではない人がほとんどであったため、Scriptから実行ファイルを生成しました。

そのときに発生したエラーについての対処方法を書きます。

実行ファイルを作成したスクリプト

Google SpreadSheet API のCredentialを作成する目的で、下記のスクリプトを使用しました。

本当は、マルチプラットフォーム対応が容易なGo言語やJavaで作れれば良かったのですが、
下記のように言語で扱う形式がバラバラ、かつRubyと同じYAMLで出力するのは他にはないため、今回の方法となっています。

  • Ruby : YAML
  • Go, .NET, Python等 : JSON
  • Java : ?(オブジェクトファイルっぽい)

実行ファイルを生成するツール

Rubyから実行ファイルの生成には ocra を使用しました。

このツールは、Rubyのスクリプトと ruby.exe や各種lib、gemをパッケージングすることで、
RubyがインストールされていないWindows環境でも実行できるようにしています。

下記のようなコマンドでocraを実行すると、libやgemの依存関係を推測するための実行が行われ、その後にexeが出力されます。

$ ocra quickstart.rb

発生したエラー

ocraを使用して生成したexeを実行すると、下記のエラーが発生しました。

  1. MultiJson::AdapterError
  2. Faraday::SSLError
  3. Faraday::ConnectionFailed

Error No.1 : MultiJson::AdapterError

実行ファイル中にJSONのライブラリが含まれていない場合に、このエラーが発生します。

$ quickstart.exe
C:/Users/xxxxx/AppData/Local/Temp/ocr2CA4.tmp/lib/ruby/2.4.0/rubygems/core_ext/kernel_require.rb:55:in `require': Did not recognize your adapter specification (cannot load such file -- multi_json/adapters/json_gem). (MultiJson::AdapterError)
from C:/Users/xxxxx/AppData/Local/Temp/ocr2CA4.tmp/lib/ruby/2.4.0/rubygems/core_ext/kernel_require.rb:55:in `require'
from C:/Users/xxxxx/AppData/Local/Temp/ocr2CA4.tmp/lib/ruby/gems/2.4.0/gems/multi_json-1.12.1/lib/multi_json.rb:157:in `load_adapter_from_string_name'
from C:/Users/xxxxx/AppData/Local/Temp/ocr2CA4.tmp/lib/ruby/gems/2.4.0/gems/multi_json-1.12.1/lib/multi_json.rb:101:in `load_adapter'
from C:/Users/xxxxx/AppData/Local/Temp/ocr2CA4.tmp/lib/ruby/gems/2.4.0/gems/multi_json-1.12.1/lib/multi_json.rb:103:in `load_adapter'
from C:/Users/xxxxx/AppData/Local/Temp/ocr2CA4.tmp/lib/ruby/gems/2.4.0/gems/multi_json-1.12.1/lib/multi_json.rb:91:in `use'
from C:/Users/xxxxx/AppData/Local/Temp/ocr2CA4.tmp/lib/ruby/gems/2.4.0/gems/multi_json-1.12.1/lib/multi_json.rb:73:in `adapter'
from C:/Users/xxxxx/AppData/Local/Temp/ocr2CA4.tmp/lib/ruby/gems/2.4.0/gems/multi_json-1.12.1/lib/multi_json.rb:133:in `current_adapter'
from C:/Users/xxxxx/AppData/Local/Temp/ocr2CA4.tmp/lib/ruby/gems/2.4.0/gems/multi_json-1.12.1/lib/multi_json.rb:120:in `load'
from C:/Users/xxxxx/AppData/Local/Temp/ocr2CA4.tmp/lib/ruby/gems/2.4.0/gems/googleauth-0.5.3/lib/googleauth/client_id.rb:82:in `block in from_file'
from C:/Users/xxxxx/AppData/Local/Temp/ocr2CA4.tmp/lib/ruby/gems/2.4.0/gems/googleauth-0.5.3/lib/googleauth/client_id.rb:80:in `open'
from C:/Users/xxxxx/AppData/Local/Temp/ocr2CA4.tmp/lib/ruby/gems/2.4.0/gems/googleauth-0.5.3/lib/googleauth/client_id.rb:80:in `from_file'
from C:/Users/xxxxx/AppData/Local/Temp/ocr2CA4.tmp/src/spredmine-credentials.rb:22:in `authorize'
from C:/Users/xxxxx/AppData/Local/Temp/ocr2CA4.tmp/src/spredmine-credentials.rb:44:in `'

原因は、下記のソースでいくつかのJSONライブラリを返すようにしているのですが、
このような場合はocraの依存関係の推測がうまく機能しないようです。

def default_adapter
return :oj if defined?(::Oj)
return :yajl if defined?(::Yajl)
return :jr_jackson if defined?(::JrJackson)
return :json_gem if defined?(::JSON::JSON_LOADED)
return :gson if defined?(::Gson)

そのため、下記のようにimport文をScriptに追加することで対処できます。

import json

実はこれは、下記のようにrubyのすべてのライブラリをパッケージングする --add-all-core オプションを指定することでも対処は可能です。
しかし、.exeのサイズの増加と展開するファイル数の増加により実行までの時間が長くなるため、最小限にした方が良いと思います。

$ ocra quickstart.rb --add-all-core

Error No.2 : Faraday::SSLError

このエラーはSSLの証明書が含まれていない場合に発生します。

$ quickstart.exe
C:/Users/xxxxx/AppData/Local/Temp/ocr580A.tmp/lib/ruby/2.4.0/net/protocol.rb:44:in `connect_nonblock': SSL_connect returned=1 errno=0 state=error: certificate verify failed (Faraday::SSLError)
from C:/Users/xxxxx/AppData/Local/Temp/ocr580A.tmp/lib/ruby/2.4.0/net/protocol.rb:44:in `ssl_socket_connect'
from C:/Users/xxxxx/AppData/Local/Temp/ocr580A.tmp/lib/ruby/2.4.0/net/http.rb:948:in `connect'
from C:/Users/xxxxx/AppData/Local/Temp/ocr580A.tmp/lib/ruby/2.4.0/net/http.rb:887:in `do_start'
from C:/Users/xxxxx/AppData/Local/Temp/ocr580A.tmp/lib/ruby/2.4.0/net/http.rb:876:in `start'
from C:/Users/xxxxx/AppData/Local/Temp/ocr580A.tmp/lib/ruby/2.4.0/net/http.rb:1407:in `request'

RubyのSSL証明書は下記のパスなどにあるのですが、ocraはSSL通信をする場合でも、この証明書をパッケージングしてくれません。

c:\Ruby24\ssl\cert.pem

そのため、下記のようにパッケージングするファイルに証明書を指定することで対処できます。

$ ocra quickstart.rb cert.pem

また、 quickstart.rb に証明書ファイルの環境変数を追加します。

ENV['SSL_CERT_FILE'] = File.join(File.dirname($0), 'cert.pem')

Error No.3 : Faraday::ConnectionFailed

Proxyの環境変数が設定されていないPCで実行した場合に発生しました。

$ quickstart.exe
C:/Users/xxxxx/AppData/Local/Temp/ocr1E44.tmp/lib/ruby/2.4.0/net/http.rb:904:in `initialize': execution expired (Faraday::ConnectionFailed)
from C:/Users/xxxxx/AppData/Local/Temp/ocr1E44.tmp/lib/ruby/2.4.0/net/http.rb:904:in `open'
from C:/Users/xxxxx/AppData/Local/Temp/ocr1E44.tmp/lib/ruby/2.4.0/net/http.rb:904:in `block in connect'
from C:/Users/xxxxx/AppData/Local/Temp/ocr1E44.tmp/lib/ruby/2.4.0/timeout.rb:103:in `timeout'
from C:/Users/xxxxx/AppData/Local/Temp/ocr1E44.tmp/lib/ruby/2.4.0/net/http.rb:902:in `connect'
from C:/Users/xxxxx/AppData/Local/Temp/ocr1E44.tmp/lib/ruby/2.4.0/net/http.rb:887:in `do_start'
from C:/Users/xxxxx/AppData/Local/Temp/ocr1E44.tmp/lib/ruby/2.4.0/net/http.rb:876:in `start'
from C:/Users/xxxxx/AppData/Local/Temp/ocr1E44.tmp/lib/ruby/2.4.0/net/http.rb:1407:in `request'
from C:/Users/xxxxx/AppData/Local/Temp/ocr1E44.tmp/lib/ruby/gems/2.4.0/gems/faraday-0.12.2/lib/faraday/adapter/net_http.rb:80:in `perform_request'
from C:/Users/xxxxx/AppData/Local/Temp/ocr1E44.tmp/lib/ruby/gems/2.4.0/gems/faraday-0.12.2/lib/faraday/adapter/net_http.rb:38:in `block in call'
from C:/Users/xxxxx/AppData/Local/Temp/ocr1E44.tmp/lib/ruby/gems/2.4.0/gems/faraday-0.12.2/lib/faraday/adapter/net_http.rb:85:in `with_net_http_connection'
from C:/Users/xxxxx/AppData/Local/Temp/ocr1E44.tmp/lib/ruby/gems/2.4.0/gems/faraday-0.12.2/lib/faraday/adapter/net_http.rb:33:in `call'
from C:/Users/xxxxx/AppData/Local/Temp/ocr1E44.tmp/lib/ruby/gems/2.4.0/gems/faraday-0.12.2/lib/faraday/request/url_encoded.rb:15:in `call'
from C:/Users/xxxxx/AppData/Local/Temp/ocr1E44.tmp/lib/ruby/gems/2.4.0/gems/faraday-0.12.2/lib/faraday/rack_builder.rb:141:in `build_response'
from C:/Users/xxxxx/AppData/Local/Temp/ocr1E44.tmp/lib/ruby/gems/2.4.0/gems/faraday-0.12.2/lib/faraday/connection.rb:386:in `run_request'
from C:/Users/xxxxx/AppData/Local/Temp/ocr1E44.tmp/lib/ruby/gems/2.4.0/gems/faraday-0.12.2/lib/faraday/connection.rb:186:in `post'
from C:/Users/xxxxx/AppData/Local/Temp/ocr1E44.tmp/lib/ruby/gems/2.4.0/gems/signet-0.7.3/lib/signet/oauth_2/client.rb:960:in `fetch_access_token'
from C:/Users/xxxxx/AppData/Local/Temp/ocr1E44.tmp/lib/ruby/gems/2.4.0/gems/signet-0.7.3/lib/signet/oauth_2/client.rb:998:in `fetch_access_token!'
from C:/Users/xxxxx/AppData/Local/Temp/ocr1E44.tmp/lib/ruby/gems/2.4.0/gems/googleauth-0.5.3/lib/googleauth/signet.rb:69:in `fetch_access_token!'
from C:/Users/xxxxx/AppData/Local/Temp/ocr1E44.tmp/lib/ruby/gems/2.4.0/gems/googleauth-0.5.3/lib/googleauth/user_authorizer.rb:178:in `get_credentials_from_code'
from C:/Users/xxxxx/AppData/Local/Temp/ocr1E44.tmp/lib/ruby/gems/2.4.0/gems/googleauth-0.5.3/lib/googleauth/user_authorizer.rb:200:in `get_and_store_credentials_from_code'
from C:/Users/xxxxx/AppData/Local/Temp/ocr1E44.tmp/src/spredmine-credentials.rb:35:in `authorize'
from C:/Users/xxxxx/AppData/Local/Temp/ocr1E44.tmp/src/spredmine-credentials.rb:45:in `'

これは、下記のようにScriptに環境変数の設定を追加することで対処ができます。

ENV['HTTP_PROXY'] = 'http://proxy:port'

修正内容

quickstart.rb

最終的に下記のようなDiffになるように修正をしました。

require 'fileutils'
+require 'json'
OOB_URI = 'urn:ietf:wg:oauth:2.0:oob'
APPLICATION_NAME = 'Google Sheets API Ruby Quickstart'
-CLIENT_SECRETS_PATH = 'client_secret.json'
+CLIENT_SECRETS_PATH = File.join(Dir.pwd, 'client_secret.json')
-CREDENTIALS_PATH = File.join(Dir.home, '.credentials', "sheets.googleapis.com-ruby-quickstart.yaml")
+CREDENTIALS_PATH = File.join(Dir.pwd, "sheets.googleapis.com-ruby-quickstart.yaml")
SCOPE = Google::Apis::SheetsV4::AUTH_SPREADSHEETS_READONLY
+# This cert.pem is included Ruby Dev environment.
+# e.g. c:\Ruby24\ssl\cert.pem
+ENV['SSL_CERT_FILE'] = File.join(File.dirname($0), 'cert.pem')
+ENV['HTTP_PROXY'] = 'http://proxy:port' # Proxy環境の場合

コマンド

下記のコマンドでgemインストール、実行ファイルの生成を行いました。

$ # Install some gems
$ gem install google-api-client ocra nokogiri
$ # Put client_secret.json to current directory
$ ocra spredmine-credentials.rb cert.pem
$ quickstart.exe

まとめ

ocraは手軽にRubyのScriptから実行ファイルを生成できますが、思わぬところでエラーが発生するようです。

ですが、今回のように必要なファイルが存在しない場合や環境変数が設定されていない場合がほとんどのようですので、慌てずにエラー内容を追ってみてください。
きっと原因が突き止められるはずです。