Barra laterale

programmazione:gtk:crosscompilare_app_gtk_su_linux_per_windows

Crosscompilare le nostre applicazioni Gtk+ su GNU/Linux per Windows

Autore: Fabio Di Matteo
Ultima revisione: 27/03/2013

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 Mingw versione per Linux. Su ubuntu è comodamente pacchettizzato (mingw32). Inoltre ci servira' il pacchetto All-in-one bundles delle Gtk+ contenente sia i runtime che i sorgenti delle Gtk.

Prepariamoci gli strumenti

Scarichiamo il pacchetto 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



main.c

#include <gtk/gtk.h>
 
//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

E' possibile distribuire i nostri progetti gtk su windows direttamente in un unnico file eseguibile tramite una versione modificata delle gtk (meno nota) 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 :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.exe

si 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 (Dotare di icona personalizzata i nostri eseguibili windows tramite crosscompilazione) vedremo come fare cio' al momento della crosscompilazione su una linuxbox .


programmazione/gtk/crosscompilare_app_gtk_su_linux_per_windows.txt · Ultima modifica: 18/04/2018 - 15:48 (modifica esterna)