Workflow for C/C++ Packages
esy providing a workflow for native development couldn't skip supporting C/C++ packages as C/C++ libraries/tools are so widespread.
There are numerous examples of Reason/OCaml code using C/C++ code via FFI: bindings to libcurl, SDL, OpenGL and so on and so on.
The easiest way to expose some C/C++ code between esy packages is with pkg-config.
Exposing C library with pkg-config
Let's consider a C library called dep
which is going to be consumed as an
dep
esy package.
First we need to write dep.pc
file which contains pkg-config configuration
file for our lib.
We do this via Makefile
with build
and install
targets:
define dep_pc
prefix=$(cur__install)
exec_prefix=$${prefix}
includedir=$${prefix}/lib
libdir=$${exec_prefix}/lib
Name: dep
Description: The dep library
Version: 1.0.0
Cflags: -I$${includedir}
Libs: -L$${libdir} -ldep
endef
export dep_pc
build:
... build commands ...
install:
... other install commands ...
echo "$$dep_pc" > $(cur__install)/lib/dep.pc
Note that we have installed out dep.pc
into $cur__install/lib
, we need to
update $PKG_CONFIG_PATH
with this in "esy.exportedEnv"
:
{
"name": "dep",
"version": "1.0.0",
"esy": {
"build": "make build",
"install": "make install",
"exportedEnv": {
"PKG_CONFIG_PATH": {
"val": "#{self.lib : $PKG_CONFIG_PATH}",
"scope": "global"
}
}
}
}
In the package.json
above #{self.lib}
is the same value as
$cur__install/lib
but represented via esy's command expression syntax.
NOTE
If you are porting some C/C++ library into esy then it is most likely this library already has
*.pc
file present. Then you need only to make sure you update$PKG_CONFIG_PATH
in thepackage.json
corresponding to the library.
Consuming C library with pkg-config
To link a C library exposed with pkg-config
a project must depend on that
library and on pkg-config
package which is hosted on GitHub at
esy-packages/pkg-config.
The entire package.json
for the project would look like this:
{
"name": "my-project",
"esy": {
"build": "make build",
"install": "make install"
},
"dependencies": {
"dep": "1.0.0",
"pkg-config": "esy-packages/pkg-config"
}
}
The Makefile
will contain a call to pkg-config
to generate command line
options for C compiler needed to compile against a library dep
and link it to
the final executable:
build:
cc $(shell pkg-config --cflags --libs dep) \
-o $(cur__target_dir)/main main.c
buildsInSource: "unsafe"
C libraries that use the GNU make as their build system often "build
in source" - ie artifacts are created alongside the source
files. esy
's buildsInSource: true
configuration is aimed at building
such libraries for the esy store - not written with incremental
development in mind.
This is why, when using buildsInSource: true
with C libraries can
lead to long build times - as esy first copies it to build directory
and then generates the build artifacts alongside the source tree. This
is perfect for esy store, but slow for incremental development. This
is particularly noticed, when for instance, one tries to use esy
to
build Janestreet's [flambda-backend
][].
For incremental development, use buildsInSource: "unsafe"
.
In this mode, esy
creates artifacts in the source tree as the
project's build system expects, without the copy step. The
subsequent build command will see the previous run's artifacts and not
rebuild them unnecessarily. buildsInSource: true
would have deleted
the previous run and copied it again to build the package from scratch.
This is the entire workflow needed to work with C/C++ code with esy.