OiO.lk Blog C++ How to fix test.nativeLink scala.scalanative.build.BuildException error
C++

How to fix test.nativeLink scala.scalanative.build.BuildException error


I’m trying to run a test on a scala native code that calls a C function from within it but got this error

[info] done compiling
[2666]   1 targets failed
[2666]   test.nativeLink scala.scalanative.build.BuildException: Failed to link /workspaces/mill-codespace/mill/out/example/scalalib/native/2-interop/local/test.dest/sandbox/run-1/out/test/nativeWorkdir.dest/native/scala.scalanative.testinterface.TestMain
[2666]   ,)

Here is my project structure

 build.mill
 src/foo/
   HelloWorld.scala
 native-src/
HelloWorld.c
 test/
     src/
         foo/
             HelloWorldTests.scala

I’m using mill build tool.
What I find strange is that mill run compiles and work successfully but when I try to run mill test, for the test I got the linking error

HelloWorld.scala

package foo
import scala.scalanative.libc._
import scala.scalanative.unsafe._

object Main {
  def main(args: Array[String]): Unit = {
    println("Running HelloWorld function")
    stdio.printf(c"Reversed: %s\n", HelloWorld.reverseString(c"Hello, World!"))
    println("Done...")
  }
}

// Define the external module, the C library containing our function "reverseString"
@extern
@link("HelloWorld")
// Arbitrary object name
object HelloWorld {
  // Name and signature of C function
  def reverseString(str: CString): CString = extern
}

HelloWorld.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

char* reverseString(const char *str) {
  int length = strlen(str);
  char *reversed = (char*) malloc((length + 1) * sizeof(char));

  for (int i = 0; i < length; i++) {
    reversed[i] = str[length - i - 1];
  }
  reversed[length] = '\0'; // Null-terminate the string

  return reversed;
}

HelloWorldTests.scala

package foo

import utest._
import scala.scalanative.unsafe._

object HelloWorldTest extends TestSuite {
  val tests = Tests {
    test("reverseString should reverse a C string correctly") {
      val expected = "!dlrow olleH"

      val result = HelloWorld.reverseString(c"Hello World!")

      // Check if the reversed string matches the expected result
      assert(fromCString(result) == expected)
      fromCString(result)
    }
  }
}

and lastly my config file

build.mill

package build
import mill._, scalalib._, scalanativelib._

object `package` extends RootModule with ScalaNativeModule {
  def scalaVersion = "2.13.11"
  def scalaNativeVersion = "0.5.5"

  object test extends ScalaNativeTests {
    def ivyDeps = Agg(ivy"com.lihaoyi::utest::0.8.4")
    def testFramework = "utest.runner.Framework"
  }

  def nativeLinkingOptions = Seq("-L" + millSourcePath.toString + "/target")

  // Compiled C
  def nativeCompiled = T {
    os.makeDir.all(millSourcePath / "target")

    os.proc("gcc", "-m64", "-shared",
      "-c", millSourcePath.toString + "/native-src/HelloWorld.c",
      "-o", millSourcePath.toString + "/target/libHelloWorld.so"
    ).call(stdout = os.Inherit)

    PathRef(T.dest / "target/libHelloWorld.so")    
  }
}

the build file change changes the nativeLinkingOptions to a custom path named target

I’ve tried changing the test framework version, and include the nativelinkOptions within test

I was expecting it to compile and run the tests successfully



You need to sign in to view this answers

Exit mobile version