This is an automated email from the ASF dual-hosted git repository. sjanc pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mynewt-newt.git
commit 410d8485a8669925b516297e54a31bd5a4288215 Author: Szymon Janc <[email protected]> AuthorDate: Wed Oct 21 21:23:43 2026 +0200 newt/builder: Link with C++ compiler if build has C++ sources The elf was always linked with compiler.path.cc. If any package contains C++ sources this fails, as gcc does not link the C++ runtime libraries: undefined reference to `operator new(unsigned long)' undefined reference to `__gxx_personality_v0' Track whether the build compiled any (not ignored) C++ source and link with compiler.path.cpp in that case, which selects the right libraries by default. The generated CMakeLists.txt sets LINKER_LANGUAGE to CXX accordingly. C-only builds are still linked with compiler.path.cc. Assisted-by: Claude:claude-opus-5-5 --- newt/builder/build.go | 12 ++++++++++++ newt/builder/cmake.go | 20 +++++++++++++++++--- newt/toolchain/compiler.go | 19 +++++++++++++++++-- 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/newt/builder/build.go b/newt/builder/build.go index 26d7cf75..3c2bade8 100644 --- a/newt/builder/build.go +++ b/newt/builder/build.go @@ -60,6 +60,9 @@ type Builder struct { linkElf string injectedSettings map[string]string modifiedExtRepos []string + + // Whether the last build compiled any C++ sources. + hasCxx bool } func NewBuilder( @@ -496,6 +499,7 @@ func (b *Builder) link(elfName string, linkerScripts []string, } c.LinkerScripts = linkerScripts + c.LinkCxx = b.hasCxx c.AutogeneratedLinkerIncludeDir, err = b.GetAutogeneratedLinkerIncludeDir() if err != nil { return err @@ -706,6 +710,7 @@ func (b *Builder) Build() error { // needs to be compiled. entries := []toolchain.CompilerJob{} bpkgCompilerMap := map[*BuildPackage]*toolchain.Compiler{} + b.hasCxx = false for _, bpkg := range bpkgs { subEntries, err := b.collectCompileEntriesBpkg(bpkg) if err != nil { @@ -713,6 +718,13 @@ func (b *Builder) Build() error { } entries = append(entries, subEntries...) + for _, entry := range subEntries { + if entry.CompilerType == toolchain.COMPILER_TYPE_CPP && + !entry.Compiler.ShouldIgnoreFile(entry.Filename) { + b.hasCxx = true + } + } + b.modifiedExtRepos = append(b.modifiedExtRepos, bpkg.getModifiedReposNames()...) if len(subEntries) > 0 { diff --git a/newt/builder/cmake.go b/newt/builder/cmake.go index af95634e..ba732998 100644 --- a/newt/builder/cmake.go +++ b/newt/builder/cmake.go @@ -176,6 +176,10 @@ func (b *Builder) CMakeBuildPackageWrite(w io.Writer, bpkg *BuildPackage, continue } + if s.CompilerType == toolchain.COMPILER_TYPE_CPP { + b.hasCxx = true + } + if s.CompilerType == toolchain.COMPILER_TYPE_ARCHIVE { arFile := trimProjectPath(s.Filename) linkDirs = append(linkDirs, filepath.Dir(arFile)) @@ -229,6 +233,7 @@ func (b *Builder) CMakeTargetWrite(w io.Writer, targetCompiler *toolchain.Compil c.AddInfo(b.GetCompilerInfo()) builtPackages := []*BuildPackage{} + b.hasCxx = false for _, bpkg := range bpkgs { builtPackage, err := b.CMakeBuildPackageWrite(w, bpkg, &linkFlags, &libraries) @@ -306,6 +311,13 @@ func (b *Builder) CMakeTargetWrite(w io.Writer, targetCompiler *toolchain.Compil cxxFlags = toolchain.SortFlags(cxxFlags...) lFlags = append(lFlags, cxxFlags...) + // Link with the C++ driver if there are C++ sources, so that the C++ + // runtime libraries are linked in by default. + linkerLang := "C" + if b.hasCxx { + linkerLang = "CXX" + } + fmt.Fprintf(w, `set_target_properties(%s PROPERTIES @@ -313,18 +325,20 @@ func (b *Builder) CMakeTargetWrite(w io.Writer, targetCompiler *toolchain.Compil LIBRARY_OUTPUT_DIRECTORY %s RUNTIME_OUTPUT_DIRECTORY %s LINK_FLAGS "%s" - LINKER_LANGUAGE C)`, + LINKER_LANGUAGE %s)`, elfName, elfOutputDir, elfOutputDir, elfOutputDir, - strings.Join(escapeFlagsSlice(lFlags), " ")) + strings.Join(escapeFlagsSlice(lFlags), " "), + linkerLang) fmt.Fprintln(w) libs := strings.Join(getLibsFromLinkerFlags(lFlags), " ") fmt.Fprintf(w, "# Workaround for gcc linker woes\n") - fmt.Fprintf(w, "set(CMAKE_C_LINK_EXECUTABLE \"${CMAKE_C_LINK_EXECUTABLE} %s\")\n", libs) + fmt.Fprintf(w, "set(CMAKE_%s_LINK_EXECUTABLE \"${CMAKE_%s_LINK_EXECUTABLE} %s\")\n", + linkerLang, linkerLang, libs) fmt.Fprintln(w) return nil diff --git a/newt/toolchain/compiler.go b/newt/toolchain/compiler.go index 80855c04..0ee968ae 100644 --- a/newt/toolchain/compiler.go +++ b/newt/toolchain/compiler.go @@ -78,6 +78,11 @@ type Compiler struct { LinkerScripts []string AutogeneratedLinkerIncludeDir string + // Link with the C++ compiler driver instead of the C one, so that the C++ + // runtime libraries are linked in by default. Set when the build + // contains C++ sources. + LinkCxx bool + // Needs to be locked whenever a mutable field in this struct is accessed // during a build: objPathList, compileCommands and the dependency // tracker's most recent file data. @@ -1191,8 +1196,9 @@ func (c *Compiler) CompileBinaryCmd(dstFile string, options map[string]bool, libList := c.getStaticLibs(util.UniqueStaticLib(staticLib)) + linkerPath, _ := c.linkerPath() cmd := []string{ - c.ccPath, + linkerPath, "-o", dstFile, } @@ -1242,6 +1248,15 @@ func (c *Compiler) CompileBinaryCmd(dstFile string, options map[string]bool, return cmd } +// Returns the compiler driver used for linking and its compiler.yml key. +func (c *Compiler) linkerPath() (string, string) { + if c.LinkCxx { + return c.cppPath, "compiler.path.cpp" + } + + return c.ccPath, "compiler.path.cc" +} + // Links the specified elf file. // // @param dstFile The filename of the destination elf file to @@ -1255,7 +1270,7 @@ func (c *Compiler) CompileBinary(dstFile string, options map[string]bool, // Make sure the compiler package info is added to the global set. c.ensureLclInfoAdded() - if err := c.checkToolPath(c.ccPath, "compiler.path.cc"); err != nil { + if err := c.checkToolPath(c.linkerPath()); err != nil { return err }
