github.com/gotranspile/cxgo@v0.3.7/CONTRIBUTING.md (about) 1 # Contributing guideline 2 3 CxGo is an open-source project, so your contributions are always welcome! 4 5 Here is how you can help the project: 6 7 - Find a C file or project that `cxgo` fails to translate. Please file an issue with your config and link to the project. 8 - Or find a case when `cxgo` changes the program behavior after the transpilation. 9 We consider these bugs critical, and they definitely deserve an issue. 10 - Found a missing C signature in `cxgo` stdlib? Feel free to PR a fix! It's usually a [single line](#adding-c-symbol-definitions-to-the-library) change. 11 - `cxgo` might be missing a specific C stdlib implementation in our Go runtime. Again, feel free to PR an implementation. 12 Even the most naive implementation is better than nothing! 13 - If you found one of the edge cases and filed an issue, you may go one step further and narrow down that edge case to a 14 test for `cxgo`. Having a test makes fixing the bug a lot easier. 15 - We will also accept a larger feature contributions to `cxgo`. But before starting the work, please do [contact us](COMMUNITY.md) 16 We might give some helpful context about `cxgo` internals and help flush the design. 17 - When you successfully convert an open-source C project to Go, we'd like to [hear your feedback](COMMUNITY.md)! Also, consider adding 18 a link to your project to our [examples](examples/README.md) section. 19 20 If you have any questions, you can always [ask for help](COMMUNITY.md). 21 22 Also, make sure to check out [project goals](#project-goals-and-principles). 23 24 ## Running tests 25 26 Before submitting a contribution, make sure to run `cxgo` tests: 27 28 ```bash 29 go test ./... 30 ``` 31 32 This will only run a "fast" test suite. 33 34 When developing larger features you may also want to run TCC tests to make sure there are no regressions: 35 36 ```bash 37 CXGO_RUN_TESTS_TCC=true go test ./tcc_test.go 38 ``` 39 40 GCC tests are also available, but are too slow to check for each iteration. You can run them with: 41 42 ```bash 43 CXGO_RUN_TESTS_GCC=true go test ./gcc_test.go 44 ``` 45 46 ## Adding a new known header 47 48 `cxgo` bundles well-known headers to simplify the transpilation and provide the mapping to native Go libraries. 49 50 The support for the new header can be added incrementally: 51 52 1. Define an empty header. This helps avoid "not found" errors. Only useful to get one step further. 53 2. Define (a subset) of declarations to the header. This will help avoid "unexpected identifier" errors, but Go compilation 54 will still fail without an implementation. Might still be useful, since functions can still be defined manually. 55 3. Provide a mapping between C and Go. This will help `cxgo` automatically replace functions with Go equivalents. 56 57 Let's consider each step separately. 58 59 ### Adding a header stub 60 61 For example, let's define a new header called `mylib/xyz.h`. All known headers are defined in a separate files in the 62 [libs](./libs) package of `cxgo`. We can take one of the smaller files in that package as the reference (e.g. [assert](libs/assert.go)) 63 and add our own header in a new Go file (`mylib_xyz.go`): 64 65 ```go 66 package libs 67 68 const xyzH = "mylib/xyz.h" 69 70 func init() { 71 RegisterLibrary(xyzH, func(c *Env) *Library { 72 return &Library{} 73 }) 74 } 75 ``` 76 77 This is a minimal possible definition that just registers the known header without defining anything in it. 78 79 Library is registered with a full path, as used in `#include`. In our case the library can be used with `#include <mylib/xyz.h>`. 80 81 The second argument to `RegisterLibrary` is a constructor for a `Library` instance. The reason why it's needed because a 82 library might define different symbols depending on the environment (e.g. OS), architecture, or symbols defined in other 83 libraries. We will consider this in the following sections. 84 85 ### Adding C symbol definitions to the library 86 87 C header is controlled by `Library.Header` field. We can either define it as a constant string, or build it incrementally 88 depending on some environment variables. 89 90 ```go 91 RegisterLibrary(xyzH, func(env *Env) *Library { 92 l := &Library{ 93 Header: ` 94 #define MY_CONST 1 95 `, 96 } 97 l.Header += fmt.Sprintf("#define MY_PTR_SIZE %d\n", env.PtrSize()) 98 return l 99 }) 100 ``` 101 102 Note that you don't need to add header guards - `cxgo` takes care of that. 103 104 As the first step for supporting a new library, it makes sense to add only a few declarations that we care about. 105 At this stage aim for simplicity: add stub types where necessary, use builtin C types instead of copying the library 1:1. 106 107 For example, if your code fails to transpile when using function `foo` from `mylib/xyz.h`, then find the declaration in 108 the original header (or in online docs), simplify it as much as possible and add it to the `Header`. 109 110 ```go 111 RegisterLibrary(xyzH, func(env *Env) *Library { 112 l := &Library{ 113 Header: ` 114 void foo(void* ptr, int n); 115 `, 116 } 117 return l 118 }) 119 ``` 120 121 Eventually all necessary declarations will be added. We consider adding original headers in full a bad practice since 122 it will be necessary to rewrite it partially anyway to get a better Go mapping. It also allows dropping legacy declarations 123 or compatibility `#ifdef` conditions that might be unused in `cxgo`. 124 125 The following step will be to introduce Go mapping to the new library. 126 127 ### Mapping to Go 128 129 Mapping to Go allows to solve the following issues: 130 131 - Different names in C and Go 132 - Automatically importing Go package(s) for symbols 133 - Using native Go types 134 - Adding methods to struct types 135 136 We will explain how those issues are resolved in `cxgo` on the following examples. 137 138 #### Mapping functions and variables 139 140 Let's start by defining our function `foo` to map to the `Foo` function from a `github.com/example/mylib` package in Go. 141 We need two things for this: define the import map and the function identifier with corresponding type and names. 142 143 Import map is as simple as specifying a short and a long name for all Go packages imported by this library: 144 145 ```go 146 Imports: map[string]string{ 147 "mylib": "github.com/example/mylib", 148 }, 149 ``` 150 151 Defining a function is more interesting. We need to define the signature as expected by Go using builtin `cxgo` helpers: 152 153 ```go 154 Idents: map[string]*types.Ident{ 155 "foo": types.NewIdentGo("foo", "mylib.Foo", env.FuncTT(nil, env.PtrT(nil), env.C().Int())), 156 }, 157 ``` 158 159 Here we specify that the symbol `foo` as found in the include header will be mapped to an identifier that has a C name 160 `foo` and Go name `mylib.Foo`. We also specify an exact signature of a function: retuning no type (`nil`, mapped to `void`) 161 and accepting a `void*` and a C `int`. 162 163 To build those types we use our `Env` object that is passed to the constructor. It allows getting builtin types for C, Go, 164 as well as using types from other mapped C libraries. 165 166 If you are mapping one of the funtions defined in Go stdlib or `cxgo` runtime, it's strongly advised to use the following 167 helper instead: 168 169 ```go 170 Idents: map[string]*types.Ident{ 171 "foo": env.NewIdent("foo", "mylib.Foo", mylib.Foo, env.FuncTT(nil, env.PtrT(nil), env.C().Int())), 172 }, 173 ``` 174 175 Notice that it uses a reference to the real `mylib.Foo` function in the `cxgo` code. It allows the helper to verify that 176 the signature matches the actual Go function that you want to map. This, however, must not be used for external libraries 177 because it introduces a new dependency to `cxgo`. 178 179 Two approaches described above allow the maximal level of control: you can declare C header separately and the mapped 180 symbol separately. There is an easier way to achieve the same and to let `cxgo` define the C header for this function: 181 182 ```go 183 RegisterLibrary(xyzH, func(env *Env) *Library { 184 l := &Library{ 185 Header: ` 186 // no declarations here 187 `, 188 Imports: map[string]string{ 189 "mylib": "github.com/example/mylib", 190 }, 191 } 192 l.Declare( 193 types.NewIdentGo("foo", "mylib.Foo", env.FuncTT(nil, env.PtrT(nil), env.C().Int())), 194 ) 195 return l 196 }) 197 ``` 198 199 This way we can omit the declaration in the header and avoid repetition of the C symbol name. 200 201 ## Useful resources 202 203 - Project [architecture](docs/architecture.md) 204 205 _TODO: add useful resources related to C, stdlib, etc_ 206 207 ## Project goals and principles 208 209 ### 1. Provide a (practical) C-to-Go transpiler 210 211 This project aims to provide a generic C-to-Go conversion tool. Ideally, everything that can be compiled with a regular 212 C compiler should be accepted and translated by `cxgo`. It means we always aim to support real-world C code and add 213 any workarounds that might be required. However, no tool is perfect, so the output is provided on the best-effort basis. 214 215 Note that we [don't plan](https://github.com/gotranspile/cxgo/issues/1) to support C++** yet! We do believe that full C 216 support will eventually help us bootstrap C++ support (by converting GCC), but that requires a ton of work. 217 So do not file issues about C++ code, unless you are ready to bootstrap C++ support yourself :D 218 219 So the goal number one can be summarized as: be practical in converting C code to Go. Ideally without human intervention, 220 but even minimizing the intervention is a huge win for us. 221 222 ### 2. Keep the program code correct 223 224 Transpilers are also called "source-to-source compilers". Similar to how bugs in compiler may lead to unexpected 225 program behavior, the same is true for transpiler such as `cxgo`. In other words, if translation rules are wrong, 226 `cxgo` will not only corrupt the program code, but the resulting program may misbehave and damage anything it touches. 227 228 Thus, we take correctness very seriously. 229 230 This is goal number two: generate a correct code. Or at least dump something useful for a human, but which will definitely 231 fail to compile (see goal 1). 232 233 ### 3. Make the generated code human-readable and idiomatic 234 235 The result of the transpilation must be kept as human-readable as possible, and even improved to make it as close to 236 idiomatic Go as possible. This is with alignment with goal 1, but has less priority than correctness. If there is only 237 an ugly way to make things work correctly - we will use it. But if there is a chance to add a special case to `cxgo` that 238 will make code more readable without sacrificing correctness, we should pursue it. 239 240 This also has a less obvious consequence: `cxgo` won't pursue any of the virtualization technique such as VMs and other 241 approaches to abstract away the C runtime. This, of course, will render the code mostly unreadable. 242 243 ### 4. Make it easy to use and/or customize 244 245 This is the hard one, but the tool will try to rewrite the output code to look more idiomatic and Go-like as possible 246 out of the box. This has a significant effect on `cxgo` design: we would try to automate as much as possible, bundle 247 everything that could be bundled, avoid C dependencies as much as possible (they are usually hard to configure), etc. 248 249 But "easy to use" also means that we should allow users to quickly change the output according to their needs. 250 And allow more advanced users to extend `cxgo` to run source code analysis over C code. 251 252 ### 5. Make it fast (as the last goal) 253 254 Of course, we would like the `cxgo` to be fast, and what is more important that our Go runtime is fast. 255 256 But as mentioned above, we would prefer to generate a more human-readable code and let the user to fix bottlenecks by 257 rewriting parts of the code to pure Go, instead of providing output that is fast, but is impossible to modify. 258 259 As for `cxgo` itself, the transpilation is a batch process, instead of a realtime process, thus we might decide to 260 sacrifice translation performance if it will lead to better generated code.