summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/README.md8
-rw-r--r--doc/number-guessing-bot/README.md154
-rw-r--r--doc/number-guessing-bot/ROOT0
-rw-r--r--doc/number-guessing-bot/TARGETS12
-rw-r--r--doc/number-guessing-bot/bot.cpp125
-rwxr-xr-xdoc/number-guessing-bot/etc/gen-repos.sh23
-rw-r--r--doc/number-guessing-bot/etc/repos.template.json8
-rwxr-xr-xdoc/number-guessing-bot/play_game.sh18
-rw-r--r--doc/number-guessing/Cargo.toml9
-rw-r--r--doc/number-guessing/README.md369
-rw-r--r--doc/number-guessing/src/main.rs35
11 files changed, 761 insertions, 0 deletions
diff --git a/doc/README.md b/doc/README.md
index aae3a1d..300e834 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -15,3 +15,11 @@ environment.
tutorials on how to mix Rust and C. On the one hand, a Rust library
is consumed by a C binary, on the other hand, a C library is
consumed by a Rust binary.
+
+- [Number guessing](./number-guessing/README.md), it showcases how to
+ *transition* a Rust-only Cargo-based project to `Justbuild` using
+ the script [`just-import-cargo.py`](../bin/just-import-cargo.py).
+
+- [Number guessing bot](./number-guessing-bot/README.md) demonstrates
+ how to *import* a Cargo-based repository in another project, written
+ in C++ in this example. \ No newline at end of file
diff --git a/doc/number-guessing-bot/README.md b/doc/number-guessing-bot/README.md
new file mode 100644
index 0000000..bc1ceed
--- /dev/null
+++ b/doc/number-guessing-bot/README.md
@@ -0,0 +1,154 @@
+# Transitioning an existing Cargo-based Rust project
+
+In this tutorial, we will write a simple c++ "bot" that will play the
+`guessing_game` of the [previous tutorial](../number-guessing/README.md). The
+focus is not the implementation but how we can automatically generate the
+required `repos.json` file.
+
+## Project structure
+
+```sh
+$ tree
+.
+├── bot.cpp
+├── etc
+│ ├── gen-repos.sh
+│ └── repos.template.json
+├── play_game.sh
+├── README.md
+├── ROOT
+└── TARGETS
+
+1 directory, 7 files
+```
+
+## The repository configuration file (`repos.json`)
+
+This project has two direct dependencies, namely, the `rules-cc` repository,
+which brings in the rules for C/C++ files, and the `guessing_game` presented in
+the [previous tutorial](../number-guessing/README.md).
+
+So, we start with a `repos.template.json` file that looks
+
+File: etc/repos.template.json
+```jsonc
+{ "main": "bot"
+, "repositories":
+ { "bot":
+ { "repository": {"type": "file", "path": "."}
+ , "bindings": {"rules-cc": "rules-cc", "guessing_game": "guessing_game"}
+ }
+ }
+}
+```
+
+The two required dependencies are listed in the `"bindings"` key, and left as
+open names. `"rules-cc"` can be retrieved using the script
+[`just-import-git.py`](https://github.com/just-buildsystem/justbuild/blob/master/bin/just-import-git.py),
+while `"guessing_game"` with
+[`just-import-cargo.py`](../../bin/just-import-cargo.py). In the following we
+assume that these two scripts are available in the `PATH` without the suffix
+`.py`. It is recommended to generate the final `repos.json` via a script, which,
+for this tutorial is provided in the file `etc/gen-repos.sh` and looks as
+follows
+
+```sh
+set -euo pipefail
+
+readonly ROOT=$(readlink -f $(dirname $0)/..)
+
+just-import-git -C ${ROOT}/etc/repos.template.json \
+ --as rules-cc -b master https://github.com/just-buildsystem/rules-cc rules \
+ | \
+ just-import-cargo --to-git --repo-root ${ROOT} ${ROOT}/../number-guessing > ${ROOT}/etc/repos.json
+```
+
+Once we run the `etc/gen-repos.sh` script, the directory tree should look
+
+```sh
+$ tree
+.
+├── bot.cpp
+├── etc
+│ ├── defaults
+│ │ └── rust
+│ │ └── TARGETS.cargo_import
+│ ├── deps-rust
+│ │ ├── TARGETS.byteorder-1.5.0
+│ │ ├── TARGETS.cfg-if-1.0.0
+│ │ ├── TARGETS.getrandom-0.2.15
+│ │ ├── TARGETS.libc-0.2.155
+│ │ ├── TARGETS.ppv-lite86-0.2.18
+│ │ ├── TARGETS.proc-macro2-1.0.86
+│ │ ├── TARGETS.quote-1.0.36
+│ │ ├── TARGETS.rand-0.8.5
+│ │ ├── TARGETS.rand_chacha-0.3.1
+│ │ ├── TARGETS.rand_core-0.6.4
+│ │ ├── TARGETS.syn-2.0.72
+│ │ ├── TARGETS.unicode-ident-1.0.12
+│ │ ├── TARGETS.zerocopy-0.6.6
+│ │ └── TARGETS.zerocopy-derive-0.6.6
+│ ├── gen-repos.sh
+│ ├── repos.json
+│ └── repos.template.json
+├── play_game.sh
+├── README.md
+├── ROOT
+└── TARGETS
+
+4 directories, 23 files
+```
+
+Since we used the option `--to-git` of the `just-import-cargo` script, we need
+to `git add` and `git commit` the `etc` folder before compiling.
+
+```sh
+$ git add etc
+$ git commit -m "Add repositories."
+```
+
+## Target descriptions
+
+In order to let our bot play the `guessing_game`, the `TARGETS` file can be as follows:
+
+File: TARGETS
+```jsonc
+{ "bot":
+ { "type": ["@", "rules-cc", "CC", "binary"]
+ , "name": ["bot"]
+ , "srcs": ["bot.cpp"]
+ }
+, "bot-test":
+ { "type": ["@", "rules-cc", "shell/test", "script"]
+ , "name": ["guessing_game"]
+ , "test": ["play_game.sh"]
+ , "deps": ["bot", ["@", "guessing_game", "", "guessing_game"]]
+ }
+}
+```
+
+## How to compile
+
+To build the test report of the `"bot-test"` target we can run
+
+```sh
+$ just-mr build bot-test
+INFO: Performing repositories setup
+INFO: Found 21 repositories to set up
+...
+INFO: Processed 34 actions, 0 cache hits.
+INFO: Artifacts built, logical paths are:
+ pwd [0a2bee0cbe8eed860dffa724e92d99c4be750731:137:f]
+ result [7ef22e9a431ad0272713b71fdc8794016c8ef12f:5:f]
+ stderr [e69de29bb2d1d6434b8b29ae775ad8c2e48c5391:0:f]
+ stdout [47f9933e607fe51c0f24b1d80d5621fd1b9b939c:9:f]
+ time-start [2fe8b0b1e52bf12f18ca667d0528492f7c68cb51:11:f]
+ time-stop [2fe8b0b1e52bf12f18ca667d0528492f7c68cb51:11:f]
+ (1 runfiles omitted.)
+INFO: Backing up artifacts of 12 export targets
+INFO: Target tainted ["test"].
+```
+
+Please refer to the "Let's build" section of the [getting-started
+tutorial](../../getting-started/README.md) for more details on how to
+find/select the `rustc` compiler.
diff --git a/doc/number-guessing-bot/ROOT b/doc/number-guessing-bot/ROOT
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/doc/number-guessing-bot/ROOT
diff --git a/doc/number-guessing-bot/TARGETS b/doc/number-guessing-bot/TARGETS
new file mode 100644
index 0000000..0b8d2ad
--- /dev/null
+++ b/doc/number-guessing-bot/TARGETS
@@ -0,0 +1,12 @@
+{ "bot":
+ { "type": ["@", "rules-cc", "CC", "binary"]
+ , "name": ["bot"]
+ , "srcs": ["bot.cpp"]
+ }
+, "bot-test":
+ { "type": ["@", "rules-cc", "shell/test", "script"]
+ , "name": ["guessing_game"]
+ , "test": ["play_game.sh"]
+ , "deps": ["bot", ["@", "guessing_game", "", "guessing_game"]]
+ }
+}
diff --git a/doc/number-guessing-bot/bot.cpp b/doc/number-guessing-bot/bot.cpp
new file mode 100644
index 0000000..f14e917
--- /dev/null
+++ b/doc/number-guessing-bot/bot.cpp
@@ -0,0 +1,125 @@
+// Copyright 2024 Huawei Cloud Computing Technology Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cstring>
+#include <iostream>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+void print_buffer(char *start, char *end) {
+ ++end;
+ while (start != end) {
+ std::cout << *start++;
+ }
+}
+
+bool ends_with(char *end, std::string const &token) {
+ auto it = token.rbegin();
+ while (it != token.rend()) {
+ if (*it++ != *end--) {
+ return false;
+ }
+ }
+ return true;
+}
+
+int main(int argc, char **argv) {
+ int pipefd_a[2]; // pipefd_a[0] reads from pipe
+ // pipefd_a[1] writes to pipe
+ int pipefd_b[2];
+ if (pipe(pipefd_a) == -1) {
+ perror("pipe error");
+ exit(EXIT_FAILURE);
+ }
+ if (pipe(pipefd_b) == -1) {
+ perror("pipe error");
+ exit(EXIT_FAILURE);
+ }
+
+ auto pid = fork();
+ if (pid == -1) {
+ perror("fork error");
+ exit(EXIT_FAILURE);
+ }
+
+ if (pid == 0) {
+ std::string const s{argv[1]};
+ std::string const token = s.substr(s.rfind('/') + 1);
+ std::cout << "playing: " << s << std::endl;
+
+ // child reads from pipefd_a[0];
+ // writes to pipefd_b[1];
+ dup2(pipefd_b[1], STDOUT_FILENO);
+ dup2(pipefd_a[0], STDIN_FILENO);
+
+ close(pipefd_a[1]); // close unused write end
+ close(pipefd_b[0]); // close unused read end
+ close(pipefd_b[1]);
+ close(pipefd_a[0]);
+ execl(s.c_str(), token.c_str());
+
+ _exit(0);
+ } else {
+ // master reads from pipefd_b[0]
+ // writes to pipefd_a[1];
+ close(pipefd_a[0]);
+ close(pipefd_b[1]);
+
+ bool run = true;
+ int a = 1;
+ int b = 101;
+ int trial = 50;
+
+ char buffer[1024];
+ char *p = buffer;
+ std::string s;
+ while (run) {
+ while (read(pipefd_b[0], p, 1)) {
+ if (*p == '\n') {
+ print_buffer(buffer, p);
+ p = buffer;
+ continue;
+ }
+ if (*p == '!') {
+ if (ends_with(p, "win!")) {
+ run = false;
+ print_buffer(buffer, p);
+ std::cout << std::endl;
+ break;
+ }
+ if (ends_with(p, "small!")) {
+ a = trial;
+ trial = (a + b) / 2;
+ }
+ if (ends_with(p, "big!")) {
+ b = trial;
+ trial = (a + b) / 2;
+ }
+ char trial_buf[80];
+ int len = sprintf(trial_buf, "%d\n", trial);
+ write(pipefd_a[1], trial_buf, len);
+ }
+ if (!run) {
+ break;
+ }
+ ++p;
+ }
+ }
+ close(pipefd_b[0]);
+ close(pipefd_a[1]);
+ int status;
+ waitpid(0, &status, 0);
+ }
+}
diff --git a/doc/number-guessing-bot/etc/gen-repos.sh b/doc/number-guessing-bot/etc/gen-repos.sh
new file mode 100755
index 0000000..6779f35
--- /dev/null
+++ b/doc/number-guessing-bot/etc/gen-repos.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+# Copyright 2024 Huawei Cloud Computing Technology Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -euo pipefail
+
+readonly ROOT=$(readlink -f $(dirname $0)/..)
+
+just-import-git -C ${ROOT}/etc/repos.template.json \
+ --as rules-cc -b master https://github.com/just-buildsystem/rules-cc rules \
+ | \
+ just-import-cargo --to-git --repo-root ${ROOT} ${ROOT}/../number-guessing > ${ROOT}/etc/repos.json
diff --git a/doc/number-guessing-bot/etc/repos.template.json b/doc/number-guessing-bot/etc/repos.template.json
new file mode 100644
index 0000000..017a992
--- /dev/null
+++ b/doc/number-guessing-bot/etc/repos.template.json
@@ -0,0 +1,8 @@
+{ "main": "bot"
+, "repositories":
+ { "bot":
+ { "repository": {"type": "file", "path": "."}
+ , "bindings": {"rules-cc": "rules-cc", "guessing_game": "guessing_game"}
+ }
+ }
+}
diff --git a/doc/number-guessing-bot/play_game.sh b/doc/number-guessing-bot/play_game.sh
new file mode 100755
index 0000000..ce7f465
--- /dev/null
+++ b/doc/number-guessing-bot/play_game.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+# Copyright 2024 Huawei Cloud Computing Technology Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e
+
+./bot guessing_game-0.1.0/guessing_game | grep "win!"
diff --git a/doc/number-guessing/Cargo.toml b/doc/number-guessing/Cargo.toml
new file mode 100644
index 0000000..7eda67a
--- /dev/null
+++ b/doc/number-guessing/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "guessing_game"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+rand = "0.8.5"
diff --git a/doc/number-guessing/README.md b/doc/number-guessing/README.md
new file mode 100644
index 0000000..0af4152
--- /dev/null
+++ b/doc/number-guessing/README.md
@@ -0,0 +1,369 @@
+# Transitioning an existing Cargo-based Rust project
+
+In this tutorial, we will transition to `justubuild` the [Guessing Game proposed
+in the Rust
+documentation](https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html),
+which consists of two files
+
+```sh
+$ tree
+.
+├── Cargo.toml
+└── src
+ └── main.rs
+
+1 directory, 2 files
+```
+
+The `Cargo.toml` file is as follows
+
+File: Cargo.toml
+```toml
+[package]
+name = "guessing_game"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+rand = "0.8.5"
+```
+
+The novelty in this tutorial is the presence of the external dependencies. In
+fact, the manifest, only the direct dependency `rand` is listed, while the
+computation and the retrieval of the required transitive dependencies are
+leveraged by `cargo`. If we run, for example, `cargo tree` we can see the tree
+of the dependencies.
+
+```sh
+$ cargo tree
+guessing_game v0.1.0 (/.../number_guessing)
+└── rand v0.8.5
+ ├── libc v0.2.155
+ ├── rand_chacha v0.3.1
+ │ ├── ppv-lite86 v0.2.18
+ │ │ └── zerocopy v0.6.6
+ │ │ ├── byteorder v1.5.0
+ │ │ └── zerocopy-derive v0.6.6 (proc-macro)
+ │ │ ├── proc-macro2 v1.0.86
+ │ │ │ └── unicode-ident v1.0.12
+ │ │ ├── quote v1.0.36
+ │ │ │ └── proc-macro2 v1.0.86 (*)
+ │ │ └── syn v2.0.72
+ │ │ ├── proc-macro2 v1.0.86 (*)
+ │ │ ├── quote v1.0.36 (*)
+ │ │ └── unicode-ident v1.0.12
+ │ └── rand_core v0.6.4
+ │ └── getrandom v0.2.15
+ │ ├── cfg-if v1.0.0
+ │ └── libc v0.2.155
+ └── rand_core v0.6.4 (*)
+```
+
+Note that the version number and or the actual list of dependency might change
+over time.
+
+To compile the `guessing_game` with `justbuild` all the listed dependencies must
+be transitioned as well. Of course, doing it by hand is error prone and time
+consuming. Therefore, in this repository, we provide the script
+[`just-import-cargo.py`](../../bin/just-import-cargo.py) that can automatize
+the transition of a Cargo-based project to `justbuild`. Let's see how to
+do it.
+
+## `just-import-cargo.py`
+
+Before showing *how* to use it, let's explain *why* to use it. `justbuild` is a
+generic build tool for multi-language projects. The `justbuild`
+equivalent of a `Cargo.lock` file is a repository configuration file named
+`repos.json`, which contains the list of all the repositories involved in a
+given project. The script `just-import-cargo.py` is meant to amend a given
+`repos.json` importing all the Rust repositories required for the Cargo project of
+concern.
+
+However, the same script can be used to transition a self contained Cargo-based
+Rust only project with a simple catch: let's start with a `repos.template.json`
+with no listed repositories. For convenience, let's create a directory `etc`
+where we store the files related to the external repositories
+
+```sh
+$ mkdir etc
+```
+
+```sh
+$ echo '{}' > etc/repos.template.json
+```
+
+The final `etc/repos.json` can be easily be obtained running the following command
+
+```sh
+$ cat etc/repos.template.json | /path/to/just-import-cargo.py . > etc/repos.json
+```
+
+The full documentation of the command line arguments supported by
+`just-import-cargo.py` can be found in the [man
+page](../../share/man/import-cargo.1.md).
+
+Before exploring the generated files and their content, let's signal
+that this directory is the root of a `justbuild` project.
+
+```sh
+$ touch ROOT
+```
+
+The directory should have now the following structure
+
+```sh
+$ tree
+.
+├── Cargo.lock
+├── Cargo.toml
+├── etc
+│ ├── defaults
+│ │ └── rust
+│ │ └── TARGETS.cargo_import
+│ ├── deps-rust
+│ │ ├── TARGETS.byteorder-1.5.0
+│ │ ├── TARGETS.cfg-if-1.0.0
+│ │ ├── TARGETS.getrandom-0.2.15
+│ │ ├── TARGETS.libc-0.2.155
+│ │ ├── TARGETS.ppv-lite86-0.2.18
+│ │ ├── TARGETS.proc-macro2-1.0.86
+│ │ ├── TARGETS.quote-1.0.36
+│ │ ├── TARGETS.rand-0.8.5
+│ │ ├── TARGETS.rand_chacha-0.3.1
+│ │ ├── TARGETS.rand_core-0.6.4
+│ │ ├── TARGETS.syn-2.0.72
+│ │ ├── TARGETS.unicode-ident-1.0.12
+│ │ ├── TARGETS.zerocopy-0.6.6
+│ │ └── TARGETS.zerocopy-derive-0.6.6
+│ └── repos.json
+├── README.md
+├── src
+│ └── main.rs
+└── TARGETS
+
+5 directories, 21 files
+```
+
+Please note that the tool calls `cargo` under the hood, and a `Cargo.lock` file
+is generated. If you want to update the Rust dependencies, run `cargo update`
+before invoking the script again.
+
+## The generated `repos.json`
+
+The generated `repos.json` has the following structure:
+
+File: etc/repos.json
+```json
+{
+ "main": "guessing_game",
+ "repositories": {
+ "guessing_game/byteorder-1.5.0": {},
+ "guessing_game/cfg-if-1.0.0": {},
+ "guessing_game/getrandom-0.2.15": {},
+ "guessing_game": {},
+ "guessing_game/libc-0.2.155": {},
+ "guessing_game/ppv-lite86-0.2.18": {},
+ "guessing_game/proc-macro2-1.0.86": {},
+ "guessing_game/quote-1.0.36": {},
+ "guessing_game/rand-0.8.5": {},
+ "guessing_game/rand_chacha-0.3.1": {},
+ "guessing_game/rand_core-0.6.4": {},
+ "guessing_game/syn-2.0.72": {},
+ "guessing_game/unicode-ident-1.0.12": {},
+ "guessing_game/zerocopy-0.6.6": {},
+ "guessing_game/zerocopy-derive-0.6.6": {},
+ "guessing_game/external-deps": {},
+ "guessing_game/rust-rules-root": {},
+ "guessing_game/rust-rules-defaults": {},
+ "guessing_game/rust-rules": {}
+ }
+}
+```
+
+Under the key `"repositories"` we can recognize `"guessing_game"`, which is the
+current project (the name is taken from the `Cargo.toml` manifest), and all the
+dependencies reported by `cargo tree`, along with the Rust rules, required by
+`justbuild`. Since the `repos.template.json` didn't contain a `"main"`
+repository, the tool automatically elected the current project as the `"main"`
+repository.
+
+If the project evolves and requires dependencies not managed by Cargo, they
+should be listed in the `etc/repos.template.json`, such that the
+`just-import-cargo.py` script can be run regularly (maybe just after `cargo
+update`) to keep the repositories up to date.
+
+More information on the
+format of the repository configuration file can be found
+[here](https://github.com/just-buildsystem/justbuild/blob/master/share/man/just-mr-repository-config.5.md).
+
+
+
+## Build descriptions of the external repositories in `etc/deps-rust`
+
+The the build descriptions for the external repositories are put in the
+`etc/deps-rust` directory. Ideally, the generated `TARGETS` files should not
+required to be amended, but the tool is at a relatively early stage, so any
+contributions is welcome :) If you find yourself in the need to patch the
+*generated* build descriptions, remember that the files are, well, generated and
+could be overwritten when the `just-import-cargo.py` script is run again.
+
+Let's have a look at one generated file, for example
+
+File: etc/deps-rust/TARGETS.rand_chacha-0.3.1
+```jsonc
+{ "rand_chacha":
+ { "type": "export"
+ , "target": "rand_chacha-internal"
+ , "flexible_config":
+ ["ARCH", "HOST_ARCH", "TARGET_ARCH", "ENV", "TOOLCHAIN_CONFIG"]
+ }
+, "rand_chacha-internal":
+ { "type": ["@", "rules", "rust", "library"]
+ , "name": ["rand_chacha"]
+ , "crate_root": ["src/lib.rs"]
+ , "srcs": ["src/chacha.rs", "src/guts.rs", "src/lib.rs"]
+ , "edition": ["2018"]
+ , "arguments_config":
+ ["ARCH", "HOST_ARCH", "TARGET_ARCH", "ENV", "TOOLCHAIN_CONFIG"]
+ , "deps":
+ [ ["@", "ppv-lite86", "", "ppv_lite86"]
+ , ["@", "rand_core", "", "rand_core"]
+ ]
+ , "cargo_features": ["std"]
+ , "stage": ["rand_chacha-0.3.1"]
+ , "version": ["0", "3", "1"]
+ , "pkg_name": ["rand_chacha"]
+ }
+, "default": {"type": ["@", "rules", "cargo", "feature"], "name": ["default"]}
+, "serde": {"type": ["@", "rules", "cargo", "feature"], "name": ["serde"]}
+, "serde1": {"type": ["@", "rules", "cargo", "feature"], "name": ["serde1"]}
+, "simd": {"type": ["@", "rules", "cargo", "feature"], "name": ["simd"]}
+, "std": {"type": ["@", "rules", "cargo", "feature"], "name": ["std"]}
+}
+```
+
+It is worth to highlight that the target named like the library of concern is of
+type `"export"`, meaning that it is eligible for high-level target caching,
+which allows to skip the analysis and traversal of entire subgraphs in the
+action graph. More details on the export targets and the target-level caching
+can be found in the [third-party software
+tutorial](https://github.com/just-buildsystem/justbuild/blob/master/doc/tutorial/third-party-software.md).
+
+## Project specific configuration
+
+The directory `etc/defaults` sets the configuration for this project, which by
+default, is the one given by these rules with the target
+["defaults"](../../rules/rust/TARGETS).
+
+## Build description of the current crate
+
+The build description of the current crate is dumped in the file `TARGETS` next
+to when the `Cargo.toml` manifest is.
+
+For this example, it looks
+
+File: TARGETS
+```jsonc
+{ "guessing_game":
+ { "type": ["@", "rules", "rust", "binary"]
+ , "name": ["guessing_game"]
+ , "crate_root": ["src/main.rs"]
+ , "srcs": ["src/main.rs"]
+ , "edition": ["2021"]
+ , "arguments_config":
+ ["ARCH", "HOST_ARCH", "TARGET_ARCH", "ENV", "TOOLCHAIN_CONFIG"]
+ , "deps": [["@", "rand", "", "rand"]]
+ , "cargo_features": []
+ , "stage": ["guessing_game-0.1.0"]
+ , "version": ["0", "1", "0"]
+ , "pkg_name": ["guessing_game"]
+ }
+}
+```
+
+## How to compile
+
+The compilation is done as usual
+
+```sh
+$ just-mr build
+INFO: Performing repositories setup
+INFO: Found 19 repositories to set up
+...
+INFO: Export targets found: 0 cached, 0 uncached, 12 not eligible for caching
+INFO: Discovered 32 actions, 0 trees, 0 blobs
+...
+INFO: Artifacts built, logical paths are:
+ guessing_game-0.1.0/guessing_game [d0133fc676de0044034622c74c44029e50f7a025:3820528:x]
+```
+
+Please refer to the "Let's build" section of the [getting-started
+tutorial](../../getting-started/README.md) for more details on how to
+find/select the `rustc` compiler.
+
+## Enabling target-level caching
+
+In order to enable the target-level caching, we need to mark as content fixed
+the imported repositories that are not thought so (e.g., the `etc/defaults`
+repository). The `just-import-git.py` script can do this if the flag `--to-git`
+(or `-g`) is given.
+
+```sh
+$ cat etc/repos.template.json | /path/to/just-import-cargo.py -g . > etc/repos.json
+```
+
+If we look at the `etc/repos.json` file we will see that a JSON object
+`"pragma":{"to_git":true}` is added to several repositories. For example,
+
+```jsonc
+...
+ , "guessing_game/rust-rules-defaults":
+ { "repository":
+ {"type": "file", "path": "etc/defaults", "pragma": {"to_git": true}}
+ }
+...
+```
+
+Now that `etc/defaults` and `etc/rust-deps` are marked as content fixed, we need
+to add them to the current git directory,
+
+```sh
+$ git add etc
+$ git commit -m "Add repositories."
+```
+
+If we now compile the project again we will see that the export targets that
+were not eligible for caching, now they will be uncached
+
+```sh
+$ just-mr build
+INFO: Performing repositories setup
+INFO: Found 19 repositories to set up
+...
+INFO: Export targets found: 0 cached, 12 uncached, 0 not eligible for caching
+INFO: Discovered 32 actions, 0 trees, 0 blobs
+...
+INFO: Artifacts built, logical paths are:
+ guessing_game-0.1.0/guessing_game [d0133fc676de0044034622c74c44029e50f7a025:3820528:x]
+INFO: Backing up artifacts of 12 export targets
+```
+
+And if we compile it again
+
+```sh
+$ just-mr build
+INFO: Performing repositories setup
+INFO: Found 19 repositories to set up
+...
+INFO: Export targets found: 1 cached, 0 uncached, 0 not eligible for caching
+INFO: Discovered 1 actions, 0 trees, 0 blobs
+...
+INFO: Processed 1 actions, 0 cache hits.
+INFO: Artifacts built, logical paths are:
+ guessing_game-0.1.0/guessing_game [d0133fc676de0044034622c74c44029e50f7a025:3820528:x]
+```
+
+To allow for reproducibility of the builds, we also highly recommend to include
+in the git tree the generated files (as `Cargo.lock` and `etc/repos.json`).
diff --git a/doc/number-guessing/src/main.rs b/doc/number-guessing/src/main.rs
new file mode 100644
index 0000000..7fcbb99
--- /dev/null
+++ b/doc/number-guessing/src/main.rs
@@ -0,0 +1,35 @@
+use rand::Rng;
+use std::cmp::Ordering;
+use std::io;
+
+fn main() {
+ println!("Guess the number!");
+
+ let secret_number = rand::thread_rng().gen_range(1..=100);
+
+ loop {
+ println!("Please input your guess.");
+
+ let mut guess = String::new();
+
+ io::stdin()
+ .read_line(&mut guess)
+ .expect("Failed to read line");
+
+ let guess: u32 = match guess.trim().parse() {
+ Ok(num) => num,
+ Err(_) => continue,
+ };
+
+ println!("You guessed: {guess}");
+
+ match guess.cmp(&secret_number) {
+ Ordering::Less => println!("Too small!"),
+ Ordering::Greater => println!("Too big!"),
+ Ordering::Equal => {
+ println!("You win!");
+ break;
+ }
+ }
+ }
+}