====== Crosscompilare le nostre applicazioni Gtk+ su GNU/Linux per Windows ======
Autore: **//Fabio Di Matteo//** \\ Ultima revisione: **//27/03/2013//** \\ \\
{{programmazione:gtk:gtktreeview.png|}} {{programmazione:gtk:gtk-static-win32.png|}}
In questo articolo vedremo come sviluppare sulla nostra Linux-box un'applicazione gtk+ per Windows. Facendo in modo tale, che una volta lanciata su windows, non richieda l'installazione delle librerie Gtk+. Distribuiremo infatti le librerie assieme alla nostra applicazione, portandoci dietro tutto quello che serve (senza compilare staticamente) . \\ \\
Avremo come risultato una cartella con una struttura simile alla seguente:
drwx------ 2 fabio fabio 4096 2011-10-21 12:22 bin (qui dentro ci sara' l'eseguibile della nostra applicazione)
drwx------ 5 fabio fabio 4096 2011-10-21 11:23 etc
drwx------ 13 fabio fabio 4096 2011-10-21 11:23 include
drwx------ 6 fabio fabio 4096 2011-10-21 11:23 lib
drwx------ 3 fabio fabio 4096 2011-10-21 11:23 man
drwx------ 2 fabio fabio 4096 2011-10-21 11:23 manifest
drwxrwxr-x 2 fabio fabio 4096 2011-10-21 12:21 mytest-src
drwx------ 10 fabio fabio 4096 2011-10-21 11:23 share
drwx------ 3 fabio fabio 4096 2011-10-21 11:23 src
==== Le dimenzioni della nostra applicazione ====
Si puo' certo notare che così il pacchetto della nostra applicazione risulti un po' troppo grande. Questo perchè stiamo includendo ogni dipendenza. Si puo' tuttavia distribuire l'eseguibile senza tutto il resto delegando all'utente l'installazione delle Gtk+ .
=== (Opzionale) Minimizzare le dimenzioni del pacchetto bundle delle Gtk+ ===
Considerato che il bundle gtk+ servira' soltanto per far andare a runtime la nostra applicazione possiamo togliere dallo stesso alcuni file
non necessari a runtime. Ecco come fare: \\
* cancellare tutti gli ''.exe'' dalla cartella **bin/** tranne quello della nostra applicazione;
* cancellare la cartella **share/locale** (solo se non abbiamo intenzione di utilizzare l'internazionalizzazione);
* cancellare la cartella **share/gtk-2.0** ;
* in **lib/** eliminare i file e non le cartelle;
Infine potremmo perfino fare un archivio autoestraente sfx che estragga tutto in una cartella temporanea e avvii in automatico la nostra applicazione. Avendo così alla fine un solo file per la nostra applicazione gtk+ su windows. (sempre che tutto cio' faccia parte del nostro obbiettivo).
===== Quello che serve =====
Per la cross-compilazione dell'eseguibile windows ci affideremo al crosscompilatore [[http://www.mingw.org/|Mingw]] versione per Linux. Su ubuntu è comodamente pacchettizzato (**mingw32**). Inoltre ci servira' il pacchetto [[http://www.gtk.org/download/win32.php|All-in-one bundles]] delle Gtk+ contenente sia i runtime che i sorgenti delle Gtk.
===== Prepariamoci gli strumenti =====
Scarichiamo il pacchetto [[http://www.gtk.org/download/win32.php|All-in-one bundles]] delle Gtk+ in una cartella a nostra scelta.
Ricaviamo i riferimenti agli header e alle librerie grazie al programma **pkg-config.exe** (eseguendolo con wine) contenuto nella sottocartella **bin/** e mettiamo tutto su un file di testo (**riferimenti.txt**). \\ \\
wine ../bin/pkg-config.exe --cflags --libs gtk+-2.0 > riferimenti.txt
Grazie ad un editor di testo, sed o altro sostituiamo ai percorsi stile windows, il nostro percorso relativo in stile unix. Ecco come si presenta il mio file riferimenti.txt (tutto su una riga): \\ \\
**riferimenti.txt**
-mms-bitfields -I../include/gtk-2.0 -I../lib/gtk-2.0/include -I../include/atk-1.0 -I../include/cairo -I../include/gdk-pixbuf-2.0
-I../include/pango-1.0 -I../include/glib-2.0 -I../lib/glib-2.0/include -I../include -I../include/freetype2 -I../include/libpng14
-L../lib -lgtk-win32-2.0 -lgdk-win32-2.0 -latk-1.0 -lgio-2.0 -lpangowin32-1.0 -lgdi32 -lpangocairo-1.0 -lgdk_pixbuf-2.0 -lpango-1.0
-lcairo -lgobject-2.0 -lgmodule-2.0 -lgthread-2.0 -lglib-2.0 -lintl
Aggiungiamo il flag **-mwindows** se non vogliamo l'apertura del prompt della shell all'avvio ella nostra applicazione.
===== Il sorgente per il test =====
{{programmazione:gtk:gtktreeview.png|}} \\ \\
**main.c**
#include
//costanti enumerative con i nomi delle colonne
enum
{
COL_ID ,
COL_COGNOME ,
COL_NOME,
COL_CITTA,
NUM_COLS
} ;
/*Con questa funzione prepariamo la struttura dati
*che sara' contenuta nel TreeView*/
static GtkTreeModel *
create_and_fill_model (void)
{
GtkListStore *store; //contenitore dati
GtkTreeIter iter; // una specie di segnalibro che tiene il conto della posizione corrente
//Creo il contenitore dei dati, specificando il tipo per ogni colonna
store = gtk_list_store_new (NUM_COLS, G_TYPE_UINT, G_TYPE_STRING,G_TYPE_STRING, G_TYPE_STRING);
/* Aggiungiamo una riga */
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
COL_ID, 1,
COL_COGNOME, "Di Matteo",
COL_NOME, "Fabio",
COL_CITTA, "Palermo",
-1);
/* Aggiungiamo una seconda riga */
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
COL_ID, 2,
COL_COGNOME, "Di Matteo",
COL_NOME, "Gioacchino",
COL_CITTA, "Palermo",
-1);
//prima di uscire la funzione ritorna il modello di tipo GtkTreeModel
return GTK_TREE_MODEL (store);
}
static GtkWidget *
create_view_and_model (void)
{
GtkCellRenderer *renderer; //la cella
GtkTreeModel *model; // il modello contenente la struttura dei dati
GtkWidget *view; // puntatore ad un widget generico
view = gtk_tree_view_new (); //il widget generico diventa un GtkTreeView
/*Disegno le colonne nel GtkTreeView (colonne, non righe! :) )*/
/* --- Colonna 1 --- */
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
-1,
"ID",
renderer,
"text", COL_ID,
NULL);
/* --- Colonna 2 --- */
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
-1,
"Cognome",
renderer,
"text", COL_COGNOME,
NULL);
/* --- Colonna 3 --- */
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
-1,
"Nome",
renderer,
"text", COL_NOME,
NULL);
/* --- Colonna 4 --- */
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
-1,
"Citta'",
renderer,
"text", COL_CITTA,
NULL);
/*Associamo al puntatore **locale** il valore di ritorno di create_and_fill_model () ,
* ovvero la struttura creata nella funzione precedente a questa */
model = create_and_fill_model ();
/*Facciamo acuisire al TreeView "view" la struttura dati "model" .
* quando distruggeremo il "view" "model" adra' distrutto con esso */
gtk_tree_view_set_model (GTK_TREE_VIEW (view), model);
g_object_unref (model);
return view;
}
int
main (int argc, char **argv)
{
GtkWidget *window;//la finestra del programma
GtkWidget *view; //il widget che diventera' il GtkTreeView
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_signal_connect (window, "delete_event", gtk_main_quit, NULL); /* dirty */
view = create_view_and_model ();
gtk_container_add (GTK_CONTAINER (window), view);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
===== Il nostro makefile =====
Il make file mettera il nostro eseguibile dentro la sottocartella **bin/** dove sono contenute altre dipendenze dello stesso.
CPP = /usr/bin/i586-mingw32msvc-gcc
# OPTS ottenuto lanciando il comando 'wine ../bin/pkg-config.exe --cflags --libs gtk+-2.0'
# e sostituendo al percorso windows il nostro percorso relativo in stile unix.
OPTS = -mwindows -mms-bitfields -I../include/gtk-2.0 -I../lib/gtk-2.0/include -I../include/atk-1.0 -I../include/cairo -I../include/gdk-pixbuf-2.0 -I../include/pango-1.0 -I../include/glib-2.0 -I../lib/glib-2.0/include -I../include -I../include/freetype2 -I../include/libpng14 -L../lib -lgtk-win32-2.0 -lgdk-win32-2.0 -latk-1.0 -lgio-2.0 -lpangowin32-1.0 -lgdi32 -lpangocairo-1.0 -lgdk_pixbuf-2.0 -lpango-1.0 -lcairo -lgobject-2.0 -lgmodule-2.0 -lgthread-2.0 -lglib-2.0 -lintl
all:
$(CPP) main.c -o ../bin/mytest.exe $(OPTS)
clean:
rm ../bin/mytest.exe
Andremo a impostare la variabile **OPTS** con il contenuto del file **riferimenti.txt** appena ottenuto.
===== Dare alla nostra applicazione un aspetto piu' gradevole =====
Gtk+ supporta i temi anche per windows cosicché se vogliamo dare un aspetto piu gradevole alla nostra applicazione possiamo creare e inserire il file **gtkrc** in **etc/gtk-2.0/** . \\ \\
**gtkrc**
gtk-theme-name = "MS-Windows"
===== Mettere tutto in un unico file eseguibile - compilazione statica =====
{{programmazione:gtk:gtk-static-win32.png|}}
E' possibile distribuire i nostri progetti gtk su windows direttamente in un unnico file eseguibile tramite una versione modificata delle gtk (meno nota) [[http://code.google.com/p/static-gtk2-mingw32/|static-gtk2-mingw32]] . La peculiarita di questa versine delle librerie(oltre a permetterci di distribuire soltanto un unico file .exe) è che di default compila con il tema per windows, quindi non ci sara' bisogno di preoccuparci delle direttive per tema grafico. \\ \\
Per prima cosa scarichiamo il pacchetto dal sito del progetto ed estraiamolo :[[http://code.google.com/p/static-gtk2-mingw32/|static-gtk2-mingw32]]. \\
Come sorgente usiamo lo stesso che abbiamo usato in precedenza in questo caso inutilmente rinominato (**mytest.c**) . e per make file il seguente: \\ \\
**makefile**
CPP = /usr/bin/i486-mingw32-gcc
# OPTS ottenuto lanciando il comando 'wine ../bin/pkg-config.exe --cflags --libs gtk+-2.0'
# e sostituendo al percorso windows il nostro percorso relativo.
OPTS = -mwindows -mms-bitfields -mms-bitfields -I./include/gtk-2.0 -I./lib/gtk-2.0/include \
-I./include/atk-1.0 -I./include/cairo -I./include/gdk-pixbuf-2.0 -I./include/pango-1.0 \
-I./include/glib-2.0 -I./lib/glib-2.0/include -I./include/pixman-1 -I./include -I./include/freetype2 \
-I/gtk-dev/include/libpng14 -L./lib -L/gtk-dev/lib -lgtk-win32-2.0 -lgdk-win32-2.0 -limm32 -lshell32 \
-luuid -latk-1.0 -lpangocairo-1.0 -lgio-2.0 -lshlwapi -ldnsapi -lgdk_pixbuf-2.0 -ltiff -ljpeg -ljasper \
-lpangoft2-1.0 -lpangowin32-1.0 -lusp10 -lfontconfig -lexpat -lpango-1.0 -lcairo -lpixman-1 -lfreetype \
-lmsimg32 -lgdi32 -lpng -lz -lm -lgobject-2.0 -lgmodule-2.0 -lgthread-2.0 -lglib-2.0 -lintl -liconv -lws2_32 -lole32
all:
$(CPP) mytest.c -o mytest.exe $(OPTS)
clean:
rm mytest.exe
Ecco il contennuto della cartella:
drwx------ 2 fabio users 4,0K 27 mar 12.46 bin
drwx------ 16 fabio users 4,0K 27 mar 12.46 include
drwx------ 9 fabio users 4,0K 27 mar 12.46 lib
-rw-r--r-- 1 fabio users 1,6K 27 mar 13.24 makefile
-rw-r--r-- 1 fabio users 4,2K 27 mar 12.58 mytest.c
-rwxr-xr-x 1 fabio users 9,9M 27 mar 13.00 mytest.exe
==== Ridurre le dimensioni dell'eseguibile ====
Per ridurre le dimensioni dell'eseguibile possiamo stripparlo in questo modo:
i486-mingw32-strip mytest.exe si arriva così a 7,5 MB circa(nel mio caso).
E se non siamo ancora contenti delle dimensioni possiamo comprimerlo con upx:
upx mytest.exesi arriva così a 2,6 MB circa(nel mio caso). \\ \\
**N.B.** Mi è capitato con alcuni antivirus di avere dei falsi positivi in seguito alla compressione con upx.
==== Dotare i nostri eseguibili di una icona personalizzata ====
Sui sistemi windows è possibile dotare gli eseguibili di un'icona personalizzata (in formato .ico) al momento della compilazione. In questo articolo ([[programmazione:mingw32:dotare_di_icona_i_nostri_exe]]) vedremo come fare cio' al momento della crosscompilazione su una linuxbox .