I’m pretty new to Macintosh development but I’ve been working in Ruby for a couple of years under Win32 and Linux. I was excited by the concept of Ruby and Rails being supported “out-of-the-box” on Leopard with the installation of XCode 3.0. But it didn’t take long for the luster to wear off.I first knew things were not going to be easy when I tried to install MySQL. Since I have a recent MacBook Pro with a Core 2 Duo processor, I went for the x86_64 disk image package (ominous background music starts now). It installed without too many difficulties (but not painless w.r.t the system preference pane). I then went to install the C-based ruby mysql bindings gem:
bimota:lib cch1$ sudo gem install mysql
Building native extensions. This could take a while...
ERROR: Error installing mysql: ERROR: Failed to build gem native extension.
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby extconf.rb install mysql
checking for mysql_query() in -lmysqlclient... nochecking for main() in -lm... yes
checking for mysql_query() in -lmysqlclient... nochecking for main() in -lz... yes
checking for mysql_query() in -lmysqlclient... nochecking for main() in -lsocket... no
checking for mysql_query() in -lmysqlclient... nochecking for main() in -lnsl... no
checking for mysql_query() in -lmysqlclient... no
Gem files will remain installed in /Library/Ruby/Gems/1.8/gems/mysql-2.7 for inspection.
Results logged to /Library/Ruby/Gems/1.8/gems/mysql-2.7/gem_make.out
…and thus began the disenchantment.The failure above is caused by the gem command attempting to compile the native MySQL extensions but not being able to find the necessary library and header files. This can be caused by several more fundamental problems:
- The path used by default to find the MySQL library files (/usr/local/lib/mysql) does not match the default MySQL installation directory (/usr/local/mysql) on Leopard. This could be remedied by passing command line options to the gem command which would ultimately passed to the configurator (extconf.rb).
- The default gem installation tries to build a fat binary with code for four architectures (ppc, ppc64, i386 and i86_64). But, assuming you installed the x86_64 MySQL package, the library file /usr/local/mysql/lib/libmysqlclient.dylib only supports i86_64. This can be remedied either by setting the ARCHFLAGS environment variable before starting the gem command, or by helping the configurator learn the architecture with another command line option.
Both of the above problems can apparently be solved nicely with one command line option that helps the configurator learn the appropriate build options directly from the MySQL installation:
bimota:mysql cch1$ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
Building native extensions. This could take a while...
Successfully installed mysql-2.7
1 gem installed
But you would be foolish to believe that it actually works. Run the same command with the verbose option enabled, and you can see that there is trouble on the horizon despite the apparent success:
bimota:mysql cch1$ sudo gem install -V mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
Installing gem mysql-2.7/Library/Ruby/Gems/1.8/gems/mysql-2.7/COPYING/Library/Ruby/Gems/1.8/gems/mysql-2.7/COPYING.ja/Library/Ruby/Gems/1.8/gems/mysql-2.7/README.html/Library/Ruby/Gems/1.8/gems/mysql-2.7/README_ja.html/Library/Ruby/Gems/1.8/gems/mysql-2.7/extconf.rb/Library/Ruby/Gems/1.8/gems/mysql-2.7/mysql.c.in/Library/Ruby/Gems/1.8/gems/mysql-2.7/test.rb/Library/Ruby/Gems/1.8/gems/mysql-2.7/tommy.css/Library/Ruby/Gems/1.8/gems/mysql-2.7/mysql.gemspec
Building native extensions. This could take a while...
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby extconf.rb install -V mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
checking for mysql_ssl_set()... no
checking for mysql.h... yes
creating Makefile
makegcc -I. -I. -I/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/universal-darwin9.0 -I. -DHAVE_MYSQL_H -I/usr/local/mysql/include -Os -arch x86_64 -fno-common -fno-common -arch ppc -arch i386 -Os -pipe -fno-common -c mysql.ccc -arch ppc -arch i386 -pipe -bundle -undefined dynamic_lookup -o mysql.bundle mysql.o -L"." -L"/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib" -L. -arch ppc -arch i386 -lruby -L/usr/local/mysql/lib -lmysqlclient -lz -lm -lpthread -ldl -lm
ld: warning in /usr/local/mysql/lib/libmysqlclient.dylib, file is not of required architecture
ld: warning in /usr/local/mysql/lib/libmysqlclient.dylib, file is not of required architecture
make install/usr/bin/install -c -m 0755 mysql.bundle /Library/Ruby/Gems/1.8/gems/mysql-2.7/libSuccessfully installed mysql-2.71 gem installed
Indeed, when you go to use (not just ‘require’) the gem, you are likely to see this nasty error:
bimota:mmweb cch1$ rake db:version(in /Users/cch1/Documents/Development/mmweb)dyld: lazy symbol binding failed: Symbol not found: _mysql_init Referenced from: /Library/Ruby/Gems/1.8/gems/mysql-2.7/lib/mysql.bundle Expected in: dynamic lookup
dyld: Symbol not found: _mysql_init Referenced from: /Library/Ruby/Gems/1.8/gems/mysql-2.7/lib/mysql.bundle Expected in: dynamic lookup
Trace/BPT trap
I’m still have no idea what causes this specific failure, but I suspect it’s related to the compiler options suggested by mysql_config being ignored/munged by the configurator. The gcc command above tries to build for three different architectures (ppc, i386, x86_64) despite mysql_config’s “recommendation” of just x86_64:
bimota:mysql cch1$ /usr/local/mysql/bin/mysql_config
Usage: /usr/local/mysql/bin/mysql_config [OPTIONS]
Options:
--cflags [-I/usr/local/mysql/include -Os -arch x86_64 -fno-common]
--include [-I/usr/local/mysql/include]
--libs [-L/usr/local/mysql/lib -lmysqlclient -lz -lm]
--libs_r [-L/usr/local/mysql/lib -lmysqlclient_r -lz -lm]
--socket [/tmp/mysql.sock]
--port [3306]
--version [5.0.51a]
--libmysqld-libs [-L/usr/local/mysql/lib -lmysqld -lz -lm]
I then try forcing the issue by setting the environment variable as well:
bimota:lib cch1$ sudo env ARCHFLAGS="-arch x86_64" gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_configBuilding native extensions. This could take a while...Successfully installed mysql-2.71 gem installed
Again, it looks promising. But now when Rails asks Ruby (1.8.6 from the default XCode 3.0 install) to load the mysql gem, the OS generates a load error due to some kind of a mismatch:
bimota:mmweb cch1$ script/console
Loading development environment (Rails 2.0.2)
>> require_library_or_gem 'mysql'
LoadError: dlopen(/Library/Ruby/Gems/1.8/gems/mysql-2.7/lib/mysql.bundle, 9): no suitable image found. Did find:
/Library/Ruby/Gems/1.8/gems/mysql-2.7/lib/mysql.bundle: mach-o, but wrong architecture - /Library/Ruby/Gems/1.8/gems/mysql-2.7/lib/mysql.bundle
from /Library/Ruby/Gems/1.8/gems/mysql-2.7/lib/mysql.bundle
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems/custom_require.rb:32:in `require'
from /Users/cch1/Documents/Development/mmweb/vendor/rails/activerecord/lib/../../activesupport/lib/active_support/dependencies.rb:496:in `require'
from /Users/cch1/Documents/Development/mmweb/vendor/rails/activerecord/lib/../../activesupport/lib/active_support/dependencies.rb:342:in `new_constants_in'
from /Users/cch1/Documents/Development/mmweb/vendor/rails/activerecord/lib/../../activesupport/lib/active_support/dependencies.rb:496:in `require'
from /Users/cch1/Documents/Development/mmweb/vendor/rails/activerecord/lib/../../activesupport/lib/active_support/core_ext/kernel/requires.rb:7:in `require_library_or_gem'
from /Users/cch1/Documents/Development/mmweb/vendor/rails/activerecord/lib/../../activesupport/lib/active_support/core_ext/kernel/reporting.rb:11:in `silence_warnings'
from /Users/cch1/Documents/Development/mmweb/vendor/rails/activerecord/lib/../../activesupport/lib/active_support/core_ext/kernel/requires.rb:5:in `require_library_or_gem'
from (irb):1
>> ^Dbimota:mmweb cch1$
Why the mismatch? Some googling led me to this post that notes that the Ruby interpreter bundled in XCode 3.0 is in fact only compiled as a 32-bit i386 executable. That’s right: the latest and greatest Macs (with Intel Core 2 Duo processors) running the latest and greatest OS (Leopard 10.5.2) are shipping with a neutered Ruby interpreter.At this point, I see two solutions:1. Install the i386/32-bit only version of MySQL (boo!). I’ve confirmed that this allows a nice working mysql binding to be built with the following command:
bimota:mysql cch1$ sudo env ARCHFLAGS="-arch i386" gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
Building native extensions. This could take a while...Successfully installed mysql-2.71 gem installedbimota:mysql cch1$
And not only does it compile, it actually works.2. Recompile Ruby with x86_64 support. That sounds like a bigger job -but there are web sites that show you how to get started. For now though, I’m leaving XCode alone until I know what works -that way, when it breaks, I can assign the failure to the 64-bit ruby interpreter.