Setting up your development environment

This tutorial will run through setting up DACE_jll.jl, DACE.jl and the DACE C++ library for development locally. It has been tested on Linux.

After running this tutorial, you should be able to make changes to the C++ code that defines the DACE Julia interface, compile those changes and test them via the DACE.jl Julia package locally.

This is based on the upstream binary builder documention for how to develop locally:

and also CxxWrap documentation:

Clone repos

Create and change to a new directory that we can clone the DACE code into. Then run the following:

  • DACE.jl Julia package
    git clone https://github.com/a-ev/DACE.jl.git
  • DACE C++ library (note specific branch required)
    git clone --branch julia-interface https://github.com/a-ev/dace.git

Set an environment variable with the path to the current directory, so we can refer back to it later:

export srcdir=$PWD

Setup DACE_jll.jl for development

Switch to the DACE.jl directory that you cloned above:

cd ${srcdir}/DACE.jl

Run julia --project to enter the Julia REPL and enter ] to enter the Pkg REPL mode, then

(DACE) pkg> instantiate
(DACE) pkg> develop DACE_jll

After running the above, press backspace to return to the Julia REPL and run

julia> using DACE_jll
julia> DACE_jll.dev_jll()

At the end of the above command it will print a location of the override directory, e.g.

...
[ Info: DACE_jll dev'ed out to /home/<username>/.julia/dev/DACE_jll with pre-populated override directory

The directory /home/<username>/.julia/dev/DACE_jll/override contains the dace shared libraries and headers in lib and includes directories. We can delete the contents of this directory and replace it with our own version that we build locally.

Don't just copy this command, make sure the path corresponds to the path in your output above.

rm -rf /home/<username>/.julia/dev/DACE_jll/override/*

Now store the path in an environment variable so that we can use it later (make sure you replace the path below with your path):

export dacejllpath=${HOME}/.julia/dev/DACE_jll/override

On Mac OS X only: build our own version of libcxxwrap-julia

Note

Skip this section and continue to the next step if you are not running on Mac OS X.

On Mac OS X it seems that currently you need to build your own version of libcxxwrap-julia in order for the remaining instructions to work correctly.

First set up an override directory where we can build our own version of libcxxwrap-julia. We should still be in the DACE.jl directory from above (cd ${srcdir}/DACE.jl). Run julia --project to enter the Julia REPL and enter ] to enter the Pkg REPL mode, then

pkg> develop libcxxwrap_julia_jll

At this point we should also make a note of the version of libcxxwrapjuliajll:

pkg> status libcxxwrap_julia_jll

The above command should output something like:

Project DACE v0.1.0
Status `/path/to/DACE.jl/Project.toml`
  [3eaa8342] libcxxwrap_julia_jll v0.13.2+0 `~/.julia/dev/libcxxwrap_julia_jll`

We can see that the version is v0.13.2 (ignore the "+" and anything after it).

Back in the Julia REPL, import the package and run dev_jll:

julia> import libcxxwrap_julia_jll
julia> libcxxwrap_julia_jll.dev_jll()

At the end of the above command it should print the path to the devved JLL, e.g. /home/<username>/.julia/dev/libcxxwrap_julia_jll. Inside that directory will be an override directory, which is where we will build our local version of libcxxwrap_julia. Make a note of the directory that was printed.

Make sure you are still in the directory where we cloned the other git repos above, clone the libcxxwrap_julia repository and checkout the tag that matches the version you found above:

cd ${srcdir}
git clone https://github.com/JuliaInterop/libcxxwrap-julia.git
cd libcxxwrap-julia
git checkout v0.13.2

Now we will change to the directory where libcxxwrapjuliajll was devved out to and build our own version:

cd /home/<username>/.julia/dev/libcxxwrap_julia_jll/override
rm -rf *
cmake -DJulia_EXECUTABLE=$(which julia) ${srcdir}/libcxxwrap-julia
cmake --build . --config Release

Build the DACE C++ library

Switch to the DACE.jl directory from above:

cd ${srcdir}/DACE.jl

Now find the CxxWrap prefix path by entering the Julia REPL (julia --project) and running

julia> import CxxWrap
julia> CxxWrap.prefix_path()

This should return a path like:

"/home/<username>/.julia/artifacts/fb412eee87eae845b84a799f0cabf241142406d7"

with a different ID at the end (although it may look like /Users/<username>/.julia/dev/libcxxwrap_julia_jll on Mac OS X). We will use this path in the CMake command later so let's store it in an environment variable (make sure you replace the path below with your path):

export prefixpath=${HOME}/.julia/artifacts/fb412eee87eae845b84a799f0cabf241142406d7

Now switch to the dace directory we cloned earlier (it should be alongside the DACE.jl directory we are currently working in):

cd ${srcdir}/dace

Make a build directory and switch to it:

mkdir build
cd build

Now run the cmake command to configure DACE:

cmake .. \
    -DCMAKE_INSTALL_PREFIX=${dacejllpath} \
    -DCMAKE_PREFIX_PATH=${prefixpath} \
    -DCMAKE_BUILD_TYPE=Release \
    -DWITH_PTHREAD=ON \
    -DWITH_ALGEBRAICMATRIX=ON \
    -DCMAKE_CXX_STANDARD=17 \
    -DWITH_JULIA=ON

and then build and install DACE with:

VERBOSE=ON cmake --build . --config Release --target install -- -j$(nproc)

Verify the DACE module is working

Switch back to the DACE.jl directory.

cd ${srcdir}/DACE.jl

Enter the Julia REPL with julia --project and run

julia> using DACE
julia> DACE.init(10, 1)
julia> ?DACE.DA

which should show some help about the DACE.DA type.

Make a change to the local DACE library

Now we will make a change to the local C++ source code and verify that the change is loaded in the Julia library.

Switch back to the dace/build directory

cd ${srcdir}/dace/build

Edit the interface file ../interfaces/julia/dace_julia.cxx using an editor such as vim or nano.

Locate the DACE.init function, it should be near the top and look like

mod.method("init", [](const unsigned int ord, const unsigned int nvar) {
        DA::init(ord, nvar);
    });

For reference,

  • mod.method adds a function to the Julia module
  • "init" is the name of the function that is being added
  • [](const unsigned int ord, const unsigned int nvar) denotes a C++ lambda function that runs when the function is called from Julia and takes two unsigned integer arguments
  • the lambda function body passes those two arguments to the DA::init routine, which is defined in the DACE C++ library

We will modify this method to add a print statement, such as

mod.method("init", [](const unsigned int ord, const unsigned int nvar) {
        std::cout << "initialising local version of DACE library..." << std::endl;
        DA::init(ord, nvar);
    });

so that we can tell the local version has been loaded. Make the above change then save the file.

We should be in the build directory still. Execute the following command to build and install your change:

VERBOSE=ON cmake --build . --config Release --target install -- -j$(nproc)

Now change back to the DACE.jl directory

cd ${srcdir}/DACE.jl

Once again, enter the Julia REPL with julia --project and run the DACE.init function we ran above:

julia> using DACE
julia> DACE.init(10, 1)

This time it should print out the string we just added (initialising local version of DACE library...).