Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create libmysql.a using dlltool.exe on mingw64 #473

Merged
merged 16 commits into from
Jan 10, 2015
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
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ end

group :development do
gem 'pry'
gem 'eventmachine' unless RUBY_PLATFORM =~ /mswin|mingw/
end

platforms :rbx do
Expand Down
30 changes: 14 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Mysql2 - A modern, simple and very fast MySQL library for Ruby - binding to libmysql

[![Build Status](https://travis-ci.org/brianmario/mysql2.png)](https://travis-ci.org/brianmario/mysql2)
Travis CI [![Travis CI Status](https://travis-ci.org/brianmario/mysql2.png)](https://travis-ci.org/brianmario/mysql2)
Appveyor CI [![Appveyor CI Status](https://ci.appveyor.com/api/projects/status/github/sodabrew/mysql2)](https://ci.appveyor.com/project/sodabrew/mysql2)

The Mysql2 gem is meant to serve the extremely common use-case of connecting, querying and iterating on results.
Some database libraries out there serve as direct 1:1 mappings of the already complex C APIs available.
Expand Down Expand Up @@ -52,24 +53,21 @@ are located somewhere different than on your build system.
This overrides any rpath calculated by default or by the options above.

### Windows
First, make sure you have the DevKit installed (http://rubyinstaller.org/downloads/) and its variables
are loaded by running devkit\devktvars.bat .
Make sure that you have Ruby and the DevKit compilers installed. We recommend
the [Ruby Installer](http://rubyinstaller.org) distribution.

Next, you need a MySQL library to link against. If you have MySQL loaded on your development machine,
you can use that. If not, you will need to either copy the MySQL directory from your server, or else
obtain a copy of the MySQL C connector: http://dev.mysql.com/downloads/connector/c/
By default, the mysql2 gem will download and use MySQL Connector/C from
mysql.com. If you prefer to use a local installation of Connector/C, add the
flag `--with-mysql-dir=c:/mysql-connector-c-x-y-z` (_this path may use forward slashes_).

If you're using the connector, I recommend just getting the .zip file and unzipping it someplace convenient.
By default, the `libmysql.dll` library will be copied into the mysql2 gem
directory. To prevent this, add the flag `--no-vendor-libmysql`. The mysql2 gem
will search for `libmysql.dll` in the following paths, in order:

Now you can install mysql2. You must use the `--with-mysql-dir` option to tell gem where your MySQL library
files are. For example, if you unzipped the connector to c:\mysql-connector-c-6.1.1-win32 you would install
the gem like this:

gem install mysql2 -- --with-mysql-dir=c:\mysql-connector-c-6.1.1-win32

Finally, you must copy libmysql.dll from the lib subdirectory of your MySQL or MySQL connector directory into
your ruby\bin directory. In the above example, libmysql.dll would be located at
c:\mysql-connector-c-6.1.1-win32\lib .
* Environment variable `RUBY_MYSQL2_LIBMYSQL_DLL=C:\path\to\libmysql.dll`
(_note the Windows-style backslashes_).
* In the mysql2 gem's own directory `vendor/libmysql.dll`
* In the system's default library search paths.

## Usage

Expand Down
2 changes: 1 addition & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
require 'rake'

# Load custom tasks (careful attention to define tasks before prerequisites)
load 'tasks/vendor_mysql.rake'
load 'tasks/rspec.rake'
load 'tasks/compile.rake'
load 'tasks/generate.rake'
load 'tasks/benchmarks.rake'
load 'tasks/vendor_mysql.rake'

task :default => :spec
43 changes: 43 additions & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
version: "{build}"
clone_depth: 10
install:
- SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
- ruby --version
- gem --version
- gem install bundler --quiet --no-ri --no-rdoc
- bundler --version
- bundle install --without benchmarks
build_script:
- bundle exec rake compile
test_script:
- '"C:\Program Files\MySQL\MySQL Server 5.6\bin\mysql" --version'
- >
"C:\Program Files\MySQL\MySQL Server 5.6\bin\mysql" -u root -p"Password12!" -e "
CREATE DATABASE IF NOT EXISTS test;
CREATE USER '%USERNAME%'@'localhost';
SET PASSWORD = PASSWORD('');
FLUSH PRIVILEGES;
"
- bundle exec rake spec
# Where do I get Unix find?
#on_failure:
# - find tmp -name "*.log" -exec cat {};
environment:
matrix:
- ruby_version: "200"
- ruby_version: "200-x64"
- ruby_version: "21"
- ruby_version: "21-x64"
cache:
- vendor
- C:\Ruby200\lib\ruby\gems\2.0.0
- C:\Ruby200\bin
- C:\Ruby200-x64\lib\ruby\gems\2.0.0
- C:\Ruby200-x64\bin
- C:\Ruby21\lib\ruby\gems\2.1.0
- C:\Ruby21\bin
- C:\Ruby21-x64\lib\ruby\gems\2.1.0
- C:\Ruby21-x64\bin
services:
- mysql
101 changes: 74 additions & 27 deletions ext/mysql2/extconf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,6 @@ def asplode lib
rpath_dir = lib
end

if RUBY_PLATFORM =~ /mswin|mingw/
exit 1 unless have_library('libmysql')
end

if have_header('mysql.h')
prefix = nil
elsif have_header('mysql/mysql.h')
Expand All @@ -98,31 +94,82 @@ def asplode lib
$CFLAGS << gcc_flags
end

case explicit_rpath = with_config('mysql-rpath')
when true
abort "-----\nOption --with-mysql-rpath must have an argument\n-----"
when false
warn "-----\nOption --with-mysql-rpath has been disabled at your request\n-----"
when String
# The user gave us a value so use it
rpath_flags = " -Wl,-rpath,#{explicit_rpath}"
warn "-----\nSetting mysql rpath to #{explicit_rpath}\n-----"
$LDFLAGS << rpath_flags
if RUBY_PLATFORM =~ /mswin|mingw/
# Build libmysql.a interface link library
require 'rake'

# Build libmysql.a interface link library
# Use rake to rebuild only if these files change
deffile = File.expand_path('../../../support/libmysql.def', __FILE__)
libfile = File.expand_path(File.join(rpath_dir, 'libmysql.lib'))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is not libmysql.lib placed in the same support folder like libmysql.def? (the .lib is only useful at build time)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

libmysql.def is checked into the repo, because tools to generate it at compile time are not a default part of MinGW :(
libmysql.lib is referenced from the MySQL directory, since it is only needed a compile time.
libmysql.dll is copied over because it is needed at run time.

file 'libmysql.a' => [deffile, libfile] do |t|
when_writing 'building libmysql.a' do
# Ruby kindly shows us where dllwrap is, but that tool does more than we want.
# Maybe in the future Ruby could provide RbConfig::CONFIG['DLLTOOL'] directly.
dlltool = RbConfig::CONFIG['DLLWRAP'].gsub('dllwrap', 'dlltool')
sh dlltool, '--kill-at',
'--dllname', 'libmysql.dll',
'--output-lib', 'libmysql.a',
'--input-def', deffile, libfile
end
end

Rake::Task['libmysql.a'].invoke
$LOCAL_LIBS << ' ' << 'libmysql.a'

# Make sure the generated interface library works (if cross-compiling, trust without verifying)
unless RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
abort "-----\nCannot find libmysql.a\n----" unless have_library('libmysql')
abort "-----\nCannot link to libmysql.a (my_init)\n----" unless have_func('my_init')
end

# Vendor libmysql.dll
vendordir = File.expand_path('../../../vendor/', __FILE__)
directory vendordir

vendordll = File.join(vendordir, 'libmysql.dll')
dllfile = File.expand_path(File.join(rpath_dir, 'libmysql.dll'))
file vendordll => [dllfile, vendordir] do |t|
when_writing 'copying libmysql.dll' do
cp dllfile, vendordll
end
end

# Copy libmysql.dll to the local vendor directory by default
if arg_config('--no-vendor-libmysql')
# Fine, don't.
puts "--no-vendor-libmysql"
else # Default: arg_config('--vendor-libmysql')
# Let's do it!
Rake::Task[vendordll].invoke
end
else
if libdir = rpath_dir[%r{(-L)?(/[^ ]+)}, 2]
rpath_flags = " -Wl,-rpath,#{libdir}"
if RbConfig::CONFIG["RPATHFLAG"].to_s.empty? && try_link('int main() {return 0;}', rpath_flags)
# Usually Ruby sets RPATHFLAG the right way for each system, but not on OS X.
warn "-----\nSetting rpath to #{libdir}\n-----"
$LDFLAGS << rpath_flags
else
if RbConfig::CONFIG["RPATHFLAG"].to_s.empty?
# If we got here because try_link failed, warn the user
warn "-----\nDon't know how to set rpath on your system, if MySQL libraries are not in path mysql2 may not load\n-----"
case explicit_rpath = with_config('mysql-rpath')
when true
abort "-----\nOption --with-mysql-rpath must have an argument\n-----"
when false
warn "-----\nOption --with-mysql-rpath has been disabled at your request\n-----"
when String
# The user gave us a value so use it
rpath_flags = " -Wl,-rpath,#{explicit_rpath}"
warn "-----\nSetting mysql rpath to #{explicit_rpath}\n-----"
$LDFLAGS << rpath_flags
else
if libdir = rpath_dir[%r{(-L)?(/[^ ]+)}, 2]
rpath_flags = " -Wl,-rpath,#{libdir}"
if RbConfig::CONFIG["RPATHFLAG"].to_s.empty? && try_link('int main() {return 0;}', rpath_flags)
# Usually Ruby sets RPATHFLAG the right way for each system, but not on OS X.
warn "-----\nSetting rpath to #{libdir}\n-----"
$LDFLAGS << rpath_flags
else
if RbConfig::CONFIG["RPATHFLAG"].to_s.empty?
# If we got here because try_link failed, warn the user
warn "-----\nDon't know how to set rpath on your system, if MySQL libraries are not in path mysql2 may not load\n-----"
end
# Make sure that LIBPATH gets set if we didn't explicitly set the rpath.
warn "-----\nSetting libpath to #{libdir}\n-----"
$LIBPATH << libdir unless $LIBPATH.include?(libdir)
end
# Make sure that LIBPATH gets set if we didn't explicitly set the rpath.
warn "-----\nSetting libpath to #{libdir}\n-----"
$LIBPATH << libdir unless $LIBPATH.include?(libdir)
end
end
end
Expand Down
23 changes: 23 additions & 0 deletions lib/mysql2.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,29 @@
require 'bigdecimal'
require 'rational' unless RUBY_VERSION >= '1.9.2'

# Load libmysql.dll before requiring mysql2/mysql2.so
# This gives a chance to be flexible about the load path
# Or to bomb out with a clear error message instead of a linker crash
if RUBY_PLATFORM =~ /mswin|mingw/
dll_path = if ENV['RUBY_MYSQL2_LIBMYSQL_DLL']
# If this environment variable is set, it overrides any other paths
# The user is advised to use backslashes not forward slashes
ENV['RUBY_MYSQL2_LIBMYSQL_DLL'].dup
elsif File.exist?(File.expand_path('../vendor/libmysql.dll', File.dirname(__FILE__)))
# Use vendor/libmysql.dll if it exists, convert slashes for Win32 LoadLibrary
File.expand_path('../vendor/libmysql.dll', File.dirname(__FILE__)).gsub('/', '\\')
else
# This will use default / system library paths
'libmysql.dll'
end

require 'Win32API'
LoadLibrary = Win32API.new('Kernel32', 'LoadLibrary', ['P'], 'I')
if 0 == LoadLibrary.call(dll_path)
abort "Failed to load libmysql.dll from #{dll_path}"
end
end

require 'mysql2/version' unless defined? Mysql2::VERSION
require 'mysql2/error'
require 'mysql2/mysql2'
Expand Down
3 changes: 1 addition & 2 deletions mysql2.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ Gem::Specification.new do |s|
s.test_files = `git ls-files spec examples`.split

# tests
s.add_development_dependency 'eventmachine'
s.add_development_dependency 'rake-compiler', '~> 0.8.1'
s.add_development_dependency 'rake-compiler', '~> 0.9.5'
s.add_development_dependency 'rake', '~> 0.9.3'
s.add_development_dependency 'rspec', '~> 2.8.0'
end
Loading