Skip to content

Commit

Permalink
Minimized code structure and fixed minor issue
Browse files Browse the repository at this point in the history
  • Loading branch information
meiuwa authored and Leonid Pliushch committed Jul 10, 2020
1 parent 169bccd commit 81c4ade
Showing 1 changed file with 80 additions and 110 deletions.
190 changes: 80 additions & 110 deletions termux-create-package
Original file line number Diff line number Diff line change
Expand Up @@ -10,81 +10,8 @@ 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, 'recommends', [])
set_default_value(manifest, 'suggests', [])
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, debscripts):
"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['recommends']:
contents += 'Recommends: ' + ','.join(manifest['recommends']) + '\n'
if manifest['suggests']:
contents += 'Suggests: ' + ','.join(manifest['suggests']) + '\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', format=tarfile.GNU_FORMAT) as control_tarfile:
control_tarfile.addfile(tarinfo=info, fileobj=control_file)
for debscript in debscripts:
if os.path.isfile(debscript): control_tarfile.add(debscript)

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', format=tarfile.GNU_FORMAT) as data_tarfile:
for input_file_path in package_files:
output_file = installation_prefix + package_files[input_file_path]
data_tarfile.add(input_file_path, arcname=output_file, recursive=True)

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",
Expand All @@ -102,6 +29,7 @@ DESCRIPTION = """Create a Termux package from a JSON manifest file. Example of m
"hello-world.1": "share/man/man1/hello-world.1"
}
}
The "maintainer", "description", "homepage", "depends", "recommends", "suggests", "provides" and "conflicts" fields are all optional.
The "depends", "recommends", and "suggests" fields should be a comma-separated list of packages that this package may depends on. Unlike "suggests", "depends" and "recommends" will be installed automatically when this package is installed using apt.
Expand All @@ -110,55 +38,97 @@ The "arch" field defaults to "all" (that is, a platform-independent package not
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).
Existing debscripts named "preinst", "postinst", "prerm", and "postrm" will be automatically included and executed upon installation and removing. They should exist within the same directory as the manifest.
Optional debscripts named "preinst", "postinst", "prerm", and "postrm" will be automatically included and executed upon installation and removing. They should exist within the same directory as the manifest.
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/"
def set_default_manifest_values(manifest):
"Setup default values in a package manifest."
properties = {"arch": "all",
"depends": [],
"recommends": [],
"suggests": [],
"provides": [],
"conflicts": [],
"maintainer": "None",
"description": "No description"}
for property, value in properties.items():
if property not in manifest:
manifest[property] = value

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)
def validate_manifest(manifest):
"Validate that the package manifest makes sense."
for property in ("name", "version", "files"):
if property not in manifest:
sys.exit(f"Missing mandatory {property} 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')

if args.prefix:
installation_prefix = str(args.prefix)
def write_control_tar(tar_path, manifest, debscripts):
"Create a data.tar.xz from the specified manifest."
contents = (f"Package: {manifest['name']}\n"
f"Version: {manifest['version']}\n"
f"Architecture: {manifest['arch']}\n"
f"Maintainer: {manifest['maintainer']}\n"
f"Description: {manifest['description']}\n")
if "homepage" in manifest:
contents += f"Homepage: {manifest['homepage']}\n"
for property in ("depends", "recommends", "suggests", "provides", "conflicts"):
if manifest[property]:
contents += f"{property.capitalize()}: {','.join(manifest[property])}\n"
control_file = io.BytesIO(contents.encode("utf8"))
control_file.seek(0, os.SEEK_END)
control_file_size = control_file.tell()
control_file.seek(0)
tarfile_info = tarfile.TarInfo(name="./control")
tarfile_info.size = control_file_size
with tarfile.open(tar_path, mode="w:xz", format=tarfile.GNU_FORMAT) as control_tarfile:
control_tarfile.addfile(tarinfo=tarfile_info, fileobj=control_file)
for debscript in debscripts:
if os.path.isfile(debscript): control_tarfile.add(debscript)

manifest_file_path = args.manifest
manifest_dirname = os.path.dirname(manifest_file_path)
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", format=tarfile.GNU_FORMAT) as data_tarfile:
for input_file_path, output_file_path in package_files.items():
output_file_path = os.path.join(installation_prefix, output_file_path)
data_tarfile.add(input_file_path, arcname=output_file_path, recursive=True)

debscripts = [
os.path.join(manifest_dirname, 'preinst'),
os.path.join(manifest_dirname, 'postinst'),
os.path.join(manifest_dirname, 'prerm'),
os.path.join(manifest_dirname, 'postrm')
]
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,
f"{directory}/debian-binary",
f"{directory}/control.tar.xz",
f"{directory}/data.tar.xz"])

if __name__ == "__main__":
"Generate a deb file from a JSON manifest."
installation_prefix = "/data/data/com.termux/files/usr/"
argument_parser = argparse.ArgumentParser(description=DESCRIPTION,
formatter_class=argparse.RawTextHelpFormatter)
argument_parser.add_argument("manifest", help="A JSON manifest file describing the package")
argument_parser.add_argument("--prefix", help=f"Set prefix dir (default: {installation_prefix})")
arguments = argument_parser.parse_args(sys.argv[1:])
if arguments.prefix:
installation_prefix = str(arguments.prefix)
manifest_file_path = arguments.manifest
manifest_dir_path = os.path.dirname(manifest_file_path)
debscripts = [(os.path.join(manifest_dir_path, debscript))
for debscript in ("preinst", "postinst", "prerm", "postrm")]
with open(manifest_file_path, 'r') as manifest_file:
manifest = json.load(manifest_file)

setup_default_manifest_values(manifest)
set_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_name = manifest["name"]
package_version = manifest["version"]
package_files = manifest["files"]
output_debfile_name = f"{package_name}_{package_version}_{manifest['arch']}.deb"
print(f"Building {output_debfile_name}")
package_tmp_directory = tempfile.TemporaryDirectory()
with open(package_tmp_directory.name + '/debian-binary', 'w') as debian_binary:
with open(f"{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, debscripts)
write_data_tar(package_tmp_directory.name + '/data.tar.xz', installation_prefix, package_files)
write_control_tar(f"{package_tmp_directory.name}/control.tar.xz", manifest, debscripts)
write_data_tar(f"{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:])

0 comments on commit 81c4ade

Please sign in to comment.