为snapcraft创建一个简单的定制的plugin

本文介绍如何为Snapcraft创建自定义插件并应用于实际项目中,通过具体示例展示了插件的基本结构及其在软件打包流程中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们知道snapcraft的plugin框架是可以扩展的.我们可以为我们的snapcraft根据新的需求定义一个新的custom plugin来build我们的项目.在今天的这篇文章中,我们将介绍如何为我们的snapcraft创建一个新的plugin.关于snapcraft的plugin的更多的知识,请参阅我们的文档.更多关于snapcraft的知识,可以参阅我们的网站snapcraft.io.我们也可以在地址找到许多的plugin的例程供我们参考.


1)创建一个最基本的custom plugin



目前,我们能够在网上找到的最相近的文档在:


按照要求,我们创建一个最基本的custom plugin的 模版.我们可以通过如下的方法把我们的代码下载下来:

$ git clone https://github.com/liu-xiao-guo/plugin_template

我们的项目的文件架构:

liuxg@liuxg:~/release/plugin_template$ tree -L 3
.
├── parts
│   └── plugins
│       └── myplugin.py
└── snapcraft.yaml

在上面的文件架构中,我们可以看出来,我们在我们的项目的根目录下创建了一个叫做parts的目录.在它的下面,我们也创建了一个叫做plugins的子目录.里面我们创建了一个最基本的文件myplugin.py.它的内容如下:

myplugin.py


# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-

import logging
import os
import glob
from pprint import pprint

import snapcraft

logger = logging.getLogger(__name__)

def _dump(name, obj):
    for attr in dir(obj):
        # logger.warning("obj.%s = %s", attr, getattr(obj, attr))
        func = getattr(obj, attr, None)
        if func:
            logger.warning("%s.%s = %s",name, attr, func)
            
    logger.warning("===================================")

class MyPlugin(snapcraft.BasePlugin):

    @classmethod
    def schema(cls):
        schema = super().schema()

        schema['properties']['myprop'] = {
            'type': 'string',
            'default': ''
        }

        # Inform Snapcraft of the properties associated with building. If these
        # change in the YAML Snapcraft will consider the build step dirty.
        schema['build-properties'].append('myprop')

        return schema
                        
    def __init__(self, name, options, project):
        super().__init__(name, options, project)
        logger.warning("__init__ begins name: %s", name)
        logger.warning("options.source: %s", options.source)
        logger.warning("options.myprop: %s", options.myprop) 
        logger.warning("self.builddir: %s", self.builddir)
        logger.warning("self.installdir: %s", self.installdir)
        # logger.warning("self.project.parallel_build_count: %d", self.project.parallel_build_count)
        # logger.warning("self.project.arch_triplet: %s", self.project.arch_triplet)
        # logger.warning("self.project.stage_dir: %s", self.project.stage_dir)
       
        logger.warning("Going to add the needed build packages...")
        # self.build_packages.append('golang-go')
        # _dump("options", options)
        # _dump("project", project)
        
        logger.warning("build-packages:")
        for pkg in options.build_packages:
            logger.warning("build package: %s", pkg)

    def build(self):
        # setup build directory
        super().build()
        logger.warning("build begins ... ")
               
    def pull(self):
        super().pull()
        logger.warning("pull begins ... ")                
       
    def clean_pull(self):
        super().clean_pull()       
        logger.warning("clean pull begins ... ")      

在这里,我们可以看出来一个最基本的最简单的一个plugin的模版.我们的MyPlugin继承于 snapcraft.BasePlugin.其实里面没有做任何实质性的东西.这里值得指出的是,我们使用了logger.warning(...)来帮我们输出调试信息.在真正的release时,我们需要删除这些输出.我们知道,在snapcraft build一个项目时,它经历如下的过程:




一个plugin在上面的5个过程中,它有经历 pullbuild.我们也可以参考在snapcraft项目中的许多plugin的 实例来如何实现上面的method.
在上面的代码中:

        schema['properties']['myprop'] = {
            'type': 'string',
            'default': ''
        }

我们也定义了一个我们自己的被叫做 myprop的property.为了展示我们对这个定制的snapcraft的plugin的调用,我们设计了如下的snapcraft.yaml文件:

snapcraft.yaml

name: plugin # you probably want to 'snapcraft register <name>'
version: '0.1' # just for humans, typically '1.2+git' or '1.3.2'
summary: This is a sample plugin for snapcraft # 79 char long summary
description: |
  This is a plugin example

grade: stable # must be 'stable' to release into candidate/stable channels
confinement: strict # use 'strict' once you have the right plugs and slots

parts:
  my-part:
    # This is a custom plugin defined in "parts" dir
    plugin: myplugin
    # This is the property defined in the plugin. It is a file name
    # to be used for the congfigure hook
    myprop: configure
    source: .    

在这里,我们运用了myplugin,同时我们也使用了在我们plugin中所定义的myprop属性为configure.source通常是一个plugin必不可少的一个属性.在我们的例程中,我们定义它为".",也即当前的目录.

至此,我们最基本的一个plugin的框架已经搭好了,那么我们来对我们的项目进行build.在项目的根目录中打入:

$ snapcraft



从上面我们可以看出来它已经成功利用我们自己设计的plugin来build我们的项目,并且我们可以看出来" pull begins ..."及" build begins ..."字样.这些都是在我们的上面的myplugin.py中的logger.warning(...)所输出的.同时,我们也打印出来一些我们可能需要的Object属性,比如self.builddir等.我们也可以看出我们的options.myprop的值为"configure".同样,我们也可以来clean我们的项目:

$ snapcraft clean



从上面可以看出来,在myplugin.py中所定义的"clean_pull(self)"也被调用.我们可以通过这个method来删除我们在pull中下载的任何东西.

到目前为止,我们的最基本的template plugin没有做任何实质性的东西,它只是展示了继承于 snapcraft.BasePlugin类里最基本的method(比如build(self))的调用.在下面的章节中,我们将展示如何利用这些方法来做我们需要做的事.


2)创建一个简单的运用snapcraft plugin的应用



在这一章节中,我们来展示如何使用我们上面的plugin来做一些有意义的事.在今天的例程中,我们想把在我们项目最终的snap包中打入如下的configure文件:

liuxg@liuxg:~/snappy/desktop/plugin/prime$ tree -L 3
.
├── bin
│   └── config
├── command-config.wrapper
└── meta
    ├── hooks
    │   └── configure
    └── snap.yaml

一旦我们在meta/hooks目录下生产这个configure(可执行文件)文件,其内容如下:

configure


#!/bin/sh

if ! username=$(snapctl get username); then
    echo "Username is required"
    exit 1
fi

if ! password=$(snapctl get password); then
    echo "Password is required"
    exit 1
fi

# Handle username and password, perhaps write to a credential file of some sort.
echo "user=$username" > $SNAP_DATA/credentials
echo "password=$password" >> $SNAP_DATA/credentials
chmod 600 $SNAP_DATA/credentials

一旦在我们的snap安装中有上面的configure文件,那么我们可以在我们的Ubuntu Core系统中,使用如下的命令:

$ sudo snap set plugin username=foo password=bar

来生产在$SNAP_DATA目录下的credentials文件.其内容如下:



由于一些原因,目前只能在Ubuntu Core的系统中进行测试(比如树莓派,dragon board, KVM等上进行测试).在Ubuntu Desktop的桌面环境中还是有一点问题.
基于这样的思路,我们重新改写我们的myplugin.py文件:

myplugin.py


# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-

import logging
import os
import glob
from pprint import pprint

import snapcraft

logger = logging.getLogger(__name__)

def _dump(name, obj):
    for attr in dir(obj):
        # logger.warning("obj.%s = %s", attr, getattr(obj, attr))
        func = getattr(obj, attr, None)
        if func:
            logger.warning("%s.%s = %s",name, attr, func)
            
    logger.warning("===================================")

class MyPlugin(snapcraft.BasePlugin):

    @classmethod
    def schema(cls):
        schema = super().schema()

        schema['properties']['myprop'] = {
            'type': 'string',
            'default': ''
        }

        # Inform Snapcraft of the properties associated with building. If these
        # change in the YAML Snapcraft will consider the build step dirty.
        schema['build-properties'].append('myprop')

        return schema
        
    def _copy(self, path_copyto):
        path = os.path.join(path_copyto, "meta/hooks/")
        logger.warning("path: %s", path);
        os.makedirs(os.path.dirname(path), exist_ok=True)
        source = self.builddir + "/" + self.options.myprop
        logger.warning("source: %s", source)
        destination = path + self.options.myprop
        logger.warning("destination: %s", destination)
        snapcraft.common.link_or_copy(source, destination, False)
                
    def __init__(self, name, options, project):
        super().__init__(name, options, project)
        logger.warning("__init__ begins name: %s", name)
        logger.warning("options.source: %s", options.source)
        logger.warning("options.myprop: %s", options.myprop) 
        logger.warning("self.builddir: %s", self.builddir)
        logger.warning("self.installdir: %s", self.installdir)
        # logger.warning("self.project.parallel_build_count: %d", self.project.parallel_build_count)
        # logger.warning("self.project.arch_triplet: %s", self.project.arch_triplet)
        # logger.warning("self.project.stage_dir: %s", self.project.stage_dir)
       
        logger.warning("Going to add the needed build packages...")
        # self.build_packages.append('golang-go')
        #_dump("options", options)
        #_dump("project", project)
        
        logger.warning("build-packages:")
        for pkg in options.build_packages:
            logger.warning("build package: %s", pkg)

    def build(self):
        # setup build directory
        super().build()
        logger.warning("build begins ... ")
        
        # copy congfigure file to parts/<part>/build/meta/hooks
        self._copy(self.builddir)
        
        # copy configure file to parts/<part>/install/meta/hooks
        self._copy(self.installdir)
       
    def pull(self):
        super().pull()
        logger.warning("pull begins ... ")                
       
    def clean_pull(self):
        super().clean_pull()       
        logger.warning("clean pull begins ... ")

在这里,我们把我们的configure文件考入到我们相应part的build及install目录里.这样我们的任务就完成了.我们主要修改的部分在"build(self)".同样地,我们也对我们的snapcraft.yaml做了如下的修改:

snapcraft.yaml


name: plugin # you probably want to 'snapcraft register <name>'
version: '0.1' # just for humans, typically '1.2+git' or '1.3.2'
summary: This is a sample plugin for snapcraft # 79 char long summary
description: |
  This is a plugin example

grade: stable # must be 'stable' to release into candidate/stable channels
confinement: strict # use 'strict' once you have the right plugs and slots

apps:
  config:
   command: bin/config

parts:
  my-part:
    # This is a custom plugin defined in "parts" dir
    plugin: myplugin
    # This is the property defined in the plugin. It is a file name
    # to be used for the congfigure hook
    myprop: configure
    source: .
    
  config:
    plugin: dump
    source: ./bin
    organize:
      "*": bin/

在这里,我们加入了一个叫做config的app.它可以被用来展示我们的设置的结果:

config


#!/bin/bash

echo "Reading config file at: $SNAP_DATA"

config_file="$SNAP_DATA/credentials"

if [ -f "$config_file" ]
then
	cat $config_file
else
	echo "$config_file not found."
	echo "Please run the following commmand to generate the file:"
	echo "sudo snap set plugin username=foo password=bar"
fi

重新build我们的项目:



从上面可以看出来我们中间输出的一些调试的信息.当然我们可以做任何我们需要做的事情,比如根据build-packages的定义来分别安装对应的debian包等.

我们也可以来测试我们的应用:


事实上我们也可以直接使用由我们的snapcraft所提供的 dump plugin来完成这个.有兴趣的开发者可以参阅我的文章" 如何为我们的Ubuntu Core应用进行设置".我们从这个例子中展示如何创建一个我们自己的snapcraft的custom plugin.

更多关于如何设计自己的custom plugin:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值