CxGo is a tool for translating C source code to Go

C to Go translator

GitHub license Gitter GoDoc

CxGo is a tool for translating C source code to Go (aka transpiler, source-to-source compiler).

It uses cc for preprocessing and parsing C (no clang/gcc dependencies!) and a custom type-checker and AST translation layer to make the best output possible.

The only requirement is: C code must compile with cxgo, including headers.

Having said that, cxgo uses a few tricks to make this process easier.

TL;DR for the project goals:

  1. Implement a practical C to Go translator (no C++ for now).
  2. Keep the output program code correct.
  3. Make the generated code human-readable and as idiomatic as possible.
  4. Make it easy to use and customize.

Check the FAQ for more common question about the project.

Status

The project is experimental! Do not rely on it in production and other sensitive environments!

Although it can successfully transpile multiple projects, it might change the behavior of the transpiled code due to yet unknown bugs.

Compiler test results:

  • TCC: 62/89 (70%)
  • GCC: 783/1236 (63%)

Installation

go get -u github.com/gotranspile/cxgo/cmd/cxgo

How to use

The fastest way to try it is:

cxgo file main.c

For more details, check our examples section.

It will guide you through basic usage patterns as well as a more advanced ones (on real-world projects).

You may also check FAQ if you have any issues.

Caveats

The following C features are currently accepted by cxgo, but may be implemented partially or not implemented at all:

  • converting #define directives to Go constants (#3)
  • preserving comments from C code (#2)
  • static (#4)
  • auto (#5)
  • bitfields (#6)
  • union with C-identical data layout (#7)
  • packed structs (#8)
  • asm
  • case in weird places (#9)
  • goto forbidden by Go (there is a workaround, though, see #10)
  • label variables (#11)
  • thread local storage (#12)
  • setjmp (will compile, but panics at runtime)
  • some stdlib functions and types are missing (good first issue!)
  • deep type inference (when converting to Go string/slices)
  • considering multiple #ifdef paths for different OS/envs

Community

Join our community! We'd like to hear back from you!

Contributing

See CONTRIBUTING.

License

MIT

Comments
  • assigning string to [] of custom type fails

    assigning string to [] of custom type fails

    #include <stdint.h>
    int main() {
    	typedef uint8_t  MYubyte;
    	MYubyte vendor[] = "Robert Winkler";
    	return 0;
    }
    

    This code compiles under gcc but it doesn't in cxgo. I get this error: Error: parsing failed: test.c:4:21: initializer for an object that has aggregate or union type shall be a brace-enclosed list of initializers for the elements or named members: array of MYubyte

  • Convert defines to constants when possible

    Convert defines to constants when possible

    It's typical to have constants defined with #define.

    cxgo should recognize those patterns and declare the corresponding constant in Go.

    Unfortunately last time I checked, cc had no info for macro expansion. Need to double-check this and possibly contribute support, if it's not there yet.

  • update parser

    update parser

    This probably shouldn't be merged until #16 has a solution but this can be used for testing. This is an update by a minor version so it shouldn't break anything.

  • panic in cxgo/convert.go

    panic in cxgo/convert.go

    panic: types.ArrayType{elem:(*types.namedType)(0xc0000e2f30), size:1, slice:false}."name" (test.c:14:10)

    struct s {
    	const char *const name;		
    };
    
    static const struct s ss[] = {
    	{NULL  },	
    };
    
    
    int
    main(void) {
    
    	if(!ss->name)
    	 	return(-1);
    
     	return 0;
    }	
    
  • Panic during libc.Realloc

    Panic during libc.Realloc

    I noticed that my project was randomly crashing from time to time with a panic similar to the following:

    unexpected fault address 0xc00017ffe0
    fatal error: fault
    [signal 0xc0000005 code=0x0 addr=0xc00017ffe0 pc=0xc797b7]
    
    goroutine 1 [running]:
    runtime.throw({0xca46bd?, 0x0?})
            C:/Program Files/Go/src/runtime/panic.go:992 +0x76 fp=0xc000071eb0 sp=0xc000071e80 pc=0xc50d76
    runtime.sigpanic()
            C:/Program Files/Go/src/runtime/signal_windows.go:261 +0x125 fp=0xc000071ef8 sp=0xc000071eb0 pc=0xc620a5
    runtime.memmove()
            C:/Program Files/Go/src/runtime/memmove_amd64.s:512 +0x657 fp=0xc000071f00 sp=0xc000071ef8 pc=0xc797b7
    github.com/gotranspile/cxgo/runtime/libc.MemCpy(...)
            C:/Users/Crazyinfin8/go/pkg/mod/github.com/gotranspile/[email protected]/runtime/libc/string.go:120
    github.com/gotranspile/cxgo/runtime/libc.Realloc(0xc00000e1e8, 0x1312d00)
            C:/Users/Crazyinfin8/go/pkg/mod/github.com/gotranspile/[email protected]/runtime/libc/memory.go:45 +0xd4 fp=0xc000071f40 sp=0xc000071f00 pc=0xc8efb4
    main.main()
            C:/Users/Crazyinfin8/Desktop/mpg-go/realloc/main.go:10 +0x79 fp=0xc000071f80 sp=0xc000071f40 pc=0xc8f239
    runtime.main()
            C:/Program Files/Go/src/runtime/proc.go:250 +0x1fe fp=0xc000071fe0 sp=0xc000071f80 pc=0xc5337e
    runtime.goexit()
            C:/Program Files/Go/src/runtime/asm_amd64.s:1571 +0x1 fp=0xc000071fe8 sp=0xc000071fe0 pc=0xc78341
    exit status 2
    

    Doing some investigation, I managed to pinpoint the issue was with the function Realloc from the package github.com/gotranspile/cxgo/runtime/libc. In doing so, I created a small case that appears to reliably reproduce this panic on my Windows machine though for some odd reason, this does not create any panics on Windows Subsystem for Linux.

    package main
    
    import "github.com/gotranspile/cxgo/runtime/libc"
    
    func main() {
    	ptr := libc.Malloc(1)
    
    	println("Original Pointer is: ", ptr)
    
    	newPtr := libc.Realloc(ptr,20_000_000) // This panics sometimes
    
    	if newPtr == nil {
    		println("newPtr is nil")
    	} else {
    		println("New Pointer is: ", newPtr)
    	}
    }
    

    I suspect the cause of this issue is that during the call to Realloc, it creates a new, bigger buffer. Then Realloc calls MemCpybut it converts both the original buffer and the new buffer as slices that are the size of the bigger buffer. This ends up making the call to Go's internal copy function try to read more bytes than the original buffer is supposed to have access to, leading to a potential panic.

  • Example of using cxgo to transpile physac 2d physics engine

    Example of using cxgo to transpile physac 2d physics engine

    Hi. I've found cxgo today and I think it's really exciting!

    I decided to try to find some small project in C which might be interesting to be transpiled to go and this experiment was really successful. The project I found was Physac. It's a small 2d physics engine written in pure C with just a few deps.

    I created a repo with end results of my try: https://github.com/koteyur/physac-go In the first commit output is almost what I get after running cxgo. To make it usable I've just replaced time functions from libc package, because some of them wasn't implemented. After that it started working just fine.

    TLDR; I think, this example of completely working program after using cxgo might be interesting.

    Thanks!

  • block statement inside if gets removed

    block statement inside if gets removed

    Using a define that contains a block scope inside of an if statement removes the brackets. Given:

    #define send_bits(s) \
    { int t = s;\
    }
    
    void main() {
      int s;
      if (0) {
        send_bits(s)
        send_bits(s)
        send_bits(s)
      }
    }
    

    The generated code:

    package main
    
    func main() {
    	var s int
    	if false {
    		var t int = s
    		_ = t
    		var t int = s
    		_ = t
    		var t int = s
    		_ = t
    	}
    }
    

    I would expect there to be an inner bracket for each time the define is used. This might be an issue with the preprocessor but I didn't know if CXGO does some "optimization" that would remove these brackets unintentionally. If there isn't an if statement the brackets are generated.

  • unsafe.Add for arrays

    unsafe.Add for arrays

    Do we want to start using unsafe.Add for arrays? It will make the code shorter and potentially easier to read. It would, however, require that we push the version of the compiler to 1.17 and all resulting code would use that version too. Example:

    var Ptr *int
    Ptr = (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(Ptr)) + uintptr(Len)))
    

    Now becomes:

    var Ptr *int
    Ptr = (*int)(unsafe.Add(unsafe.Pointer(Ptr)), Len)
    
  • RAND_MAX missing

    RAND_MAX missing

    One of the c projects I am trying to convert uses RAND_MAX found in stdlib.h. I added it to the header in libs/stdlib.go and a go constant in runtime/libc/rand.go. I wondered if there is a way to make the header define equal to the go constant? This way future changes don't require two places to be updated.

  • panic: interface conversion: types.Type is types.IntType, not *types.StructType

    panic: interface conversion: types.Type is types.IntType, not *types.StructType

    I tried to transpile some C source from this project.

    Getting this panic:

    /Users/prologic/tmp/genext2fs # cxgo file genext2fs.c
    panic: interface conversion: types.Type is types.IntType, not *types.StructType
    
    goroutine 1 [running]:
    github.com/gotranspile/cxgo.(*translator).convertStructType.func1()
    	/go/pkg/mod/github.com/gotranspile/[email protected]/c_type.go:357 +0x6bd
    github.com/gotranspile/cxgo.(*translator).newOrFindNamedType(0xc001932840, {0xc0000b4558, 0x8}, 0xc000fbfd98)
    	/go/pkg/mod/github.com/gotranspile/[email protected]/c_type.go:149 +0x8e
    github.com/gotranspile/cxgo.(*translator).convertStructType(0xc001932840, {{0x0, 0x0}, 0x0, {0x0, 0x0}, 0x0, {0x0, 0x0}, 0x0, ...}, ...)
    	/go/pkg/mod/github.com/gotranspile/[email protected]/c_type.go:389 +0x275
    github.com/gotranspile/cxgo.(*translator).newTypeCC(0xc001932840, {{0x0, 0x0}, 0x0, {0x0, 0x0}, 0x0, {0x0, 0x0}, 0x0, ...}, ...)
    	/go/pkg/mod/github.com/gotranspile/[email protected]/c_type.go:241 +0x618
    github.com/gotranspile/cxgo.(*translator).convertType(0xc001932840, {{0x0, 0x0}, 0x0, {0x0, 0x0}, 0x0, {0x0, 0x0}, 0x0, ...}, ...)
    	/go/pkg/mod/github.com/gotranspile/[email protected]/c_type.go:59 +0x3c5
    github.com/gotranspile/cxgo.(*translator).convertDecl(0xc001932840, 0xc000198640)
    	/go/pkg/mod/github.com/gotranspile/[email protected]/convert.go:391 +0x24f8
    github.com/gotranspile/cxgo.(*translator).translateC(0xc001932840, {0x7ffcd8a76f0c?, 0xc0000bcc60?}, 0xc0017cf080)
    	/go/pkg/mod/github.com/gotranspile/[email protected]/translate.go:320 +0x133
    github.com/gotranspile/cxgo.(*translator).translate(0xc001932840, {0x7ffcd8a76f0c, 0xb}, 0x4?)
    	/go/pkg/mod/github.com/gotranspile/[email protected]/translate.go:267 +0x4b
    github.com/gotranspile/cxgo.TranslateAST({0x7ffcd8a76f0c, _}, _, _, {{0x0, 0x0}, {0x876f27, 0x4}, {0xc0000b4810, 0xc}, ...})
    	/go/pkg/mod/github.com/gotranspile/[email protected]/translate.go:163 +0x98
    github.com/gotranspile/cxgo.Translate({0x0, _}, {_, _}, {_, _}, _, {{0x0, 0x0}, {0x876f27, ...}, ...})
    	/go/pkg/mod/github.com/gotranspile/[email protected]/translate.go:77 +0x233
    main.init.0.func1(0xc0000b6780?, {0xc000098870?, 0x1?, 0x1?})
    	/go/pkg/mod/github.com/gotranspile/[email protected]/cmd/cxgo/file.go:40 +0x19a
    github.com/spf13/cobra.(*Command).execute(0xc0000b6780, {0xc000098840, 0x1, 0x1})
    	/go/pkg/mod/github.com/spf13/[email protected]/command.go:826 +0x67c
    github.com/spf13/cobra.(*Command).ExecuteC(0xb765c0)
    	/go/pkg/mod/github.com/spf13/[email protected]/command.go:914 +0x2ee
    github.com/spf13/cobra.(*Command).Execute(...)
    	/go/pkg/mod/github.com/spf13/[email protected]/command.go:864
    main.main()
    	/go/pkg/mod/github.com/gotranspile/[email protected]/cmd/cxgo/main.go:63 +0x25
    /Users/prologic/tmp/genext2fs #
    

    To reproduce:

    $ git clone https://github.com/bestouff/genext2fs.git
    $ docker run -i -t --rm -v $PWD:$PWD -w $PWD golang:alpine /bin/sh
    # apk add musl-dev
    # ln -s /usr/include .
    # go install github.com/gotranspile/cxgo/cmd/[email protected]
    # cxgo file genext2fs.c
    
  • Add newline after CHAR_BIT definition in limits.h

    Add newline after CHAR_BIT definition in limits.h

    I noticed that some code I was trying to tranpile was complaining about CHAR_BITS not being defined after including <limits.h>. The cause was a missing new-line after CHAR_BIT was written to the limits buffer.

  • fail with cygwin

    fail with cygwin

    cxgo 2022/02/17 07:31:29 clonning https://github.com/skyrpex/potrace.git to C:\cygwin 64\tmp\potrace Cloning into 'C:\cygwin64\tmp\potrace'... remote: Enumerating objects: 151, done. remote: Counting objects: 100% (151/151), done. remote: Compressing objects: 100% (123/123), done. remote: Total 151 (delta 28), reused 69 (delta 26), pack-reused 0 Receiving objects: 100% (151/151), 648.91 KiB | 1.03 MiB/s, done. Resolving deltas: 100% (28/28), done. Note: switching to 'd32fc5acc2b53b14d0b682cee8c641320fb1ef44'.

    You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by switching back to a branch.

    If you want to create a new branch to retain commits you create, you may do so (now or later) by using -c with the switch command. Example:

    git switch -c

    Or undo this operation with:

    git switch -

    Turn off this advice by setting config variable advice.detachedHead to false

    2022/02/17 07:31:31 writing to D:\code\cxgo-main.examples\potrace-go 2022/02/17 07:31:31 auxiliary.h Error: auxiliary.h: parsing failed: C:\cygwin64\tmp\potrace\src\auxiliary.h:15:1 : include file not found: stdlib.h (wd D:\code\cxgo-main\examples\potrace) search paths: C:\cygwin64\tmp\potrace\src\includes C:\cygwin64\tmp\potrace\src\include /_cxgo_overrides cxgo_predef.h:2:1: include file not found: cxgo_builtin.h (wd D:\code\cxgo-main
    examples\potrace) search paths: C:\cygwin64\tmp\potrace\src\includes C:\cygwin64\tmp\potrace\src\include /_cxgo_overrides Usage: cxgo [flags] cxgo [command]

    Available Commands: completion Generate the autocompletion script for the specified shell file transpile a single C file to Go help Help about any command version print cxgo version

    Flags: -c, --config string config file path (default "cxgo.yml") -h, --help help for cxgo

    Use "cxgo [command] --help" for more information about a command.

    auxiliary.h: parsing failed: C:\cygwin64\tmp\potrace\src\auxiliary.h:15:1: inclu de file not found: stdlib.h (wd D:\code\cxgo-main\examples\potrace) search paths: C:\cygwin64\tmp\potrace\src\includes C:\cygwin64\tmp\potrace\src\include /_cxgo_overrides cxgo_predef.h:2:1: include file not found: cxgo_builtin.h (wd D:\code\cxgo-main
    examples\potrace) search paths: C:\cygwin64\tmp\potrace\src\includes C:\cygwin64\tmp\potrace\src\include /_cxgo_overrides

  • inner struct doesn't generate

    inner struct doesn't generate

    Given the following C code:

    struct hello {
      struct inside {
        int x;
      } stuff;
    };
    

    I get this Go code:

    package main
    
    type hello struct {
    	Stuff inside
    }
    

    It doesn't generate the inside struct.

  • Handle struct vs function name collisions

    Handle struct vs function name collisions

    In C it's valid to have struct foo defined alongside with void foo(). When using cxgo this causes name collision.

    We should detect this case and append _t to a converted Go type name automatically.

  • Support typed nil values

    Support typed nil values

    Currently cxgo fails on Nuklear due to the fact that we erase types for nil values:

    typedef int ptrdiff_t;
    #define NK_ALIGNOF(t) ((char*)(&((struct {char c; t _h;}*)0)->_h) - (char*)0)
    int a = NK_ALIGNOF(int);
    
  • Compile fails for Nuklear

    Compile fails for Nuklear

    $ cat nuklear.c
    #define NK_IMPLEMENTATION
    #define NK_INCLUDE_FIXED_TYPES
    #define NK_INCLUDE_STANDARD_IO
    #define NK_INCLUDE_DEFAULT_ALLOCATOR
    #define NK_INCLUDE_FONT_BAKING
    #define NK_INCLUDE_DEFAULT_FONT
    #define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
    
    #include "nuklear.h"
    

    nuklear.h is from https://github.com/Immediate-Mode-UI/Nuklear/blob/master/nuklear.h

    Output of cxgo file nuklear.c

    Error: parsing failed: nuklear.h:428:1: front-end: undefined: size_t
    nuklear.h:429:1: front-end: undefined: size_t
    nuklear.h:430:1: front-end: undefined: size_t
    nuklear.h:431:1: front-end: undefined: size_t
    nuklear.h:432:1: front-end: undefined: size_t
    
Open source re-implementation of the original Resident Evil 2 / Biohazard 2
Open source re-implementation of the original Resident Evil 2 / Biohazard 2

OpenBiohazard2 Open source re-implementation of the original Resident Evil 2 engine written in Go and OpenGL. You must own a copy of the original game

Jan 3, 2023
Weave Ignite is an open source Virtual Machine (VM) manager with a container UX and built-in GitOps management.
Weave Ignite is an open source Virtual Machine (VM) manager with a container UX and built-in GitOps management.

Weave Ignite is an open source Virtual Machine (VM) manager with a container UX and built-in GitOps management.

Nov 16, 2021
A multi-pass compiler written in Go comprised of scanner, recursive-descent parser, generation of AST, intermediate representation (ILOC), and code generation (Armv8).

GoLite Project - Go Huskies! This is a project conducted and led in the course MPCS 51300 Compilers at the University of Chicago. In a group of two, w

Jan 10, 2022
PC-INFO is a tool that gathers information of your system components.
PC-INFO is a tool that gathers information of your system components.

PC-INFO PC-INFO is a tool that gathers information of your system components. Download CLICK HERE TO DOWNLOAD Features Mainboard CPU GPU RAM HOSTNAME

Aug 16, 2022
Go-silk - Skype silk for go, original transpiled by cxgo

go silk skype silk for go, original transpiled by cxgo 贡献 由于转译器还不够聪明需要手工修改一部分, 需

Jan 3, 2022
i18n (Internationalization and localization) engine written in Go, used for translating locale strings.

go-localize Simple and easy to use i18n (Internationalization and localization) engine written in Go, used for translating locale strings. Use with go

Nov 29, 2022
2D triangulation library. Allows translating lines and polygons (both based on points) to the language of GPUs.
2D triangulation library. Allows translating lines and polygons (both based on points) to the language of GPUs.

triangolatte 2D triangulation library. Allows translating lines and polygons (both based on points) to the language of GPUs. Features normal and miter

Dec 23, 2022
:triangular_ruler:gofmtmd formats go source code block in Markdown. detects fenced code & formats code using gofmt.
:triangular_ruler:gofmtmd formats go source code block in Markdown. detects fenced code & formats code using gofmt.

gofmtmd gofmtmd formats go source code block in Markdown. detects fenced code & formats code using gofmt. Installation $ go get github.com/po3rin/gofm

Oct 31, 2022
octocov is a tool for collecting code metrics (code coverage, code to test ratio and test execution time).

octocov is a tool for collecting code metrics (code coverage, code to test ratio and test execution time).

Jan 9, 2023
sail is an operation framework based on Ansible/Helm. sail follows the principles of Infrastructure as Code (IaC), Operation as Code (OaC), and Everything as Code. So it is a tool for DevOps.

sail 中文文档 sail is an operation framework based on Ansible/Helm. sail follows the principles of Infrastructure as Code (IaC), Operation as Code (OaC),a

Dec 16, 2021
The most opinionated Go source code linter for code audit.
The most opinionated Go source code linter for code audit.

go-critic Highly extensible Go source code linter providing checks currently missing from other linters. There is never too much static code analysis.

Jan 6, 2023
Clean-Swift source and test code auto-generator. It can save you time typing 500-600 lines of code.
Clean-Swift source and test code auto-generator. It can save you time typing 500-600 lines of code.

Clean-Swift source & test code auto generator Overview Run Output Basic Usage make config.yaml target_project_name: Miro // target project name copyri

Apr 13, 2022
🔎 Help find Trojan Source vulnerability in code 👀 . Useful for code review in project with multiple collaborators

TrojanSourceFinder TrojanSourceFinder helps developers detect "Trojan Source" vulnerability in source code. Trojan Source vulnerability allows an atta

Nov 9, 2022
Read k8S-source-code notes, help quickly understand the K8S-code organization rules
Read k8S-source-code notes, help quickly understand the K8S-code organization rules

K8S源码阅读笔记 以下笔记针对 kubernetes V1.23.1(截至2022年01月01日最新版本),并不保证对其它版本的有效性 一、架构图 二、阅读前准备 由于kubernetes项目巧妙的设计和代码高度的封装性,建议在阅读代码前,尽可能的进行以下内容的准备: 1. 编程知识配备 编程语准

Feb 16, 2022
Test your code without writing mocks with ephemeral Docker containers 📦 Setup popular services with just a couple lines of code ⏱️ No bash, no yaml, only code 💻

Gnomock – tests without mocks ??️ Spin up entire dependency stack ?? Setup initial dependency state – easily! ?? Test against actual, close to product

Dec 29, 2022
SigNoz helps developers monitor their applications & troubleshoot problems, an open-source alternative to DataDog, NewRelic, etc. 🔥 🖥. 👉 Open source Application Performance Monitoring (APM) & Observability tool
SigNoz helps developers monitor their applications & troubleshoot problems, an open-source alternative to DataDog, NewRelic, etc. 🔥 🖥.   👉  Open source Application Performance Monitoring (APM) & Observability tool

Monitor your applications and troubleshoot problems in your deployed applications, an open-source alternative to DataDog, New Relic, etc. Documentatio

Sep 24, 2021
depth is tool to retrieve and visualize Go source code dependency trees.

depth is tool to retrieve and visualize Go source code dependency trees. Install Download the appropriate binary for your platform from the Rele

Dec 30, 2022
Nut is a tool to manage versioned Go source code packages, called "nuts".

nut Nut is a tool to manage versioned Go source code packages, called "nuts". gonuts.io – central repository (source code) Documents Stable API Mailin

Jul 15, 2021