Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

packaging: ZPK's and general packaging support #7

Merged
merged 20 commits into from
Apr 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ bin
sh-bin
*.sw[op]
*.core
*.zpk
32 changes: 30 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,44 @@ cd project
bob run
```

### Packaging

Bob used to work at an Amazon distribution centre! 📦

To create a ZPK package, run:

```console
bob package zpk
```

## Projects which use Bob

Here's a list of projects which use Bob:

- [Bob the Builder](https://github.com/inobulles/bob)
- [IAR](https://github.com/inobulles/iar)
- [Umber](https://github.com/inobulles/umber)
- [aquaBSD Aquariums](https://github.com/inobulles/aquarium)
- [`aqua-unix`](https://github.com/inobulles/aqua-unix)
- [`aqua-kos`](https://github.com/inobulles/aqua-kos)
- [`aqua-devices`](https://github.com/inobulles/aqua-devices)
- [LLN Gamejam 2023](https://github.com/obiwac/lln-gamejam-2023)
- [iface](https://github.com/inobulles/iface)
- [`libmkfs_msdos`](https://github.com/inobulles/libmkfs_msdos)
- [`libcopyfile`](https://github.com/inobulles/libcopyfile)

I use this list to not forget to update them when I add a new feature to Bob!

## Features

- [x] Written in C and with a very basic project structure, so can easily be bootstrapped.
- [x] Automatically installs (certain) dependencies.
- [x] Uses [Wren](https://wren.io/) as a dynamic configuration language because regular configuration files sucks.
- [x] Centralised system for building, testing, and installing.
- [x] Centralized system for building, testing, and installing.
- [ ] Logging class (like with `Log.error`, `Log.warn`) to provide feedback from within build configurations.
- [ ] Works with AQUA, i.e. AQUA projects may be built and run just as easily as any other project using Bob.
- [x] Easy method for building out a project skeleton from a template (serves the same purpose as [`aqua-manager`](https://github.com/inobulles/aqua-manager) did).
- [ ] Packaging (AQUA ZPK & FreeBSD PKG formats).
- [x] Packaging (AQUA ZPK & FreeBSD PKG formats).
- [ ] Understands other popular buildsystems (simple makefile, cmake, autoconf, qmake, setup.py, &c) to eliminate tmw you have to google stuff when you just want something to compile 🤪
- [ ] Watch source files to automatically rebuild them.
- [ ] Can build in sandboxes (local CI in background truly continuously by watching source files instead of just on commits? integration with other CI providers, e.g. with Cirrus CI by Automatically generating `.cirrus.yml`?).
Expand Down
4 changes: 2 additions & 2 deletions build.wren
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ class Runner {
// installation map

var install = {
"bob": "%(Meta.prefix())/bin/bob",
"skeletons": "%(Meta.prefix())/share/bob/skeletons",
"bob": "bin/bob",
"skeletons": "share/bob/skeletons",
}

// testing
Expand Down
25 changes: 24 additions & 1 deletion skeletons/c/build.wren
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ src

// link program

var linker = Linker.new(cc)
var linker = Linker.new()
linker.link(src.toList, [], "cmd")

// running
Expand All @@ -24,6 +24,29 @@ class Runner {
static run(args) { File.exec("cmd", args) }
}

// installation map

var entry = "bin/bob-skeleton-c"

var install = {
"cmd": entry,
}

// packaging

var pkg = Package.new(entry)

pkg.name = "Bob C Skeleton"
pkg.description = "Skeleton for a C project with Bob."
pkg.version = "0.1.0"
pkg.author = "Bob the Builder"
pkg.organization = "Inobulles"
pkg.www = "https://github.com/inobulles/bob"

var packages = {
"default": pkg,
}

// testing

class Tests {
Expand Down
25 changes: 24 additions & 1 deletion skeletons/rust/build.wren
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,37 @@ src
// link program

var linker = Linker.new()
linker.link(src.toList, ["std-66b716baf5a60e28"], "cmd")
linker.link(src.toList, ["std-7c7f3bd22bdaa9dd"], "cmd")

// running

class Runner {
static run(args) { File.exec("cmd", args) }
}

// installation map

var entry = "bin/bob-skeleton-rust"

var install = {
"cmd": entry,
}

// packaging

var pkg = Package.new(entry)

pkg.name = "Bob Rust Skeleton"
pkg.description = "Skeleton for a Rust project with Bob."
pkg.version = "0.1.0"
pkg.author = "Bob the Builder"
pkg.organization = "Inobulles"
pkg.www = "https://github.com/inobulles/bob"

var packages = {
"default": pkg,
}

// testing

class Tests {
Expand Down
2 changes: 1 addition & 1 deletion src/base/base.h

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions src/base/base.wren
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,28 @@ foreign class Linker {
foreign archive(path_list, out)
}

foreign class Package {
construct new(entry) {}

// getters

foreign name ()
foreign description ()
foreign version ()
foreign author ()
foreign organization()
foreign www ()

// setters

foreign name= (name)
foreign description= (description)
foreign version= (version)
foreign author= (author)
foreign organization=(organization)
foreign www= (www)
}

// static classes

class Deps {
Expand Down
8 changes: 2 additions & 6 deletions src/classes/cc.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,7 @@ void cc_new(WrenVM* vm) {
void cc_del(void* _cc) {
cc_t* const cc = _cc;

if (cc->path)
free(cc->path);

strfree(&cc->path);
opts_free(&cc->opts);
}

Expand All @@ -113,9 +111,7 @@ void cc_set_path(WrenVM* vm) {
cc_t* const cc = foreign;
char const* const path = wrenGetSlotString(vm, 1);

if (cc->path)
free(cc->path);

strfree(&cc->path);
cc->path = strdup(path);
}

Expand Down
126 changes: 126 additions & 0 deletions src/classes/package.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// much of this mimics the behaviour of the old aqua-manager tool:
// https://github.com/inobulles/aqua-manager

#include <util.h>

#include <classes/package.h>
#include <classes/package_t.h>

#include <pwd.h>
#include <unistd.h>

// foreign method binding

WrenForeignMethodFn package_bind_foreign_method(bool static_, char const* signature) {
// getters

BIND_FOREIGN_METHOD(false, "name()", package_get_name)
BIND_FOREIGN_METHOD(false, "description()", package_get_description)
BIND_FOREIGN_METHOD(false, "version()", package_get_version)
BIND_FOREIGN_METHOD(false, "author()", package_get_author)
BIND_FOREIGN_METHOD(false, "organization()", package_get_organization)
BIND_FOREIGN_METHOD(false, "www()", package_get_www)

// setters

BIND_FOREIGN_METHOD(false, "name=(_)", package_set_name)
BIND_FOREIGN_METHOD(false, "description=(_)", package_set_description)
BIND_FOREIGN_METHOD(false, "version=(_)", package_set_version)
BIND_FOREIGN_METHOD(false, "author=(_)", package_set_author)
BIND_FOREIGN_METHOD(false, "organization=(_)", package_set_organization)
BIND_FOREIGN_METHOD(false, "www=(_)", package_set_www)

// unknown

return wren_unknown_foreign;
}

// constructor/destructor

void package_new(WrenVM* vm) {
CHECK_ARGC("Package.new", 1, 1)

ASSERT_ARG_TYPE(1, WREN_TYPE_STRING)

package_t* const package = wrenSetSlotNewForeign(vm, 0, 0, sizeof *package);
char const* const entry = wrenGetSlotString(vm, 1);

package->entry = strdup(entry);

// defaults

package->name = strdup("Untitled Project");
package->description = strdup("Untitled project which has no title");
package->version = strdup("0.69.420");
package->www = strdup("https://youtu.be/dQw4w9WgXcQ");

// attempt to set the author to the username

uid_t const uid = getuid();
struct passwd* const passwd = getpwuid(uid);

package->author = strdup(passwd ?
passwd->pw_name :
"Anonymousia de Bergerac-Fleur");

// attempt to set the organization to the hostname

size_t const len = sysconf(_SC_HOST_NAME_MAX);
package->organization = calloc(1, len + 1);

if (gethostname(package->organization, len) < 0) {
free(package->organization);
package->organization = strdup("Knights of Can-A-Lot");
}
}

void package_del(void* _package) {
package_t* const package = _package;

strfree(&package->entry);
strfree(&package->name);
strfree(&package->description);
strfree(&package->version);
strfree(&package->author);
strfree(&package->organization);
strfree(&package->www);
}

// getters

#define GETTER(name) \
void package_get_##name(WrenVM* vm) { \
CHECK_ARGC("Package." #name, 0, 0) \
\
package_t* const package = foreign; \
wrenSetSlotString(vm, 0, package->name); \
}

GETTER(name)
GETTER(description)
GETTER(version)
GETTER(author)
GETTER(organization)
GETTER(www)

// setters

#define SETTER(name) \
void package_set_##name(WrenVM* vm) { \
CHECK_ARGC("Package." #name "=", 1, 1) \
\
ASSERT_ARG_TYPE(1, WREN_TYPE_STRING) \
\
package_t* const package = foreign; \
char const* const name = wrenGetSlotString(vm, 1); \
\
strfree(&package->name); \
package->name = strdup(name); \
}

SETTER(name)
SETTER(description)
SETTER(version)
SETTER(author)
SETTER(organization)
SETTER(www)
30 changes: 30 additions & 0 deletions src/classes/package.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#pragma once

#include <wren.h>

// foreign method binding

WrenForeignMethodFn package_bind_foreign_method(bool static_, char const* signature);

// constructor/destructor

void package_new(WrenVM* vm);
void package_del(void* _package);

// getters

void package_get_name(WrenVM* vm);
void package_get_description(WrenVM* vm);
void package_get_version(WrenVM* vm);
void package_get_author(WrenVM* vm);
void package_get_organization(WrenVM* vm);
void package_get_www(WrenVM* vm);

// setters

void package_set_name(WrenVM* vm);
void package_set_description(WrenVM* vm);
void package_set_version(WrenVM* vm);
void package_set_author(WrenVM* vm);
void package_set_organization(WrenVM* vm);
void package_set_www(WrenVM* vm);
15 changes: 15 additions & 0 deletions src/classes/package_t.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once

// XXX usually we'd want this to only be local to package.c's TU, but here we'd like to access the package object outside of it
// so we make it accessible through this package_t.h header

typedef struct {
char* entry;
char* name;
char* description;
char* version;
char* author;
char* organization;
char* www;
} package_t;

10 changes: 8 additions & 2 deletions src/instr.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,15 @@
#include <util.h>
#include <wren.h>

// stuff in common between instructions
// macros

#define INSTALL_MAP "install"
#define PACKAGE_MAP "packages"

// common stuff between instructions

void setup_env(char* working_dir);
int read_installation_map(state_t* state, WrenHandle** map_handle_ref, size_t* keys_len_ref);
int install(state_t* state);

// actual instructions

Expand All @@ -19,3 +24,4 @@ int do_run(int argc, char** argv);
int do_install(void);
int do_skeleton(int argc, char** argv);
int do_test(void);
int do_package(int argc, char** argv);
Loading