From 2eb39785fe9d0188e444fd1eb29f1ce2c7a89419 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sat, 27 Jan 2024 12:43:45 +0100 Subject: [PATCH] cgo: add support for printf The C printf function is sometimes needed for C files included using CGo. This commit makes sure they're available on all systems where CGo is fully supported (that is, everywhere except on AVR). For baremetal systems using picolibc, I've picked the integer-only version of printf to save on flash size. We might want to consider providing a way to pick the floating point version instead, if needed. --- GNUmakefile | 4 ++++ builder/build.go | 5 +++-- builder/mingw-w64.go | 26 +++++++++++++++++++++----- builder/musl.go | 4 ++++ builder/picolibc.go | 1 + testdata/cgo/main.c | 5 +++++ testdata/cgo/main.go | 4 ++++ testdata/cgo/main.h | 2 ++ testdata/cgo/out.txt | 1 + 9 files changed, 45 insertions(+), 7 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index d560ae82e..986d51940 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -856,6 +856,7 @@ build/release: tinygo gen-device wasi-libc $(if $(filter 1,$(USE_SYSTEM_BINARYEN @mkdir -p build/release/tinygo/lib/CMSIS/CMSIS @mkdir -p build/release/tinygo/lib/macos-minimal-sdk @mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common + @mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/stdio @mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults @mkdir -p build/release/tinygo/lib/musl/arch @mkdir -p build/release/tinygo/lib/musl/crt @@ -891,10 +892,12 @@ endif @cp -rp lib/musl/src/include build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/internal build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/legacy build/release/tinygo/lib/musl/src + @cp -rp lib/musl/src/locale build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/linux build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/malloc build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/mman build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/math build/release/tinygo/lib/musl/src + @cp -rp lib/musl/src/multibyte build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/signal build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/stdio build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/string build/release/tinygo/lib/musl/src @@ -904,6 +907,7 @@ endif @cp -rp lib/mingw-w64/mingw-w64-crt/def-include build/release/tinygo/lib/mingw-w64/mingw-w64-crt @cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/api-ms-win-crt-* build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common @cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/kernel32.def.in build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common + @cp -rp lib/mingw-w64/mingw-w64-crt/stdio/ucrt_* build/release/tinygo/lib/mingw-w64/mingw-w64-crt/stdio @cp -rp lib/mingw-w64/mingw-w64-headers/crt/ build/release/tinygo/lib/mingw-w64/mingw-w64-headers @cp -rp lib/mingw-w64/mingw-w64-headers/defaults/include build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults @cp -rp lib/nrfx/* build/release/tinygo/lib/nrfx diff --git a/builder/build.go b/builder/build.go index 54f5c101b..d16bf170b 100644 --- a/builder/build.go +++ b/builder/build.go @@ -176,11 +176,12 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe defer unlock() libcDependencies = append(libcDependencies, libcJob) case "mingw-w64": - _, unlock, err := libMinGW.load(config, tmpdir) + job, unlock, err := libMinGW.load(config, tmpdir) if err != nil { return BuildResult{}, err } - unlock() + defer unlock() + libcDependencies = append(libcDependencies, job) libcDependencies = append(libcDependencies, makeMinGWExtraLibs(tmpdir, config.GOARCH())...) case "": // no library specified, so nothing to do diff --git a/builder/mingw-w64.go b/builder/mingw-w64.go index 6b0c966fa..32cf58f53 100644 --- a/builder/mingw-w64.go +++ b/builder/mingw-w64.go @@ -27,14 +27,30 @@ var libMinGW = Library{ _, err = io.Copy(outf, inf) return err }, - sourceDir: func() string { return "" }, // unused + sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/mingw-w64") }, cflags: func(target, headerPath string) []string { - // No flags necessary because there are no files to compile. - return nil + mingwDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/mingw-w64") + return []string{ + "-nostdlibinc", + "-isystem", mingwDir + "/mingw-w64-headers/crt", + "-I", mingwDir + "/mingw-w64-headers/defaults/include", + "-I" + headerPath, + } }, librarySources: func(target string) ([]string, error) { - // We only use the UCRT DLL file. No source files necessary. - return nil, nil + // These files are needed so that printf and the like are supported. + sources := []string{ + "mingw-w64-crt/stdio/ucrt_fprintf.c", + "mingw-w64-crt/stdio/ucrt_fwprintf.c", + "mingw-w64-crt/stdio/ucrt_printf.c", + "mingw-w64-crt/stdio/ucrt_snprintf.c", + "mingw-w64-crt/stdio/ucrt_sprintf.c", + "mingw-w64-crt/stdio/ucrt_vfprintf.c", + "mingw-w64-crt/stdio/ucrt_vprintf.c", + "mingw-w64-crt/stdio/ucrt_vsnprintf.c", + "mingw-w64-crt/stdio/ucrt_vsprintf.c", + } + return sources, nil }, } diff --git a/builder/musl.go b/builder/musl.go index 87253fce1..8130981e6 100644 --- a/builder/musl.go +++ b/builder/musl.go @@ -92,6 +92,8 @@ var libMusl = Library{ "-Wno-ignored-pragmas", "-Wno-tautological-constant-out-of-range-compare", "-Wno-deprecated-non-prototype", + "-Wno-format", + "-Wno-parentheses", "-Qunused-arguments", // Select include dirs. Don't include standard library includes // (that would introduce host dependencies and other complications), @@ -119,11 +121,13 @@ var libMusl = Library{ "internal/syscall_ret.c", "internal/vdso.c", "legacy/*.c", + "locale/*.c", "linux/*.c", "malloc/*.c", "malloc/mallocng/*.c", "mman/*.c", "math/*.c", + "multibyte/*.c", "signal/*.c", "stdio/*.c", "string/*.c", diff --git a/builder/picolibc.go b/builder/picolibc.go index 82b3c5319..d33f31c09 100644 --- a/builder/picolibc.go +++ b/builder/picolibc.go @@ -29,6 +29,7 @@ var libPicolibc = Library{ "-D_HAVE_ALIAS_ATTRIBUTE", "-DTINY_STDIO", "-DPOSIX_IO", + "-DFORMAT_DEFAULT_INTEGER", // use __i_vfprintf and __i_vfscanf by default "-D_IEEE_LIBM", "-D__OBSOLETE_MATH_FLOAT=1", // use old math code that doesn't expect a FPU "-D__OBSOLETE_MATH_DOUBLE=0", diff --git a/testdata/cgo/main.c b/testdata/cgo/main.c index 7fb702ed6..4a5bd6b9c 100644 --- a/testdata/cgo/main.c +++ b/testdata/cgo/main.c @@ -1,5 +1,6 @@ #include #include "main.h" +#include int global = 3; bool globalBool = 1; @@ -72,3 +73,7 @@ void arraydecay(int buf1[5], int buf2[3][8], int buf3[4][7][2]) { double doSqrt(double x) { return sqrt(x); } + +void printf_single_int(char *format, int arg) { + printf(format, arg); +} diff --git a/testdata/cgo/main.go b/testdata/cgo/main.go index fa3380bce..ddd1992e2 100644 --- a/testdata/cgo/main.go +++ b/testdata/cgo/main.go @@ -179,6 +179,10 @@ func main() { // libc: test basic stdio functionality putsBuf := []byte("line written using C puts\x00") C.puts((*C.char)(unsafe.Pointer(&putsBuf[0]))) + + // libc: test whether printf works in C. + printfBuf := []byte("line written using C printf with value=%d\n\x00") + C.printf_single_int((*C.char)(unsafe.Pointer(&printfBuf[0])), -21) } func printUnion(union C.joined_t) C.joined_t { diff --git a/testdata/cgo/main.h b/testdata/cgo/main.h index f5405ade6..e7c64ffc3 100644 --- a/testdata/cgo/main.h +++ b/testdata/cgo/main.h @@ -152,3 +152,5 @@ typedef int arraydecay_buf3[4][7][2]; void arraydecay(int buf1[5], int buf2[3][8], arraydecay_buf3 buf3); double doSqrt(double); + +void printf_single_int(char *format, int arg); diff --git a/testdata/cgo/out.txt b/testdata/cgo/out.txt index ae92c87a7..4ea45d864 100644 --- a/testdata/cgo/out.txt +++ b/testdata/cgo/out.txt @@ -77,3 +77,4 @@ copied string: foobar CGo sqrt(3): +1.732051e+000 C sqrt(3): +1.732051e+000 line written using C puts +line written using C printf with value=-21