flatpak——Hello World



  • 哈哈,最近论坛新鲜事好多呀!我也来凑凑热闹。
    原文参考: https://blog.csdn.net/qq_32768743/article/details/80024622

    前言

    deepin自从宣布全力支持flatpak后,我研究flatpak很久了,一直没有什么进展。最近重新学了不少东西,整理一下,写下这篇文章。我的环境是Deepin 15.5。学习flatpak,官网和官网文档肯定是第一手资料。印象中flatpak的官网都改版了几次了。如果使用ubuntu,安装官方文档的指引,完成Building Your First Flatpak肯定是毫无压力的。可是,deepin毕竟是带有中国特色的linux发行版,总会有些不可思议的存在的。首先来尝试一下,各种花式Hello World。在正式开始之前,需要做一些准备工作,安装一些依赖。

    sudo apt install flatpak flatpak-builder deepin-fprt-com.deepin.platform-15.5 deepin-fprt-com.deepin.sdk-15.5
    

    这些依赖并不小,需要一段时间。为了方便后面使用,先添加一个仓库

    flatpak --user remote-add --no-gpg-verify --if-not-exists tutorial-repo tutorial-repo
    

    下面所有的操作都是在/home/pikachu/flatpak文件夹下进行的。

    Shell版

    这个版本的Hello World就是执行一个shell脚本,使用echo命令输出一行Hello World。代码很简单,脚本如下

    hello.sh

    #!/bin/bash
    echo "Hello World"
    

    把这个文件命名为hello.sh,给予执行权限,偷个懒,命令就是chmod 777 hello.sh。接着还有一个json文件,内容如下

    cn.net.pikachu.Hello.json

    {
        "app-id":"cn.net.pikachu.Hello",
        "runtime":"com.deepin.Platform",
        "runtime-version":"15.5.2",
        "sdk":"com.deepin.Sdk",
        "command":"hello.sh",
        "modules":[
            {
                "name":"hello",
                "buildsystem":"simple",
                "build-commands":[
                    "install -D hello.sh /app/bin/hello.sh"
                ],
                "sources":[
                    {
                        "type":"file",
                        "path":"hello.sh"
                    }
                ]
            }
        ]
    }
    

    把这个文件命名为cn.net.pikachu.Hello.json,姑且先不管各个项是什么含义,继续进行构建。于是,在同一个文件夹下,有cn.net.pikachu.Hello.jsonhello.sh两个文件。执行构建命令

    flatpak-builder --repo=tutorial-repo hello cn.net.pikachu.Hello.json
    

    这里写图片描述
    接着,安装,运行

    flatpak --user install tutorial-repo cn.net.pikachu.Hello
    flatpak run cn.net.pikachu.Hello
    

    这里写图片描述
    最后,可以打一个包,当别人使用时,就可以非常简单的安装了。

    flatpak build-bundle tutorial-repo hello.flatpak cn.net.pikachu.Hello
    

    这里写图片描述
    再次安装时,可以简单的使用flatpak install hello.flatpak就OK。

    小结

    Hello World只是开胃小菜,真正的好戏还在后面。
    有一个地方,我一直很疑惑。注意到modules.sources.path的值是一个字符串,如果我有多个文件,该怎么在json文件中写呢?试过把path的值改为数组,报错了。

    C++ 版

    c++版就是使用cmake作为构建工具,输出一个简单的Hello World.

    CMakeLists.txt

    cmake_minimum_required(VERSION 2.8)
    
    project(hello_cmake)
    add_executable(${PROJECT_NAME} "main.cpp")
    set(CMAKE_INSTALL_PREFIX /usr)
    install(TARGETS hello_cmake DESTINATION bin)
    

    这里需要注意的是,写了一个set(CMAKE_INSTALL_PREFIX /usr),在flatpak构建时,要求把可执行文件放在/app/bin,所以可以看到后面用sed做了替换。

    cn.net.pikachu.cmake.Hello.json

    {
        "app-id":"cn.net.pikachu.cmake.Hello",
        "runtime":"com.deepin.Platform",
        "runtime-version":"15.5.2",
        "sdk":"com.deepin.Sdk",
        "command":"hello_cmake",
        "finish-args":[
        "--share=ipc",
        "--socket=x11",
        "--socket=pulseaudio",
        "--share=network",
        "--device=dri",
        "--filesystem=home"
        ],
        "modules":[
            {
                "name":"hello",
                "no-autogen":true,
                "sources":[
                    {
                        "type":"git",
                        "url":"file:///home/pikachu/flatpak/hello_cmake",
                        "branch":"master"
                    },
                    {
                        "type":"script",
                        "commands":[
                            "sed -i 's|/usr|/app|g' CMakeLists.txt",
                            "cmake ."
                        ],
                        "dest-filename":"configure"
                    }
                ]
            }
        ]
    }
    

    小结

    多个文件,我一直不知道该怎么弄。后来在Deepin论坛上,看到有人做的一个图形化打包界面,了解后发现是依赖Git工作的。这里,我也是依赖Git完成的。

    Qt 版

    Qt版的Hello World,就是显示一个简单的对话框,在对话框中间显示Hello World。最后,稍微接触一下Deepin开发的dxcb插件。

    一梭子命令

    有了前面的经验,再一次构建就非常快了。

    flatpak-builder --repo=tutorial-repo hello cn.net.pikachu.qt.Hello.json --force-clean 
    flatpak --user install tutorial-repo cn.net.pikachu.qt.Hello
    flatpak run cn.net.pikachu.qt.Hello
    
    

    cn.net.pikachu.qt.Hello.json

    {
        "app-id":"cn.net.pikachu.qt.Hello",
        "runtime":"com.deepin.Platform",
        "runtime-version":"15.5.2",
        "sdk":"com.deepin.Sdk",
        "command":"hello",
        "finish-args":[
            "--socket=x11",
            "--device=dri"
        ],
        "modules":[
            {
                "name":"hello",
                "no-autogen":true,
                "sources":[
                    {
                        "type":"git",
                        "url":"file:///home/pikachu/flatpak/hello_qt",
                        "branch":"master"
                    },
                    {
                        "type":"script",
                        "commands":[
                            "sed -i 's|/usr|/app|g' deepin-voice-recorder.pro",
                            "qmake PREFIX=/app"
                        ],
                        "dest-filename":"configure"
                    }
                ]
            }
        ]
    }
    

    代码: https://download.csdn.net/download/qq_32768743/10363976
    其实代码不重要,随便搭建一个GUI界面就行了。重点在于怎么用flatpak的manfest文件。
    这里写图片描述
    这里写图片描述
    需要注意的是,Deepin的Runtime是自带Qt的,看到About Qt中,Qt的版本居然是5.9。在自己的机器上,Deepin仓库里Qt的版本是5.6.1。在flatpak中,也是可以使用Deepin的dxcb插件的。
    主要有两点,一是使用DApplication::loadDXcbPlugin();;二是把QApplication换成DApplication。修改代码如下

    #include "dialog.h"
    #include <QApplication>
    #include <DApplication>
    #include <DLog>
    #include <QtCore>
    DWIDGET_USE_NAMESPACE
    DCORE_USE_NAMESPACE
    int main(int argc, char *argv[])
    {
        DLogManager::registerConsoleAppender();
        auto flag = DApplication::loadDXcbPlugin();
        qDebug()<<"load dxcb plugin"<<flag;
        DApplication a(argc, argv);
        a.setTheme("light");
        Dialog w;
        w.show();
    
        return a.exec();
    }
    
    

    这里写图片描述

    小结

    Qt GUI程序设计是使用flatpak的大头,现在deepin已经把很多应用迁移到flatpak上去了,我也计划把我的一个小工具用flatpak打包。

    谈一谈自己的一点想法

    从3个Hello World的构建来看,前面的信息都差不多,最主要的一个地方就是modules怎么去写,我相信,这也就是flatpak解决linux原有的版本问题的一种方式。当依赖变得复杂的时候,该怎么办?这将是下一次需要考虑的问题。在Hello World的例子中,暂且不管。但不管怎么样,首先是要对已有的命令,工具熟悉。新的工具总是或兼容、或依赖、或改进已有的工具的。无论是deb打包也好,flatpak打包也好,首先要做的是把软件开发好,把依赖梳理好,然后再根据相应的规则打包,对外发布。

    就目前的情况,也看不出flatpak什么奇特的地方。反而很晕,为什么弄这么复杂呢?还有很多新的概念。就目前关于flatpak的信息,也非常少。



  • 还有延迟加载,好棒



  • Flatpak是一种Linux的打包方案吗?同时我也看到了Linux有AppImage等解决方案,当然这些解决方案可能有这样或者那样的问题,我们也是根据自己项目的应用情况,选择性地使用这些方案吧。
    此外linuxdeployqt这个是AppImage团队做出的一个独立运行的软件,它的作用和windeployqt类似,是打包Linux应用程序到AppImage的。



  • 是的
    red hat的flatpak和ubuntu的snap是两个现在比较火的打包方式
    AppImage由于自身存在一些问题,没有大公司支持



  • @jiangcaiyang linux下有Qt GUI程序有什么比较好的发布方法吗?



  • 关注一波



  • @大黄老鼠 我觉得也没有太多的打包方法,虽然AppImage我尝试过,但是有一个问题,就是沙箱机制也许不是我想要的。有些应用需要保存配置文件的,如果被AppImage封装,配置文件就无法读取了,又是重新一个实例。这非常让人沮丧。比如说用AppImage打包的软件“Krita”就是如此。我目前采用的方法就是静态编译所有的库,成为一个standalone文件,这样就可以了。

    另:本来也想用upx进行压缩的,可惜upx压缩后执行总是出现段错误,所以直接放弃了。



  • 老铁,写的可以啊


Log in to reply
 

走马观花

最近的回复

  • Qt for MCUs

    搭建Qt for MCUs PC端开发环境。qt for mcus提供了一个完整的图形框架和工具包,包含了在MCUs上设计、开发和部署gui所需的一切。它允许您在裸机或实时操作系统上运行应用程序。

    先决条件

    开发主机环境支持仅限于Windows 10

    MSVC compiler v19.16 (Visual Studio 2017 15.9.9 or newer) x64

    CMake v3.13 or newer (you can install it using the Qt Online installer) x64

    使用Qt联机安装程序安装Qt for MCUs,该安装程序可通过Qt帐户下载

    安装Qt 5.14和Qt Creator 4.11 or higher

    安装链接

    › Qt: https://account.qt.io/downloads
    › CMake: https://cmake.org/download/
    › Python 2.7 32-bit: https://www.python.org/downloads/release/python-2716/
    › Arm GCC: https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnutoolchain/gnu-rm/downloads
    › J-Link Software Pack: https://www.segger.com/downloads/jlink/JLink_Windows.exe
    › J-Link OpenSDA Firmware: https://www.segger.com/downloads/jlink/OpenSDA_MIMXRT1050-EVKHyperflash
    › STM32CubeProgrammer: https://www.st.com/en/development-tools/stm32cubeprog.html
    › STM32 ST-LINK Utility: https://www.st.com/en/development-tools/stsw-link004.html​​​​​​​

    Qt Creator设置 启用Qt Creator插件 选择“帮助>关于插件”,然后从列表中选择“MCU支持(实验性)”插件,重新启动Qt Creator以应用更改
    替代文字 为MCU创建Qt工具包

    选择工具>选项>设备>MCU

    选择Qt for MCUs-Desktop 32bpp作为目标

    如果尚未设置,请提供Qt for MCUs安装目录的路径。

    单击Apply应用。

    替代文字

    替代文字
    替代文字

    注意:

    编译器要选X64,Qt版本要选64bit,CMake Tool选x64

    打开恒温器项目demo

    选择文件>打开文件或项目。。。

    打开CMakefiles.txt文件来自thermo文件夹的文件。

    选择Qt作为MCU-桌面32bpp套件。

    单击“配置项目”以完成。

    替代文字

    问题

    开发主机环境支持仅限于Windows 10

    C++编译失败,文本大字体.pixelSize.

    文本类型无法正确呈现需要复杂文本布局的unicode序列。对复杂文本使用StaticText

    read more
  • H

    hi 有问题请教你,方便加个联系方式吗

    read more
  • boost.asio是一个很棒的网络库,这回儿我也开始系统地学习起来了。想想当年接触boost,也有八年多了。这次开始接触boost,觉得既熟悉又陌生。熟悉的是小写字母+下划线的命名方式、晦涩的模板、很慢的编译速度以及较大的程序体积,陌生的是asio的各种概念:io服务、接收器、套接字等等:我之前对网络编程不是非常了解。

    于是根据我的理解,参考《Boost.Asio C++网络编程》实现了这样一个简单的客户端和服务端通信的例子,例子非常简单,还不完善,但是幸运的是,可以在本机上互通了。
    下面是客户端的代码:

    #include <iostream> #include <boost/asio.hpp> #include <boost/proto/detail/ignore_unused.hpp> using namespace std; using namespace boost::asio; using namespace boost::system; using namespace boost::proto::detail;// 提供ignore_unused方法 void writeHandler( const boost::system::error_code& ec, size_t bytesTransferred ) { if ( ec ) { cout << "Write data error, code: " << ec.value( ) << "transferred: " << bytesTransferred << endl; } else { cout << "OK! " << bytesTransferred << "bytes written. " << endl; } } int main(int argc, char *argv[]) { ignore_unused( argc ); ignore_unused( argv ); io_service service; ip::tcp::socket sock( service ); ip::tcp::endpoint ep( ip::address::from_string( "127.0.0.1" ), 6545 ); boost::system::error_code ec; sock.connect( ep, ec ); if ( ec ) { cout << "Connect error, code: " << ec.value( ) << ", We will exit." << endl; return ec.value( ); } else { char buf[1024] = "Hello world!"; sock.async_write_some( buffer( buf ), writeHandler ); sock.close( ); } return service.run( ); }

    下面是服务端的代码:

    #include <iostream> #include <boost/asio.hpp> #include <boost/proto/detail/ignore_unused.hpp> using namespace std; using namespace boost::asio; using namespace boost::system; using namespace boost::proto::detail;// 提供ignore_unused方法 void acceptHandle( const boost::system::error_code& code ) { cout << "Accepted." << endl; } int main(int argc, char *argv[]) { ignore_unused( argc ); ignore_unused( argv ); io_service service; ip::tcp::endpoint ep( ip::address::from_string( "127.0.0.1" ), 6545 ); boost::system::error_code ec; ip::tcp::socket sock( service ); ip::tcp::acceptor acceptor( service, ep ); acceptor.async_accept( sock, acceptHandle ); if ( ec ) { cout << "There is an error in server. code: " << ec.value( ) << endl; } return service.run( );// 阻塞运行 }

    运行结果是这样的:
    78448d7b-b3ae-42fc-9e2e-4dd2fbdac2c2-image.png

    我对boost.asio中几个概念的理解:

    io_service,这就是一个类似事件循环的东西,它为io设备提供服务,故名。不管是套接字、文件还是串口设备,都要使用它的服务。它的run()函数相当于启动了一个事件循环。一旦有消息了,即进行响应。这也是实现异步编程的重要基础。 socket,这个类则是套接字,可以处理TCP或者是UDP请求。有同步以及异步的处理方式,也有带异常以及不带异常的处理方式。 acceptor,接收器,仅仅是服务端使用。相当于其余框架中的listener,作接收用的。

    比较浅显,如果有不当之处,敬请指正。

    read more
  • 843143141.jpg
    闲下来了,我又开始大规模地学习了。
    最近开始学习内存模型和无锁结构。因为这个是和操作系统密切相关的,懂得这些对于编写C++服务端应用程序
    有着非常好的帮助。之前我对内存模型以及无锁结构几乎没有什么了解,我就询问群里的大佬看看有没有可以参考的资料。
    大佬很高兴,并且推荐了我一本名为《Memory Model》的电子书。这本电子书虽然页数不多,但是从起源到发展,
    从源码到汇编,都给我们详细地介绍了。看了一遍,不是非常理解,但是依然尝试将自己的理解写下来,以便日后翻阅。
    首先因为多核处理器成为主流,多线程的程序已经非常常见,因此我们不可避免地要处理多线程程序的同步问题。
    然后,因为编译器默认都对源码进行了优化,在单核处理器中这通常不是什么问题,但是在多核处理器中,就会因为编译器
    对其进行了乱序处理而导致程序出现问题。由此深入地探讨内存模型。
    内存模型主要分为:
    载-载 顺序(load-load order)
    载-存 顺序(load-store order)
    存-载 顺序(store-load order)
    存-存 顺序(store-store order)
    依赖载入顺序(dependent loads order)

    通过内存栅栏(memory barrier)能够避免编译器对指令的乱序。Linux中有

    READ_ONCE( x, value ) WRITE_ONCE( x )

    避免这些读写被编译器乱序或者是优化掉。

    这里谈到volatile关键字。在另外一篇博客上说,volatile具有“易变性、不可优化性、顺序性”。简单说,由于
    被volatile声明的变量,指令须从内存读取,并且不能被编译器乱序以及优化。在Java(语言扩展)和MSVC(系统兼容)上,
    还附带了Accquire()和Release()语义,因此可部分用于多线程环境。但多数情况下,还是慎用volatile,
    因为不同架构的处理器,它的内存模型是千变万化的,不能一而概之。

    至于C++11,它提供了std::atomic<T>这个模板类,相当于提供了很多方式来实现不同内存模型的原子操作。
    它的load()和store()方法,第二个参数有以下几个选项:

    std::memory_order_relaxed std::memory_order_seq_cst std::memory_order_acq_rel std::memory_order_acquire std::memory_order_release std::memory_order_consume

    我们最常用来实现RCpc(Release Consistency、Processor Consistency)是使用

    std::memory_order_acquire std::memory_order_release

    这两对。

    作为例子,在实现自旋锁时使用std::atomic<T>是这样的:

    struct SpinLock2 { void lock( ) { for ( ; ; ) { while ( lock_.load( std::memory_order_relaxed ) ); if ( !lock_.exchange( true, std::memory_order_acquire ) ) break; } } void unlock( ) { lock_.store( false, std::memory_order_release ); } std::atomic<bool> lock_ = { false }; };

    read more

关注我们

微博
QQ群