summaryrefslogtreecommitdiff
path: root/doc/source/developing/strict-mode.rst
blob: a3ee2318397f54f3df188e7445dc6a27207c5d85 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251


.. _developing_strict_mode:

Strict mode
===========
In this section, we will cover the usage of :ref:`strict vs non-strict <user_config_strict_mode>`
build plans in conjunction with :ref:`workspaces <developing_workspaces>`, and how this
can help to improve your edit/compile/test cycles.

.. note::

   This example is distributed with BuildStream
   in the `doc/examples/strict-mode
   <https://gitlab.com/BuildStream/buildstream/tree/master/doc/examples/strict-mode>`_
   subdirectory.


Overview
--------
When working with BuildStream to create integrations, it is typical that you have a
lot of components to build, and you frequently need to modify a component
at various levels of the stack. When developing one or more applications, you might
want to open a workspace and fix a bug in an application, or you might need to
open a workspace on a low level shared library to fix the behavior of one or
more misbehaving applications.

By default, BuildStream will always choose to be deterministic in order to
produce the most correct build results as possible. As such, modifying a low
level library will result in rebuilding all of it's reverse dependencies, but
this can be very time consuming and inconvenient for your edit/compile/test
cycles.

This is when enabling :ref:`non-strict build plans <user_config_strict_mode>`
can be helpful.

To illustrate the facets of how this works, this example will present a project
consisting of an application which is linked both statically and dynamically
linked to a common library.


Project structure
-----------------
This project is mostly based on the :ref:`integration commands <tutorial_integration_commands>`
example, as such we will ignore large parts of this project and only focus
on the elements which are of specific interest.

To illustrate the relationship of these two applications and the library,
let's briefly take a look at the underlying Makefiles which are used in this
project, starting with the library and followed by both Makefiles used to
build the application.


``files/libhello/Makefile``
~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. literalinclude:: ../../examples/strict-mode/files/libhello/Makefile
   :language: Makefile


``files/hello/Makefile.dynamic``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. literalinclude:: ../../examples/strict-mode/files/hello/Makefile.dynamic
   :language: Makefile


``files/hello/Makefile.static``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. literalinclude:: ../../examples/strict-mode/files/hello/Makefile.static
   :language: Makefile

As we can see, we have a library that is distributed both as the dynamic
library ``libhello.so`` and also as the static archive ``libhello.a``.

Now let's take a look at the two separate elements which build the
application, first the dynamically linked version and then the static one.


``elements/hello-dynamic.bst``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. literalinclude:: ../../examples/strict-mode/elements/hello-dynamic.bst
   :language: yaml

Nothing very special to observe about this hello program, just a
:mod:`manual <elements.manual>` element quite similar to the one we've
already seen in the :ref:`running commands <tutorial_running_commands>`
example.


``elements/hello-static.bst``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. literalinclude:: ../../examples/strict-mode/elements/hello-static.bst
   :language: yaml

Almost the same as the dynamic element, except here we have declared the
dependency to the ``libhello.bst`` element differently: this time we have enabled
the ``strict`` option in the :ref:`dependency declaration <format_dependencies>`.

The side effect of setting this option is that ``hello-static.bst`` will be
rebuilt any time that ``libhello.bst`` has changed, even when
:ref:`non-strict build plans <user_config_strict_mode>` have been enabled.

.. tip::

   Some element plugins are designed to consume the content of their
   dependencies entirely, and output an artifact without any transient
   runtime dependencies, an example of this is the :mod:`compose <elements.compose>`
   element.

   In cases such as :mod:`compose <elements.compose>`, it is not necessary to
   explicitly annotate their dependencies as ``strict``.

   It is only helpful to set the ``strict`` attribute on a
   :ref:`dependency declaration <format_dependencies>` in the case that the
   specific dependency relationship causes data to be consumed verbatim,
   as is the case with static linking.


Using the project
-----------------
For the sake of brevity, let's assume that you've already built all of the
elements of this project, and that you want to make some changes to the
``libhello.bst`` element, and test how it might effect the hello program.


Everything is already built
~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. raw:: html
   :file: ../sessions/strict-mode-show-initial.html


Open a workspace and modify libhello.c
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Now let's open up a workspace on the hello library

.. raw:: html
   :file: ../sessions/strict-mode-workspace-open.html

And go ahead and make a modification like this:

.. literalinclude:: ../../examples/strict-mode/update.patch
    :language: diff


Observing ``hello-dynamic.bst``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Let's take a look at the :ref:`bst show <invoking_show>` output for
the dynamically linked ``hello-dynamic.bst`` element.

.. raw:: html
   :file: ../sessions/strict-mode-show-dynamic-strict.html

As one might expect, the ``libhello.bst`` element is ready to be built
after having been modified, and the ``hello-dynamic.bst`` element is
waiting for ``libhello.bst`` to be built before it can build.

Now let's take a look at the same elements if we pass the ``--no-strict``
option to ``bst``:

.. raw:: html
   :file: ../sessions/strict-mode-show-dynamic-no-strict.html

Note that this time, the ``libhello.bst`` still needs to be built,
but the ``hello-dymamic.bst`` element is showing up as ``cached``.

.. tip::

   The :ref:`bst show <invoking_show>` output will show some cache
   keys dimmed out in the case that they are not entirely deterministic.

   Here we can see that ``hello-dynamic.bst`` is dimmed out because
   it will not be rebuilt against the changed ``libhello.bst`` element,
   and it also has a different cache key because of this.


Observing ``hello-static.bst``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Now let's observe the ``hello-static.bst`` element with strict mode
disabled:

.. raw:: html
   :file: ../sessions/strict-mode-show-static-no-strict.html

Note that in this case the ``hello-strict.bst`` is going to be
rebuilt even in strict mode. This is because we annotated the
declaration of the ``libhello.bst`` dependency with the ``strict``
attribute.

We did this because ``hello-strict.bst`` consumes the input of
``libhello.bst`` verbatim, by way of statically linking to it, instead
of merely being affected by the content of ``libhello.bst`` at runtime,
as would be the case of static linking.


Building and running ``hello-dynamic.bst``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Now let's build ``hello-dynamic.bst`` with strict mode disabled.

.. raw:: html
   :file: ../sessions/strict-mode-build-dynamic-no-strict.html

Note that the :ref:`bst build <invoking_build>` command completed without
having to build ``hello-dynamic.bst`` at all.

And now we can also run ``hello-dynamic.bst``

.. raw:: html
   :file: ../sessions/strict-mode-run-dynamic-no-strict.html

When running ``hello-dynamic.bst`` with no-strict mode, we are
actually reusing the old build of ``hello-dynamic.bst`` staged against
the new build of the modified ``libhello.bst`` element.


Building and running ``hello-static.bst``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Finally, if we build ``hello-static.bst`` with strict mode disabled,
we can see that it will be rebuilt regardless of strict mode being
enabled.

.. raw:: html
   :file: ../sessions/strict-mode-build-static-no-strict.html

This is of course because we declared its dependency on ``libhello.bst``
as a ``strict`` dependency.

And by the same virtue, we can see that when we run the example
it has properly relinked against the changed static archive, and
has the updated text in the greeting:

.. raw:: html
   :file: ../sessions/strict-mode-run-static-no-strict.html


Summary
-------
In this chapter we've explored how to use :ref:`non-strict build plans <user_config_strict_mode>`
in order to avoid rebuilding reverse dependencies of a lower level
element you might be working with in a :ref:`workspace <invoking_workspace_open>`,
consequently improving your edit/compile/test experience.

We've also explained how to ensure your project still works properly
with non-strict build plans when some elements perform static linking
(or other operations which consume data from their dependencies
verbatim), by annotating :ref:`dependency declarations <format_dependencies>`
as ``strict``.