termux-create-package
A utility to create binary deb packages.
By default it creates binary deb packages for installation in the Termux Linux environment, but by passing the --prefix /usr
argument or defining installation_prefix: "/usr"
field in the YAML
or JSON
manifest, a deb file can be created for linux distributions such as Debian or Ubuntu, etc.
Contents
- Compatibility
- Downloads
- Installation
- Current Features
- Usage
- Package Control File Fields
- Package Create Info Fields
- Package Data Files Fields
- Other Control Files
- Examples
Compatibility
- Android version
>= 7.0
using Termux App. - Linux distros.
- Windows using cygwin or WSL. (Untested)
Downloads
Latest version is v0.12.0
.
Installation
Check INSTALLATION.md file for the install instructions.
Note that termux-create-package
is no longer updated on the Python Package Index (PyPI)
and should not be installed with pip
. Latest version on PyPi
is 0.7
.
Current Features
- Define package build info in
YAML
1.2.0
orJSON
manifest files. - Create binary deb packages as per debian policy.
- Automatically create
control
,md5sums
,conffiles
file. - Automatically set permissions and ownership to files.
- Run specific actions on package files like setting shebangs.
Usage
termux-create-package command is used to create binary deb packages.
Usage:
termux-create-package [optional arguments] manifests...
positional arguments:
manifests YAML or JSON manifest file(s) describing the package(s)
optional arguments:
-h, --help show this help message and exit
--help-extra show extra help message and exit
--version show program's version number and exit
-v "set verbose level,
pass once for log level "INFO" and twice for "DEBUG
--control-files-dir CONTROL_FILES_DIR
path to directory of maintainer scripts and other control files,
(default: current directory,
unless "control_files_dir" field is set or "--files-dir" is passed or "files_dir" manifest field is set)
--deb-dir DEB_DIR path to directory to create deb file in,
(default: current directory,
unless "deb_dir" manifest field is set)
--deb-name DEB_NAME name of deb file to create,
(default: "${Package}_${Version}_S{Architecture}.deb",
unless "deb_name" manifest field is set)
--files-dir FILES_DIR
path to directory of package files,
(default: relative to current directory,
unless "files_dir" manifest field is set)
--pkg-arch PKG_ARCH architecture the package was compiled for or will run on,
(default: "Architecture" manifest "control" dict field)
--pkg-version PKG_VERSION
version for the package,
(default: "Version" manifest "control" dict field)
--prefix PREFIX path under which to install the files on the target system
(default: /data/data/com.termux/files/usr,
unless "installation_prefix" manifest field is set)
--yaml force consider manifest to be in yaml format,
(default: false
The paths to YAML or JSON manifest file(s) must be passed as "manifests".
The termux-create-package
script expects the package manifest files containing info on how to build the package to be defined in YAML
1.2.0
or JSON
format. YAMl
is the preferred format since its a better configuration language than JSON
, specially due to support for comments and splitting strings on multiple lines.
The deb file created will be as per Debian Policy Manual and will contain the following files:
-
debian-binary
will contain the package format version number. Currently2.0
. -
control.tar*
will contain thecontrol
file containing package info,md5sums
containingmd5
hashes of package files, maintainer scripts and other control files. More info oncontrol
file can be found at dpkg-dev/deb-control. -
data.tar*
will contain the package data files.
Note that any optional arguments passed will override their respective manifest fields in ALL manifests passed, so use wisely, or use them only if passing only a single manifest.
Beware of trailing commas in JSON
manifest for the last item of lists and dictionaries since otherwise an exception will be raised.
Example YAML manifest:
control:
Package: hello-world
Version: 0.1.0
Architecture: all
Maintainer: GithubNick <[email protected]>
Depends: python (>= 3.0), libandroid-support
Homepage: https://hello-world.com
Description: |-
This is the hello world package
It is just an example for termux-create-package
.
It is just prints 'Hello world'
installation_prefix: /data/data/com.termux/files/usr
data_files:
bin/hello-world:
source: hello-world.py
set_shebang: "#!/data/data/com.termux/files/usr/bin/env python3"
share/man/man1/hello-world.1:
source: hello-world.1
Example JSON manifest:
{
"control": {
"Package": "hello-world",
"Version": "0.1.0",
"Architecture": "all",
"Maintainer": "GithubNick <[email protected]>",
"Depends": "python (>= 3.0), libandroid-support",
"Homepage": "https://hello-world.com",
"Description": [
"This is the hello world package",
" It is just an example for termux-create-package",
" .",
" It is just prints 'Hello, world'"
]
},
"installation_prefix": "/data/data/com.termux/files/usr",
"data_files": {
"bin/hello-world": { "source": "hello-world.py",
"set_shebang": "#!/data/data/com.termux/files/usr/bin/env python3" },
"share/man/man1/hello-world.1": { "source": "hello-world.1" }
}
}
Package Control File Fields
The fields in the manifest file under the control
dictionary will be added to the control
file in control.tar*
and should contain the package info.
The field name must be composed of US-ASCII
characters within the range 32
/U+0021
through 126
/U+007E
excluding space
and colon :
characters and it must not begin with hyphen -
or comment #
characters. Field names are not case-sensitive, but it is usual to capitalize the first letter of words as per CamelCase
format. The fields must be defined in CamelCase
for versions >= 0.12.0
but old format is also supported to maintain backward compatibility. Some keys were named differently previously, which are also supported.
The common fields that are added to the control file in order if they exist in the manifest are Package
, Source
, Version
, Architecture
, Maintainer
, Installed-Size
, Section
, Priority
, Essential
, Depends
, Pre-Depends
, Recommends
, Suggests
, Breaks
, Conflicts
, Replaces
, Enhances
, Provides
, Homepage
, Description
. Any other field that exists in the manifest are added after the Provides
field and before the Homepage
field. The Installed-Size
field will be automatically added by calculating data files sizes if its not already defined in the manifest.
The Package
, Version
, Architecture
, Maintainer
and Description
fields are mandatory as per debian policy and their valid values must exist in the manifest. The Architecture
value can optionally be passed as a command option if the manifest is to be shared between different architectures.
If the field is not of type list
, then it is converted to a normal string and added to the control file. Fields that are null
or empty are not added. Check syntax of control files section in debian policy for further details.
If the field is of type list
and is not one of the package relationship fields, then each item of the list will be joined together by newlines and added to the control file. This is helpful to define multi-line fields like Description
.
If the field is of type list
and is one of the package relationship fields, i.e Depends
, Pre-Depends
, Recommends
, Suggests
, Breaks
, Conflicts
, Replaces
, Enhances
and Provides
, then it will automatically be joined on a comma followed by space character ,
. If these fields are defined as a string, then a comma must be added between each package of the field in the manifest, otherwise dpkg
will throw an error like parsing file '/var/lib/dpkg/tmp.ci/control' near line 6 package 'hello-world': 'Depends' field, syntax error after reference to package 'python3'
during deb
installation. These fields are currently not validated due to them being a bit complex to easily parse/validate. Check declaring relationships between packages section in debian policy for further details.
All below fields are of type string
unless otherwise specified.
Package
The name of the package. This field was named name
in versions < 0.12.0
. Check here for more details.
Valid Values: It must consist only of lower case letters a-z
, digits 0-9
, plus +
and hyphen -
signs, and periods .
. It must be at least two characters long and must start with an alphanumeric character. The same rules apply to the optional Source
field.
Version
The version of the package. Check here for more details.
Valid Values: It must be in the format [epoch:]upstream_version[-debian_revision]
. epoch
can only be an integer. upstream_version
and debian_revision
must consist only of upper or lower case letters a-zA-Z
, digits 0-9
, plus +
and tilde ~
signs, and periods .
. The upstream_version
must start with a digit. The hyphen -
is only allowed if debian_revision
is set.
Architecture
The architecture the package was compiled for or will run on. This field was named arch
in versions < 0.12.0
. Check here for more details.
Set to all
if the package only contains architecture-independent data. If installation prefix starts with /data/data/<app_package>/files/
, then it must be one of all
, arm
, i686
, aarch64
or x86_64
since android only supports those architectures, however, you can override this by adding "ignore_android_specific_rules": true
entry to the manifest.
Valid Values: It must must contain a space separated list of architectures or architecture wildcards that consist only of lower case letters a-z
, digits 0-9
, plus +
and hyphen -
signs, and periods .
. It must be at least two characters long and must start with an alphanumeric character.
Maintainer
The name and contact of the maintainer. It should be in the format name <email>
, example: Foo Bar <[email protected]>
. It is typically the person who created the package, as opposed to the author of the software that is to be packaged. Check here for more details.
Depends
Comma-separated list of packages that this package depends on. Those packages will be installed automatically when this package is installed using apt
.
Homepage
The project home page URL. Check here for more details.
Description
Contains the description of the binary package, consisting of two parts, the synopsis or the short description, and the long description. Check here for more details. It is a multi-line
field with the following format:
Description: Single line short description
extended description over several lines
.
some more description
For multi-line
fields in control
file, each line after the first line must have a space character
as the first character and must contain at least one non-whitespace character like a dot .
, otherwise dpkg
will throw an error like parsing file '/var/lib/dpkg/tmp.ci/control' near line 8 package 'hello-world': field name 'It' must be followed by colon
during deb
installation. To represent empty lines, set the line to a space character followed by a dot .
To define a multi-line
field in a YAML
manifest, the field value could be set as a literal block style |
with strip block chomping indication -
(to strip trailing newlines), like |-
. Each line will be joined together with a newline character \n
. Do not forget -
after |
, otherwise validation will fail since last line due to trailing newline will be considered an empty line.
Description: |-
Single line short description
extended description over several lines
.
some more description
To define a multi-line field in a JSON
manifest the field value could be set to a list
. Each item of the list
will be joined together with a newline character \n
.
"Description": [
"Single line short description",
" extended description over several lines",
" .",
" some more description"
]
Package Create Info Fields
The fields in the manifest file outside the control
and data_files
dictionaries are used for storing information on how to create the package and what files need to be added to the package. These fields are not added the control
file.
The currently used package create info fields are the following. All fields are of type string
unless otherwise specified.
allow_bad_user_names_and_ids
The bool
field that decides whether is should be allowed to add package files to the deb that are considered invalid as per debian policy. Check owner_uid
and owner_uname
for details on what is considered valid.
conffiles_prefix_to_replace
The optional path prefix that should be replaced with the installation prefix in the files added in the locally defined conffiles
file in control_files_dir
or files_dir
. For example if this is set to /usr
and conffiles
contains a file with the path /usr/etc/hello-world/hello-world.config
and installation prefix is set to /data/data/com.termux/files/usr
, then the final conffiles
added to the deb file with have the path set to /data/data/com.termux/files/usr/etc/hello-world/hello-world.config
instead. This does not apply to the conffiles
file dynamically generated with data_files
that have is_conffile
set to true
.
control_files_dir
The path to directory containing maintainer scripts preinst
, postinst
, prerm
, postrm
, config
and other control files conffiles
, templates
, shlibs
to include in the deb package. The default is relative to current directory, unless --control-files-dir
or --files-dir
argument is passed to the script or files_dir
field is set. This is useful if the same files/build directory is used for different distros or architectures but different maintainer scripts or conffiles
for each.
deb_architecture_tag
The architecture tag to use for the file name of the deb file to be created. If deb_name
is not set and --deb-name
argument is not passed to the script, then deb file will be named ${Package}_${Version}_S{deb_architecture_tag}.deb
instead of ${Package}_${Version}_S{Architecture}.deb
. This is helpful if you want to use the same package and version tag defined in Package
and Version
fields respectively but a different architectures tag than the one defined in Architecture
field.
deb_dir
The path of the directory to create the deb file in. The default is current directory, unless --deb-dir
argument is passed to the script.
deb_name
The file name of the deb file to be created. If this is not set, then deb file will be named ${Package}_${Version}_S{Architecture}.deb
by default, unless --deb-name
argument is passed to the script.
files_dir
The path to directory containing package files to include in the deb package. The default is relative to current directory, unless --files-dir
argument is passed to the script.
fix_perms
The bool
field that decides whether permissions of source files that are added as package data_files
should be automatically fixed when adding them to the deb
file as per dh_fixperms
(impl) rules if needed.
The fix_perms
value is the global value that can be set to true
(default) or false
to enable or disable fixing permissions respectively for all data_files
. If it is true
, then you can optionally disable fixing permissions for specific files only by setting their file level fix_perm
field value to false
. If it is false
, then you can optionally enable fixing permissions for specific files only by setting their file level fix_perm
field value to true
. The permissions are not fixed if a data file has the perm
field set.
ignore_android_specific_rules
The bool
field that can be set to true
to ignore the following android specific rules:
- Only allow android specific architectures if prefix is set under the
/data/data/<app_package>/files/
path. - Remove group and others permissions while setting permissions for files to be added to
data.tar*
under the/data/data/<app_package>/files/
path ifperm
field is not set and globalfix_perms
and/or file levelfix_perm
istrue
. - Set permissions of parent directory paths for files to be added to
data.tar*
under the/data/data/<app_package>/files/
path to700
instead of755
.
installation_prefix
The prefix under which to install the files on the target system. The termux prefix /data/data/com.termux/files/usr
is used by default if the field is not defined, unless --prefix
argument is passed to the script. It must be an absolute path that starts with forward slash /
, ends with /usr
and must only contain characters in the range a-zA-Z0-9_./
. It cannot contain parent path references ../
.
maintainer_scripts_shebang
The shebang that should be set on the maintainer scripts if the first line starts with #!
. Example: #!/bin/bash
for linux distros and #!/data/data/com.termux/files/usr/bin/bash
for termux.
tar_compression
The compression type of control.tar*
and data.tar*
. The xz
tar compression is used by default if the field is not defined since that is the default for current versions of dpkg
. If none
is set, then compression will not be done. Check dpkg-dev/deb for more details.
Valid Values: none
, gz
and xz
.
tar_format
The tar format of control.tar*
and data.tar*
. The GNU tar
format is used by default if the field is not defined since that is officially supported by dpkg
. You may get package corrupted errors if other formats are used, specially pax
. Check dpkg-dev/deb for more details.
Valid Values: gnutar
, ustar
and pax
.
Package Data Files Fields
The data_files
dictionary
is a mandatory field containing a nested dictionary
type where the parent key is the destination path for the data file inside the deb package or target system and the value is a dictionary
containing the following keys/value pairs for version >= 0.12.0
.
If destination path is an absolute path starting with a forward slash /
, then that will be used. Otherwise it will be considered relative to the installation prefix. If the destination path is an empty string ""
, then it automatically expand to the installation prefix.
Fields that are null
or empty are not used, other than source
path.
Non utf-8
characters are not allowed in any paths as per debian policy.
Mandatory attribute key/value pairs:
source
The source path for the data file from which to read the file that should be added to the package. If source path is an absolute path starting with a forward slash /
, then that will be used, otherwise it will be considered relative to the current working directory, unless --files-dir
argument is passed to the script or files_dir
field is set. If the source path is an empty string ""
, then an empty directory will be added at the destination path.
Optional attribute key/value pairs:
fix_perm
The bool
value that defines the file
level setting for whether fixing permissions should be done for the source file when adding it to the deb. Check fix_perms
for more info.
is_conffile
The bool
value that defines whether this data file is a conffile
and should be added to the dynamically generated conffiles
file.
owner_uid
The owner user id that should be set to the data file when adding it to the deb. It must be within the 0-99
, 60000-64999
and 65534
ranges. By default user id 0
is set, unless source_ownership
is set. The source_ownership
takes precedence over owner_uid
.
Check debian policy
, useradd manual
, systemd uid/gid docs
and shadow
find_new_uid.c
for more info.
Note that when installing the deb
file on android with the termux app, any custom ownership value will not be set and all files will be set to termux
user ownership.
owner_uname
The owner user name that should be set to the data file when adding it to the deb. It must begin with a lower case letter or an underscore, followed by lower case letters, digits, underscores, or hyphens. It can end with a dollar sign. In regular expression terms: [a-z_][a-z0-9_-]*[$]?
. It may also be only up to 32
characters long. By default user name root
is set, unless source_ownership
is set. The source_ownership
takes precedence over owner_uname
.
Check debian policy
, useradd manual
, shadow
useradd.c
, systemd user name docs
, shadow
chkname.c
and posix docs
for more info.
group_uid
The group user id that should be set to the data file when adding it to the deb. Same rules as owner_uid
apply.
group_uname
The group user name that should be set to the data file when adding it to the deb. Same rules as owner_uname
apply.
perm
The 3
or 4
digit permission octal that should be set to the data file when adding it to the deb. Example: 755
for rwxr-xr-x
or 4755
for rwsr-xr-x
where setuid
bit is also set. If a custom value in perm
field is not set, then the permissions will be automatically fixed. Check fix_perms
for more info.
source_ownership
The bool
value for whether source ownership should be used when adding the data file to the deb. If source ownership is not compliant, as per debian policy, then it is ignored and root:root
ownership is used. Check owner_uid
and owner_uname
for details on what is considered valid.
Optional action key/value pairs:
ignore
The bool
value for whether this data file defined in the manifest should be ignored and not added to the deb. Source file existence check is not done.
ignore_if_no_exist
The bool
value for whether this data file defined in the manifest should be ignored and not added to the deb if it not does not exist at source
path instead of command exiting with failure.
set_parent_perm
The bool
value for whether the permissions should be set to the parent directory paths for of the data file directory the same as the source permissions or the one defined by perm
. For example, if a destination entry is added for opt/hello-world/cache
, then opt
and opt/hello-world
will also be set to the same permissions. This is useful to define a directory hierarchy with the same specific permissions.
set_shebang
The shebang that should be set on the data file if the first line starts with #!
. Example: #!/bin/bash
for linux distros and #!/data/data/com.termux/files/usr/bin/bash
for termux.
source_readlink
The bool
value for whether the source
path should be traversed if its a symlink. By default if source
file is a symlink
, then the symlink
itself is added to the deb file instead of its target file.
source_recurse
The bool
value for whether all files under the source
path should be recursively added to the deb if its a directory. By default files under source
directories are not automatically/recursively added to the deb. Each file that needs to be added to the deb must be added separately. This is useful for cases where the files/build directory contains lots of files but you only want specific files to be added to the deb. You may optionally only add a directory entry and not add any file entries, which would ideally result in an empty directory at the target system if it didn't already exist there.
symlink_destinations
The list
value that defines the symlinks that should automatically be created and added to the deb that target the destination data file path. For example adding the entry for the destination file bin/hello-world.1
and adding bin/hello-world
to symlink_destinations
will create a file at bin/hello-world
that points to bin/hello-world.1
. This is helpful in defining one or more symlinks dynamically for a file, without having to create symlink files on source system.
If specific permission needs to be set to the parent directory of a file that needs to be installed at the target system, then add an empty source
entry with the perm
field set before the entry of the file, optionally with set_parent_perm
set to true
as well. However, if the directory already exists at the target system, then the permission is unlikely to change, use maintainer scripts instead.
"data_files": {
"bin": { "source": "", "perm": "755" },
"bin/hello-world": { "source": "hello-world.py", "perm": "755" },
"share/man/man1/hello-world.1": { "source": "hello-world.1", "perm": "644" }
}
Old files
format:
This data_files
field was named files
in versions < 0.12.0
and it had the source
as the dictionary key instead of the destination. That had the design flaw that a single source file could only be added to one destination path in the deb. Moreover, since destination was the value instead of the key, multiple source files could be added for the same destination. The new design does not have such issues and paths are normalized to check for duplicates too. Old format is also fully supported to maintain backward compatibility. In version >= 0.8
, with the files
field, the files in source path directories were recursively added to the deb, but that will not happen in version >= 0.12.0
with the new data_files
field as mentioned in source_recurse
. Note that any files whose ownership is not compliant with debian policy, as detailed in owner_uid
and owner_uname
, will have their ownership replaced with root:root
. That is likely the only breaking change and users on old formats should shift to newer format and set allow_bad_user_names_and_ids
to true
if they want to go against debian policy.
"files": {
"hello-world.py": "bin/hello-world",
"hello-world.1": "share/man/man1/hello-world.1"
}
Other Control Files
The maintainer scripts preinst
, postinst
, prerm
, postrm
, config
and other control files conffiles
, templates
, shlibs
are automatically added to control.tar*
if they exist in control_files_dir
or files_dir
.
The ownership of all files added to the control.tar*
is automatically set to root:root
as per debian policy. The permission of maintainer scripts is automatically set to 755
and other control files to 644
.
The maintainer_scripts_shebang
field is helpful if the same architecture independent scripts need to be added to different debs for linux distros and termux. Check scripts section in debian policy for further details.
The conffiles
can be added to the deb in two ways. Either a predefined file can be added to control_files_dir
or files_dir
that should be added or it can be dynamically generated by setting the is_conffile
field to true
for the data_files
that should be added to the conffiles
. If even a single file has is_conffile
set to true
, then any predefined conffiles
in control_files_dir
or files_dir
will not be added and conffiles
generated dynamically will be added. All files in the conffiles
added are validated for existence in the deb and an error will be raised if any file does not exist. If a predefined file is defined, the conffiles_prefix_to_replace
may be useful as well if debs are being built for linux distros and termux. The conffiles
file content must be utf-8
encodable as per debian policy and all files must be regular files and not symlinks since that is not officially supported and can result in unpredictable behaviour, directories are not supported either. Each line must contain an absolute file path. Empty lines are not allowed. Check configuration files section in debian policy for further details.
Examples
After creating a manifest for the project, run termux-create-package </path/to/manifest>
command to create the deb file. Example manifests are provided for both YAML
and JSON
in the examples
directory.
-
Create a deb package file with defaults:
termux-create-package manifest.yml
-
Create a deb package file with specific installation prefix, files directory, deb directory and deb name:
termux-create-package --prefix '/usr' --files-dir '/path/to/files_directory' --deb-dir '/path/to/deb_directory' --deb-name 'some-name.deb' manifest.json
-
Create example manifest deb from
termux-create-package
repo source:cd examples/hello-world; ../../src/termux-create-package -vv manifest-ubuntu.yml
The deb file can be installed by running dpkg -i package.deb
. The dpkg
install command will not install dependencies, you can install them by running apt-get -f install
afterwards.
The deb file can also be added to a custom apt repository created with termux-apt-repo
or any other available tool.