Skip to content

Building Gnocl with CMake for Windows

zdia edited this page Mar 7, 2011 · 5 revisions

Using MinGW to compile Gnocl for Windows

Quick installation guide

Tested with XP SP3, Gtk 2.16, Cmake 2.8, MinGW 4.50, Gnocl 0.9.95

  1. Install Tcl. Binaries you can find here: http://www.activestate.com/activetcl/
  2. 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
  3. Install Cmake 2.8 from http://www.cmake.org/files/v2.8/cmake-2.8.3-win32-x86.exe
  4. Install MinGW for Windows from http://sourceforge.net/projects/mingw/files/Automated MinGW Installer/mingw-get-inst (default C:\MinGW)
  5. Install tarball or zipball with Gnocl sourcecode from https://github.com/zdia/gnocl/zipball/0.9.95
  6. Build the shared library libgnocl.dll in the Powershell or with CMD
    cd build\Windows
    cmake ..\..\src
    make
    make install
  7. Make sure the system’s path can find the installed binaries
  8. Test the library with tclsh
    load [file join [pwd] libgnocl.dll] Gnocl
    package require Gnocl
    gnocl::window
  9. Go on with the demos

Pure runtime installation

  • 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

Detailed installation guide

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)

Testing

> 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:

Gnocl for Windows screenshot