Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python3
- "termux-create-package: Utility to create .deb files."
- import argparse
- import io
- import json
- import os
- import subprocess
- import sys
- import tarfile
- import tempfile
- def validate_manifest(manifest):
- "Validate that the package manifest makes sense."
- for prop in 'name', 'version', 'files':
- if prop not in manifest:
- sys.exit('Missing mandatory "' + prop + '" property')
- if manifest['arch'] not in ['all', 'arm', 'i686', 'aarch64', 'x86_64']:
- sys.exit('Invalid "arch" - must be one of all/arm/i686/aarch64/x86_64')
- def set_default_value(manifest, property_name, default_value):
- "Set a default property value if one does not exist."
- if property_name not in manifest:
- manifest[property_name] = default_value
- def setup_default_manifest_values(manifest):
- "Setup default values in a package manifest."
- set_default_value(manifest, 'arch', 'all')
- set_default_value(manifest, 'conflicts', [])
- set_default_value(manifest, 'depends', [])
- set_default_value(manifest, 'description', 'No description')
- set_default_value(manifest, 'maintainer', 'None')
- set_default_value(manifest, 'provides', [])
- def write_control_tar(tar_path, manifest):
- "Create a data.tar.xz from the specified manifest."
- contents = 'Package: ' + manifest['name'] + "\n"
- contents += 'Version: ' + manifest['version'] + "\n"
- contents += 'Architecture: ' + manifest['arch'] + "\n"
- contents += 'Maintainer: ' + manifest['maintainer'] + "\n"
- contents += 'Description: ' + manifest['description'] + "\n"
- if 'homepage' in manifest:
- contents += 'Homepage: ' + manifest['homepage'] + "\n"
- if manifest['depends']:
- contents += 'Depends: ' + ','.join(manifest['depends']) + '\n'
- if manifest['provides']:
- contents += 'Provides: ' + ','.join(manifest['provides']) + '\n'
- if manifest['conflicts']:
- contents += 'Conflicts: ' + ','.join(manifest['conflicts']) + '\n'
- control_file = io.BytesIO(contents.encode('utf8'))
- control_file.seek(0, os.SEEK_END)
- file_size = control_file.tell()
- control_file.seek(0)
- info = tarfile.TarInfo(name="control")
- info.size = file_size
- with tarfile.open(tar_path, mode='w:xz') as control_tarfile:
- control_tarfile.addfile(tarinfo=info, fileobj=control_file)
- def write_data_tar(tar_path, installation_prefix, package_files):
- "Create a data.tar.xz from the specified package files."
- with tarfile.open(tar_path, mode='w:xz') as data_tarfile:
- for input_file_path in package_files:
- file_stat = os.stat(input_file_path)
- # The tar file path should not start with slash:
- if installation_prefix.startswith('/'):
- installation_prefix = installation_prefix[1:]
- if not installation_prefix.endswith('/'):
- installation_prefix += '/'
- output_file = installation_prefix + package_files[input_file_path]
- info = tarfile.TarInfo(name=output_file)
- info.mode = file_stat.st_mode
- info.mtime = file_stat.st_mtime
- info.size = file_stat.st_size
- with open(input_file_path, 'rb') as input_file:
- data_tarfile.addfile(tarinfo=info, fileobj=input_file)
- def create_debfile(debfile_output, directory):
- "Create a debfile from a directory containing control and data tar files."
- subprocess.check_call(['ar', 'r', debfile_output,
- directory + '/debian-binary',
- directory + '/control.tar.xz',
- directory + '/data.tar.xz'
- ])
- DESCRIPTION = """Create a Termux package from a JSON manifest file. Example of manifest:
- {
- "name": "mypackage",
- "version": "0.1",
- "arch": "all",
- "maintainer": "@MyGithubNick",
- "description": "This is a hello world package",
- "homepage": "https://example.com",
- "depends": ["python", "vim"],
- "provides": ["vi"],
- "conflicts": ["vim-python-git"],
- "files" : {
- "hello-world.py": "bin/hello-world",
- "hello-world.1": "share/man/man1/hello-world.1"
- }
- }
- The "maintainer", "description", "homepage", "depends", "provides" and "conflicts" fields are all optional.
- The "depends" field should be a comma-separated list of packages that this package depends on. They will be installed automatically when this package is installed using apt.
- The "arch" field defaults to "all" (that is, a platform-independent package not containing native code) and can be any of arm/i686/aarch64/x86_64. Run "uname -m" to find out arch name if building native code inside Termux.
- The "files" map is keyed from paths to files to include (relative to the current directory) while the values contains the paths where the files should end up after installation (relative to $PREFIX).
- The resulting .deb file can be installed by Termux users with:
- apt install ./package-file.deb
- or by hosting it in an apt repository using the termux-apt-repo tool."""
- def main(argv):
- "Generate a deb file from a JSON manifest."
- installation_prefix = "/data/data/com.termux/files/usr/"
- parser = argparse.ArgumentParser(description=DESCRIPTION,
- formatter_class=argparse.RawTextHelpFormatter)
- parser.add_argument("manifest", help="a JSON manifest file describing the package")
- parser.add_argument("--prefix", help="set prefix dir (default: " + installation_prefix + ")")
- args = parser.parse_args(argv)
- if args.prefix:
- installation_prefix = str(args.prefix)
- manifest_file_path = args.manifest
- with open(manifest_file_path, 'r') as manifest_file:
- manifest = json.load(manifest_file)
- setup_default_manifest_values(manifest)
- validate_manifest(manifest)
- package_name = manifest['name']
- package_version = manifest['version']
- package_files = manifest['files']
- output_debfile_name = package_name + '_' + package_version + '_' + manifest['arch'] + '.deb'
- print('Building ' + output_debfile_name)
- package_tmp_directory = tempfile.TemporaryDirectory()
- with open(package_tmp_directory.name + '/debian-binary', 'w') as debian_binary:
- debian_binary.write("2.0\n")
- write_control_tar(package_tmp_directory.name + '/control.tar.xz', manifest)
- write_data_tar(package_tmp_directory.name + '/data.tar.xz', installation_prefix, package_files)
- create_debfile(output_debfile_name, package_tmp_directory.name)
- if __name__ == "__main__":
- main(sys.argv[1:])
Advertisement
Add Comment
Please, Sign In to add comment