2010-10-21

How to construct a makefile on Windows and make life easier for a QGIS Python Plugin

Development of a Python plugin for QGIS is a fun experience and I plan to share my relevant adventures in a future post. But for now, I want to focus on the deployment aspect of it, which can be frustrating if done manually.


The problem is, we need to compile our Qt files into the formats that can be understood by QGIS. To do this we need to use the 'pyuic.py' and 'pyrcc4' tools of the PyQt4 on our Qt files (*.ui and *.qrc files). This means, every time our GUI and resources change, we need to recompile them using PyQt4.

The standard solution to ease a compilation process in the industry is to use a makefile; so I have also tried to construct a makefile for the compilation and deployment needs of my plugin. This was also an educational experience for me since that was my first makefile generation :).

I am currently on a Windows system; that means I need to find a 'make' utility for my platform (Unix platforms have it by default). 'make' port from the GnuWin32 project looks perfect for the job. You can download it from http://sourceforge.net/projects/gnuwin32/files/make/ under the make directory.

After some trial and errors, I've come up with the following solution for my attempt to a makefile (I will briefly explain the mechanism afterward):

The Files:

makefile:
QGISPLUGINSPATH = C:\Users\Chuckle\.qgis\python\plugins

UICPATH = C:\Python25\Lib\site-packages\PyQt4\uic\pyuic.py
UIC = python $(UICPATH)
RCC = pyrcc4

QRCPYFILES := $(patsubst %.qrc,%.py,$(wildcard *.qrc))
UIPYFILES := $(patsubst %.ui,%.py,$(wildcard *.ui))

all: build

build: $(QRCPYFILES) $(UIPYFILES)

%.py: %.qrc
    $(RCC) -o $(basename $@)_rc$(suffix $@) $<

%.py: %.ui
    $(UIC) -o $@ $<

install:
    mkdir2 "$(QGISPLUGINSPATH)\vap"
    copy __init__.py "$(QGISPLUGINSPATH)\vap"
    copy resources_rc.py "$(QGISPLUGINSPATH)\vap"
    copy ui.py "$(QGISPLUGINSPATH)\vap"
    copy ui_control.py "$(QGISPLUGINSPATH)\vap"
    copy vap.py "$(QGISPLUGINSPATH)\vap"
    copy vap_tool.py "$(QGISPLUGINSPATH)\vap"
    copy VAP_icon.png "$(QGISPLUGINSPATH)\vap"
    copy Click_Target_Icon.png "$(QGISPLUGINSPATH)\vap"

clean:
    del resources_rc.py
    del ui.py
    del *.pyc

It needs a complementary file named mkdir2.bat.

mkdir2.bat:
@echo off
IF EXIST %1 GOTO exit
MKDIR %1
:exit

Finally, to run it with a simple click, a third file is included:

build.bat:
cd "%vapdev%"
"C:\Program Files\GnuWin32\bin\make" clean build install

   
Usage and Explanation


This makefile have three targets (operations): clean, build, and install. To perform all of them in a single click, just run 'build.bat'. The first target deletes the output files. The second one uses the PyQt4 tools on the suitable files it can find in the directory. The final one copies the output files into the QGIS plugins directory.

Now let's analyze the makefile line by line:


QGISPLUGINSPATH = C:\Users\Chuckle\.qgis\python\plugins

This line defines our QGIS python plugins directory.

UICPATH = C:\Python25\Lib\site-packages\PyQt4\uic\pyuic.py
UIC = python $(UICPATH)
RCC = pyrcc4


These lines define the PyQt4 tools. Since 'pyuic.py' is a Python application, we will call it using the python interpreter. We also need to add PyQt 'bin' directory to the system path ('C:\Python25\Lib\site-packages\PyQt4\bin' for my setup).

QRCPYFILES := $(patsubst %.qrc,%.py,$(wildcard *.qrc))
UIPYFILES := $(patsubst %.ui,%.py,$(wildcard *.ui))


These two lines define the wanted output files. Wildcard expression finds all the matching files. Then, patsubst function replaces their extensions with the '.py' extensions.

all: build

build: $(QRCPYFILES) $(UIPYFILES) 


We list the output files for the 'build' target here. This essentially causes 'make' to search for ways to produce those new .py files ('all' is the default target).

%.py: %.qrc
    $(RCC) -o $(basename $@)_rc$(suffix $@) $<

%.py: %.ui
    $(UIC) -o $@ $<

These next lines provide such a way :)  Basically, we tell 'make' to use pyrcc4 utility on *.qrc files and pyuic utility on *.ui files in order to produce corresponding *.py files.

install:
    mkdir2 "$(QGISPLUGINSPATH)\vap"
    copy __init__.py "$(QGISPLUGINSPATH)\vap"
    copy resources_rc.py "$(QGISPLUGINSPATH)\vap"
    copy ui.py "$(QGISPLUGINSPATH)\vap"
    copy ui_control.py "$(QGISPLUGINSPATH)\vap"
    copy vap.py "$(QGISPLUGINSPATH)\vap"
    copy vap_tool.py "$(QGISPLUGINSPATH)\vap"
    copy VAP_icon.png "$(QGISPLUGINSPATH)\vap"
    copy Click_Target_Icon.png "$(QGISPLUGINSPATH)\vap"


These lines define our target for the installation phase. The necessary files are copied to the QGIS plugins directory. We can not use 'mkdir' command directly here; because if the files already exist in the destination, it will give an error breaking the operation. So, I have written another batch file called 'mkdir2.bat' where I first check for the existence of files before issuing the command.

clean:
    del resources_rc.py
    del ui.py
    del *.pyc

Finally, we define the target for the cleaning operation where we basically delete the output files; and that's all.

Now I can just click on the 'build.bat' batch file and the updated plugin will be ready inside the QGIS plugins directory!

As you can see, use of a makefile greatly simplified the process of developing Python plugins for me and I strongly recommend this approach if you develop such plugins.

I repeat that this is my first attempt for a makefile and I am not an expert. If I've made some mistakes, or if there are better ways to do the same thing, please inform me so; thanks!


summary: 'Software is about automation... automation is frustrating to learn, a breeze to use :)'
mood: feel like to listen some great jazz with a cup of fine coffee...mmmm

No comments:

Post a Comment