aboutsummaryrefslogtreecommitdiffhomepage
path: root/notes/versioning.txt
blob: d384b673d063830ed607681f9c7a55f317440b1c (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

Versioning of symbols of modules
================================

Versioning optimization
-----------------------

Currently, versioning is done per identifier:

 \verdef 1.0
 \verdef 1.1
 \verdef 1.2
 \verdef 2.0
 \verdef 1.2.1

 since 1.0
 func f()
 since 1.2
 func f(int x)
 since 1.1
 func g(int x) -> int
 since 1.0
 func h() -> int

The versions are duplicated
- First, they appear in the \verdef
- Second, they appear in the "since ..." lines
Could it be done more efficiently?


Versions are usually not longer than a pointer, so maybe not?
Also, there will usually only be one version per symbol.

Solution 1: Version index directly as lookup key (won't work?)
--------------------------------------------------------------

On the other hand, it could make sense to include a "version index"
field in the version, that can be compared correctly in an easy way:

 \verdef 1.0            # index 0
 \verdef 1.1            # index 1
 \verdef 1.2            # index 2
 \verdef 2.0            # index 3
 \verdef 1.2.1          # index 4

Parsing with "version indices":
- parse \verdef and build tree
- when parsing since... definitions, look up in \verdef tree

Solution 2: Version index in wrapper
------------------------------------

 versions_root -> tree of
    - version wrapper with:
        - version index
        - version status (normal, deleted, deprecated, ...?)
        - identdecl

Maybe something like this:

 #define VDT_NORMAL      0
 #define VDT_DELETED     1
 #define VDT_DEPRECATED  2
 #define VDT_BACKPORTED  3

 /**
  * Header for versioned declarations. Fields are only valid for declared
  * identifiers (must be exported), not for referenced external identifiers
  * (if we even should allow versioned external references...)
  */
 struct VersionedDeclHeader {
    unsigned int vertype : 3;
    unsigned int verindex : 29;
 };
 
 union VersionedDecl {
    struct VersionedDeclHeader hdr;
    struct {
        struct VersionedDeclHeader hdr;
        struct IdentDecl decl;
    } normal;
    struct {
        struct VersionedDeclHeader hdr;
        struct VersionedDecl *from_ver;
    } backport;
 };

After all \verdefs are parsed, then we can build a lookup table
from index to the version definition itself.

 unsigned int num_versions;
 struct VerDef **versions;

Non-sequential versions (e.g. maintenance branches)
---------------------------------------------------
Problem:

    0       1       2       3       4       5
    1.0     1.1     1.2     2.0     1.2.1   2.1

    4 >/= 3 (because it does not satisfy the 2.0 API)
    3 >/= 4 (because 4 (1.2.1) might contain improvements to the 1.x API)

Definitions of versions:
("a" and "b" are indices)

   version(b) > version(a)    <=>   b>a AND (ver_str(b) > ver_str(a))
   version(b) < version(a)    <=>   b<a AND (ver_str(b) < ver_str(a))
   version(b) == version(a)   <=>   b==a    <=>   ver_str(b) == ver_str(a)
   version(b) </> version(a)  for non-sequential versions

Use "previous indices":


index:      0       1       2       3       4       5       6       7
ver_str:    1.0     1.1     1.2     2.0     1.2.1   2.1     2.2     1.2.2
prev:       -       0       1       2       2       3       5       4

Version definitions
-------------------
Version definitions need at least this info:

 struct VerDef {
     struct TreeNode version;
     unsigned int verindex : 29;
     
 };


Avoiding too many versions
--------------------------
There should only be a \verdef when the API changes (exception: major
versions). This could be enforced.

It would be nice to be able to hide *fully* deleted versions.
Perhaps we should have a "\verdef ... clean" to not inherit old stuff
(including version history) from older versions.
That way, older definitions (AND old \verdefs) can be removed in the
following major version.

For example:
 # in version 0.x:
 \verdef 0.1 abc3242...
 \verdef 0.2 3df2424...
 # in version 1.x:
 \verdef 0.1 abc3242...
 \verdef 0.2 3df2424...
 \verdef 1.0 clean d327983...
 \verdef 1.1 5233532...
 # in version 2.x:
 # 0.x \verdef deleted!
 \verdef 1.0 clean d327983...
 \verdef 1.1 5233532...
 \verdef 2.0 clean 82fe819...

Old version could perhaps be provided through a wrapper/emulation library:

 # in version 2.x:
 \verdef 1.0 clean d327983...
 \verdef 1.1 5233532...
 \verdef 2.0 clean 82fe819...
 # in emulation library for version 0.x
 # (this should use the test suite for 0.x)
 \module somelib
 \type emulation
 \depends somelib 1.0
 \verdef 0.1 abc3242...
 \verdef 0.2 3df2424...
 
What should the maximum number of versions be? Perhaps 10 000?

SLUL ABI and emulation
----------------------
The ABI of SLUL itself might need to change at some point. In fact, we may
want to have multiple ABIs from the start (C compatible and "improved" ABI).

This could be done by adding an extra string of letters just before the
version.
On DLL and other non-symver platforms:
    somefunc__a_1_0_0    <-- ABI A
    somefunc__b_1_0_0    <-- ABI B
On SO-with-symver platforms:
    somefunc@SOMELIB__A_1_0_0   <-- ABI A
    somefunc@SOMELIB__B_1_0_0   <-- ABI B

Some ABIs may be able to co-exist in the same library (through wrapper calls
for the older one). With other ABI combations, that may not be possible (for
example, if the layout of data structures changes).

SLUL ABI changes should be invisible to, and not controlled by, the source
code being compiled. It should be a compiler option.


Keyword for version definitions
-------------------------------

 \api_ver (best?)
 \verdef
 \interface
 \interface_ver
 \interface_version

Adding versioning to symbols
----------------------------
It should be allowed to make unversioned symbols versioned.
In order to be API-safe, we need to require that:

 1. the added version is the oldest version
 2. the oldest version is not a "clean" version

=> The API hashes need to suffix the oldest version to any unversioned
   symbols, but only if the oldest version is not a "clean" version.

Undoing mistakes in APIs / Rolling back a version
-------------------------------------------------
Normally, SLUL does not allow symbols to be removed. However, it would
be nice to be able to completely undo changes in case of errors that are
detected quickly after release and/or errors that MUST be "removed from
history" (e.g. accidental inclusion of personal data or private tokens,
or copyright violations).

This could be done by marking a version as deleted, and not basing later
versions upon it.

For example:
    \slul 0.0.0
    ...
    \api_def 0.1.0 TWlzc2luZyBvbmUgc2luZ2xlIGJ5dGUx
    \api_def 1.0.0 deleted
    \api_def 1.0.1 VGVzdAABAgMwMTIzNDU2Nzg5YWJjZGVm

Only the last version/versions can be deleted, because a version cannot
be based on a deleted version.

Tightening constraints in versions
----------------------------------

Input constraints:
- These cannot be made stricter, but warnings could be added.
Output constraints:
- These could be made stricter (but must be versioned still)

Example syntax:

    since 1.0
    func do_stuff(int x) -> int
        cond x >= 0
        bestpractice x >= 1 since 1.1
        cond return < 100
        cond return < 90 since 1.2

Major versions in names?
------------------------

For modules that break the API in major versions (e.g. that strictly follow
semantic versioning), it could be useful to allow the major version to go
directly into the name somehow.

Basic solution with merging name and version:

    # in the module itself:
    \name something1
    \version 1.0.1

    # in dependencies:
    \depends something1.0.1

The downside is that it is easy to mistype. And it could also be confusing.

Another way could be to search for both "something" and "something1" when
searching for dependencies, and then check whether that module uses semantic
versioning (this could perhaps be indicated by an additional keyword after
the version, e.g. "\version 1.0.1 semver").
(And for semvar at 0.x, the compiler could implicitly add "unstable" as well).

Alpha/beta/RC versions
----------------------

Currently, SLUL uses the tilde character for this, e.g. "1.0~rc1" syntax.
That is also what Debian does.

But SemVer has added support for pre-releases as well, and there the
dash character is used for this purpose. (Even SemVer 1.0 has this).