qpm - Qt 下的套件管理程式

一直以來,我的 Qt 程式如果需要第三方的模組,通常都是使用 git submodule 去將他加 入到我的專案來,但是這樣總是缺少了一些彈性,畢竟蠻多人不太會用 git 的 submodule 功能 :( 。

qpm 是一個類似 node.js 的 npm 的套件管理程式,透過他可以讓你安裝第三方的 Qt 模組 就像開發 node.js 程式那樣方便,當你使用 qpm 安裝新的模組時,他下載的是 程式碼 ,因此你可以確保你的程式編譯出來可以完整相容第三方套件,而不會有因為 API 改變而 導致程式掛掉的問題。

讓我們來開始使用 qpm 吧 :)

安裝 qpm

如果你是 Windows、Mac OSX 的使用者,你可以直接連到 http://qpm.io ,上面會有安裝 教學。

由於目前沒有提供 Linux 的二進制版,因此必須自己編譯,Linux 使用者請參考 手動編譯 qpm 一節來編譯你的 qpm 程式。

手動編譯 qpm

目前 qpm 提供了 Windows、Mac OSX 兩個系統的 binary 程式,Linux 下就必須自己動手編 譯,不過也不要灰心,編譯 qpm 是非常容易的。

首先我們需要安裝 Go 語言的支援,在我的 Gentoo Linux 系統下是這樣安裝的:

coldnew@gentoo ~ $ sudo emerge dev-lang/go

如果你的系統是 Ubuntu,你則是可以參考 這份文件 ,使用以下命令安裝:

coldnew@ubuntu ~ $ sudo apt-get install golang

接下來我們需要取得 qpm 的程式碼,並更新其使用的 git submodule

git clone https://github.com/Cutehacks/qpm.git && cd qpm
git submodule init
git submoodule update

完成後你只需要使用 make 命令,qpm 就編譯完成囉 ~

coldnew@gentoo ~/qpm $ make
GOPATH=:~/qpm: go install qpm.io/qpm

你會在當前目錄看到有一個 bin 的目錄產生,裡面就是我們需要的 qpm 執行檔,將他移 動到你的 PATH 變數可以找到的地方即可 (ex: /usr/local/bin)

coldnew@gentoo ~/qpm $ ls bin
qpm

Hello Qpm

我們首先使用 QtCreator 建立一個簡單的 Qt Quick Application 專案

當然名稱、專案的路徑就隨你高興囉 ~

這邊我選用 Qt QuickControl 1.3,不過其實選什麼都可以 (只要你建的專案可以顯示 QML 程式就好)

選擇 Desktop 版本

要不要加入版本管理也是隨個人的喜好 ~

按下 Done 按鈕後基本的 Qt Quick 程式就完成了,是 qpm 出馬的時候了,我們先開 shell 切到專案目錄,並使用 qpm 安裝 io.qpm.example

coldnew@gentoo ~/untitle $ qpm install io.qpm.example
Installing io.qpm.example@0.0.1

你會發現增加了 vendor 目錄以及 qpm.json 檔案,而 vendor 目錄下則是第三方程式 的程式碼

coldnew@Sara ~/untitled $ tree
.
├── MainForm.ui.qml
├── deployment.pri
├── main.cpp
├── main.qml
├── qml.qrc
├── qpm.json
├── untitled.pro
├── untitled.pro.user
└── vendor
    ├── io
    │   └── qpm
    │       └── example
    │           ├── HelloQpm.qml
    │           ├── LICENSE
    │           ├── README.md
    │           ├── helloqpm.cpp
    │           ├── helloqpm.h
    │           ├── helloqpm.js
    │           ├── io_qpm_example.pri
    │           ├── io_qpm_example.qrc
    │           ├── qmldir
    │           ├── qpm.h
    │           └── qpm.json
    └── vendor.pri

4 directories, 20 files

qpm.json 實際上就是類似 node.js 的 package.json 這樣的角色,我們可以看到裡面紀錄 了我們安裝的套件的版本資訊

{
  "name": "",
  "description": "",
  "dependencies": [
    "io.qpm.example@0.0.1"
  ],
  "license": "NONE",
  "pri_filename": "",
  "webpage": ""
}

接下來,我們必須編輯我們專案的 *.pro 檔案,以本文的範例就是 untitle.pro ,在 裡面加入以下這一行,讓 Qt 知道要去查看 vendor 目錄下的其他程式

include(vendor/vendor.pri)

接著修改 main.cpp ,加入 QPM_INIT 這個巨集,他會讓 Qt 程式知道第三方模組的 resource 位置 (ex: qml, images)。

如果我們要使用 io.qpm.example 裡面的 C++ class, 則記得要使用相對應的 namespace。

diff --git a/main.cpp b/main.cpp
--- a/main.cpp
+++ b/main.cpp
@@ -1,12 +1,18 @@
 #include <QApplication>
 #include <QQmlApplicationEngine>
+#include <QDebug>

+#include <io/qpm/example/helloqpm.h>

 int main(int argc, char *argv[])
 {
     QApplication app(argc, argv);

+    io::qpm::example::HelloQpm hello;
+    qDebug() << hello.message();
+
     QQmlApplicationEngine engine;
+    QPM_INIT(engine) // <-- Add this for qml, images resource
     engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

     return app.exec();

接著修改 main.qml ,並將程式改成如下:

import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2

import io.qpm.example 1.0

ApplicationWindow {
    title: qsTr("Hello Qpm")
    width: 200
    height: 100
    visible: true

    HelloQpm {
        anchors.fill: parent
    }
}

編譯並執行,你就會發現我們顯示的視窗的確是使用到了 io.qpm.example 裡面的模組, 此外也會看到 console 顯示了 Hello from C++ 這樣的訊息。

更多 qpm 使用方式

想要知道完整的 qpm 使用方式,問 qpm 最快了,目前我使用的 qpm 顯示的結果如下:

coldnew@Sara ~ $ qpm -h

qpm is a tool for managing Qt dependencies

Usage:
	qpm COMMAND [args]

The currently supported commands are:

	uninstall		Uninstalls a package
	check			Checks the package for common errors
	sign			Creates a PGP signature for the package (experimental)
	verify			Verifies the package PGP signature (experimental)
	ping			Pings the server
	init			Initializes a new module in the current directory
	info			Displays information about the specified package
	install			Installs a new package
	publish			Publishes a new module
	help			Shows the help text for a command
	search			Searches for packages containing the given string
	list			Lists all packages in the registry

qpm@0.9.0

使用方式大部分都和 npm 一樣,我們可以使用 qpm list 顯示目前有哪些套件

假設我們想知道更多關於 io.qpm.example 的資訊,則可以使用 qpm info 去查詢

coldnew@Sara ~ $ qpm list

Package                                  Author
---------------------------------------------------------------------------
com.cutehacks.circularimage@1.0.5        Jens Bache-Wiig <jens@cutehacks.com>
com.cutehacks.duperagent@0.9.3           Jason Barron <jason@cutehacks.com>
com.cutehacks.fontawesome@0.0.3          Henrik Hartz <henrik@cutehacks.com>
com.cutehacks.navigationdrawer@0.1.0     Jens Bache-Wiig <jens@cutehacks.com>
com.cutehacks.relativetime@0.0.1         Marius Bugge Monsen <marius@cutehacks.com>
com.github.benlau.quickpromise@0.0.1     Ben Lau <xbenlau@gmail.com>
com.github.quickflux@0.0.2               Ben Lau <xbenlau@gmail.com>
de.nebulon.async@0.0.2                   Johannes Zellner <johannes@nebulon.de>
de.nebulon.request@0.0.2                 Johannes Zellner <johannes@nebulon.de>
io.qpm.example@0.0.1                     Jason Barron <jason@cutehacks.com>
coldnew@Sara ~ $ qpm info io.qpm.example

Name: io.qpm.example
Author: Jason Barron (jason@cutehacks.com)
Webpage:
License: MIT
Repository: https://github.com/Cutehacks/qpm-example.git
Description:

建立 qpm 套件

會使用 qpm 以後,我們也可以試著自己動手製作自己的 qpm 模組,首先先建立一個新的資 料夾,並使用 qpm init 初始化他,這時 qpm 會問你一些關於該模組的資訊

coldnew@Sara ~/io.coldnew.hello $ qpm init
Your name: [Yen-Chin Lee]
Your email: [coldnew.tw@gmail.com]
Unique package name: [com.gmail.io.coldnew.hello] io.coldnew.hello
Initial version: [0.0.1]
WARNING: Could not auto-detect repository URL.
Clone URL: https://github.com/coldnew/qpm-hello
Package .pri file: [io_coldnew_hello.pri]
Generate boilerplate: [Y/n] y

完成後,你會發現到我們的目錄下增加了以下這些檔案

coldnew@Sara ~/io.coldnew.hello $ tree
.
├── LICENSE
├── io_coldnew_hello.pri
├── io_coldnew_hello.qrc
├── qmldir
└── qpm.json

0 directories, 5 files

接下來我們就可以來新增我們自己的 Qt 程式,比如說我們建立一個 HelloQpm 模組,首先 建立 helloqpm.h 並填入以下資訊

#include <QObject>
#include "qpm.h"

QPM_BEGIN_NAMESPACE(io, coldnew, hello)

class HelloQpm : public QObject
{
    Q_OBJECT

    Q_PROPERTY(QString message READ message NOTIFY messageChanged)

public:
    HelloQpm(QObject *parent = 0);
    inline QString message() const {return m_message;}

signals:
    void messageChanged(QString message);

private:
    QString m_message;
};

QPM_END_NAMESPACE(io, coldnew, hello)

接下來則是 helloqpm.cpp:

#include "helloqpm.h"

QPM_BEGIN_NAMESPACE(io, coldnew, hello);

HelloQpm::HelloQpm(QObject *parent) : QObject(parent) {
    m_message = "Hello from C++";
}

QPM_END_NAMESPACE(io, coldnew, hello);

完成後要把 helloqpm.h、helloqpm.cpp 資訊加入 io_coldnew_hello.pri 裡面

diff --git a/io_coldnew_hello.pri b/io_coldnew_hello.pri
--- a/io_coldnew_hello.pri
+++ b/io_coldnew_hello.pri
@@ -1,3 +1,9 @@

 RESOURCES += \
     $$PWD/io_coldnew_hello.qrc
+
+HEADERS += \
+    $$PWD/helloqpm.h
+
+SOURCES += \
+    $$PWD/helloqpm.cpp
+

我們也可以建立我們要用的 qml 模組,首先建立 HelloQpm.qml 並加入以下程式

import QtQuick 2.0

Rectangle {
    width: 200
    height: 100
    color: "red"

    Text {
        color: "white"
        text: "Hello Qpm!!"
        anchors.centerIn: parent
        font.pixelSize: parent.height / 3
    }
}

接著修改 qmldir,讓他知道有 HelloQpm.qml 的存在以及相對應的版本、模組名稱

diff --git a/qmldir b/qmldir
--- a/qmldir
+++ b/qmldir
@@ -1,2 +1,3 @@

 module io.coldnew.hello
+HelloQpm 1.0 HelloQpm.qml

我們也記得要將這個 qml 加入到我們的 resource 檔案: io_coldnew_hello.qrc

diff --git a/io_coldnew_hello.qrc b/io_coldnew_hello.qrc
--- a/io_coldnew_hello.qrc
+++ b/io_coldnew_hello.qrc
@@ -2,5 +2,6 @@
 <RCC>
     <qresource prefix="/io/coldnew/hello">
         <file>qmldir</file>
+        <file>HelloQpm.qml</file>
     </qresource>
 </RCC>

當你確定你的修改都 ok 後,將變更 commit 進去,是時候將他送到 qpm 裡面了,我們首先 先用 qpm check 在檢查是否有設定出錯

coldnew@Sara ~/io.coldnew.hello $ qpm check
OK!

接下來就可以使用 qpm publish 進行發佈,若你帳號不存在的話會順便幫你建立帳號以及 tag。

coldnew@Sara ~/io.coldnew.hello $ qpm publish
email: coldnew.tw@gmail.com
password:
User not found. Confirm password to create a new user.
password:
Running check
OK!
Publishing
Tag release: [Y/n] y
SUCCESS!
Publised package: io.coldnew.hello@0.0.1
Revision: 506bf4fc369e736ad3b498fb59a4383c8e05cf6e

完成後,我們可以在使用 qpm search 檢查是否已經送到 qpm 伺服器上了

coldnew@Sara ~/io.coldnew.hello $ qpm search io.coldnew.hello

Package                                  Author
---------------------------------------------------------------------------
io.coldnew.hello@0.0.1                   Yen-Chin Lee <coldnew.tw@gmail.com> 

當然也可以使用 qpm info 顯示我們發佈的套件資訊

coldnew@Sara ~/io.coldnew.hello $ qpm info io.coldnew.hello

Name: io.coldnew.hello
Author: Yen-Chin Lee (coldnew.tw@gmail.com)
Webpage:
License: MIT
Repository: https://github.com/coldnew/qpm-hello
Description: