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.