{"id":654,"date":"2015-06-09T19:25:47","date_gmt":"2015-06-09T17:25:47","guid":{"rendered":"http:\/\/hobbykeller.spdns.de\/?p=654"},"modified":"2019-08-22T12:49:33","modified_gmt":"2019-08-22T10:49:33","slug":"compiling-custom-packages-for-openwrt","status":"publish","type":"post","link":"https:\/\/hobbykeller.spdns.de\/?p=654","title":{"rendered":"Compiling custom packages for OpenWRT"},"content":{"rendered":"<p>I recently wanted to run <a href=\"http:\/\/duplicity.nongnu.org\/\" target=\"_blank\" rel=\"noopener noreferrer\">duplicity<\/a> (a wonderful tool which allows rsync-style backups to a remote location with gpg encryption) on my oWRT router. Unfortunately, the tool was not included in the default oWRT packages. The only thing I could find was a 5-year-old <a href=\"https:\/\/dev.openwrt.org\/ticket\/6645\" target=\"_blank\" rel=\"noopener noreferrer\">feature request<\/a> which was rejected as &#8220;Won&#8217;t fix&#8221;. So I gave it a shot and tried to cross-compile it by myself. This post will first show the general procedure how to cross-compile packages and will then deal with cross-compiling duplicity.<!--more--><\/p>\n<h1><span style=\"color: #ff0000;\">Deprecation notice<\/span><\/h1>\n<p><span style=\"color: #ff0000;\">This article contains outdated information:<\/span><\/p>\n<ul>\n<li><span style=\"color: #ff0000;\">refers to oWRT Barrierer Breaker and not to Chaos Calmer release<\/span><\/li>\n<li><span style=\"color: #ff0000;\">davfs2 instructions are deprecated, see <a href=\"http:\/\/hobbykeller.spdns.de\/?p=783\" target=\"_blank\" rel=\"noopener noreferrer\">new article<\/a> for updated procedure<\/span><\/li>\n<\/ul>\n<h1>General procedure for cross-compiling<\/h1>\n<p>To be sincere, I have had no clue how cross-compiling worked &#8211; and my first attempts just confirmed what was evident from so many postings on the web: cross-compiling software for embedded devices is a nightmare. But once you did it, it&#8217;s not that difficult.<\/p>\n<h2>Step 1: Get a build environment<\/h2>\n<p>If you don&#8217;t have a build environment create a directory under your home directory and download the openwrt sources into that directory. I am going to call mine <code>~\/myowrt<\/code>. We are going to work with the Barrier Breaker release.<\/p>\n<pre class=\"lang:default decode:true\">mkdir ~\/myowrt\r\ncd ~\/myowrt\r\ngit clone git:\/\/git.openwrt.org\/14.07\/openwrt.git<\/pre>\n<p>git will now shuffle a dozen of mega bytes into your build directory. On top of that, you will need the packages which are already officially endorsed by the oWRT community:<\/p>\n<pre class=\"lang:default decode:true \">cd openwrt\/\r\n.\/scripts\/feeds update -a<\/pre>\n<p>This step will download another dozen of mega bytes into your feeds subdirectory. After this operation, your download directory should look like this:<\/p>\n<pre class=\"lang:default decode:true\">ilek@i7:~\/myowrt\/openwrt$ cd feeds\/\r\nilek@i7:~\/myowrt\/openwrt\/feeds$ ls\r\nluci        management.index   oldpackages.tmp  routing        telephony.index\r\nluci.index  management.tmp     packages         routing.index  telephony.tmp\r\nluci.tmp    oldpackages        packages.index   routing.tmp\r\nmanagement  oldpackages.index  packages.tmp     telephony\r\n<\/pre>\n<p>This step has just downloaded the sources from the package feeds defined in the <code>openwrt\/feeds.conf.default<\/code> file. If you issue the <code>make menuconfig<\/code> command in the <code>openwrt<\/code> root directory and navigate through the installation options, you will see, that the additional packages do not yet appear in <code>menuconfig<\/code>.<\/p>\n<figure id=\"attachment_658\" aria-describedby=\"caption-attachment-658\" style=\"width: 542px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/hobbykeller.spdns.de\/wp-content\/uploads\/2015\/06\/menuconf_empty.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-658\" src=\"http:\/\/hobbykeller.spdns.de\/wp-content\/uploads\/2015\/06\/menuconf_empty.png\" alt=\"menuconfig Language section before installing packages\" width=\"542\" height=\"347\" srcset=\"https:\/\/hobbykeller.spdns.de\/wp-content\/uploads\/2015\/06\/menuconf_empty.png 542w, https:\/\/hobbykeller.spdns.de\/wp-content\/uploads\/2015\/06\/menuconf_empty-300x192.png 300w, https:\/\/hobbykeller.spdns.de\/wp-content\/uploads\/2015\/06\/menuconf_empty-469x300.png 469w\" sizes=\"auto, (max-width: 542px) 100vw, 542px\" \/><\/a><figcaption id=\"caption-attachment-658\" class=\"wp-caption-text\">menuconfig Language section before installing packages<\/figcaption><\/figure>\n<p>In order to make the packages available to <code>menuconfig<\/code>, we would have to issue the <code>.\/scripts\/feeds\/install -a<\/code> commmand. But we will first add our own packages.<\/p>\n<h2>\u00a0Step 2: Create a directory for your custom packages<\/h2>\n<p>Under your <code>~\/myowrt<\/code> working directory, create an additional directory for your custom packages:<\/p>\n<pre class=\"lang:default decode:true\">mkdir ~\/myowrt\/custpacks\r\n<\/pre>\n<p>In order to tell oWRT that this directory also contains packages, you have got to edit the <code>feeds.conf.default<\/code> file. Just uncomment and adapt the last line in that file as follows:<\/p>\n<pre class=\"lang:default decode:true\">src-link custom \/home\/ilek\/myowrt\/custpacks\r\n<\/pre>\n<p>For the sake of example, we would like to install the davfs2 package. It comes in handily that there is already a package under development <a href=\"https:\/\/github.com\/openwrt\/packages\/tree\/master\/net\/davfs2\" target=\"_blank\" rel=\"noopener noreferrer\">on the oWRT github page<\/a>. We can download the package folder with the following command (strangely the most convenient way to <a href=\"http:\/\/stackoverflow.com\/questions\/7106012\/download-a-single-folder-or-directory-from-a-github-repo\" target=\"_blank\" rel=\"noopener noreferrer\">download a specific folder from a git repo<\/a> is to use svn):<\/p>\n<pre class=\"lang:default decode:true\">cd ~\/myowrt\/custpacks\r\nsvn checkout https:\/\/github.com\/openwrt\/packages\/trunk\/net\/davfs2<\/pre>\n<p>Another example &#8211; also relating to the webdav file system &#8211; is cadaver which is a very useful command line webdav client. Although it is not included in the trunk, I have found an oWRT Makefile provided by a Russian developer \/ blogger at erinome.net. All you have to do is click on the <a href=\"https:\/\/fs.erinome.net\/get\/?hash=b3BlbndydC9wYWNrYWdlcy9jYWRhdmVyLw\" target=\"_blank\" rel=\"noopener noreferrer\">Get full directory link<\/a> which will give you a tar file that you can download to your <code>n~\/myoowrt\/custpacks <\/code>directory where you hold your own package sources.<\/p>\n<h1>Step 3: Prepare the build<\/h1>\n<p>We can now issue the <code>.\/scripts\/feeds install -a<\/code> command.<\/p>\n<pre class=\"lang:default decode:true\">cd ~\/myowrt\/openwrt\r\n.\/scripts\/feeds update -a\r\n.\/scripts\/feeds install -a<\/pre>\n<p>Now run <code>make menuconfig<\/code> from the build root directory, navigate to Network \/ Filesystem. Select the <code>davfs2<\/code> package to be built as a module (M).<\/p>\n<figure id=\"attachment_659\" aria-describedby=\"caption-attachment-659\" style=\"width: 542px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-659\" src=\"http:\/\/hobbykeller.spdns.de\/wp-content\/uploads\/2015\/06\/menuconfig_davfs2.png\" alt=\"Selecting the package for build mode in menuconfig\" width=\"542\" height=\"347\" srcset=\"https:\/\/hobbykeller.spdns.de\/wp-content\/uploads\/2015\/06\/menuconfig_davfs2.png 542w, https:\/\/hobbykeller.spdns.de\/wp-content\/uploads\/2015\/06\/menuconfig_davfs2-300x192.png 300w, https:\/\/hobbykeller.spdns.de\/wp-content\/uploads\/2015\/06\/menuconfig_davfs2-469x300.png 469w\" sizes=\"auto, (max-width: 542px) 100vw, 542px\" \/><figcaption id=\"caption-attachment-659\" class=\"wp-caption-text\">Selecting the package for build mode in menuconfig<\/figcaption><\/figure>\n<h2>Step 4: Build and enjoy<\/h2>\n<p>If this is the first time, you start to build something from the system sources, it is advisable to start with running a full build of the image because this will ensure that all necessary components from the toolchain which does the cross-compiling are built.<\/p>\n<pre class=\"lang:default decode:true \">cd ~\/myowrt\/openwrt\r\nmake -j 5<\/pre>\n<p>We should <code>cd<\/code> to the build root and run the <code>make<\/code> command to compile the package.<\/p>\n<pre class=\"lang:default decode:true\">cd ~\/myowrt\/openwrt\r\nmake package\/davfs2\/compile<\/pre>\n<p>If the compile fails, <code>make<\/code> will tell you to re-run the compile with the <code>V=s<\/code> option to check what is going on. In the case of davfs2, make produces a couple of error messages concerning <code>autoreconf<\/code>. As autoreconf (a tool which best guesses the configuration for compiling) is not even needed here, we just delete the following line from <code>~\/myowrt\/custpacks\/davfs2\/Makefile <\/code>(should be line 18 in the file):<\/p>\n<pre class=\"lang:default decode:true\">PKG_FIXUP:=gettext-version autoreconf<\/pre>\n<p>We can then run the make package\/davfs2\/compile again. If the compile works fine, the binary can be found in ~\/myowrt\/openwrt\/bin\/&lt;your_target_arch&gt;\/packages\/custom. You can copy that file to any oWRT device which has the specified target architecture and install it by running <code>opkg install davfs2_1.5.2-1_mpc85xx.ipk<\/code>.<\/p>\n<h1>Cross-compiling duplicity<\/h1>\n<p>Compiling duplicity is a bit more complicated, because duplicity is written in Python. That means that we do not only have to run the compile but during the process, the compiler must then internally run the setup.py, produce the relevant duplicity files and finally repackage them into an ipk file for installation.<\/p>\n<p>There is another complication: While in our davfs example, we were lucky enough to find already an oWRT package source with patches and an oWRT Makefile which (almost) ran out of the box, we have to produce these ourselves.<\/p>\n<p>One handy alternative would be to install python-setuptools or python-pip, which are also available as oWRT sources from github and then just issue a command like <code>pip install duplicity<\/code> and let pip do the rest. Unfortunately, this never worked for me, so we have to go the hard way and build our duplicity package with our own <code>Makefile<\/code> and<\/p>\n<h1>Step 1: Inspect the package sources<\/h1>\n<p>Normally, it is not even necessary to have the package sources on your local compiling machine. Instead, the oWRT Makefile only contains information from where it should be downloaded. This can be seen from the first lines of the Makefile in the previous davfs2 example:<\/p>\n<pre class=\"nums:true lang:default decode:true\" title=\"First lines of davfs2 Makefile\">#\r\n# Copyright (C) 2006-2015 OpenWrt.org\r\n#\r\n# This is free software, licensed under the GNU General Public License v2.\r\n# See \/LICENSE for more information.\r\n#\r\n\r\ninclude $(TOPDIR)\/rules.mk\r\n\r\nPKG_NAME:=davfs2\r\nPKG_VERSION:=1.5.2\r\nPKG_RELEASE:=1\r\n\r\nPKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz\r\nPKG_SOURCE_URL:=http:\/\/download.savannah.gnu.org\/releases\/davfs2\/\r\nPKG_MD5SUM:=376bc9346454135cba78afacbcb23f86\r\n<\/pre>\n<p>If any kind of oWRT specific adaptions of the sources are needed, this is done by patchfiles which internally modify the downloaded sources before they are compiled.<\/p>\n<p>If we have to set up a Makefile and maybe some patches ourselves, the first thing we should look at are the package sources. So we just <a href=\"https:\/\/launchpad.net\/duplicity\/0.7-series\/0.7.05\/+download\/duplicity-0.7.05.tar.gz\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"broken_link\">download<\/a> and untar them in a temp directory to see what they include.<\/p>\n<h2>Step 2: Patch the setup.py file<\/h2>\n<p>The <code>setup.py<\/code> file is the probably most interesting one among the sources, as it would normally constitute the point of entry in a regular installation on a fully loaded Debian machine. We will create a copy of that file as <code>setup.new<\/code> and see what we can adapt.<\/p>\n<h3>Replace setuptools<\/h3>\n<pre class=\"nums:true lang:default decode:true\" title=\"duplicity's original setup.py file\">#!\/usr\/bin\/env python2\r\n# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-\r\n#\r\n# Copyright 2002 Ben Escoto &lt;ben@emerose.org&gt;\r\n# Copyright 2007 Kenneth Loafman &lt;kenneth@loafman.com&gt;\r\n#\r\n# This file is part of duplicity.\r\n#\r\n# Duplicity is free software; you can redistribute it and\/or modify it\r\n# under the terms of the GNU General Public License as published by the\r\n# Free Software Foundation; either version 2 of the License, or (at your\r\n# option) any later version.\r\n#\r\n# Duplicity is distributed in the hope that it will be useful, but\r\n# WITHOUT ANY WARRANTY; without even the implied warranty of\r\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r\n# General Public License for more details.\r\n#\r\n# You should have received a copy of the GNU General Public License\r\n# along with duplicity; if not, write to the Free Software Foundation,\r\n# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r\n\r\nimport sys\r\nimport os\r\nfrom setuptools import setup, Extension\r\nfrom setuptools.command.test import test\r\nfrom setuptools.command.install import install\r\nfrom setuptools.command.sdist import sdist\r\n\r\nversion_string = \"0.7.02\"\r\n\r\nif sys.version_info[:2] &lt; (2, 6):\r\n    print(\"Sorry, duplicity requires version 2.6 or later of python\")\r\n    sys.exit(1)\r\n\r\nincdir_list = libdir_list = None\r\n\r\nif os.name == 'posix':\r\n    LIBRSYNC_DIR = os.environ.get('LIBRSYNC_DIR', '')\r\n    args = sys.argv[:]\r\n    for arg in args:\r\n        if arg.startswith('--librsync-dir='):\r\n            LIBRSYNC_DIR = arg.split('=')[1]\r\n            sys.argv.remove(arg)\r\n    if LIBRSYNC_DIR:\r\n        incdir_list = [os.path.join(LIBRSYNC_DIR, 'include')]\r\n        libdir_list = [os.path.join(LIBRSYNC_DIR, 'lib')]\r\n\r\ndata_files = [('share\/man\/man1',\r\n               ['bin\/duplicity.1',\r\n                'bin\/rdiffdir.1']),\r\n              ('share\/doc\/duplicity-%s' % version_string,\r\n               ['COPYING',\r\n                'README',\r\n                'README-REPO',\r\n                'README-LOG',\r\n                'CHANGELOG']),\r\n              ]\r\n\r\ntop_dir = os.path.dirname(os.path.abspath(__file__))\r\nassert os.path.exists(os.path.join(top_dir, \"po\")), \"Missing 'po' directory.\"\r\nfor root, dirs, files in os.walk(os.path.join(top_dir, \"po\")):\r\n    for file in files:\r\n        path = os.path.join(root, file)\r\n        if path.endswith(\"duplicity.mo\"):\r\n            lang = os.path.split(root)[-1]\r\n            data_files.append(\r\n                ('share\/locale\/%s\/LC_MESSAGES' % lang,\r\n                 [\"po\/%s\/duplicity.mo\" % lang]))\r\n\r\n\r\nclass TestCommand(test):\r\n    def run(self):\r\n        # Make sure all modules are ready\r\n        build_cmd = self.get_finalized_command(\"build_py\")\r\n        build_cmd.run()\r\n        # And make sure our scripts are ready\r\n        build_scripts_cmd = self.get_finalized_command(\"build_scripts\")\r\n        build_scripts_cmd.run()\r\n\r\n        # make symlinks for test data\r\n        if build_cmd.build_lib != top_dir:\r\n            for path in ['testfiles.tar.gz', 'gnupg']:\r\n                src = os.path.join(top_dir, 'testing', path)\r\n                target = os.path.join(build_cmd.build_lib, 'testing', path)\r\n                try:\r\n                    os.symlink(src, target)\r\n                except Exception:\r\n                    pass\r\n\r\n        os.environ['PATH'] = \"%s:%s\" % (\r\n            os.path.abspath(build_scripts_cmd.build_dir),\r\n            os.environ.get('PATH'))\r\n\r\n        test.run(self)\r\n\r\n\r\nclass InstallCommand(install):\r\n    def run(self):\r\n        # Normally, install will call build().  But we want to delete the\r\n        # testing dir between building and installing.  So we manually build\r\n        # and mark ourselves to skip building when we run() for real.\r\n        self.run_command('build')\r\n        self.skip_build = True\r\n\r\n        # This should always be true, but just to make sure!\r\n        if self.build_lib != top_dir:\r\n            testing_dir = os.path.join(self.build_lib, 'testing')\r\n            os.system(\"rm -rf %s\" % testing_dir)\r\n\r\n        install.run(self)\r\n\r\n\r\n# TODO: move logic from dist\/makedist inline\r\nclass SDistCommand(sdist):\r\n    def run(self):\r\n        version = version_string\r\n        if version[0] == '$':\r\n            version = \"0\"\r\n        os.system(os.path.join(top_dir, \"dist\", \"makedist\") + \" \" + version)\r\n        os.system(\"rm -f duplicity.spec\")\r\n        os.system(\"mkdir -p \" + self.dist_dir)\r\n        os.system(\"mv duplicity-\" + version + \".tar.gz \" + self.dist_dir)\r\n\r\n\r\nsetup(name=\"duplicity\",\r\n      version=version_string,\r\n      description=\"Encrypted backup using rsync algorithm\",\r\n      author=\"Ben Escoto &lt;ben@emerose.org&gt;\",\r\n      author_email=\"bescoto@stanford.edu\",\r\n      maintainer=\"Kenneth Loafman &lt;kenneth@loafman.com&gt;\",\r\n      maintainer_email=\"kenneth@loafman.com\",\r\n      url=\"http:\/\/duplicity.nongnu.org\/index.html\",\r\n      packages=['duplicity',\r\n                  'duplicity.backends',\r\n                  'duplicity.backends.pyrax_identity',\r\n                  'testing',\r\n                  'testing.functional',\r\n                  'testing.overrides',\r\n                  'testing.unit'],\r\n      package_dir={\"duplicity\": \"duplicity\",\r\n                   \"duplicity.backends\": \"duplicity\/backends\", },\r\n      ext_modules=[Extension(\"duplicity._librsync\",\r\n                             [\"duplicity\/_librsyncmodule.c\"],\r\n                             include_dirs=incdir_list,\r\n                             library_dirs=libdir_list,\r\n                             libraries=[\"rsync\"])],\r\n      scripts=['bin\/rdiffdir', 'bin\/duplicity'],\r\n      data_files=data_files,\r\n      tests_require=['lockfile', 'mock', 'pexpect'],\r\n      test_suite='testing',\r\n      cmdclass={'test': TestCommand,\r\n                'install': InstallCommand,\r\n                'sdist': SDistCommand},\r\n      )<\/pre>\n<p>The first thing which is going to cause trouble when cross-compiling is the import of the <code>setuptools<\/code> module in lines 25-28. The python environment which the cross-compiler offers only has the <code>setuptools<\/code> predecessor which is called <code>distutils<\/code>. Fortunately both modules have identical APIs so that we can simply replace all occurences of <code>setuptools<\/code> against <code>distutils<\/code> in these lines.<\/p>\n<p>The only line in which this is not going to work is line 26, because <code>distutils<\/code> did not have any <code>test<\/code> submodule. We will therefore delete line 26 completely from the original file. After we are done with that section, it should look as follows:<\/p>\n<pre class=\"lang:default decode:true \" title=\"Excerpt from modifications in setup.new\"># You should have received a copy of the GNU General Public License\r\n# along with duplicity; if not, write to the Free Software Foundation,\r\n# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r\n\r\nimport sys\r\nimport os\r\nfrom distutils import setup, Extension\r\nfrom distutils.command.install import install\r\nfrom distutils.command.sdist import sdist\r\n\r\nversion_string = \"0.7.02\"<\/pre>\n<h3>Remove any references to setuptools.command.test<\/h3>\n<p>Given that we have completely removed the line which imported <code>setuptools.command.test<\/code>, we also have to remove any code which relies on that import. We therefore delete the whole <code>TestCommand<\/code> class in lines 72-97 (line numbers in the original file).<\/p>\n<p>Additionally the packages list in lines 137-140 (line numbers in original file) contains some references to the testing class which we also delete. We will also delete any reference to the test module in lines 150-152 (line numbers in original file). After the cleanup, the final part of the setup.new file should look like this:<\/p>\n<pre class=\"lang:default decode:true \">setup(name=\"duplicity\",\r\n      version=version_string,\r\n      description=\"Encrypted backup using rsync algorithm\",\r\n      author=\"Ben Escoto &lt;ben@emerose.org&gt;\",\r\n      author_email=\"bescoto@stanford.edu\",\r\n      maintainer=\"Kenneth Loafman &lt;kenneth@loafman.com&gt;\",\r\n      maintainer_email=\"kenneth@loafman.com\",\r\n      url=\"http:\/\/duplicity.nongnu.org\/index.html\",\r\n      packages=['duplicity',\r\n                  'duplicity.backends',\r\n                  'duplicity.backends.pyrax_identity'],\r\n      package_dir={\"duplicity\": \"duplicity\",\r\n                   \"duplicity.backends\": \"duplicity\/backends\", },\r\n      ext_modules=[Extension(\"duplicity._librsync\",\r\n                             [\"duplicity\/_librsyncmodule.c\"],\r\n                             include_dirs=incdir_list,\r\n                             library_dirs=libdir_list,\r\n                             libraries=[\"rsync\"])],\r\n      scripts=['bin\/rdiffdir', 'bin\/duplicity'],\r\n      data_files=data_files,\r\n      cmdclass={'install': InstallCommand,\r\n                'sdist': SDistCommand},\r\n      )<\/pre>\n<p>We can now save our modified <code>setup.new<\/code> file and run <code>diff<\/code> to produce a patch file:<\/p>\n<pre class=\"lang:default decode:true\">diff -u setup.py setup.new &gt; 000-duplicity-setup-patch<\/pre>\n<p>The only thing we still have to change in the patch file is the header. Change the first lines so that they look as follows:<\/p>\n<pre class=\"lang:default decode:true \">--- a\/setup.py  2015-03-11 13:33:38.000000000 +0100\r\n+++ b\/setup.py  2015-06-10 17:31:57.656533070 +0200\r\n<\/pre>\n<p>We can now create a patch directory under our duplicity custom package directory and copy that file there:<\/p>\n<pre class=\"lang:default decode:true \">mkdir -p ~\/myowrt\/custpacks\/duplicity\/patches\r\ncp 000-duplicity-setup-patch ~\/myowrt\/custpacks\/duplicity\/patches\r\n<\/pre>\n<h2>Step 3: Write an oWRT Makefile<\/h2>\n<p>Now comes the tricky part &#8211; at least for me as I do not have any in-depth experience with oWRT Makefiles. What I did was trying some Makefiles from the python modules which already ship with the oWRT packages. Adapting the <code>Makefile<\/code> from pysqlite worked best (though it&#8217;s not yet perfect, as we will see later). Your final <code>Makefile<\/code> should look like this:<\/p>\n<pre class=\"lang:default decode:true \" title=\"duplicity Makefile for OpenWRT\">include $(TOPDIR)\/rules.mk\r\n\r\nPKG_NAME:=duplicity\r\nPKG_VERSION:=0.7.05\r\nPKG_RELEASE:=1\r\n\r\nPKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz\r\nPKG_SOURCE_URL:=https:\/\/code.launchpad.net\/duplicity\/0.7-series\/0.7.05\/+download\/\r\nPKG_MD5SUM:=b2f28a48aa392e81c740ac30d28e9250\r\n\r\nPKG_BUILD_DEPENDS:=python\r\n\r\ninclude $(INCLUDE_DIR)\/package.mk\r\n$(call include_mk, python-package.mk)\r\n\r\ndefine Package\/duplicity\r\n SUBMENU:=Python\r\n SECTION:=lang\r\n CATEGORY:=Languages\r\n TITLE:=Intelligent backup with encryption (Custom package)\r\n URL:=http:\/\/duplicity.nongnu.org\/\r\n DEPENDS:=+python +librsync +python-expat +lockfile +gnupg\r\nendef\r\n\r\ndefine Package\/duplicity\/description\r\n Duplicity allows rsync-style incremental backups with GPG encryption.\r\n This package does not ship with the official oWRT distro.\r\nendef\r\n\r\ndefine PyPackage\/duplicity\/filespec\r\n+|$(PYTHON_PKG_DIR)\/duplicity\r\n-|$(PYTHON_PKG_DIR)\/duplicity\/test\r\nendef\r\n\r\ndefine Build\/Configure\r\n\tcp .\/files\/setup.cfg.in $(PKG_BUILD_DIR)\/setup.cfg\r\n\t$(SED) \\\r\n\t\t's,@INCLUDE_DIRS@,$(STAGING_DIR)\/usr\/include,g' \\\r\n\t\t-e 's,@INCLUDE_DIRS@,$(STAGING_DIR)\/usr\/include,g' \\\r\n\t\t$(PKG_BUILD_DIR)\/setup.cfg\r\nendef\r\n\r\ndefine Build\/Compile\r\n\t$(if $(Build\/Compile\/PyMod),,@echo Python packaging code not found.; false)\r\n\t$(call Build\/Compile\/PyMod,., \\\r\n\t\tinstall --prefix=\"$(PKG_INSTALL_DIR)\/usr\", \\\r\n\t)\r\nendef\r\n\r\n$(eval $(call PyPackage,duplicity))\r\n$(eval $(call BuildPackage,duplicity))<\/pre>\n<p>The pysqlite package which I used as the boiler plate for creating my <code>Makefile<\/code> also contained a files subdirectory with a <code>setup.cfg.in<\/code> file. Im am not sure if we really need this file and was too lazy to read the <a href=\"https:\/\/docs.python.org\/2\/distutils\/configfile.html\" target=\"_blank\" rel=\"noopener noreferrer\">python reference<\/a>. Instead, I left it in the files subdirectory after adapting it as follows:<\/p>\n<pre class=\"lang:default decode:true\" title=\"setup.cfg.in file for duplicity\">[build_ext]\r\ndefine=\r\ninclude_dirs=@INCLUDE_DIRS@\r\nlibrary_dirs=@LIBRARY_DIRS@\r\nlibraries=rsync<\/pre>\n<h2>Step 4: Prepare the target device<\/h2>\n<p>As duplicity&#8217;s Python code imports the <a href=\"https:\/\/pypi.python.org\/pypi\/lockfile\" target=\"_blank\" rel=\"noopener noreferrer\">Python lockfile<\/a> module, we have to make sure that this module is installed on the target device. Unfortunately, the lockfile module is &#8211; again &#8211; not included in the oWRT package repositories and we have to find a way to get them onto our oWRT device.<\/p>\n<p>I also tried to compile those from source as presented above. Make will compile the lockfile sources but the resulting <code>ipk<\/code> package has a size of less then 1 kB &#8211; so something must be wrong in my configuration and I cannot find out what.<\/p>\n<p>Fortunately, we can get the lockfile package on the target device the easy way this time. Simply issuing <code>easy_install lockfile<\/code> on the target device will install the package directly from the net.<\/p>\n<h2>Step 5: Compile and adapt the result<\/h2>\n<p>We are now ready to compile the package:<\/p>\n<pre class=\"lang:default decode:true\">cd ~\/myowrt\/openwrt\r\nmake package\/duplicity\/compile<\/pre>\n<p>We can then copy the resulting <code>ipk<\/code> file from the <code>bin\/&lt;your-arch&gt;\/packages\/custom<\/code> directory to the target oWRT device and install it with the <code>opkg install<\/code> command.<\/p>\n<p>After the package is installed locally, there are two minor problems remaining. Firstly, the main <code>duplicity<\/code> front end file is missing in the <code>\/usr\/bin<\/code> directory of the target device. As the <code>duplicity<\/code> file is nothing more than an executable Python file, we can just copy that file into the <code>\/usr\/bin<\/code> directory from any running installation.<\/p>\n<p>The second problem is that the duplicity file in this version throws an error on the target device when launched:<\/p>\n<pre class=\"lang:default decode:true\">root@TESTING:~# duplicity\r\nTraceback (most recent call last):\r\n  File \"\/usr\/bin\/duplicity\", line 1509, in &lt;module&gt;\r\n    with_tempdir(main)\r\n  File \"\/usr\/bin\/duplicity\", line 1503, in with_tempdir\r\n    fn()\r\n  File \"\/usr\/bin\/duplicity\", line 1333, in main\r\n    \"\"\"), log.WarningCode.deprecate_0_6)\r\nAttributeError: class WarningCode has no attribute 'deprecate_0_6'<\/pre>\n<p>There is an easy solution to get rid of that Error &#8211; just delete the lines 1328-1334 from the <code>duplicity<\/code> file as suggested in this post. For those keen on a short-cut, just <a href=\"https:\/\/hobbykeller.spdns.de\/index.php\/s\/fKS1RMonUrIAQuj\" target=\"_blank\" rel=\"noopener noreferrer\">download the patched duplicity file from here<\/a> and copy it to the <code>\/usr\/bin<\/code> directory on your oWRT device.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I recently wanted to run duplicity (a wonderful tool which allows rsync-style backups to a remote location with gpg encryption) on my oWRT router. Unfortunately, the tool was not included<span class=\"more-button\"><a href=\"https:\/\/hobbykeller.spdns.de\/?p=654\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\">Compiling custom packages for OpenWRT<\/span><\/a><\/span><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[156,64,16],"tags":[141,118,81,82,121,117,119,120,17,122,83],"class_list":["post-654","post","type-post","status-publish","format-standard","hentry","category-deprecated","category-linux","category-openwrt","tag-cadaver","tag-cross-compiling","tag-custom-package","tag-davfs2","tag-distutils","tag-duplicity","tag-encrypted-incremental-backup","tag-lockfile","tag-openwrt","tag-python-setuptools","tag-webdav"],"_links":{"self":[{"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=\/wp\/v2\/posts\/654","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=654"}],"version-history":[{"count":21,"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=\/wp\/v2\/posts\/654\/revisions"}],"predecessor-version":[{"id":1258,"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=\/wp\/v2\/posts\/654\/revisions\/1258"}],"wp:attachment":[{"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=654"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=654"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=654"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}