Adding C to a package
Adding C to a package
Declaring the build
C sources are listed explicitly in the manifest, not discovered. The
convention is c/<subsystem>/.
[native]
sources = ["c/random/random.c"]
include = ["c/random"]
[native.flags]
all = ["-O2", "-fno-strict-aliasing"]
[native.link]
linux = ["-lpthread", "-lm"]
android = ["-llog"]flags and link are keyed by all or a platform name; every matching key
contributes. An unrecognized key is an error.
peko.h is always on the include path, so a header only needs #include <peko.h>.
A complete module
The smallest real example in the standard library is std::random. It has three
parts.
The C implementation, c/random/random.c, an ordinary source file.
The header, c/random/random.peko.h:
#include <peko.h>
PEKO_BEGIN
/* Fast non-cryptographic CMWC PRNG primitives. Scalars only cross the
boundary; the Rng handle is an unmanaged malloc pointer the caller owns
and frees. */
p_fn void peko_random_seed(p_i32 seed);
p_fn p_i32 peko_random_int(p_i32 min, p_i32 max);
p_fn p_f32 peko_random_float();
p_fn p_opaque peko_rng_new();
p_fn void peko_rng_free(p_opaque rng);
p_fn p_i32 peko_rng_int(p_opaque rng, p_i32 min, p_i32 max);
PEKO_ENDThe PekoScript half, random.peko:
import c::random::random as cmwc;
[public] fn next_int(min: number, max: number) => number {
let lo: i32 = danger_cast<i32>(min.to_raw())
let hi: i32 = danger_cast<i32>(max.to_raw())
let result: i32 = cmwc::peko_random_int(lo, hi)
return new number(danger_cast<f64>(result))
}
[public] class Rng {
[private] handle: opaque
constructor() {
this.handle = cmwc::peko_rng_new()
}
[public] fn free() {
cmwc::peko_rng_free(this.handle)
}
}That is the whole idiom: unbox with .to_raw() and danger_cast, call the raw
function, re-box the result, and hold C state as opaque with an explicit
free.
How it builds
Each source is compiled separately with the target's clang. C++ sources get the toolchain's C++ standard; C and Objective-C do not. Objective-C sources get the Objective-C flags as well.
Compilation is incremental by modification time, and there is no header
dependency tracking. Editing a .h does not rebuild the .c files that
include it. Touch the sources or run peko clean.
Object files are named with the package and version leading, so two packages can
each ship an alloc.c without colliding.
Link order
Toolchain flags first, then the package's [native.link] arguments, then the
objects: your compiled PekoScript, your native objects, your vendored archives,
and finally the toolchain's runtime objects. A package's archives come after that
package's own objects, so their members resolve the symbols those objects
reference.