#!/usr/bin/env python3

import argparse, io, json, os, sys, subprocess, tarfile, tempfile

PREFIX = "/data/data/com.termux/files/usr/"

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": "usr/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."""

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: " + PREFIX + ")")
args = parser.parse_args()
if args.prefix: PREFIX = str(args.prefix)

manifest_file_path = args.manifest
with open(manifest_file_path) as f: manifest = json.load(f)

for prop in 'name', 'version', 'files':
	if prop not in manifest: sys.exit('Missing mandatory "' + prop + '" property')

package_name = manifest['name']
package_version = manifest['version'];
package_files = manifest['files']

package_arch = 'all'
if 'arch' in manifest: package_arch = manifest['arch']
if package_arch not in ['all', 'arm', 'i686', 'aarch64', 'x86_64']:
	sys.exit('Invalid "arch" - must be one of all/arm/i686/aarch64/x86_64')

package_maintainer = 'None'
if 'maintainer' in manifest: package_maintainer = manifest['maintainer']

package_description = 'No description'
if 'description' in manifest: package_description = manifest['description']

package_homepage = 'No homepage'
if 'homepage' in manifest: package_homepage = manifest['homepage']

package_deps = []
if 'depends' in manifest: package_deps = manifest['depends']

package_provides = []
if 'provides' in manifest: package_provides = manifest['provides']

package_conflicts = []
if 'conflicts' in manifest: package_conflicts = manifest['conflicts']

output_debfile_name = package_name + '_' + package_version + '_' + package_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")

with tarfile.open(package_tmp_directory.name + '/control.tar.xz', mode = 'w:xz') as control_tarfile:
	contents = 'Package: ' + package_name + "\n"
	contents += 'Version: ' + package_version + "\n"
	contents += 'Architecture: ' + package_arch + "\n"
	contents += 'Maintainer: ' + package_maintainer + "\n"
	contents += 'Description: ' + package_description + "\n"
	contents += 'Homepage: ' + package_homepage + "\n"

	if len(package_deps) > 0:
		contents += 'Depends: '
		delim = ''
		for d in package_deps:
			contents += delim + d
			delim = ','
		contents += '\n'

	if len(package_provides) > 0:
		contents += 'Provides: '
		delim = ''
		for d in package_provides:
			contents += delim + d
			delim = ','
		contents += '\n'

	if len(package_conflicts) > 0:
		contents += 'Conflicts: '
		delim = ''
		for d in package_conflicts:
			contents += delim + d
			delim = ','
		contents += '\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
	control_tarfile.addfile(tarinfo=info, fileobj=control_file)

with tarfile.open(package_tmp_directory.name + '/data.tar.xz', mode = 'w:xz') as data_tarfile:
	for input_file in package_files:
		file_stat = os.stat(input_file)

		# The tar file path should not start with slash:
		if PREFIX.startswith('/'): PREFIX = PREFIX[1:]
		if not PREFIX.endswith('/'): PREFIX += '/'

		output_file = PREFIX + package_files[input_file]
		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, 'rb') as f:
			data_tarfile.addfile(tarinfo=info, fileobj=f)

subprocess.check_call(['ar', 'r', output_debfile_name,
  package_tmp_directory.name + '/debian-binary',
  package_tmp_directory.name + '/control.tar.xz',
  package_tmp_directory.name + '/data.tar.xz'
])
