Section 8: Packages
8.0 Introduction
Packages can be viewed as a collection of modules.
In Keli, every package is essentially one Git repository. Thus, packaging system of Keli has two external dependencies, namely Git and Git repositories hosting website, such as Github, GitLab, and etc.
Keli Package System (KPS) strive to be a packaging system that have the following characteristics:
easy package distribution
easy package installation
prevent package duplication (in the case of diamond-shaped dependency)
allow highly repeatable builds
8.1 Summary
The following items will be discussed in this section:
How each Keli package should be structured
How to create a new package
Structure of the package manifest file
How to add new dependency
How to install dependencies
How to publish a package
8.2 Package structure
Each Keli package will have the following structure:
a
_srcfolder, containing all the source file of this package, and apurse.jsonfilea
_testfolder, containing all the test scripts for testing this packagepurse.jsonfile contains a list of dependencies of this package (refer Section 8.4)a
.gitignorefile that will ignore every folders(which are effectively the external dependencies of the current package), except the_srcfolder.a README file, to describe this package
a LICENSE file
For example, suppose we want to create a package named Graph.
Graph/
.gitignore
LICENSE
README.md
_src/
toposort.keli
graph.keli
purse.json
_test/
test1.keli
test2.keli
MathOrg.Math.0.0.1/
purse.json
numbers.keli
cartesian.keliEach package is structured this way so that there are no implicit mechanism to resolve modules when importing an external module. This contrast with almost all popular programming languages that have a built-in mechanism to import modules from external dependencies.
That is to say, to importing a module from external dependency, we just have to use the same mechanism, which is using relative paths.
For example, based on Folder Structure 1, we can import numbers.keli into graph.keli by:
= module.import("../MathOrg.Math.0.0.1/numbers.keli")Also, by structuring each package this way, we can prevent duplicated modules being fetched, in the case of diamond dependency. For instance, suppose a new package A depends on B and C, while both B and C, depends on D, the package D will not be downloaded twice, as long as B and C depends on the same version of D. In other words, we can achieve a flat dependencies hierarchies, instead of a nested one.
Moreover, due to this design, it is possible to have multiple versions of the same package to be used at the same time.
8.3 Creating new package
A new Keli package can be created by invoking the compiler CLI command as follows:
keli new-package PackageNameNote that the package name should follows the PascalCase or camelCase convention, and MUST NOT include any symbols except for underscore.
For example, suppose the following command is invoked under the user home directory ~ ,
keli new-package MyPackageThen, a folder named MyPackage will appear under the ~ directory, as such:
MyPackage/
_src/
purse.json
_test/
.gitignore
README.md
LICENSEThe contents of .gitignore are as follows:
# ignore all folders
/*
# except
!.gitignore
!README.md
!LICENSE
!_src/
!_test/The format of purse.json is explained on the next section.
8.4 Manifest file
The manifest file for each Keli package is named purse.json . The contents of the file should strictly adhere to the following format:
{
"os" : "<OS>",
"arch" : "<ARCH>",
"compiler" : "<VERSION>",
"git" : "<VERSION>",
"node" : "<VERSION>",
"dependencies": [{
"url" : "<GRURL>",
"tag" : "<VERSION>"
}]
}<OS> stands for the name of the operating system, for example Windows, Darwin (MacOS), Linux etc.
<ARCH> stands for the system architecture, for example i386 .
Each <VERSION> is a semantic versioning string, e.g. 0.0.1 .
Meanwhile, each <GRURL> is a valid Git repository URL. Every GRURL must have all of the following characteristics:
The name of owner of the Git repository must be present (either a username, or an organization name).
The name of the repository must be present.
Must end with
.git.
Examples of valid GRURL are:
https://gitlab.com/gitlab-org/gitlab-ce.git
https://github.com/red/red.gitBasically, every Github or GitLab repository URL can be considered a valid GRURL.
8.5 Adding dependency
To add new dependency, we can simply use the following command:
keli add-dependency <GRURL> <TAG>For example:
keli add-dependency https://github.com/KeliLanguage/corelib 0.0.1By invoking this command, two things will be done:
_src/purse.jsonwill be updatedThe process of installing the new package will be triggered
8.6 Installing defined dependencies
Dependencies can be installed using the following command:
keli install <path_to_purse.json>For example, to install all the dependencies for the Graph package (refer Folder Structure 1), we would type the following command inside the Graph directory:
keli install _src/purse.jsonThe following pseudocode shall describe how the dependency installation algorithm works:
install($depPath) {
$kpurls = fs.readAllLines($depPath)
$kpurls
.forEach(validate)
.forEach($url -> {
{$authorName, $repoName, $tag} = extractNames($url)
$name = "$authorName.$repoName.$tag"
if($name.existsIn(".")) {
# no need to install this dependency
} else {
runCommand("git clone -b '$tag' --single-branch --depth 1 $url $name")
fs.moveFilesFrom("$name/_src/*") to("$name/")
fs.deleteFolder("$name/_src")
fs.deleteFolder("$name/_test")
fs.deleteFolder("$name/.git")
install("$name/purse.json")
}
})
}8.7 Publishing package
To publish a package, we just need to push our package to a Git repository hosting sites such as GitHub or GitLab. Secondly, it must be tagged using the git tag command.
Last updated
Was this helpful?