diff --git a/.github/workflows/llgo.yml b/.github/workflows/llgo.yml index 72950dde..31e15674 100644 --- a/.github/workflows/llgo.yml +++ b/.github/workflows/llgo.yml @@ -125,6 +125,13 @@ jobs: export LLGO_FULL_RPATH=true bash .github/workflows/test_demo.sh + - name: Test C header generation + run: | + echo "Testing C header generation in different build modes..." + cd _demo/go/export + chmod +x test.sh + ./test.sh + - name: _xtool build tests run: | cd _xtool diff --git a/.gitignore b/.gitignore index c388e11d..6976dfda 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ *.dll *.so *.dylib +*.a test.db demo.ll diff --git a/_demo/go/export/.gitignore b/_demo/go/export/.gitignore new file mode 100644 index 00000000..a2750a6c --- /dev/null +++ b/_demo/go/export/.gitignore @@ -0,0 +1 @@ +libexport.h diff --git a/_demo/go/export/c/c.go b/_demo/go/export/c/c.go new file mode 100644 index 00000000..5063a769 --- /dev/null +++ b/_demo/go/export/c/c.go @@ -0,0 +1,29 @@ +package C + +// XType - struct for export.go to use +type XType struct { + ID int32 `json:"id"` + Name string `json:"name"` + Value float64 `json:"value"` + Flag bool `json:"flag"` +} + +func XAdd(a, b int) int { + return a + b +} + +func Sub(a, b int64) int64 { + return a - b +} + +func sub(a, b uint32) uint32 { + return a - b +} + +func Xmul(a, b float32) float32 { + return a * b +} + +func Concat(a, b string) string { + return a + b +} diff --git a/_demo/go/export/export.go b/_demo/go/export/export.go new file mode 100644 index 00000000..4ec280fc --- /dev/null +++ b/_demo/go/export/export.go @@ -0,0 +1,545 @@ +package main + +import ( + "unsafe" + + C "github.com/goplus/llgo/_demo/go/export/c" +) + +// Small struct +type SmallStruct struct { + ID int8 `json:"id"` + Flag bool `json:"flag"` +} + +// Large struct +type LargeStruct struct { + ID int64 `json:"id"` + Name string `json:"name"` + Values [10]float64 `json:"values"` + Metadata map[string]int `json:"metadata"` + Children []SmallStruct `json:"children"` + Extra1 int32 `json:"extra1"` + Extra2 uint64 `json:"extra2"` + Extra3 float32 `json:"extra3"` + Extra4 bool `json:"extra4"` + Extra5 uintptr `json:"extra5"` +} + +// Self-referential struct +type Node struct { + Data int `json:"data"` + Next *Node `json:"next"` +} + +// Named types +type MyInt int +type MyString string + +// Complex struct with mixed arrays and slices +type ComplexData struct { + Matrix [3][4]int32 `json:"matrix"` // 2D array + Slices [][]string `json:"slices"` // slice of slices - commented out + IntArray [5]int `json:"int_array"` // 1D array + DataList []float64 `json:"data_list"` // slice - commented out +} + +//export HelloWorld +func HelloWorld() { + println("Hello, World!") +} + +// Functions with small struct parameters and return values + +//export CreateSmallStruct +func CreateSmallStruct(id int8, flag bool) SmallStruct { + return SmallStruct{ID: id, Flag: flag} +} + +//export ProcessSmallStruct +func ProcessSmallStruct(s SmallStruct) SmallStruct { + s.ID += 1 + s.Flag = !s.Flag + return s +} + +//export ProcessSmallStructPtr +func ProcessSmallStructPtr(s *SmallStruct) *SmallStruct { + if s != nil { + s.ID *= 2 + s.Flag = !s.Flag + } + return s +} + +// Functions with large struct parameters and return values + +//export CreateLargeStruct +func CreateLargeStruct(id int64, name string) LargeStruct { + return LargeStruct{ + ID: id, + Name: name, + Values: [10]float64{1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.0}, + Metadata: map[string]int{"count": 42, "size": 100}, + Children: []SmallStruct{{ID: 1, Flag: true}, {ID: 2, Flag: false}}, + Extra1: 12345, + Extra2: 67890, + Extra3: 3.14, + Extra4: true, + Extra5: 0x1000, + } +} + +//export ProcessLargeStruct +func ProcessLargeStruct(ls LargeStruct) int64 { + total := ls.ID + int64(len(ls.Name)) + for _, v := range ls.Values { + total += int64(v) + } + total += int64(len(ls.Children)) + total += int64(ls.Extra1) + int64(ls.Extra2) + int64(ls.Extra3) + if ls.Extra4 { + total += 1000 + } + total += int64(ls.Extra5) + return total +} + +//export ProcessLargeStructPtr +func ProcessLargeStructPtr(ls *LargeStruct) *LargeStruct { + if ls != nil { + ls.ID += 100 + ls.Name = "processed_" + ls.Name + ls.Extra1 *= 2 + ls.Extra4 = !ls.Extra4 + } + return ls +} + +// Functions with self-referential struct + +//export CreateNode +func CreateNode(data int) *Node { + return &Node{Data: data, Next: nil} +} + +//export LinkNodes +func LinkNodes(first, second *Node) { + if first != nil { + first.Next = second + } +} + +//export TraverseNodes +func TraverseNodes(head *Node) int { + count := 0 + current := head + for current != nil { + count++ + current = current.Next + if count > 100 { // Safety check + break + } + } + return count +} + +// Functions covering all basic types + +//export ProcessBool +func ProcessBool(b bool) bool { + return !b +} + +//export ProcessInt8 +func ProcessInt8(x int8) int8 { + return x + 1 +} + +//export ProcessUint8 +func ProcessUint8(x uint8) uint8 { + return x + 1 +} + +//export ProcessInt16 +func ProcessInt16(x int16) int16 { + return x * 2 +} + +//export ProcessUint16 +func ProcessUint16(x uint16) uint16 { + return x * 2 +} + +//export ProcessInt32 +func ProcessInt32(x int32) int32 { + return x * 3 +} + +//export ProcessUint32 +func ProcessUint32(x uint32) uint32 { + return x * 3 +} + +//export ProcessInt64 +func ProcessInt64(x int64) int64 { + return x * 4 +} + +//export ProcessUint64 +func ProcessUint64(x uint64) uint64 { + return x * 4 +} + +//export ProcessInt +func ProcessInt(x int) int { + return x + 100 +} + +//export ProcessUint +func ProcessUint(x uint) uint { + return x + 200 +} + +//export ProcessUintptr +func ProcessUintptr(x uintptr) uintptr { + return x + 300 +} + +//export ProcessFloat32 +func ProcessFloat32(x float32) float32 { + return x * 1.5 +} + +//export ProcessFloat64 +func ProcessFloat64(x float64) float64 { + return x * 2.5 +} + +//export ProcessString +func ProcessString(s string) string { + return "processed_" + s +} + +//export ProcessUnsafePointer +func ProcessUnsafePointer(p unsafe.Pointer) unsafe.Pointer { + return p +} + +// Functions with named types + +//export ProcessMyInt +func ProcessMyInt(x MyInt) MyInt { + return x * 10 +} + +//export ProcessMyString +func ProcessMyString(s MyString) MyString { + return MyString("modified_" + string(s)) +} + +// Functions with arrays, slices, maps, channels + +//export ProcessIntArray +func ProcessIntArray(arr [5]int) int { + total := 0 + for _, v := range arr { + total += v + } + return total +} + +//export CreateComplexData +func CreateComplexData() ComplexData { + return ComplexData{ + Matrix: [3][4]int32{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}, + Slices: [][]string{{"helo"}}, + IntArray: [5]int{10, 20, 30, 40, 50}, + DataList: []float64{1.0}, + } +} + +//export ProcessComplexData +func ProcessComplexData(data ComplexData) int32 { + // Sum all matrix elements + var sum int32 + for i := 0; i < 3; i++ { + for j := 0; j < 4; j++ { + sum += data.Matrix[i][j] + } + } + return sum +} + +// Functions with multidimensional arrays as parameters and return values + +//export ProcessMatrix2D +func ProcessMatrix2D(matrix [3][4]int32) int32 { + var sum int32 + for i := 0; i < 3; i++ { + for j := 0; j < 4; j++ { + sum += matrix[i][j] + } + } + return sum +} + +//export CreateMatrix1D +func CreateMatrix1D() [4]int32 { + return [4]int32{1, 2, 3, 4} +} + +//export CreateMatrix2D +func CreateMatrix2D() [3][4]int32 { + return [3][4]int32{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}} +} + +//export ProcessMatrix3D +func ProcessMatrix3D(cube [2][3][4]uint8) uint32 { + var sum uint32 + for i := 0; i < 2; i++ { + for j := 0; j < 3; j++ { + for k := 0; k < 4; k++ { + sum += uint32(cube[i][j][k]) + } + } + } + return sum +} + +//export CreateMatrix3D +func CreateMatrix3D() [2][3][4]uint8 { + var cube [2][3][4]uint8 + val := uint8(1) + for i := 0; i < 2; i++ { + for j := 0; j < 3; j++ { + for k := 0; k < 4; k++ { + cube[i][j][k] = val + val++ + } + } + } + return cube +} + +//export ProcessGrid5x4 +func ProcessGrid5x4(grid [5][4]float64) float64 { + var sum float64 + for i := 0; i < 5; i++ { + for j := 0; j < 4; j++ { + sum += grid[i][j] + } + } + return sum +} + +//export CreateGrid5x4 +func CreateGrid5x4() [5][4]float64 { + var grid [5][4]float64 + val := 1.0 + for i := 0; i < 5; i++ { + for j := 0; j < 4; j++ { + grid[i][j] = val + val += 0.5 + } + } + return grid +} + +//export ProcessIntSlice +func ProcessIntSlice(slice []int) int { + total := 0 + for _, v := range slice { + total += v + } + return total +} + +//export ProcessStringMap +func ProcessStringMap(m map[string]int) int { + total := 0 + for _, v := range m { + total += v + } + return total +} + +//export ProcessIntChannel +func ProcessIntChannel(ch chan int) int { + select { + case val := <-ch: + return val + default: + return -1 + } +} + +// Functions with interface + +//export ProcessInterface +func ProcessInterface(i interface{}) int { + switch v := i.(type) { + case int: + return v + case string: + return len(v) + default: + return 0 + } +} + +// Functions with various parameter counts + +//export NoParams +func NoParams() int { + return 42 +} + +//export OneParam +func OneParam(x int) int { + return x * 2 +} + +//export TwoParams +func TwoParams(a int, b string) string { + return string(rune(a)) + b +} + +//export ThreeParams +func ThreeParams(a int32, b float64, c bool) float64 { + result := float64(a) + b + if c { + result *= 2 + } + return result +} + +//export MultipleParams +func MultipleParams(a int8, b uint16, c int32, d uint64, e float32, f float64, g string, h bool) string { + return g + "_processed" +} + +// Functions returning no value + +//export NoReturn +func NoReturn(message string) { + println("Message:", message) +} + +// Functions using XType from c package + +//export CreateXType +func CreateXType(id int32, name string, value float64, flag bool) C.XType { + return C.XType{ + ID: id, + Name: name, + Value: value, + Flag: flag, + } +} + +//export ProcessXType +func ProcessXType(x C.XType) C.XType { + x.ID += 100 + x.Name = "processed_" + x.Name + x.Value *= 2.0 + x.Flag = !x.Flag + return x +} + +//export ProcessXTypePtr +func ProcessXTypePtr(x *C.XType) *C.XType { + if x != nil { + x.ID *= 2 + x.Name = "ptr_" + x.Name + x.Value += 10.0 + x.Flag = !x.Flag + } + return x +} + +func main() { + println("=== Export Demo ===") + + // Test small struct + small := CreateSmallStruct(5, true) + println("Small struct:", small.ID, small.Flag) + + processed := ProcessSmallStruct(small) + println("Processed small:", processed.ID, processed.Flag) + + // Test large struct + large := CreateLargeStruct(12345, "test") + println("Large struct ID:", large.ID, "Name:", large.Name) + + total := ProcessLargeStruct(large) + println("Large struct total:", total) + + // Test self-referential struct + node1 := CreateNode(100) + node2 := CreateNode(200) + LinkNodes(node1, node2) + + count := TraverseNodes(node1) + println("Node count:", count) + + // Test basic types + println("Bool:", ProcessBool(true)) + println("Int8:", ProcessInt8(10)) + println("Float32:", ProcessFloat32(3.14)) + println("String:", ProcessString("hello")) + + // Test named types + myInt := ProcessMyInt(MyInt(42)) + println("MyInt:", int(myInt)) + + myStr := ProcessMyString(MyString("world")) + println("MyString:", string(myStr)) + + // Test collections + arr := [5]int{1, 2, 3, 4, 5} + println("Array sum:", ProcessIntArray(arr)) + + slice := []int{10, 20, 30} + println("Slice sum:", ProcessIntSlice(slice)) + + m := make(map[string]int) + m["a"] = 100 + m["b"] = 200 + println("Map sum:", ProcessStringMap(m)) + + // Test multidimensional arrays + matrix2d := CreateMatrix2D() + println("Matrix2D sum:", ProcessMatrix2D(matrix2d)) + + matrix3d := CreateMatrix3D() + println("Matrix3D sum:", ProcessMatrix3D(matrix3d)) + + grid5x4 := CreateGrid5x4() + println("Grid5x4 sum:", ProcessGrid5x4(grid5x4)) + + // Test complex data with multidimensional arrays + complexData := CreateComplexData() + println("ComplexData matrix sum:", ProcessComplexData(complexData)) + + // Test various parameter counts + println("NoParams:", NoParams()) + println("OneParam:", OneParam(5)) + println("TwoParams:", TwoParams(65, "_test")) + println("ThreeParams:", ThreeParams(10, 2.5, true)) + println("MultipleParams:", MultipleParams(1, 2, 3, 4, 5.0, 6.0, "result", true)) + + // Test XType from c package + xtype := CreateXType(42, "test", 3.14, true) + println("XType:", xtype.ID, xtype.Name, xtype.Value, xtype.Flag) + + processedX := ProcessXType(xtype) + println("Processed XType:", processedX.ID, processedX.Name, processedX.Value, processedX.Flag) + + ptrX := ProcessXTypePtr(&xtype) + if ptrX != nil { + println("Ptr XType:", ptrX.ID, ptrX.Name, ptrX.Value, ptrX.Flag) + } + + NoReturn("demo completed") +} diff --git a/_demo/go/export/libexport.h.want b/_demo/go/export/libexport.h.want new file mode 100644 index 00000000..4e7cf626 --- /dev/null +++ b/_demo/go/export/libexport.h.want @@ -0,0 +1,291 @@ +/* Code generated by llgo; DO NOT EDIT. */ + +#ifndef __LIBEXPORT_H_ +#define __LIBEXPORT_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Platform-specific symbol renaming macro +#ifdef __APPLE__ + #define GO_SYMBOL_RENAME(go_name) __asm("_" go_name); +#else + #define GO_SYMBOL_RENAME(go_name) __asm(go_name); +#endif + +// Go runtime types +typedef struct { const char *p; intptr_t n; } GoString; +typedef struct { void *data; intptr_t len; intptr_t cap; } GoSlice; +typedef struct { void *data; } GoMap; +typedef struct { void *data; } GoChan; +typedef struct { void *data; void *type; } GoInterface; +typedef struct { float real; float imag; } GoComplex64; +typedef struct { double real; double imag; } GoComplex128; + +typedef struct { + int32_t Matrix[3][4]; + GoSlice Slices; + intptr_t IntArray[5]; + GoSlice DataList; +} main_ComplexData; + +typedef struct { + double data[5][4]; +} Array_double_5_4; + +typedef struct { + int8_t ID; + _Bool Flag; +} main_SmallStruct; + +typedef struct { + int64_t ID; + GoString Name; + double Values[10]; + GoMap Metadata; + GoSlice Children; + int32_t Extra1; + uint64_t Extra2; + float Extra3; + _Bool Extra4; + uintptr_t Extra5; +} main_LargeStruct; + +typedef struct { + int32_t data[4]; +} Array_int32_t_4; + +typedef struct { + int32_t data[3][4]; +} Array_int32_t_3_4; + +typedef struct { + uint8_t data[2][3][4]; +} Array_uint8_t_2_3_4; + +typedef struct main_Node main_Node; +struct main_Node { + intptr_t Data; + main_Node* Next; +}; + +typedef struct { + int32_t ID; + GoString Name; + double Value; + _Bool Flag; +} C_XType; + +typedef intptr_t main_MyInt; + +typedef GoString main_MyString; + +GoString +Concat(GoString a, GoString b); + +int64_t +Sub(int64_t a, int64_t b); + +intptr_t +Add(intptr_t a, intptr_t b); + +float +mul(float a, float b); + +void +github_com_goplus_llgo__demo_go_export_c_init(void) GO_SYMBOL_RENAME("github.com/goplus/llgo/_demo/go/export/c.init") + +main_ComplexData +CreateComplexData(void); + +Array_double_5_4 +CreateGrid5x4(void); + +main_LargeStruct +CreateLargeStruct(int64_t id, GoString name); + +Array_int32_t_4 +CreateMatrix1D(void); + +Array_int32_t_3_4 +CreateMatrix2D(void); + +Array_uint8_t_2_3_4 +CreateMatrix3D(void); + +main_Node* +CreateNode(intptr_t data); + +main_SmallStruct +CreateSmallStruct(int8_t id, _Bool flag); + +C_XType +CreateXType(int32_t id, GoString name, double value, _Bool flag); + +void +HelloWorld(void); + +void +LinkNodes(main_Node* first, main_Node* second); + +GoString +MultipleParams(int8_t a, uint16_t b, int32_t c, uint64_t d, float e, double f, GoString g, _Bool h); + +intptr_t +NoParams(void); + +void +NoReturn(GoString message); + +intptr_t +OneParam(intptr_t x); + +_Bool +ProcessBool(_Bool b); + +int32_t +ProcessComplexData(main_ComplexData data); + +float +ProcessFloat32(float x); + +double +ProcessFloat64(double x); + +double +ProcessGrid5x4(double grid[5][4]); + +intptr_t +ProcessInt(intptr_t x); + +int16_t +ProcessInt16(int16_t x); + +int32_t +ProcessInt32(int32_t x); + +int64_t +ProcessInt64(int64_t x); + +int8_t +ProcessInt8(int8_t x); + +intptr_t +ProcessIntArray(intptr_t arr[5]); + +intptr_t +ProcessIntChannel(GoChan ch); + +intptr_t +ProcessIntSlice(GoSlice slice); + +intptr_t +ProcessInterface(GoInterface i); + +int64_t +ProcessLargeStruct(main_LargeStruct ls); + +main_LargeStruct* +ProcessLargeStructPtr(main_LargeStruct* ls); + +int32_t +ProcessMatrix2D(int32_t matrix[3][4]); + +uint32_t +ProcessMatrix3D(uint8_t cube[2][3][4]); + +main_MyInt +ProcessMyInt(main_MyInt x); + +main_MyString +ProcessMyString(main_MyString s); + +main_SmallStruct +ProcessSmallStruct(main_SmallStruct s); + +main_SmallStruct* +ProcessSmallStructPtr(main_SmallStruct* s); + +GoString +ProcessString(GoString s); + +intptr_t +ProcessStringMap(GoMap m); + +uintptr_t +ProcessUint(uintptr_t x); + +uint16_t +ProcessUint16(uint16_t x); + +uint32_t +ProcessUint32(uint32_t x); + +uint64_t +ProcessUint64(uint64_t x); + +uint8_t +ProcessUint8(uint8_t x); + +uintptr_t +ProcessUintptr(uintptr_t x); + +void* +ProcessUnsafePointer(void* p); + +C_XType +ProcessXType(C_XType x); + +C_XType* +ProcessXTypePtr(C_XType* x); + +double +ThreeParams(int32_t a, double b, _Bool c); + +intptr_t +TraverseNodes(main_Node* head); + +GoString +TwoParams(intptr_t a, GoString b); + +void +github_com_goplus_llgo__demo_go_export_init(void) GO_SYMBOL_RENAME("github.com/goplus/llgo/_demo/go/export.init") + +void +github_com_goplus_llgo_runtime_abi_init(void) GO_SYMBOL_RENAME("github.com/goplus/llgo/runtime/abi.init") + +void +github_com_goplus_llgo_runtime_internal_clite_bdwgc_init(void) GO_SYMBOL_RENAME("github.com/goplus/llgo/runtime/internal/clite/bdwgc.init") + +void +github_com_goplus_llgo_runtime_internal_clite_bitcast_init(void) GO_SYMBOL_RENAME("github.com/goplus/llgo/runtime/internal/clite/bitcast.init") + +void +github_com_goplus_llgo_runtime_internal_clite_debug_init(void) GO_SYMBOL_RENAME("github.com/goplus/llgo/runtime/internal/clite/debug.init") + +void +github_com_goplus_llgo_runtime_internal_clite_pthread_init(void) GO_SYMBOL_RENAME("github.com/goplus/llgo/runtime/internal/clite/pthread.init") + +void +github_com_goplus_llgo_runtime_internal_clite_pthread_sync_init(void) GO_SYMBOL_RENAME("github.com/goplus/llgo/runtime/internal/clite/pthread/sync.init") + +void +github_com_goplus_llgo_runtime_internal_runtime_goarch_init(void) GO_SYMBOL_RENAME("github.com/goplus/llgo/runtime/internal/runtime/goarch.init") + +void +github_com_goplus_llgo_runtime_internal_runtime_math_init(void) GO_SYMBOL_RENAME("github.com/goplus/llgo/runtime/internal/runtime/math.init") + +void +github_com_goplus_llgo_runtime_internal_runtime_init(void) GO_SYMBOL_RENAME("github.com/goplus/llgo/runtime/internal/runtime.init") + + + +#ifdef __cplusplus +} +#endif + +#endif /* __LIBEXPORT_H_ */ diff --git a/_demo/go/export/test.sh b/_demo/go/export/test.sh new file mode 100755 index 00000000..f778f3c4 --- /dev/null +++ b/_demo/go/export/test.sh @@ -0,0 +1,239 @@ +#!/bin/bash + +# Test script for C header generation in different build modes +# This script tests the header generation functionality with various buildmode options + +set -e # Exit on any error + +# Get script directory +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Function to print colored output +print_status() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +print_warning() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +print_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Function to check if file exists and is not empty +check_file() { + local file="$1" + local description="$2" + + if [[ -f "$file" ]]; then + if [[ -s "$file" ]]; then + print_status "$description exists and is not empty" + return 0 + else + print_error "$description exists but is empty" + return 1 + fi + else + print_error "$description does not exist" + return 1 + fi +} + +# Function to compare header with expected content +compare_header() { + local header_file="$1" + local expected_file="$2" + local test_name="$3" + + if [[ -f "$expected_file" ]]; then + if diff -q "$header_file" "$expected_file" >/dev/null 2>&1; then + print_status "$test_name: Header content matches expected" + return 0 + else + print_warning "$test_name: Header content differs from expected" + print_warning "Run 'diff $header_file $expected_file' to see differences" + return 1 + fi + else + print_warning "$test_name: No expected file found at $expected_file" + print_status "Generated header content:" + echo "--- START OF HEADER ---" + cat "$header_file" + echo "--- END OF HEADER ---" + return 0 + fi +} + +# Function to cleanup generated files +cleanup() { + local files=("$@") + for file in "${files[@]}"; do + if [[ -f "$file" ]]; then + rm -f "$file" + print_status "Cleaned up $file" + fi + done +} + +# Check if llgo.sh exists +LLGO_SCRIPT="../../../llgo.sh" +if [[ ! -f "$LLGO_SCRIPT" ]]; then + print_error "llgo.sh not found at $LLGO_SCRIPT" + exit 1 +fi + +print_status "Starting C header generation tests..." +print_status "Working directory: $SCRIPT_DIR" + +echo "" + +# Test 1: c-shared mode +print_status "=== Test 2: Building with -buildmode c-shared ===" +if $LLGO_SCRIPT build -buildmode c-shared -o export .; then + print_status "Build succeeded" + + # Check generated files (different extensions on different platforms) + if [[ "$OSTYPE" == "darwin"* ]]; then + # macOS + check_file "libexport.dylib" "Dynamic library (libexport.dylib)" + SHARED_LIB="libexport.dylib" + else + # Linux and others + check_file "libexport.so" "Dynamic library (libexport.so)" + SHARED_LIB="libexport.so" + fi + + check_file "libexport.h" "C header (libexport.h)" + + # Compare with expected header if it exists + if [[ -f "libexport.h" ]]; then + compare_header "libexport.h" "libexport.h.want" "c-shared" + fi + + # Test C demo with shared library + print_status "=== Testing C demo with shared library ===" + if cd use; then + if LINK_TYPE=shared make clean && LINK_TYPE=shared make; then + print_status "C demo build succeeded with shared library" + if LINK_TYPE=shared make run; then + print_status "C demo execution succeeded with shared library" + else + print_warning "C demo execution failed with shared library" + fi + else + print_warning "C demo build failed with shared library" + fi + cd .. + else + print_error "Failed to enter use directory" + fi + + # Cleanup + cleanup "$SHARED_LIB" "libexport.h" +else + print_error "Build failed for c-shared mode" +fi + +# Test 2: c-archive mode +print_status "=== Test 1: Building with -buildmode c-archive ===" +if $LLGO_SCRIPT build -buildmode c-archive -o export .; then + print_status "Build succeeded" + + # Check generated files + check_file "libexport.a" "Static library (libexport.a)" + check_file "libexport.h" "C header (libexport.h)" + + # Compare with expected header if it exists + if [[ -f "libexport.h" ]]; then + compare_header "libexport.h" "libexport.h.want" "c-archive" + fi + + # Test C demo with static library + print_status "=== Testing C demo with static library ===" + if cd use; then + if make clean && make; then + print_status "C demo build succeeded with static library" + if make run; then + print_status "C demo execution succeeded with static library" + else + print_warning "C demo execution failed with static library" + fi + else + print_warning "C demo build failed with static library" + fi + cd .. + else + print_error "Failed to enter use directory" + fi + + # # Cleanup + # cleanup "libexport.a" "libexport.h" +else + print_error "Build failed for c-archive mode" +fi + +echo "" + +# TODO(lijie): Uncomment if https://github.com/goplus/llgo/pull/1268 merged +# # Test 3: ESP32 target with c-archive mode +# print_status "=== Test 3: Building with -target esp32 -buildmode c-archive ===" +# if $LLGO_SCRIPT build -target esp32 -buildmode c-archive -o export .; then +# print_status "Build succeeded" + +# # Check generated files +# check_file "libexport.a" "Static library for ESP32 (libexport.a)" +# check_file "libexport.h" "C header for ESP32 (libexport.h)" + +# # Compare with expected header if it exists +# if [[ -f "libexport.h" ]]; then +# compare_header "libexport.h" "libexport.h.want" "esp32-c-archive" +# fi + +# # Don't cleanup ESP32 files - keep them for inspection +# print_status "ESP32 build files kept for inspection" +# else +# print_error "Build failed for ESP32 target" +# fi + +# echo "" + +# Final summary +print_status "=== Test Summary ===" +if [[ -f "libexport.a" ]] && [[ -f "libexport.h" ]]; then + print_status "All tests completed successfully:" + print_status " ✅ C header generation (c-archive and c-shared modes)" + print_status " ✅ C demo compilation and execution" + print_status " ✅ Cross-platform symbol renaming" + print_status " ✅ Init function export and calling" + print_status "" + print_status "Final files available:" + print_status " - libexport.a (static library)" + print_status " - libexport.h (C header file)" + print_status " - use/main.out (C demo executable)" + + echo "" + echo "===================" +else + print_error "Some tests may have failed. Check the output above." +fi + +# Show file sizes for reference +if [[ -f "libexport.a" ]]; then + SIZE=$(wc -c < libexport.a) + print_status "Static library size: $SIZE bytes" +fi + +if [[ -f "libexport.h" ]]; then + LINES=$(wc -l < libexport.h) + print_status "Header file lines: $LINES" +fi + +print_status "C header generation and demo tests completed!" diff --git a/_demo/go/export/use/Makefile b/_demo/go/export/use/Makefile new file mode 100644 index 00000000..468bb8cd --- /dev/null +++ b/_demo/go/export/use/Makefile @@ -0,0 +1,84 @@ +# Makefile for C demo using Go exported library +# Use LINK_TYPE environment variable to choose library type: +# LINK_TYPE=static - Link with static library (default) +# LINK_TYPE=shared - Link with shared library + +CC = clang +CFLAGS = -Wall -Wextra -std=c99 +INCLUDES = -I.. +TARGET = main.out +SOURCES = main.c +HEADER = ../libexport.h + +# Default to static linking +LINK_TYPE ?= static + +# Platform detection +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S),Darwin) + SHARED_EXT = dylib + PLATFORM_LIBS = +else + SHARED_EXT = so + PLATFORM_LIBS = $(shell pkg-config --libs libunwind 2>/dev/null || echo -lunwind) +endif + +# Library and flags based on link type +ifeq ($(LINK_TYPE),shared) + BUILDMODE = c-shared + LIBRARY = ../libexport.$(SHARED_EXT) + LDFLAGS = -L.. -lexport -lpthread -lm $(shell pkg-config --libs bdw-gc || echo -lgc) $(PLATFORM_LIBS) + BUILD_MSG = "Building Go shared library..." + LINK_MSG = "Linking with shared library..." +else + BUILDMODE = c-archive + LIBRARY = ../libexport.a + LDFLAGS = $(LIBRARY) -lpthread -lm $(shell pkg-config --libs bdw-gc || echo -lgc) $(PLATFORM_LIBS) + BUILD_MSG = "Building Go static library..." + LINK_MSG = "Linking with static library..." +endif + +.PHONY: all clean run build-go + +all: build-go $(TARGET) + +# Build the Go library first +build-go: + @echo $(BUILD_MSG) + cd .. && ../../../llgo.sh build -buildmode $(BUILDMODE) -o export . + +# Build the C executable +$(TARGET): $(SOURCES) $(LIBRARY) $(HEADER) + @echo $(LINK_MSG) + $(CC) $(CFLAGS) $(INCLUDES) -o $(TARGET) $(SOURCES) $(LDFLAGS) + +# Run the executable +run: $(TARGET) + @echo "Running C demo..." +ifeq ($(LINK_TYPE),shared) + @echo "Setting library path for shared library..." + LD_LIBRARY_PATH=.. DYLD_LIBRARY_PATH=.. ./$(TARGET) +else + ./$(TARGET) +endif + +# Clean build artifacts +clean: + rm -f $(TARGET) + rm -f ../libexport.a ../libexport.h ../libexport.so ../libexport.dylib + +# Help target +help: + @echo "Available targets:" + @echo " all - Build Go library and C executable" + @echo " build-go - Build only the Go library" + @echo " run - Build and run the C demo" + @echo " clean - Clean all build artifacts" + @echo " help - Show this help message" + @echo "" + @echo "Environment variables:" + @echo " LINK_TYPE - Library type: 'static' (default) or 'shared'" + @echo "" + @echo "Examples:" + @echo " make run # Use static library" + @echo " LINK_TYPE=shared make run # Use shared library" \ No newline at end of file diff --git a/_demo/go/export/use/main.c b/_demo/go/export/use/main.c new file mode 100644 index 00000000..96006efc --- /dev/null +++ b/_demo/go/export/use/main.c @@ -0,0 +1,179 @@ +#include +#include +#include +#include "../libexport.h" + +int main() { + printf("=== C Export Demo ===\n"); + fflush(stdout); // Force output + + // Initialize packages - call init functions first + github_com_goplus_llgo__demo_go_export_c_init(); + github_com_goplus_llgo__demo_go_export_init(); + + // Test HelloWorld + HelloWorld(); + printf("\n"); + + // Test small struct + main_SmallStruct small = CreateSmallStruct(5, 1); // 1 for true + printf("Small struct: %d %d\n", small.ID, small.Flag); + + main_SmallStruct processed = ProcessSmallStruct(small); + printf("Processed small: %d %d\n", processed.ID, processed.Flag); + + main_SmallStruct* ptrSmall = ProcessSmallStructPtr(&small); + if (ptrSmall != NULL) { + printf("Ptr small: %d %d\n", ptrSmall->ID, ptrSmall->Flag); + } + + // Test large struct - create GoString for name parameter + GoString name = {"test_large", 10}; // name and length + main_LargeStruct large = CreateLargeStruct(12345, name); + printf("Large struct ID: %" PRId64 "\n", large.ID); + + int64_t total = ProcessLargeStruct(large); + printf("Large struct total: %" PRId64 "\n", total); + + main_LargeStruct* ptrLarge = ProcessLargeStructPtr(&large); + if (ptrLarge != NULL) { + printf("Ptr large ID: %" PRId64 "\n", ptrLarge->ID); + } + + // Test self-referential struct + main_Node* node1 = CreateNode(100); + main_Node* node2 = CreateNode(200); + LinkNodes(node1, node2); + + int count = TraverseNodes(node1); + printf("Node count: %d\n", count); + + // Test basic types + printf("Bool: %d\n", ProcessBool(1)); // 1 for true + printf("Int8: %d\n", ProcessInt8(10)); + printf("Uint8: %d\n", ProcessUint8(10)); + printf("Int16: %d\n", ProcessInt16(10)); + printf("Uint16: %d\n", ProcessUint16(10)); + printf("Int32: %d\n", ProcessInt32(10)); + printf("Uint32: %u\n", ProcessUint32(10)); + printf("Int64: %" PRId64 "\n", ProcessInt64(10)); + printf("Uint64: %" PRIu64 "\n", ProcessUint64(10)); + printf("Int: %ld\n", ProcessInt(10)); + printf("Uint: %lu\n", ProcessUint(10)); + printf("Uintptr: %lu\n", ProcessUintptr(0x1000)); + printf("Float32: %f\n", ProcessFloat32(3.14f)); + printf("Float64: %f\n", ProcessFloat64(3.14)); + + // Test unsafe pointer + int test_val = 42; + void* ptr_result = ProcessUnsafePointer(&test_val); + printf("UnsafePointer: %p\n", ptr_result); + + // Test named types + main_MyInt myInt = ProcessMyInt(42); + printf("MyInt: %ld\n", (long)myInt); + + // Test arrays + intptr_t arr[5] = {1, 2, 3, 4, 5}; + printf("Array sum: %ld\n", ProcessIntArray(arr)); + + // Test complex data with multidimensional arrays + main_ComplexData complex = CreateComplexData(); + printf("Complex data matrix sum: %" PRId32 "\n", ProcessComplexData(complex)); + + // Test interface - this is more complex in C, we'll skip for now + printf("Interface test skipped (complex in C)\n"); + + // Test various parameter counts + printf("NoParams: %ld\n", NoParams()); + printf("OneParam: %ld\n", OneParam(5)); + printf("ThreeParams: %f\n", ThreeParams(10, 2.5, 1)); // 1 for true + + // Test XType from c package - create GoString for name parameter + GoString xname = {"test_x", 6}; // name and length + C_XType xtype = CreateXType(42, xname, 3.14, 1); // 1 for true + printf("XType: %d %f %d\n", xtype.ID, xtype.Value, xtype.Flag); + + C_XType processedX = ProcessXType(xtype); + printf("Processed XType: %d %f %d\n", processedX.ID, processedX.Value, processedX.Flag); + + C_XType* ptrX = ProcessXTypePtr(&xtype); + if (ptrX != NULL) { + printf("Ptr XType: %d %f %d\n", ptrX->ID, ptrX->Value, ptrX->Flag); + } + + // Test multidimensional arrays + printf("\n=== Multidimensional Array Tests ===\n"); + + // Create and test 2D matrix [3][4] + // Note: CreateMatrix2D returns [3][4]int32, but function returns need special handling in C + printf("Testing 2D matrix functions...\n"); + + // Create a test 2D matrix [3][4]int32 + int32_t test_matrix[3][4] = { + {1, 2, 3, 4}, + {5, 6, 7, 8}, + {9, 10, 11, 12} + }; + int32_t matrix_sum = ProcessMatrix2D(test_matrix); + printf("Matrix2D sum: %d\n", matrix_sum); + + // Create a test 3D cube [2][3][4]uint8 + uint8_t test_cube[2][3][4]; + uint8_t val = 1; + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 3; j++) { + for (int k = 0; k < 4; k++) { + test_cube[i][j][k] = val++; + } + } + } + uint32_t cube_sum = ProcessMatrix3D(test_cube); + printf("Matrix3D (cube) sum: %u\n", cube_sum); + + // Create a test 5x4 grid [5][4]double + double test_grid[5][4]; + double grid_val = 1.0; + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 4; j++) { + test_grid[i][j] = grid_val; + grid_val += 0.5; + } + } + double grid_sum = ProcessGrid5x4(test_grid); + printf("Grid5x4 sum: %f\n", grid_sum); + + // Test functions that return multidimensional arrays (as multi-level pointers) + printf("\n=== Testing Return Value Functions ===\n"); + + // Test CreateMatrix1D() which returns Array_int32_t_4 + printf("About to call CreateMatrix1D()...\n"); + fflush(stdout); + Array_int32_t_4 matrix1d = CreateMatrix1D(); + printf("CreateMatrix1D() call completed\n"); + printf("CreateMatrix1D() returned struct, first element: %d\n", matrix1d.data[0]); + + // Test CreateMatrix2D() which returns Array_int32_t_3_4 + printf("About to call CreateMatrix2D()...\n"); + fflush(stdout); + Array_int32_t_3_4 matrix2d = CreateMatrix2D(); + printf("CreateMatrix2D() call completed\n"); + printf("CreateMatrix2D() returned struct, first element: %d\n", matrix2d.data[0][0]); + + // Test CreateMatrix3D() which returns Array_uint8_t_2_3_4 + Array_uint8_t_2_3_4 cube = CreateMatrix3D(); + printf("CreateMatrix3D() returned struct, first element: %u\n", cube.data[0][0][0]); + + // Test CreateGrid5x4() which returns Array_double_5_4 + Array_double_5_4 grid = CreateGrid5x4(); + printf("CreateGrid5x4() returned struct, first element: %f\n", grid.data[0][0]); + + // Test NoReturn function + // Note: This function takes a string parameter which is complex to pass from C + // We'll skip it for now or pass a simple string if the binding allows + printf("NoReturn test skipped (string parameter)\n"); + + printf("C demo completed!\n"); + + return 0; +} \ No newline at end of file