aboutsummaryrefslogtreecommitdiff
path: root/doc/packaging.cli
blob: c1e79a957a57d93453881546c97c69dee5346313 (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
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
// file      : doc/packaging.cli
// license   : MIT; see accompanying LICENSE file

"\name=build2-packaging-guide"
"\subject=toolchain"
"\title=Packaging Guide"

// NOTES
//
// - Maximum <pre> line is 70 characters.
//

"
\h0#preface|Preface|

This document provides guidelines for converting third-party C/C++ projects to
the \c{build2} build system and making them available as packages from
\l{https://cppget.org cppget.org}, the \c{build2} community's central package
repository. For additional information, including documentation for individual
\c{build2} toolchain components, man pages, HOWTOs, etc., refer to the project
\l{https://build2.org/doc.xhtml Documentation} page.

\N|This document is a work in progress and is incomplete.|


\h1#intro|Introduction|

The aim of this guide is to ease the convertion of third-party C/C++ projects
to the \c{build2} build system and publishing them to the
\l{https://cppget.org cppget.org} package repository by codifying the best
practices and techniques. By following the presented guidelines you also make
it easier for others to review your work and help with ongoing maintenance.

The primary focus of this guide are existing C/C++ projects that use a
different build system and that are maintained by a third-party, which we will
refer to as \i{upstream}. Unless upstream is willing to incorporate support
for \c{build2} directly into their repository, such projects are normally
packaged for \c{build2} in a separate \c{git} repository under the
\l{https://github.com/build2-packaging github.com/build2-packaging}
organization. Note, however, that many of the presented guidelines are also
applicable when converting your own projects (that is, where you are the
upstream) as well as projects that use languages other than C or C++.

Most C/C++ packages that are published to \l{https://cppget.org cppget.org}
are either libraries or executables (projects that provide both are normally
split into several packages) with libraries being in the strong majority.
Libraries are also generally more difficult to build correctly. As a result,
this guide uses libraries as a baseline. In most cases, a library-specific
step is easily distinguished as such and can be skipped when dealing with
executables. And in cases where a more nuanced change is required, a note will
be provided.

At the high-level, packaging a third-party project involves the following
steps:

\ol|

\li|Create the \c{git} repository and import upstream source code.|

\li|Generate \c{buildfile} templates that match upstream layout.|

\li|Tweak the generated \c{buildfiles} to match upstream build.|

\li|Test using the \l{https://ci.cppget.org \c{build2} CI service}.|

\li|Publish the package to \l{https://cppget.org cppget.org}.|

|

Once this process is completed and the package is published, new releases
normally require a small amount of work provided there are no drastic changes
in the upstream layout or build. The sequence of steps for a new release would
typical look like this:

\ol|

\li|Add new and/or remove old upstream source code, if any.|

\li|Tweak \c{buildfiles} to match changes to upstream build, if any.|

\li|Test using the \l{https://ci.cppget.org \c{build2} CI service}.|

\li|Publish the package to \l{https://cppget.org cppget.org}.|

|

While packaging a simple library or executable is relatively straightforward,
the C and C++ languages and their ecosystem is famous for a large amount
varience in the platforms, compilers, and build systems used. This leads to
what appears to be an endless list of special considerations that are
applicable in certain, more complex cases.

As result, the presented guidelines are divided into four chapters: The
\l{#core Core Guidelines} cover steps that are applicable to all or most
packaging efforts. As mentioned earlier, these steps will assume packaging a
library but they should be easy to adapt to executables. This chapter is
followed by \l{#dont-do What Not to Do} which covers the common packaging
mistakes and omissions. These are unfortunately relatively common because
experience with other build systems often does not translate directly to
\c{build2} and some techniques (such as header-only libraries) are
discouraged. The last two chapters are \l{#howto HOWTO} and \l{#faq FAQ} which
cover the above-mentioned long list of special considerations that are only
applicable in certain cases as well as answer frequent packaging-related
questions, respectively.

Besides the presented guidelines you may also find the existing packages found
in \l{https://github.com/build2-packaging github.com/build2-packaging} a good
source of example material. The repositories pinned to the front page are the
recommended starting point.


\h1#core|Core Guidelines|

\h1#dont-do|What Not to Do|


\h#dont-from-scratch|Don't write \c{buildfile}s from scratch, use \c{bdep-new}|

Unless you have good reasons not to, create the initial project layout
automatically using \l{bdep-new(1)}, then tweak it if necessary and fill with
upstream source code.

The main rationale here is that there are many nuances in getting the build
right and auto-generated \c{buildfile}s had years of refinement and
fine-tuning. The familiar structure also makes it easier for others to
understand your build, for example while reviewing your package submission.

The \l{bdep-new(1)} command supports a wide variety of
\l{bdep-new.xhtml#src-layout source layouts}. While it may take a bit of time
to understand the customization points necessary to achieve the desired layout
for your first package, this will pay off in spades when you work on
converting subsequent packages. The recommended sequence of steps is
as follows:

\ol|

\li|Study the upstream source layout. We want to stay as close to upstream as
possible since this has the best chance of producing an issues-free result
(see \l{#dont-change-upstream Don't change upstream source code layout} for
details).|

\li|Craft and execute the \l{bdep-new(1)} command line necessary to achieve
the upstream layout.|

\li|Study the auto-generated \c{buildfile}s for things that don't fit and need
to change. But don't rush to start manually editing the result. First get an
overview of the required changes and then check if it's possible to achieve
these changes automatically using one of \l{bdep-new(1)} sub-options.

For example, if you see that the generated project assumes the wrong C++ file
extensions, these can be changed with \c{--lang|-l} sub-options.|

\li|Once you have squeezed as much as possible out of \l{bdep-new(1)}, it's
time for manual customizations. These would normally include:

\ul|

\li|Replace generated source code with upstream, normally as symlinks from the
    \c{upstream/} \c{git} submodule.|

\li|Tweak source subdirectory \c{buildfile} that builds the main target
    (library, executable).|

\li|Add tests and, if necessary, examples.|

\li|Tweak \c{manifest} (in particular the \c{version}, \c{summary}, and
    \c{license} values).|

\li|Fill in \c{README.md}.|||

|


\h#dont-change-upstream|Don't change upstream source code layout|

It's a good idea to stay as close to the upstream's source code layout as
possible. This has the best chance of giving us a build without any compile
errors since the header inclusion in the project can be sensitive to this
layout. This also makes it easier for upstream to adopt the \c{build2}
build.

Sometimes, however, there are good reasons for deviating from upstream,
especially in cases where upstream is clearly following bad practices, for
example installing generically-named headers without a library prefix. If you
do decide to change the layout, it's usually less disruptive (to the build) to
rearrange things at the outer levels than at the inner. For example, it should
normally be possible to move/rename the top-level \c{tests/} directory or to
place the library source directory into a subdirectory.


\h#dont-forget-update-manifest|Don't forget to update \c{manifest} values|

After \l{#dont-from-scratch generating the project template with \c{bdep-new}},
don't forget to update at least the key values in the generated \c{manifest}:
\l{#dont-forget-update-manifest-version \c{version}},
\l{#dont-forget-update-manifest-license \c{license}}, and
\l{#dont-forget-update-manifest-summary \c{summary}}.


\h2#dont-forget-update-manifest-version|Don't forget to update \c{manifest} value \c{version}|

For \c{version}, use the upstream version directly if it is semver (or
semver-like, that is, has three version components). Otherwise, see
\l{https://github.com/build2/HOWTO/blob/master/entries/handle-projects-which-dont-use-semver.md
How do I handle projects that don't use semantic versioning?} and
\l{https://github.com/build2/HOWTO/blob/master/entries/handle-projects-which-dont-use-version.md
How do I handle projects that don't use versions at all?}


\h2#dont-forget-update-manifest-license|Don't forget to update \c{manifest} value \c{license}|

For \c{license}, use the \l{https://spdx.org/licenses/ SPDX license ID} if at
all possible. If multiple licenses are involved, use the SPDX License
expression. See the
\l{https://build2.org/bpkg/doc/build2-package-manager-manual.xhtml#manifest-package-license
\c{license} manifest value} documentation for details and the list of the
most commonly used SPDX license IDs.


\h2#dont-forget-update-manifest-summary|Don't forget to update \c{manifest} value \c{summary}|

For \c{summary} use a brief description of the functionality provided by the
package. Less than 70 characters is a good target to aim for. Don't capitalize
subsequent words unless proper nouns and omit the trailing dot. For example:

\
summary: Vim xxd hexdump utility
\

Omit weasel words such as \"modern\", \"simple\", \"fast\", \"small\", etc.,
since they don't convey anything specific. Omit \"header-only\" or
\"single-header\" for C/C++ libraries since at least in the context of
\c{build2} it does not imply any advantage.

If upstream does not offer a sensible summary, the following template is
recommended for libraries:

\
summary: <functionality> C library
summary: <functionality> C++ library
\

For example:

\
summary: Event notification C library
summary: Validating XML parsing and serialization C++ library
\

If the project consists of multiple packages it may be tempting to name each
package in terms of the overall project name, for example:

\
summary: libigl's core module
\

This doesn't give the user any clue about what functionality is provided
unless they find out what \c{libigl} is about. Better:

\
summary: Geometry processing C++ library, core module
\

If you follow the above pattern, then to produce a summary for external tests
or examples packages simply add \"tests\" or \"examples\" at the end,
for example:

\
summary: Event notification C library tests
summary: Geometry processing C++ library, core module examples
\


\h#dont-header-only|Don't make a library header-only if it can be compiled|

Some libraries offer two alternative modes: header-only and compiled. Unless
there are good reasons not to, a \c{build2} build of such a library should use
the compiled mode.

\N|Some libraries use the \i{precompiled} term to describe the non-header-only
mode. We don't recommend using this term in the \c{build2} build since it has
a strong association with precompiled headers and can therefore be
confusing. Instead, use the \i{compiled} term.|

The main rationale here is that a library would not be offering a compiled
mode if there were no benefits (usually faster compile times of library
consumers) and there is no reason not to take advantage of it in the
\c{build2} build.

There are, however, reasons why a compiled mode cannot be used, the most
common of which are:

\ul|

\li|The compiled mode is not well maintained/tested by upstream and therefore
offers inferior user experience.|

\li|The compiled mode does not work on some platforms, usually Windows due to
the lack of symbol export support (but see \l{b##cc-auto-symexport Automatic
DLL Symbol Exporting}).|

\li|Uses of the compiled version of the library requires changes to the
library consumers, for example, inclusion of different headers.|

|

If a compiled mode cannot be always used, then it may be tempting to support
both modes potentially making the mode user-configurable. Unless there are
strong reasons to, you should resist this temptation and, if the compiled
mode is not universally usable, then use the header-only mode everywhere.

The main rationale here is that variability adds complexity which makes the
result more prone to bugs, more difficult to use, and harder to review and
maintain. If you really want to have the compiled mode, then the right
way to do it is to work with upstream to fix any issues that prevent its
use in \c{build2}.

There are, however, reasons why supporting both mode may be needed, the most
common of which are:

\ul|

\li|The library is widely used in both modes but switching from one mode to
the other requires changes to the library consumers (for example, inclusion of
different headers). In this case only supporting one mode would mean not
supporting a large number of library consumers.|

\li|The library consists of a large number of independent components and its
common for applications to only use a small subset of them. On the other hand,
compiling all of them in the compiled mode takes a substantial amount of time.
(Note that this can also be addressed by making the presence of optional
components user-configurable.)|

|


\h#dont-main-target-root-buildfile|Don't build your main targets in root \c{buldfile}|

It may be tempting to have your main targets (libraries, executables) in the
root \c{buildfile}, especially if it allows you to symlink entire directories
from \c{upstream/} (which is not possible if you have to have a \c{buildfile}
inside). However, this is a bad idea except for the simplest projects.

Firstly, this quickly gets messy since you have to combine managing
\c{README}s, \c{LICENSE}s, and subdirectories with you main target builds.
But, more importantly, this means that when you main target is imported (and
thus the \c{buildfile} that defines this target must be loaded), your entire
project will be loaded, including any \c{tests/} and \c{examples/} subproject,
which is wasteful.

Note also that it's easy to continue symlinking entire directories from
\c{upstream/} without moving everything to the root \c{buildfile} by simply
creating another subdirectory. Let's look at a concrete example. Here is
the directory structure where everything is in the root \c{buildfile}:

\
libigl-core/
├── igl/ -> upstream/igl/
├── tests/
└── buildfile                      # Defines lib{igl-core}.
\

And here is the alternative structure where we have added the \c{libigl-core}
subdirectory with its own \c{buildfile}:

\
libigl-core/
├── libigl-core/
│   ├── igl/ -> ../upstream/igl/
│   └── buildfile                 # Defines lib{igl-core}.
├── tests/
└── buildfile
\

Below is the \l{bdep-new(1)} invocation that can be used to automatically
create this alternative structure (see \l{bdep-new.xhtml#src-layout SOURCE
LAYOUT} for details):

\
$ bdep new -t lib,prefix=libigl-core,no-subdir,no-version libigl-core
\

\h1#howto|Packaging HOWTO|

\h1#faq|Packaging FAQ|

\h#faq-publish-stage|Where to publish if package requires staged toolchain?|

If your package requires the \l{https://build2.org/community.xhtml#stage staged
toolchain}, for example, because it needs a feature or bugfix that is not yet
available in the released toolchain, then you won't be able to publish it to
\c{cppget.org}. Specifically, if your package has the accurate \c{build2}
version constraint and you attempt to publish it, you will get an error like
this:

\
error: package archive is not valid
  info: unable to satisfy constraint (build2 >= 0.17.0-) for package foo
  info: available build2 version is 0.16.0
\

There are three alternative ways to proceed in this situation:

\ol|

\li|Wait until the next release and then publish the package to
\c{cppget.org}.|

\li|If the requirement for the staged toolchain is \"minor\", that is, it
doesn't affect the common functionality of the package or only affects a small
subset of platforms/compilers, then you can lower the toolchain version
requirement and publish the package to \c{cppget.org}. For example, if
you require the staged toolchain because of a bugfix that only affects
one platform, it doesn't make sense to delay publishing the package
since it is perfectly usable on all the platforms in the meantime.|

\li|Publish it to \l{https://queue.stage.build2.org queue.stage.build2.org},
the staging package repository. This repository contain new packages that
require the staged toolchain to work and which will be automatically
moved to \c{cppget.org} once the staged version is released. The other
advantage of publishing to this repository (besides not having to remember
to manually publish the package once the staged version is released) is
that your package becomes available from an archive repository (which is
substantially faster than a \c{git} repository).

To publish to this repository, use the following \c{bdep-publish} command
line:

\
$ bdep publish --repository=https://stage.build2.org ...
\

||


"