Building Gnocl with CMake for Windows
Tested with XP SP3, Gtk 2.16, Cmake 2.8, MinGW 4.50, Gnocl 0.9.95
- Install Tcl. Binaries you can find here: http://www.activestate.com/activetcl/
- Install development Gtk+ 2.16 package from http://ftp.gnome.org/pub/gnome/binaries/win32/gtk+/2.16/gtk+-bundle_2.16.6-20100912_win32.zip
- Install Cmake 2.8 from http://www.cmake.org/files/v2.8/cmake-2.8.3-win32-x86.exe
- Install MinGW for Windows from http://sourceforge.net/projects/mingw/files/Automated MinGW Installer/mingw-get-inst (default C:\MinGW)
- Install tarball or zipball with Gnocl sourcecode from https://github.com/zdia/gnocl/zipball/0.9.95
- Build the shared library libgnocl.dll in the Powershell or with CMD
cd build\Windows
cmake ..\..\src
make
make install - Make sure the system’s path can find the installed binaries
- Test the library with tclsh
load [file join [pwd] libgnocl.dll] Gnocl
package require Gnocl
gnocl::window - Go on with the demos
- Install the runtime environment of Gtk2: http://downloads.sourceforge.net/gtk-win/gtk2-runtime-2.22.0-2010-10-21-ash.exe?download
- Install Tcl
- Copy xml2.dll and libglade-2.0.dll from https://github.com/zdia/gnocl/downloads into the Gtk/bin folder
- Copy also libgnocl.dll from there into the Tcl/bin folder
- Make your tests like above
Useful links:
http://www.cmake.org/Wiki/CmakeMingw
http://ricardo.ecn.wfu.edu/~cottrell/cross-gtk/: Building GTK apps for MS Windows on Linux
There is actually available a Windows port for Gnocl 0.9.91 created in 2007 which offers a README.txt and a gnocl-0.9.91.patch. This port is using the Cygwin environment which has its own Gtk+ packages and relies on a running X11 server on Windows but we will use the native Windows APIs. Because Windows has not so much flavours like the *nix systems a cross-compiled gnocl.dll probably will run on all win32 system >= Windows 2000 so there is no need to compile the Gnocl library from scratch.
Installing Gtk+ on Windows
Preliminary consideration:
“It is expected that people who build installers for GTK+ applications for Windows bundle GT K+ with them.
For this purpose runtime packages are offered but for now we will choose the older but stable developer bundle v. 2.16.6 which is the last version which runs on Windows 2000.
These packages are not for developing or running programs that use the Cygwin Unix emulation environment. Cygwin has GTK+ packages available directly in its installer which you should use. Note that the Cygwin GTK+ uses the X11 backend, so you will need to also run an X11 server then when you run GTK+ programs on Cygwin, but presumably that is what Cygwin users want.
[…]
The current GTK+ stack uses APIs that are available only on Windows 2000 or later." (http://www.gtk.org/download-windows.html)
So let’s get http://ftp.gnome.org/pub/gnome/binaries/win32/gtk+/2.16/gtk+-bundle_2.16.6-20100912_win32.zip and install it.
The Windows Test environment is a XP SP3 running in a VirtualBox on a Zenwalk 6.4 (Slackware based distro) Linux host. In the seamless mode with the Power Shell installed it’s very comfortable to work with Windows. Even the Windows clipboard is accessible by the Linux host.
A first we’ll run a test with double-clicking the gtk-demo:
(Could be a base for the Gnocl test suite)
Where to put all the Gtk+ stuff?
We create a folder C:\Gtk and store all the runtime libraries and the developer packages into it. Then we change the system’s path to C:\Gtk\bin. We test if we can call pkg-config:
PS C:\Dokumente und Einstellungen\dia> pkg-config.exe --libs gtk+-2.0
-LC:/Gtk/lib -lgtk-win32-2.0 -lgdk-win32-2.0 -latk-1.0 -lgio-2.0 -lgdk_pixbuf-2.0 -lpangowin32-1.0 -lgdi32 -lpangocairo-1.0 -lpango-1.0 -lcairo -lgobject-2.0 -lgmodule-2.0 -lgthread-2.0 -lglib-2.0 -lintl
So CMake will find the Gtk+ libraries. Installation of the Gtk+ based editor Geany shows no problems, too.
Configure the MingGW Makefile with CMake
We get the Windows version here: http://www.cmake.org/files/v2.8/cmake-2.8.3-win32-x86.exe
To get the MinGW compiler we look here: http://sourceforge.net/projects/mingw/files/Automated MinGW Installer/mingw-get-inst
After all we should have the following directories installed:
C:\Gtk
C:\MinGW
C:\Tcl
The toolchain method advised by the CMake site did not work so we create a special Window version for CMakeLists.txt.
At first we will set some information for the CMake tool that we are working on a Windows system with the MinGW compiler:
# the name of the target operating system SET(CMAKE_SYSTEM_NAME Windows)
# which compilers to use for C and C++ SET(CMAKE_C_COMPILER mingw32-gcc) SET(CMAKE_CXX_COMPILER mingw32-g++)
# define all the target environment directories SET(CMAKE_FIND_ROOT_PATH C:/MinGW C:/Tcl "C:/Programme/CMake 2.8" C:/Gtk )
# adjust the default behaviour of the FIND_XXX() commands: # search headers and libraries in the target environment, search # programs in the host environment set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
Then the shell call for Tcl has to be modified: The windows shell is cmd
# get the Tclsh version number # Note: ActiveState installs Tcl on Windows per default as "tclsh85.exe"
find_program (TCL_PROGRAM NAMES tclsh tclsh85.exe ENV PATH)
if ( CMAKE_SYSTEM_NAME STREQUAL "Windows" ) execute_process ( COMMAND cmd.exe /c echo puts $tcl_version COMMAND ${TCL_PROGRAM} OUTPUT_VARIABLE TCL_VERSION ) endif()
Unfortunately the libglade library is not included in the gtk+-2.0 package and the creation of a libglade-2.0.pc is not trivial.
The libglade-2.0-0.dll can be found on Gnomes Ftp Server and to find this library we will go on in a CMake way:
find_library ( LIBGLADE libglade-2.0-0 REQUIRED HINTS C:/Gtk/lib )
# FIXME. search a pkg-config solution or a portable solution set ( LIBGLADE_INCLUDE_PATH C:/Gtk/include/libglade-2.0 )
include_directories( ${GTK+_INCLUDE_DIRS} ${TCL_INCLUDE_PATH} ${LIBGLADE_INCLUDE_PATH})
We are ready for a test:
cmake -G "MinGW Makefiles" ../src
-- Configuring done -- Generating done
:-))
Compiling
mingw32-make.exe
C:\Dokumente und Einstellungen\dia\gnocl\src\plug.c:31:22: fatal error: gdk/gdkx.h: No such file or directory compilation terminated.
So it’s patch time. After a look into the patch file of Gnocl Windows version 0.9.91 we will include the Windows header file in the source file plug.c:
#ifdef WIN32
#include <gdk/gdkwin32.h>
#else
#include <gdk/gdkx.h>
#endif
In socket.c there will be besides this change two additional changes:
#ifdef WIN32 xid = gtk_socket_get_id(socket); #else xid = GDK_WINDOW_XWINDOW ( GTK_WIDGET ( socket )->window ); #endif
...
#ifdef WIN32 xid = gdk_win32_drawable_get_handle( socket->plug_window ); #else xid = GDK_WINDOW_XWINDOW ( socket->plug_window ); #endif
That’s all and now compiling runs through.
Linking
CMake does not find the necessary libraries provided by the pkg-config module. Besides that it is very complicated to create .pc files for MinGW:
prefix=c:/devel/target/9610f18eee106f8364d39d03467cc6de
prefix=C:/Gtk/lib does not work.
So we will try to modify the shipped FindGTK2 module which depends on the gtkmm suite.
We copy FindGTK2.cmake to our src/cmake/Modules folder and add the path where our Windows Gtk package is located:
find_path(${_var} ${_hdr}
PATHS
C:/Gtk/lib
C:/Gtk/include
/usr/local/lib64
/usr/local/lib
We have still to check if the name of the libglade library is libglade-2.0 and not e.g. libglade-2.0-0 or something else.
We make a try with: mingw32-make.exe
Linking C shared library libgnocl.dll
Creating library file: libgnocl.dll.a
CMakeFiles\gnocl.dir/objects.a(aboutDialog.c.obj):aboutDialog.c:(.text+0x2c): undefined reference to `tclStubsPtr'
CMakeFiles\gnocl.dir/objects.a(aboutDialog.c.obj):aboutDialog.c:(.text+0x69): undefined reference to `tclStubsPtr'
CMakeFiles\gnocl.dir/objects.a(aboutDialog.c.obj):aboutDialog.c:(.text+0x90): undefined reference to `tclStubsPtr'
CMakeFiles\gnocl.dir/objects.a(aboutDialog.c.obj):aboutDialog.c:(.text+0x116): undefined reference to `tclStubsPtr'
CMakeFiles\gnocl.dir/objects.a(aboutDialog.c.obj):aboutDialog.c:(.text+0x128): undefined reference to `tclStubsPtr'
CMakeFiles\gnocl.dir/objects.a(aboutDialog.c.obj):aboutDialog.c:(.text+0x1b4): more undefined references to `tclStubsPtr' follow
CMakeFiles\gnocl.dir/objects.a(gnocl.c.obj):gnocl.c:(.text+0xeea): undefined reference to `Tcl_InitStubs'
CMakeFiles\gnocl.dir/objects.a(gnocl.c.obj):gnocl.c:(.text+0xf03): undefined reference to `tclStubsPtr'
CMakeFiles\gnocl.dir/objects.a(gnocl.c.obj):gnocl.c:(.text+0xf6c): undefined reference to `tclStubsPtr'
CMakeFiles\gnocl.dir/objects.a(gnocl.c.obj):gnocl.c:(.text+0xf97): undefined reference to `tclStubsPtr'
CMakeFiles\gnocl.dir/objects.a(gnocl.c.obj):gnocl.c:(.text+0xfb5): undefined reference to `tclStubsPtr'
CMakeFiles\gnocl.dir/objects.a(gnocl.c.obj):gnocl.c:(.text+0x1006): undefined reference to `tclStubsPtr'
CMakeFiles\gnocl.dir/objects.a(gnocl.c.obj):gnocl.c:(.text+0x1059): more undefined references to `tclStubsPtr' follow
CMakeFiles\gnocl.dir/objects.a(print.c.obj):print.c:(.text+0x361): undefined reference to `pango_cairo_show_layout'
CMakeFiles\gnocl.dir/objects.a(print.c.obj):print.c:(.text+0x3f1): undefined reference to `pango_cairo_show_layout'
CMakeFiles\gnocl.dir/objects.a(print.c.obj):print.c:(.text+0x453): undefined reference to `pango_cairo_show_layout'
CMakeFiles\gnocl.dir/objects.a(print.c.obj):print.c:(.text+0x5d5): undefined reference to `tclStubsPtr'
CMakeFiles\gnocl.dir/objects.a(print.c.obj):print.c:(.text+0x5f3): undefined reference to `tclStubsPtr'
CMakeFiles\gnocl.dir/objects.a(print.c.obj):print.c:(.text+0x606): undefined reference to `tclStubsPtr'
CMakeFiles\gnocl.dir/objects.a(print.c.obj):print.c:(.text+0x687): undefined reference to `tclStubsPtr'
CMakeFiles\gnocl.dir/objects.a(print.c.obj):print.c:(.text+0x8c5): undefined reference to `tclStubsPtr'
CMakeFiles\gnocl.dir/objects.a(progressBar.c.obj):progressBar.c:(.text+0x74): more undefined references to `tclStubsPtr' follow
Ok, the author of the Windows version 0.9.91 already advertised that it would be necessary to compile Tcl from scratch because the ActiveState tclstub85.lib is created by Visual Studio. Unfortunately no trick to convert this library to a format understood by MinGW ( using reimp to create a *.def file and then dlltool to create a dynamic library ) worked, so, alas, compiling Tcl was inevitable.
Get the Tcl source: http://sourceforge.net/projects/tcl/files/Tcl/8.5.9/tcl859-src.zip/download
Get the MSYS developer environment (a cygwin fork) from the MinGW site: http://www.mingw.org/wiki/msys
It is sufficent to download and install these two executables: MSYS 1.0.11 and then MSYS DTK 1.0 in C:\msys\1.0.
After a double-click on the MSYS icon on the Desktop a console is opened which allows you to cd to your INSTALLPATH/tcl8.5.9/win and start the classical ./configure ; make process. Finally we will find the desired libtclstub85.a in the win/ folder. After renaming all the original tclstub libraries in the C:/Tcl/lib folder we copy the newly generated libtclstub85.a into it.
We don’t need the MSYS console any more and return to the Powershell console. After deletion of the CMakeCache.txt file we are ready for a second trial:
> Scanning dependencies of target gnocl
Linking C shared library libgnocl.dll
Creating library file: libgnocl.dll.a
CMakeFiles\gnocl.dir/objects.a(print.c.obj):print.c:(.text+0x361): undefined reference to `pango_cairo_show_layout'
CMakeFiles\gnocl.dir/objects.a(print.c.obj):print.c:(.text+0x3f1): undefined reference to `pango_cairo_show_layout'
CMakeFiles\gnocl.dir/objects.a(print.c.obj):print.c:(.text+0x453): undefined reference to `pango_cairo_show_layout'
collect2: ld returned 1 exit status
Much ado about 20KB but the tclstub issue was fixed :-)
So there is still an undefined reference to `pango_cairo_show_layout’. A quick look into the contents of the variable GTK2_LIBRARIES shows that the module FindGTK2.cmake has to be modified.
We will add a search call for pangocairo into the FindGTK2.cmake’s foreach loop which is responsible for looking up the Gtk2 libraries:
foreach(_GTK2_component ${GTK2_FIND_COMPONENTS})
if(_GTK2_component STREQUAL "gtk")
_GTK2_FIND_INCLUDE_DIR(GTK2_GLIB_INCLUDE_DIR glib.h)
...
_GTK2_FIND_LIBRARY (GTK2_PANGOCAIRO_LIBRARY pangocairo false true)
...
And here is the result of the following build run:
[ 97%] Building C object CMakeFiles/gnocl.dir/treeList.c.obj
[ 98%] Building C object CMakeFiles/gnocl.dir/volumeButton.c.obj
[100%] Building C object CMakeFiles/gnocl.dir/window.c.obj
Linking C shared library libgnocl.dll
Creating library file: libgnocl.dll.a
[100%] Built target gnocl
Whew! Development with Linux is really easier …
Surely we want to know if the new library works:
PS ~\gnocl\build> tclsh85.exe
% lappend auto_path [pwd]
C:/Tcl/lib/tcl8.5 C:/Tcl/lib c:/tcl/lib/teapot/package/win32-ix86/lib c:/tcl/lib/teapot/package/tcl/lib {C:/Dokumente und Einstellungen/dia/gnocl/build}
% load [file join [pwd] libgnocl.dll] Gnocl
couldn't load library "C:/Dokumente und Einstellungen/dia/gnocl/build/libgnocl.dll": this library or a dependent library could not be found in library path
Welcome in the dll hell …
The Tcl manual suggests:
When a load fails with “library not found” error, it is also possible that a dependent library was not found. To see the dependent libraries, type “dumpbin -imports ” in a DOS console to see what the library must import.
Well, dumpbin is part of the Microsoft Visual Studio suite. But there is a free programm called dependencywalker (see: http://www.dependencywalker.com/depends22_x86.zipx86.zip
For the following dll’s a required implicit or forwarded dependency was not found.
LIBGLADE-2.0-0.DLL
MSJAVA.DLL
MSJAVA.DLL??? It is required by MSHTML.DLL which is called by SHELL32.DLL … So to use Gtk we need to install a Java runtime? → delay-load dependent module (like in tclsh85.dll)
Renamed libglade-2.0-0.dll.a to libglade-2.0.dll.a. This trick doesn’t work because the renamed library has an internally defined name:
> pexports.exe C:\gtk\lib\libglade-2.0-0.dll
LIBRARY libglade-2.0-0.dll
Do we find a pure libglade-2.0.dll win32 version?
FIXME: Found win32 version of libglade on:
ftp://ftp.gnome.org/pub/GNOME/binaries/win32/libglade/2.6/libglade-dev_2.6.4-1_win32.zip
There is a libglade-2.0.dll.a and a suitable *.pc file, too. Hm, use the pkg-config utility and abandon FindGtk2 module?
It seems libxml-2.0 is missing.
So better try once again a pkg-config run because of the unknown dependencies: On Linux we have:
$ pkg-config --libs libglade-2.0
-lglade-2.0 -lgtk-x11-2.0 -lxml2 -lgdk-x11-2.0 -latk-1.0 -lgio-2.0 -lpangoft2-1.0 -lgdk_pixbuf-2.0 -lpangocairo-1.0 -lcairo -lpango-1.0 -lfreetype -lfontconfig -lgobject-2.0 -lgmodule-2.0 -lglib-2.0
$ pkg-config --cflags libglade-2.0
-I/usr/include/libglade-2.0 -I/usr/include/gtk-2.0 -I/usr/include/libxml2 -I/usr/lib/gtk-2.0/include -I/usr/include/atk-1.0 -I/usr/include/cairo -I/usr/include/pango-1.0 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/pixman-1 -I/usr/include/freetype2 -I/usr/include/libpng14
Note: gnocl 0.9.91 hasn’t included the libglade libraries
The dependency-walker helped to discover that the following DLL’s must be present at runtime. So we will put them into the c:/gtk/bin folder.
- iconv.dll
- libglade-2.0-0.dll
- libxml2-2.dll
Rename libxml2.dll to libxml2-2.dll if necessary.
Finally we have to add the following compiling option: -mms-bitfields (not in pkg-config mode … to be revised)
Added to the glade component in the FindGTK2 module:
_GTK2_FIND_LIBRARY (GTK2_XML2_LIBRARY xml2 false true)
> tclsh85.exe
% load [file join [pwd] libgnocl.dll] Gnocl
% package require Gnocl
0.9.95
% set label [gnocl::label \
-text "This is Gnocl for Windows version 0.9.95"]
::gnocl::_WID1
% gnocl::window \
-title "Window" \
-child $label \
-defaultWidth 200 \
-onDestroy { exit }
::gnocl::_WID2
And here is the result: