-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathCONTRIBUTING
128 lines (100 loc) · 6.28 KB
/
CONTRIBUTING
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
Neopolitan Contributor’s Guide
Written by Alexander Nicholi
Copyright © 2019-2021 Aquefir
Released under BSD-2-Clause.
§1. PROGRAMMING PARADIGMS
--------------------------
1. A mixture of procedural programming and functional programming is applied.
Object-oriented programming is eliminated in project code, and handled
carefully with libraries using it.
2. This conceptual table illustrates the duality of the code, in ascending
order of abstraction:
+----------------------+---------------------------------------+
| Model level | Relationship |
+----------------------+---------------------------------------+
| Programmer’s [Lv. 1] | Code file (.c) <-> Header file (.h) |
| Engineer’s [Lv. 2] | Implementation <-> Interface |
| Architect’s [Lv. 3] | Computation <-> Coordination |
+----------------------+---------------------------------------+
§2. APPLICATION OF MODULARITY
------------------------------
1. Modules are in play with Neopolitan, using Brian Will’s distinction of
‘state modules’ and ‘logic modules’. This status is affixed onto every
interface (header file) when possible, for coordination.
2. Summary of Will’s video:
a. ‘state modules’: internally and externally stateful
b. ‘logic modules’: internally stateless (pure functions)
c. ‘data types’: conceptually separate from the state and logic dichotomy
d. ‘public interface’: what the module opts to expose to other modules
e. ‘private implementation’: converse of the public interface
3. The interface relationship model applies just as well in the absence of an
API. This project treats C code files and their associated header files as a
pair jointly comprising a module. Sometimes modules may lack a C code file.
Note that each C code file corresponds to one compilation unit (CU).
4. It should be noted that functions modifying state passed to them, even by
call parameters, count as stateful. The rationale for this is that basic
hygiene will easily lead to containment of global state into the call stack,
which is good for state management but does nothing to isolate and reduce the
amount of state given to functions; pure functions are a cornerstone of the
functional programming paradigm that keeps code easy to work with and reason
about.
5. To preserve the integrity of the logic-state module divide, an API-level
rule is in place using the const modifier. While primitives may be passed as
arguments as-is, all struct types (which are passed by pointer reference) must
have their contents declared const. Additionally, any pointer-type variables
within must also have their contents declared const, and so on; otherwise,
they may not be passed into logic modules. All pointer types (to arrays or
otherwise) must additionally have their contents (i.e. the object pointed to)
be declared const as well, even within structs.
§3. PULL REQUEST CHECKLIST
---------------------------
1. Read and follow ADP 1 within each subproject folder in this modular
monorepo.
2. Read ADP 4 to understand the build process.
3. Header guards should match the regular expression
/^INC(_API)?__([A-Z]+)_(([A-Z]+_)*[A-Z]+)_H$/, where $2 is the project
shortname in its all-caps form (in this case, ‘UNI’), $3 is the relative path
to the file, in all-caps without its extension, and $4 inside $3 contains the
directory parts of the path with the directory separator replaced with an
underscore. Directories in between the repository root and the source root are
not included in said directory parts (like ‘src/’, or a subdir for #include
conflict resolution). $1 is present to separate public API header guards from
private API ones. So, for example, the header guard for ‘src/scenes/title.h’
would be ‘INC__UNI_SCENES_TITLE_H’, and the header guard for
‘include/uni/scenes/title.h’ would be ‘INC_API__UNI_SCENES_TITLE_H’.
4. Header should hoist a comment boilerplate block with the project name and
its copyright notice. See {projectname}/etc/BOILERPLATE for copy-pastable
renditions of this.
5. Header should specify whether it exposes a state module, logic module, or
type module.
6. Both header and code files should #include all of the headers with symbols
they depend on, even if they happen to be #included in another header.
7. A code file with an associated header should #include it before all other
headers, using the local quotation mark style.
a. All other headers should be #included using the angle bracket style.
b. #include blocks (as described here) are separated by an empty line, for
the autoformatter’s sake.
c. C standard library headers should be #included in a block by themselves
after the associated header #include (if present).
d. All other headers should be #included in the last block.
e. All #include blocks must be alphabetised (use the autoformatter for this).
8. Ensure that every commit to ‘master’ successfully compiles. This is done by
running ‘make distclean’ and then ‘make format’ before a normal build. If it
is necessary to do extensive work where failing WIP commits need to be made,
create and push a new branch for that work and merge it when it’s done.
9. Always format code with ‘make format’ before committing.
10. Do clean builds with address sanitisation (command ‘make asan’) to resolve
memory errors. It is good practise to make sure that there are no segfaults
of any kind during runtime. While normally we use ASan as it has much milder
performance hits than other solutions, some memory bugs will be stubborn – for
these, recompile the usual debug target and run it through Valgrind.
11. Before trudging through ASan or Valgrind, it may be helpful to identify
bugs more proactively with a sanity check build, which is available under the
‘make check’ recipe command. Compilers will have wildly varying degrees of
leniency towards undefined behaviour – don’t count on vendor defaults to tell
you everything CC knows.
12. Every non-static function should be documented in the corresponding text
file inside the ‘doc/’ folder, with a top-level section (§) dedicated to it.
13. (IMPORTANT!) All contributions must be signed by their contributors using
OpenPGP. If the repository’s host provides an interface to associate OpenPGP
keys with their service-specific accounts, they must be associated as well.