diff options
Diffstat (limited to 'doc/tutorial/hello-world.org')
-rw-r--r-- | doc/tutorial/hello-world.org | 370 |
1 files changed, 0 insertions, 370 deletions
diff --git a/doc/tutorial/hello-world.org b/doc/tutorial/hello-world.org deleted file mode 100644 index 342eaf82..00000000 --- a/doc/tutorial/hello-world.org +++ /dev/null @@ -1,370 +0,0 @@ -* Building C++ Hello World - -/justbuild/ is a true language-agnostic (there are no more-equal languages) and -multi-repository build system. As a consequence, high-level concepts (e.g., C++ -binaries, C++ libraries, etc.) are not hardcoded built-ins of the tool, but -rather provided via a set of rules. These rules can be specified as a true -dependency to your project like any other external repository your project might -depend on. - -** Setting up the Multi-Repository Configuration - -To build a project with multi-repository dependencies, we first need to provide -a configuration that declares the required repositories. Before we begin, we -need to declare where the root of our workspace is located by creating an empty -file ~ROOT~: - -#+BEGIN_SRC sh -$ touch ROOT -#+END_SRC - -Second, we also need to create the multi-repository configuration ~repos.json~ -in the workspace root: - -#+SRCNAME: repos.json -#+BEGIN_SRC js -{ "main": "tutorial" -, "repositories": - { "rules-cc": - { "repository": - { "type": "git" - , "branch": "master" - , "commit": "123d8b03bf2440052626151c14c54abce2726e6f" - , "repository": "https://github.com/just-buildsystem/rules-cc.git" - , "subdir": "rules" - } - } - , "tutorial": - { "repository": {"type": "file", "path": "."} - , "bindings": {"rules": "rules-cc"} - } - } -} -#+END_SRC - -In that configuration, two repositories are defined: - - 1. The ~"rules-cc"~ repository located in the subdirectory ~rules~ of - [[https://github.com/just-buildsystem/rules-cc/tree/123d8b03bf2440052626151c14c54abce2726e6f][just-buildsystem/rules-cc:123d8b03bf2440052626151c14c54abce2726e6f]], - which contains the high-level concepts for building C/C++ binaries and - libraries. - - 2. The ~"tutorial"~ repository located at ~.~, which contains the targets that - we want to build. It has a single dependency, which is the /rules/ that are - needed to build the target. These rules are bound via the open name - ~"rules"~ to the just created repository ~"rules-cc"~. In this way, the - entities provided by ~"rules-cc"~ can be accessed from within the - ~"tutorial"~ repository via the fully-qualified name - ~["@", "rules", "<module>", "<name>"]~; fully-qualified - names (for rules, targets to build (like libraries, binaries), - etc) are given by a repository name, a path specifying a - directory within that repository (the "module") where the - specification file is located, and a symbolic name (i.e., an - arbitrary string that is used as key in the specification). - -The final repository configuration contains a single ~JSON~ object with the key -~"repositories"~ referring to an object of repository names as keys and -repository descriptions as values. For convenience, the main repository to pick -is set to ~"tutorial"~. - -** Description of the helloworld target - -For this tutorial, we want to create a target ~helloworld~ that produces a -binary from the C++ source ~main.cpp~. To define such a target, create a -~TARGETS~ file with the following content: - -#+SRCNAME: TARGETS -#+BEGIN_SRC js -{ "helloworld": - { "type": ["@", "rules", "CC", "binary"] - , "name": ["helloworld"] - , "srcs": ["main.cpp"] - } -} -#+END_SRC - -The ~"type"~ field refers to the rule ~"binary"~ from the module ~"CC"~ of the -~"rules"~ repository. This rule additionally requires the string field ~"name"~, -which specifies the name of the binary to produce; as the generic interface of -rules is to have fields either take a list of strings or a list of targets, -we have to specify the name as a list (this rule will simply concatenate all -strings given in this field). Furthermore, at least one -input to the binary is required, which can be specified via the target fields -~"srcs"~ or ~"deps"~. In our case, the former is used, which contains our single -source file (files are considered targets). - -Now, the last file that is missing is the actual source file ~main.cpp~: - -#+SRCNAME: main.cpp -#+BEGIN_SRC cpp -#include <iostream> - -int main() { - std::cout << "Hello world!\n"; - return 0; -} -#+END_SRC - -** Building the helloworld target - -To build the ~helloworld~ target, we need specify it on the ~just-mr~ command -line: - -#+BEGIN_SRC sh -$ just-mr build helloworld -INFO: Requested target is [["@","tutorial","","helloworld"],{}] -INFO: Analysed target [["@","tutorial","",helloworld"],{}] -INFO: Export targets found: 0 cached, 0 uncached, 0 not eligible for caching -INFO: Discovered 2 actions, 1 trees, 0 blobs -INFO: Building [["@","helloworld","","helloworld"],{}]. -INFO: Processed 2 actions, 0 cache hits. -INFO: Artifacts built, logical paths are: - helloworld [b5cfca8b810adc4686f5cac00258a137c5d4a3ba:17088:x] -$ -#+END_SRC - -Note that the target is taken from the ~tutorial~ repository, as it specified as -the main repository in ~repos.json~. If targets from other repositories should -be build, the repository to use must be specified via the ~--main~ option. - -~just-mr~ reads the repository configuration, fetches externals (if any), -generates the actual build configuration, and stores it in its cache directory -(by default under ~$HOME/.cache/just~). Afterwards, the generated configuration -is used to call the ~just~ binary, which performs the actual build. - -Note that these two programs, ~just-mr~ and ~just~, can also be run -individually. To do so, first run ~just-mr~ with ~setup~ and capture the path to -the generated build configuration from stdout by assigning it to a shell -variable (e.g., ~CONF~). Afterwards, ~just~ can be called to perform the actual -build by explicitly specifying the configuration file via ~-C~: - -#+BEGIN_SRC sh -$ CONF=$(just-mr setup tutorial) -$ just build -C $CONF helloworld -#+END_SRC - -Note that ~just-mr~ only needs to be run the very first time and only once again -whenever the ~repos.json~ file is modified. - -By default, the BSD-default compiler front-ends (which are also defined for most -Linux distributions) ~cc~ and ~c++~ are used for C and C++ (variables ~"CC"~ and -~"CXX"~). If you want to temporarily use different defaults, you can use ~-D~ to -provide a JSON object that sets different default variables. For instance, to -use Clang as C++ compiler for a single build invocation, you can use the -following command to provide an object that sets ~"CXX"~ to ~"clang++"~: - -#+BEGIN_SRC sh -$ just-mr build helloworld -D'{"CXX":"clang++"}' -INFO: Requested target is [["@","tutorial","","helloworld"],{"CXX":"clang++"}] -INFO: Analysed target [["@","tutorial","","helloworld"],{"CXX":"clang++"}] -INFO: Export targets found: 0 cached, 0 uncached, 0 not eligible for caching -INFO: Discovered 2 actions, 1 trees, 0 blobs -INFO: Building [["@","tutorial","","helloworld"],{"CXX":"clang++"}]. -INFO: Processed 2 actions, 0 cache hits. -INFO: Artifacts built, logical paths are: - helloworld [b8cf7b8579d9dc7172b61660139e2c14521cedae:16944:x] -$ -#+END_SRC - -** Defining project defaults - -To define a custom set of defaults (toolchain and compile flags) for your -project, you need to create a separate file root for providing required -~TARGETS~ file, which contains the ~"defaults"~ target that should be used by -the rules. This file root is then used as the /target root/ for the rules, i.e., -the search path for ~TARGETS~ files. In this way, the description of the -~"defaults"~ target is provided in a separate file root, to keep the rules -repository independent of these definitions. - -We will call the new file root ~tutorial-defaults~ and need to create a module -directory ~CC~ in it: - -#+BEGIN_SRC sh -$ mkdir -p ./tutorial-defaults/CC -#+END_SRC - -In that module, we need to create the file ~tutorial-defaults/CC/TARGETS~ that -contains the target ~"defaults"~ and specifies which toolchain and compile flags -to use; it has to specify the complete toolchain, but can specify a ~"base"~ -toolchain to inherit from. In our case, we don't use any base, but specify all -the required fields directly. - -#+SRCNAME: tutorial-defaults/CC/TARGETS -#+BEGIN_SRC js -{ "defaults": - { "type": ["CC", "defaults"] - , "CC": ["cc"] - , "CXX": ["c++"] - , "CFLAGS": ["-O2", "-Wall"] - , "CXXFLAGS": ["-O2", "-Wall"] - , "AR": ["ar"] - , "PATH": ["/bin", "/usr/bin"] - } -} -#+END_SRC - -To use the project defaults, modify the existing ~repos.json~ to reflect the -following content: - -#+SRCNAME: repos.json -#+BEGIN_SRC js -{ "main": "tutorial" -, "repositories": - { "rules-cc": - { "repository": - { "type": "git" - , "branch": "master" - , "commit": "123d8b03bf2440052626151c14c54abce2726e6f" - , "repository": "https://github.com/just-buildsystem/rules-cc.git" - , "subdir": "rules" - } - , "target_root": "tutorial-defaults" - , "rule_root": "rules-cc" - } - , "tutorial": - { "repository": {"type": "file", "path": "."} - , "bindings": {"rules": "rules-cc"} - } - , "tutorial-defaults": - { "repository": {"type": "file", "path": "./tutorial-defaults"} - } - } -} -#+END_SRC - -Note that the ~"defaults"~ target uses the rule ~["CC", "defaults"]~ without -specifying any external repository (e.g., ~["@", "rules", ...]~). This is -because ~"tutorial-defaults"~ is not a full-fledged repository but merely a file -root that is considered local to the ~"rules-cc"~ repository. In fact, the -~"rules-cc"~ repository cannot refer to any external repository as it does not -have any defined bindings. - -To rebuild the project, we need to rerun ~just-mr~ (note that due to -configuration changes, rerunning only ~just~ would not suffice): - -#+BEGIN_SRC sh -$ just-mr build helloworld -INFO: Requested target is [["@","tutorial","","helloworld"],{}] -INFO: Analysed target [["@","tutorial","","helloworld"],{}] -INFO: Export targets found: 0 cached, 0 uncached, 0 not eligible for caching -INFO: Discovered 2 actions, 1 trees, 0 blobs -INFO: Building [["@","tutorial","","helloworld"],{}]. -INFO: Processed 2 actions, 0 cache hits. -INFO: Artifacts built, logical paths are: - helloworld [487dc9e47b978877ed2f7d80b3395ce84b23be92:16992:x] -$ -#+END_SRC - -Note that the output binary may have changed due to different defaults. - -** Modeling target dependencies - -For demonstration purposes, we will separate the print statements into a static -library ~greet~, which will become a dependency to our binary. Therefore, we -create a new subdirectory ~greet~ with the files ~greet/greet.hpp~: - -#+SRCNAME: greet/greet.hpp -#+BEGIN_SRC cpp -#include <string> - -void greet(std::string const& s); -#+END_SRC - -and ~greet/greet.cpp~: - -#+SRCNAME: greet/greet.cpp -#+BEGIN_SRC cpp -#include "greet.hpp" -#include <iostream> - -void greet(std::string const& s) { - std::cout << "Hello " << s << "!\n"; -} -#+END_SRC - -These files can now be used to create a static library ~libgreet.a~. To do so, -we need to create the following target description in ~greet/TARGETS~: - -#+SRCNAME: greet/TARGETS -#+BEGIN_SRC js -{ "greet": - { "type": ["@", "rules", "CC", "library"] - , "name": ["greet"] - , "hdrs": ["greet.hpp"] - , "srcs": ["greet.cpp"] - , "stage": ["greet"] - } -} -#+END_SRC - -Similar to ~"binary"~, we have to provide a name and source file. Additionally, -a library has public headers defined via ~"hdrs"~ and an optional staging -directory ~"stage"~ (default value ~"."~). The staging directory specifies where -the consumer of this library can expect to find the library's artifacts. Note -that this does not need to reflect the location on the file system (i.e., a -full-qualified path like ~["com", "example", "utils", "greet"]~ could be used to -distinguish it from greeting libraries of other projects). The staging directory -does not only affect the main artifact ~libgreet.a~ but also it's /runfiles/, -a second set of artifacts, usually those a consumer needs to make proper use the -actual artifact; in the case of a library, the runfiles are its public headers. -Hence, the public header will be staged to ~"greet/greet.hpp"~. With that -knowledge, we can now perform the necessary modifications to ~main.cpp~: - -#+SRCNAME: main.cpp -#+BEGIN_SRC cpp -#include "greet/greet.hpp" - -int main() { - greet("Universe"); - return 0; -} -#+END_SRC - -The target ~"helloworld"~ will have a direct dependency to the target ~"greet"~ -of the module ~"greet"~ in the top-level ~TARGETS~ file: - -#+SRCNAME: TARGETS -#+BEGIN_SRC js -{ "helloworld": - { "type": ["@", "rules", "CC", "binary"] - , "name": ["helloworld"] - , "srcs": ["main.cpp"] - , "private-deps": [["greet", "greet"]] - } -} -#+END_SRC - -Note that there is no need to explicitly specify ~"greet"~'s public headers here -as the appropriate artifacts of dependencies are automatically added to the -inputs of compile and link actions. The new binary can be built with the same -command as before (no need to rerun ~just-mr~): - -#+BEGIN_SRC sh -$ just-mr build helloworld -INFO: Requested target is [["@","tutorial","","helloworld"],{}] -INFO: Analysed target [["@","tutorial","","helloworld"],{}] -INFO: Export targets found: 0 cached, 0 uncached, 0 not eligible for caching -INFO: Discovered 4 actions, 2 trees, 0 blobs -INFO: Building [["@","tutorial","","helloworld"],{}]. -INFO: Processed 4 actions, 0 cache hits. -INFO: Artifacts built, logical paths are: - helloworld [2b81e3177afc382452a2df9f294d3df90a9ccaf0:17664:x] -$ -#+END_SRC - -To only build the static library target ~"greet"~ from module ~"greet"~, run the -following command: - -#+BEGIN_SRC sh -$ just-mr build greet greet -INFO: Requested target is [["@","tutorial","greet","greet"],{}] -INFO: Analysed target [["@","tutorial","greet","greet"],{}] -INFO: Export targets found: 0 cached, 0 uncached, 0 not eligible for caching -INFO: Discovered 2 actions, 1 trees, 0 blobs -INFO: Building [["@","tutorial","greet","greet"],{}]. -INFO: Processed 2 actions, 2 cache hits. -INFO: Artifacts built, logical paths are: - greet/libgreet.a [83ed406e21f285337b0c9bd5011f56f656bba683:2992:f] - (1 runfiles omitted.) -$ -#+END_SRC |