1.8.6 OCI binary extension question


#1

I’m trying to build a new release of RMagick that is compatible with the
Ruby 1.8.6 One-Click Installer. In the past I have used MS Visual Studio
2005 Express Edition.

However, new releases of ImageMagick for Windows are built with Visual
Studio 2008 and when I build RMagick with the 2005 Visual Studio the
resulting library fails, apparently because of some incompatibility. (I
confess I am very inexperienced with Windows development, so I’m not
sure what the incompatibility would be.)

Does anybody know for sure if a binary extension built with Visual
Studio 2008 will work with the current 1.8.6 OCI?


#2

On Jan 17, 3:00 pm, Tim H. removed_email_address@domain.invalid wrote:

Does anybody know for sure if a binary extension built with Visual
Studio 2008 will work with the current 1.8.6 OCI?

Well, could work, but is doom to failure.

Visual Studio 2008 links to MSVCR80, which is a different CRT than the
one used by OCI (MSVCRT).

Memory allocated by MSVCR80 cannot be freed by other CRT, if happens,
will segfault.

Out of curiosity, what are the chances of getting ImageMagick built
with MinGW? MinGW (GCC) + MSYS (for make and such) links to MSVCRT and
will allow you compile Ruby C Extensions that are compatible.

Shameless plug: you can take a look to rake-compiler[1] for cross-
compilation, so you can generate the extension and the gem from Linux/
OSX.

On a side note and feature request: if you do it, then please make a
static library of ImageMagick, so in that way people can start
installing RMagick gem like a normal gem.

Regards,

Luis L.

[1] http://github.com/luislavena/rake-compiler


#3

Luis L. wrote:

will segfault.
static library of ImageMagick, so in that way people can start
installing RMagick gem like a normal gem.

Regards,

Luis L.

[1] http://github.com/luislavena/rake-compiler

Thanks for your reply, Luis. Actually I was thinking about waiting for
the 1.9.1 and then building a static version, as you suggest. Any
timeline?

I’ve already bookmarked rake-compiler. I’ll check it out.


#4

On Jan 17, 7:32 pm, Tim H. removed_email_address@domain.invalid wrote:

sure what the incompatibility would be.)
will segfault.
static library of ImageMagick, so in that way people can start

I’m working in getting both 1.8.6 and 1.9.1 build with the same
compiler (MinGW) for One-Click Ruby Installer. It’s been hard since
there are several changes from 1.8.x to 1.9.1 that affect the paths
and the recipes for compilation.

I’m targeting end of January to mid-February, soon after 1.9.1 go out.

I’ve already bookmarked rake-compiler. I’ll check it out.

Great :wink:


#5

On Jan 17, 4:10 pm, Luis L. removed_email_address@domain.invalid wrote:
(snip)

Shameless plug: you can take a look torake-compiler[1] for cross-
compilation, so you can generate the extension and the gem from Linux/
OSX.

Luis,

Is there a way to configure rake-compiler to specify a name for the
binary executable that is different from the extension name? The
RMagick binary is RMagick2.so, but rake-compiler assumes it is simply
RMagick. Given the Rakefile:

require ‘rake/extensiontask’

Rake::ExtensionTask.new(‘RMagick’)

tim@linux:~/RMagick/projects/RMagick$ rake -T
(in /home/tim/RMagick/projects/RMagick)
rake clean # Remove any temporary products.
rake clobber # Remove any generated file.
rake compile # Compile all the extensions
rake compile:RMagick # Compile RMagick
tim@linux:~/RMagick/projects/RMagick$ rake compile
(in /home/tim/RMagick/projects/RMagick)
cd tmp/i686-linux/RMagick
make: Nothing to be done for `all’.
cd -
cp tmp/i686-linux/RMagick/RMagick.so lib/RMagick.so
rake aborted!
No such file or directory - tmp/i686-linux/RMagick/RMagick.so


#6

On Jan 20, 9:42 pm, “removed_email_address@domain.invalid” removed_email_address@domain.invalid wrote:

binary executable that is different from the extension name? The
RMagick binary is RMagick2.so, but rake-compiler assumes it is simply
RMagick. Given the Rakefile:

Is RMagick2 the name of the extension folder?

rake-compiler is more convention-over-configuration while still
letting you do some changes.

http://github.com/luislavena/rake-compiler/tree/master
(see customization section)

require ‘rake/extensiontask’

Rake::ExtensionTask.new(‘RMagick’)

Just giving that line, rake-compiler is expecting:

ext/RMagick/extconf.rb

Which will generate RMagick.so (or .bundle, depending on the host and
or target).

cd -
cp tmp/i686-linux/RMagick/RMagick.so lib/RMagick.so
rake aborted!
No such file or directory - tmp/i686-linux/RMagick/RMagick.so

If it’s saying nothing to be done is because it already build the
makefile and it already build? what is the output of “rake clean; rake
compile”

Can you point me to the extconf.rb file of RMagick?

There is room definitely there is room to add more customization, but
I based rake-compiler structure in the PickAxe.

Please let me know if you make progress on this.

Regards,


#7

On Jan 20, 10:57 pm, “removed_email_address@domain.invalid” removed_email_address@domain.invalid wrote:

Thanks for your prompt reply, Luis. With the exception of the .so
filename I believe rake-compiler is working normally.

rake-compiler is expecting that the name you supply to it is the
expected name of build so file.

The extconf.rb for RMagick is below.

I recognize that rake-compiler wants “convention-over-configuration”
and that this can make it difficult to “import” existing projects,
especially those that don’t match the convention. I appreciate your
patience.

We can bend the convention, so adding this to my todo list:

  • Make ext.ext_dir work fully (if it’s overwritten do not try to
    append another folder)

Adding that, you will have the option to keep the extension name but
with a different directory name.

Indeed, changing the name of the directory to RMagick2 causes rake-
compiler to know that the name of the .so file is RMagick2.so, at the
cost of changing the name of the directory. The directory structure,
which I based on setup.rb, has been my convention from the beginning
and I prefer not to change it. I believe that it is compatible with
the Pickaxe convention.

From my experience I’ve always keep one extension per folder, and
everything inside ext directory.

But is important keep backward compatibility to some extend.

Just added it:

http://wiki.github.com/luislavena/rake-compiler/todo

When do that change, you will be able to:

require ‘rake/extensiontask’

Rake::ExtensionTask.new(‘RMagick2’) do |ext|
ext.ext_dir = ‘ext/RMagick’
end

Sound good?

RMagick has both a Ruby part and a binary part. The Ruby part is
RMagick.rb and the binary part is RMagick2.so for RMagick 2 and simply
RMagick.so for RMagick 1.

I understand.

How you deal with the require in the same folder? you explicitly
require “RMagick.so” ?

Anyway, will work on fix that issue before your next release :wink:

Right now I have a broken Ruby environment, when fixed will patch and
do another rake-compiler release.

Thank you for the feedback Tom (because I know is you) :wink:

Regards,


#8

On Jan 21, 8:47 am, Luis L. removed_email_address@domain.invalid wrote:

Right now I have a broken Ruby environment, when fixed will patch and
do anotherrake-compilerrelease.

Thank you for the feedback Tom (because I know is you) :wink:

Thanks, Luis. I’m eagerly awaiting the next release.


#9

On Jan 20, 7:15 pm, Luis L. removed_email_address@domain.invalid wrote:

Can you point me to the extconf.rb file of RMagick?

There is room definitely there is room to add more customization, but
I basedrake-compilerstructure in the PickAxe.

Please let me know if you make progress on this.

Thanks for your prompt reply, Luis. With the exception of the .so
filename I believe rake-compiler is working normally.

The extconf.rb for RMagick is below.

I recognize that rake-compiler wants “convention-over-configuration”
and that this can make it difficult to “import” existing projects,
especially those that don’t match the convention. I appreciate your
patience.

Indeed, changing the name of the directory to RMagick2 causes rake-
compiler to know that the name of the .so file is RMagick2.so, at the
cost of changing the name of the directory. The directory structure,
which I based on setup.rb, has been my convention from the beginning
and I prefer not to change it. I believe that it is compatible with
the Pickaxe convention.

RMagick has both a Ruby part and a binary part. The Ruby part is
RMagick.rb and the binary part is RMagick2.so for RMagick 2 and simply
RMagick.so for RMagick 1.

require “mkmf”
require “date”

RMAGICK_VERS = “0.0.0”
MIN_RUBY_VERS = “1.8.2”
MIN_RUBY_VERS_NO = MIN_RUBY_VERS.tr(".","").to_i
MIN_IM_VERS = “6.3.0”
MIN_IM_VERS_NO = MIN_IM_VERS.tr(".","").to_i

Test for a specific value in an enum type

def have_enum_value(enum, value, headers=nil, &b)
checking_for “#{enum}.#{value}” do
if try_compile(<<“SRC”, &b)
#{COMMON_HEADERS}
#{cpp_include(headers)}
/top/
int main() { #{enum} t = #{value}; t = t; return 0; }
SRC
$defs.push(format("-DHAVE_ENUM_%s", value.upcase))
true
else
false
end
end
end

Test for multiple values of the same enum type

def have_enum_values(enum, values, headers=nil, &b)
values.each do |value|
have_enum_value(enum, value, headers, &b)
end
end

def exit_failure(msg)
Logging::message msg
message msg+"\n"
exit(1)
end

Seems like lots of people have multiple versions of ImageMagick

installed.
def check_multiple_imagemagick_versions()
versions = []
path = ENV[‘PATH’].split(File::PATH_SEPARATOR)
path.each do |dir|
file = File.join(dir, “Magick-config”)
if File.executable? file
vers = #{file} --version.chomp.strip
prefix = #{file} --prefix.chomp.strip
versions << [vers, prefix, dir]
end
end
versions.uniq!
if versions.size > 1
msg = “\nWarning: Found more than one ImageMagick installation.
This could cause problems at runtime.\n”
versions.each do |vers, prefix, dir|
msg << " #{dir}/Magick-config reports version #{vers}
is installed in #{prefix}\n"
end
msg << “Using #{versions[0][0]} from #{versions[0][1]}.\n\n”
Logging::message msg
message msg
end
end

if RUBY_PLATFORM =~ /mswin/
abort <<END_MSWIN
±---------------------------------------------------------------------------
+
| This rmagick gem is for use only on Linux, BSD, OS X, and similar
systems |
| that have a gnu or similar toolchain installed. The rmagick-win32
gem is a |
| pre-compiled version of RMagick bundled with ImageMagick for use
on |
| Microsoft Windows systems. The rmagick-win32 gem is available on
RubyForge.|
| See http://rmagick.rubyforge.org/install-faq.html for more
information. |
±---------------------------------------------------------------------------
+
END_MSWIN
end

unless checking_for(“Ruby version >= #{MIN_RUBY_VERS}”) do
version = RUBY_VERSION.tr(".","").to_i
version >= MIN_RUBY_VERS_NO
end
exit_failure “Can’t install RMagick #{RMAGICK_VERS}. Ruby #
{MIN_RUBY_VERS} or later required.\n”
end

Magick-config is not available on Windows

if RUBY_PLATFORM !~ /mswin/

Check for compiler. Extract first word so ENV[‘CC’] can be a

program name with arguments.
cc = (ENV[“CC”] or Config::CONFIG[“CC”] or “gcc”).split(’ ').first
unless find_executable(cc)
exit_failure “No C compiler found in ${ENV[‘PATH’]}. See mkmf.log
for details.”
end

Check for Magick-config

unless find_executable(“Magick-config”)
exit_failure “Can’t install RMagick #{RMAGICK_VERS}. Can’t find
Magick-config in #{ENV[‘PATH’]}\n”
end

check_multiple_imagemagick_versions()

Ensure minimum ImageMagick version

unless checking_for(“ImageMagick version >= #{MIN_IM_VERS}”) do
version = Magick-config --version.chomp.tr(".","").to_i
version >= MIN_IM_VERS_NO
end
exit_failure “Can’t install RMagick #{RMAGICK_VERS}. You must have
ImageMagick #{MIN_IM_VERS} or later.\n”
end

$magick_version = Magick-config --version.chomp

Ensure ImageMagick is not configured for HDRI

unless checking_for(“HDRI disabled version of ImageMagick”) do
not (Magick-config --version[“HDRI”])
end
exit_failure “\nCan’t install RMagick #{RMAGICK_VERS}.”+
“\nRMagick does not work when ImageMagick is configured for
High Dynamic Range Images.”+
“\nDon’t use the --enable-hdri option when configuring
ImageMagick.\n”
end

Save flags

$CFLAGS = ENV[“CFLAGS”].to_s + " " + Magick-config -- cflags.chomp
$CPPFLAGS = ENV[“CPPFLAGS”].to_s + " " + Magick-config -- cppflags.chomp
$LDFLAGS = ENV[“LDFLAGS”].to_s + " " + Magick-config -- ldflags.chomp
$LOCAL_LIBS = ENV[“LIBS”].to_s + " " + Magick-config -- libs.chomp

else # mswin

convert -version =~ /Version: ImageMagick (\d.\d.\d+)-\d /
abort “Unable to get ImageMagick version” unless $1
$magick_version = $1
$CFLAGS = “-W3”
$CPPFLAGS = %Q{-I"C:\Program Files\Microsoft Platform SDK for
Windows Server 2003 R2\Include" -I"C:\Program Files\ImageMagick-#
{$magick_version}-Q8\include"}

The /link option is required by the Makefile but causes warnings

in the mkmf.log file.
$LDFLAGS = %Q{/link /LIBPATH:“C:\Program Files\Microsoft Platform
SDK for Windows Server 2003 R2\Lib” /LIBPATH:“C:\Program Files
\ImageMagick-#{$magick_version}-Q8\lib” /LIBPATH:“C:\ruby\lib”}
$LOCAL_LIBS = ‘CORE_RL_magick_.lib X11.lib’

end

#headers = %w{assert.h ctype.h errno.h float.h limits.h math.h
stdarg.h stddef.h stdint.h stdio.h stdlib.h string.h time.h}
headers = %w{assert.h ctype.h stdio.h stdlib.h math.h time.h}
headers << “stdint.h” if have_header(“stdint.h”) # defines uint64_t
headers << “sys/types.h” if have_header(“sys/types.h”)

unless have_header(“magick/MagickCore.h”)
exit_failure “Can’t install RMagick #{RMAGICK_VERS}. Can’t find
MagickCore.h.\n”
else
headers << “magick/MagickCore.h”
end

if RUBY_PLATFORM !~ /mswin/

unless have_library(“Magick”, “InitializeMagick”, headers) ||
have_library(“MagickCore”, “InitializeMagick”, headers) || have_library
(“Magick++”, “InitializeMagick”)
exit_failure "Can’t install RMagick #{RMAGICK_VERS}. " +
"Can’t find the ImageMagick library or one of the dependent
libraries. " +
“Check the mkmf.log file for more detailed information.\n”
end
end

have_func(“snprintf”, headers)

[“AcquireQuantumMemory”, # 6.3.5-9
“AcquireImage”, # 6.4.1
“AffinityImage”, # 6.4.3-6
“AffinityImages”, # 6.4.3-6
“ClutImageChannel”, # 6.3.5-8
“CompositeLayers”, # 6.3.3-?
“ConvertHSLToRGB”, # 6.3.5-9
“ConvertRGBToHSL”, # 6.3.5-9
“DeskewImage”, # 6.4.2-5
“DistortImage”, # 6.3.5
“EncipherImage”, # 6.3.8-6
“EqualizeImageChannel”, # 6.3.6-9
“ExcerptImage”, # 6.3.5-8
“ExtentImage”, # 6.3.1
“FloodfillPaintImage”, # 6.3.7
“GetAuthenticPixels”, # 6.4.5-6
“GetImageAlphaChannel”, # 6.3.9-2
“GetImageProperty”, # 6.3.1
“GetNextImageProperty”, # 6.3.1
“GetStringInfoDatum”, # 6.3.2
“GetVirtualPixels”, # 6.4.5-6
“InterpretImageProperties”, # 6.3.2
“IsHistogramImage”, # 6.3.5
“LevelImageColors”, # 6.4.2
“LevelizeImageChannel”, # 6.4.2
“LinearStretchImage”, # 6.3.1
“LiquidRescaleImage”, # 6.3.8-2
“MagickCoreGenesis”, # 6.3.4
“OpaquePaintImageChannel”, # 6.3.7-10
“PolaroidImage”, # 6.3.1-6
“RecolorImage”, # 6.3.1-3
“RemapImage”, # 6.4.4-0
“RemoveImageArtifact”, # 6.3.6
“ResetImagePage”, # 6.3.3
“ResizeQuantumMemory”, # 6.3.5-9
“SetImageAlphaChannel”, # 6.3.6-9
“SetImageArtifact”, # 6.3.6
“SetImageMask”, # 6.3.1
“SetImageProperty”, # 6.3.1
“SetImageRegistry”, # 6.3.4
“SparseColorImage”, # 6.3.4-?
“SyncImageProfiles”, # 6.3.2
“SyncAuthenticPixels”, # 6.4.5-6
“TransparentPaintImage”, # 6.3.7-10
“TransparentPaintImageChroma” # 6.4.5-6
].each do |func|
have_func(func, headers)
end

checking_for(“QueryMagickColorname() new signature”) do
if try_compile(<<“SRC”)
#{COMMON_HEADERS}
#{cpp_include(headers)}
/top/
int main() {
MagickBooleanType okay;
Image *image;
MagickPixelPacket *color;
char *name;
ExceptionInfo *exception;
okay = QueryMagickColorname(image, color, SVGCompliance, name,
exception);
return 0;
}
SRC
$defs.push("-DHAVE_NEW_QUERYMAGICKCOLORNAME")
true
else
false
end
end

have_struct_member(“ImageInfo”, “profile”, headers) # 6.3.2
have_struct_member(“Image”, “tile_offset”, headers) # 6.3.4
have_struct_member(“Image”, “type”, headers) # ???
have_struct_member(“DrawInfo”, “kerning”, headers) # 6.4.7-8
have_struct_member(“DrawInfo”, “interword_spacing”, headers) #
6.4.8-0
have_type(“AlphaChannelType”, headers) # 6.3.5
have_type(“DitherMethod”, headers) # 6.4.2
have_type(“ImageLayerMethod”, headers) # 6.3.6 replaces
MagickLayerMethod
have_type(“SpreadMethod”, headers) # 6.3.1
have_type(“long double”, headers)
#have_type(“unsigned long long”, headers)
#have_type(“uint64_t”, headers)
#have_type("__int64", headers)
#have_type(“uintmax_t”, headers)
#check_sizeof(“unsigned long”, headers)
#check_sizeof(“Image *”, headers)

have_enum_value(“AlphaChannelType”, “CopyAlphaChannel”,
headers) # 6.4.3-7
have_enum_value(“ColorspaceType”, “CMYColorspace”,
headers) # 6.3.5
have_enum_values(“CompositeOperator”,
[“ChangeMaskCompositeOp”, # 6.3.3

“LinearLightCompositeOp”, # 6.3.5
“DivideCompositeOp”],
headers) # 6.3.5
have_enum_values(“CompressionType”,
[“DXT1Compression”, # 6.3.9-3

“DXT3Compression”, # 6.3.9-3
“DXT5Compression”],
headers) # 6.3.9-3
have_enum_values(“DistortImageMethod”,
[“ArcDistortion”, # 6.3.5-5

“BarrelDistortion”, # 6.4.2-5

“BarrelInverseDistortion”, # 6.4.3-8

“DePolarDistortion”, # 6.4.2-6

“PerspectiveProjectionDistortion”, # 6.3.5-9

“PolarDistortion”, # 6.4.2-6

“PolynomialDistortion”, # 6.4.2-4
“ShepardsDistortion”],
headers) # 6.4.2-4
have_enum_value(“DitherMethod”, “NoDitherMethod”,
headers) # 6.4.3
have_enum_values(“FilterTypes”,
[“KaiserFilter”, # 6.3.6

“WelshFilter”, # 6.3.6-4

“ParzenFilter”, # 6.3.6-4

“LagrangeFilter”, # 6.3.7-2

“BohmanFilter”, # 6.3.7-2

“BartlettFilter”, # 6.3.7-2
“SentinelFilter”],
headers) # 6.3.7-2
have_enum_value(“InterpolatePixelMethod”, “SplineInterpolatePixel”,
headers) # 6.3.5
have_enum_values(“InterlaceType”,
[“GIFInterlace”, # 6.3.4

“JPEGInterlace”, # 6.3.4
“PNGInterlace”],
headers) # 6.3.4
have_enum_values(“MagickEvaluateOperator”,
[“PowEvaluateOperator”, # 6.4.1-9

“LogEvaluateOperator”, # 6.4.2

“ThresholdEvaluateOperator”, # 6.4.3

“ThresholdBlackEvaluateOperator”, # 6.4.3

“ThresholdWhiteEvaluateOperator”, # 6.4.3

“GaussianNoiseEvaluateOperator”, # 6.4.3

“ImpulseNoiseEvaluateOperator”, # 6.4.3

“LaplacianNoiseEvaluateOperator”, # 6.4.3

“MultiplicativeNoiseEvaluateOperator”, # 6.4.3

“PoissonNoiseEvaluateOperator”, # 6.4.3

“UniformNoiseEvaluateOperator”, # 6.4.3

“CosineEvaluateOperator”, # 6.4.8-5

“SineEvaluateOperator”, # 6.4.8-5

“AddModulusEvaluateOperator”], # 6.4.8-5

headers)
have_enum_values(“MagickLayerMethod”,
[“OptimizeTransLayer”, # 6.3.3-4

“RemoveDupsLayer”, # 6.3.3-6

“RemoveZeroLayer”, # 6.3.3-6

“CompositeLayer”, # 6.3.3-6
“OptimizeImageLayer”],
headers) # 6.3.3-?
have_enum_values(“ImageLayerMethod”,
[“FlattenLayer”, # 6.3.6-2

“MergeLayer”, # 6.3.6

“MosaicLayer”, # 6.3.6-2
“TrimBoundsLayer” ],
headers) # 6.4.3-8
have_enum_value(“MetricType”, “MeanErrorPerPixelMetric”,
headers) # 6.3.4-?
have_enum_value(“NoiseType”, “RandomNoise”,
headers) # 6.3.5-0
have_enum_value(“SpreadMethod”, “ReflectSpread”,
headers) # 6.3.1
have_enum_values(“VirtualPixelMethod”,
[“MaskVirtualPixelMethod”, # 6.3.3

“BlackVirtualPixelMethod”, # 6.3.5

“GrayVirtualPixelMethod”, # 6.3.5

“HorizontalTileVirtualPixelMethod”, # 6.4.2-6

“VerticalTileVirtualPixelMethod”, # 6.4.2-6
“WhiteVirtualPixelMethod”],
headers) # 6.3.5

Now test Ruby 1.9.0 features.

headers = [“ruby.h”]
if have_header(“ruby/io.h”)
headers << “ruby/io.h”
else
headers << “rubyio.h”
end

have_func(“rb_frame_this_func”, headers)

Miscellaneous constants

$defs.push("-DRUBY_VERSION_STRING=“ruby #{RUBY_VERSION}”")
$defs.push("-DRMAGICK_VERSION_STRING=“RMagick #{RMAGICK_VERS}”")

create_header()

Prior to 1.8.5 mkmf duplicated the symbols on the command line and

in the

extconf.h header. Suppress that behavior by removing the symbol

array.
$defs = []

Force re-compilation if the generated Makefile changed.

$config_h = “Makefile rmagick.h”

create_makefile(“RMagick2”)

SUMMARY = <<“END_SUMMARY”

#{"=" * 70}
#{DateTime.now.strftime("%a %d%b%y %T")}
This installation of RMagick #{RMAGICK_VERS} is configured for
Ruby #{RUBY_VERSION} (#{RUBY_PLATFORM}) and ImageMagick #
{$magick_version}
#{"=" * 70}

END_SUMMARY

Logging::message SUMMARY
message SUMMARY


#10

On Jan 21, 8:47 am, Luis L. removed_email_address@domain.invalid wrote:

How you deal with the require in the same folder? you explicitly
require “RMagick.so” ?

(Oops. Forgot to answer your question.)

Yes, exactly.