この記事は、はてなエンジニア Advent Calendarの15日目です。
OpenSSLに依存しているモジュールをインストールしようと思ったときにライブラリが見つからなくて困ることがあります。
例えばmacOS上でhomebrewを使ってOpenSSLをインストールした場合は /usr/local/opt/openssl
以下に配置されるため、モジュールのインストール時にインクルードパスとライブラリパスを指定しないといけません。
cpanmであれば--configure-args
経由でビルドする際の引数を渡すことができますが、コンパイラにどのように渡されるかはMakefile.PL
を読む必要があります。大変ですね。
そこで作ったのが、いいかんじにOpenSSLのパスを取ってきてくれるモジュールCrypt::OpenSSL::Guessです。
これはNet::SSLeayで使われているOpenSSLのパス解決部分をモジュール化したものです。このモジュールは現在Crypt::OpenSSL::RSAとCrypt::OpenSSL::Randomで使われています。
使い方はMakefile.PL
内に以下のように書くだけです。
use ExtUtils::MakerMaker; use Crypt::OpenSSL::Guess; WriteMakefile( # ... LIBS => ['-lssl -lcrypto ' . openssl_lib_paths()], INC => openssl_inc_paths(), );
実装は素直で、パスを順番に探して見つかったものを返すというものです。
この中に含まれていない場合でも簡単にパスを指定できるように、環境変数OPENSSL_PREFIX
に設定できるようにしています。
sub find_openssl_prefix { my ($dir) = @_; if (defined $ENV{OPENSSL_PREFIX}) { return $ENV{OPENSSL_PREFIX}; } my @guesses = ( '/home/linuxbrew/.linuxbrew/opt/openssl/bin/openssl' => '/home/linuxbrew/.linuxbrew/opt/openssl', # LinuxBrew openssl '/usr/local/opt/openssl/bin/openssl' => '/usr/local/opt/openssl', # OSX homebrew openssl '/usr/local/bin/openssl' => '/usr/local', # OSX homebrew openssl '/opt/local/bin/openssl' => '/opt/local', # Macports openssl '/usr/bin/openssl' => '/usr', '/usr/sbin/openssl' => '/usr', '/opt/ssl/bin/openssl' => '/opt/ssl', '/opt/ssl/sbin/openssl' => '/opt/ssl', '/usr/local/ssl/bin/openssl' => '/usr/local/ssl', '/usr/local/openssl/bin/openssl' => '/usr/local/openssl', '/apps/openssl/std/bin/openssl' => '/apps/openssl/std', '/usr/sfw/bin/openssl' => '/usr/sfw', # Open Solaris 'C:\OpenSSL\bin\openssl.exe' => 'C:\OpenSSL', 'C:\OpenSSL-Win32\bin\openssl.exe' => 'C:\OpenSSL-Win32', $Config{prefix} . '\bin\openssl.exe' => $Config{prefix}, # strawberry perl $Config{prefix} . '\..\c\bin\openssl.exe' => $Config{prefix} . '\..\c', # strawberry perl '/sslexe/openssl.exe' => '/sslroot', # VMS, openssl.org '/ssl$exe/openssl.exe' => '/ssl$root', # VMS, HP install ); while (my $k = shift @guesses and my $v = shift @guesses) { if ( -x $k ) { return $v; } } (undef, $dir) = check_no_path() and return $dir; return; }
PerlにはThere's more than one way to do it(やり方はいくつもある)というモットーがありますが、それに続くbut sometimes consistency is not a bad thing either(ときには共通のやり方があっても悪くはない)という言葉があるように、各モジュールが行っていたOpenSSLのパスの解決の仕組みを共通のやり方で出来るようにモジュール化したという話でした。