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 _src folder, containing all the source file of this package, and a purse.json file
    a _test folder, containing all the test scripts for testing this package
    purse.json file contains a list of dependencies of this package (refer Section 8.4)
    a .gitignore file that will ignore every folders(which are effectively the external dependencies of the current package), except the _src folder.
    a README file, to describe this package
    a LICENSE file
For example, suppose we want to create a package named Graph.
Folder Structure 1
1
Graph/
2
.gitignore
3
LICENSE
4
README.md
5
6
_src/
7
toposort.keli
8
graph.keli
9
purse.json
10
11
_test/
12
test1.keli
13
test2.keli
14
15
MathOrg.Math.0.0.1/
16
purse.json
17
numbers.keli
18
cartesian.keli
Copied!
Each 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:
graph.keli
1
= module.import("../MathOrg.Math.0.0.1/numbers.keli")
Copied!
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:
1
keli new-package PackageName
Copied!
Note 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 ~ ,
1
keli new-package MyPackage
Copied!
Then, a folder named MyPackage will appear under the ~ directory, as such:
1
MyPackage/
2
_src/
3
purse.json
4
_test/
5
.gitignore
6
README.md
7
LICENSE
Copied!
The contents of .gitignore are as follows:
.gitignore
1
# ignore all folders
2
/*
3
4
# except
5
!.gitignore
6
!README.md
7
!LICENSE
8
!_src/
9
!_test/
Copied!
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:
1
{
2
"os" : "<OS>",
3
"arch" : "<ARCH>",
4
"compiler" : "<VERSION>",
5
"git" : "<VERSION>",
6
"node" : "<VERSION>",
7
"dependencies": [{
8
"url" : "<GRURL>",
9
"tag" : "<VERSION>"
10
}]
11
}
Copied!
<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:
    1.
    The name of owner of the Git repository must be present (either a username, or an organization name).
    2.
    The name of the repository must be present.
    3.
    Must end with .git .
Examples of valid GRURL are:
1
https://gitlab.com/gitlab-org/gitlab-ce.git
2
https://github.com/red/red.git
Copied!
Basically, 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:
1
keli add-dependency <GRURL> <TAG>
Copied!
For example:
1
keli add-dependency https://github.com/KeliLanguage/corelib 0.0.1
Copied!
By invoking this command, two things will be done:
    1.
    _src/purse.json will be updated
    2.
    The process of installing the new package will be triggered

8.6 Installing defined dependencies

Dependencies can be installed using the following command:
1
keli install <path_to_purse.json>
Copied!
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:
1
keli install _src/purse.json
Copied!
The following pseudocode shall describe how the dependency installation algorithm works:
1
install($depPath) {
2
$kpurls = fs.readAllLines($depPath)
3
$kpurls
4
.forEach(validate)
5
.forEach($url -> {
6
{$authorName, $repoName, $tag} = extractNames($url)
7
$name = "$authorName.$repoName.$tag"
8
if($name.existsIn(".")) {
9
# no need to install this dependency
10
} else {
11
runCommand("git clone -b '$tag' --single-branch --depth 1 $url $name")
12
fs.moveFilesFrom("$name/_src/*") to("$name/")
13
fs.deleteFolder("$name/_src")
14
fs.deleteFolder("$name/_test")
15
fs.deleteFolder("$name/.git")
16
install("$name/purse.json")
17
}
18
})
19
}
Copied!

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 modified 2yr ago