Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Scala Native Examples #3657

Draft
wants to merge 37 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
3346097
Create Foo.scala
c0d33ngr Oct 3, 2024
52a38c4
Add files via upload
c0d33ngr Oct 3, 2024
367bcb0
import scala native lib properly
c0d33ngr Oct 3, 2024
a48faac
Update Foo.scala
c0d33ngr Oct 3, 2024
7df4367
Create FooTests.scala
c0d33ngr Oct 3, 2024
cee766e
Update build.mill
c0d33ngr Oct 3, 2024
ef802d3
Create Hello world.c
c0d33ngr Oct 3, 2024
11a6e75
Create HelloWorld.scala
c0d33ngr Oct 3, 2024
2783005
Rename Hello world.c to HelloWorld.c
c0d33ngr Oct 3, 2024
24b9543
update interop example
c0d33ngr Oct 3, 2024
b1015e7
Create build.mill
c0d33ngr Oct 3, 2024
8740b01
Create Makefile
c0d33ngr Oct 3, 2024
19ed282
Update build.mill of interop example
c0d33ngr Oct 3, 2024
2c9d4ec
add some example files
c0d33ngr Oct 4, 2024
61f53b7
add changes to 2-interop example
c0d33ngr Oct 5, 2024
9dc9053
update scala-native examples code
c0d33ngr Oct 5, 2024
4f3a61b
update scala=native example code
c0d33ngr Oct 5, 2024
dfb0e5e
Create draft build.mill for multi-module example
c0d33ngr Oct 5, 2024
bd1f756
Create MyResource.txt
c0d33ngr Oct 5, 2024
e41d609
Create MyOtherResource.txt
c0d33ngr Oct 5, 2024
69ffd7d
add common-config code
c0d33ngr Oct 6, 2024
7cada73
add doc file for scala-native examples
c0d33ngr Oct 6, 2024
bf2edb2
add draft build.mill for common-config example
c0d33ngr Oct 6, 2024
8dc0853
update 1-simple example code
c0d33ngr Oct 6, 2024
a367ead
fix the errors in 1-simple and 2-interop examples
c0d33ngr Oct 7, 2024
55c143f
add third party dependency to 1-simple
c0d33ngr Oct 8, 2024
c37e9a0
update code to fix some errors
c0d33ngr Oct 9, 2024
24729ea
updated code examples
c0d33ngr Oct 12, 2024
f047193
fix syntax in 1-simple example build.mill file
c0d33ngr Oct 12, 2024
3919933
update code
c0d33ngr Oct 12, 2024
950440a
use task in build.mill
c0d33ngr Oct 17, 2024
dda37bf
add pathref in build.mill
c0d33ngr Oct 17, 2024
ca18f5f
update 4-common-config to scala native specific configs
c0d33ngr Oct 18, 2024
7cbf92e
update the build.mill files
c0d33ngr Oct 20, 2024
82ed7c0
rework the scala native examples
c0d33ngr Oct 21, 2024
ba260e5
add 1-simple to testsuite
c0d33ngr Oct 23, 2024
aed36a5
add 4-common-config to testsuite
c0d33ngr Oct 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions docs/modules/ROOT/pages/scalalib/native-examples.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
= Scala Native Examples
:page-aliases: Scala_Native_Examples.adoc

++++
<script>
gtag('config', 'AW-16649289906');
</script>
++++


This page contains examples of using Mill as a build tool for scala-native applications.
It covers setting up a basic basic scala-native application that calls C function within it,
as well as example of two modules with a scala-naive application.
c0d33ngr marked this conversation as resolved.
Show resolved Hide resolved

== Simple

include::partial$example/scalalib/native/1-simple.adoc[]

== Interop

include::partial$example/scalalib/native/2-interop.adoc[]

== Multi-Module

include::partial$example/scalalib/native/3-multi-module.adoc[]

== Common Config

include::partial$example/scalalib/native/4-common-config.adoc[]

137 changes: 137 additions & 0 deletions example/scalalib/native/1-simple/build.mill
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
//// SNIPPET:BUILD
package build
import mill._, scalalib._, scalanativelib._

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

// You can have arbitrary numbers of third-party dependencies
def ivyDeps = Agg(
ivy"com.lihaoyi::fansi::0.5.0",
ivy"com.lihaoyi::mainargs::0.7.6"
)

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

// This is a basic Mill build for a single `ScalaNativeModule`, with two
// third-party dependencies and a test suite using the uTest framework. As a
// single-module project, it `extends RootModule` to mark ``object `package``` as the
// top-level module in the build. This lets us directly perform operations
// `./mill compile` or `./mill run` without needing to prefix it as
// `foo.compile` or `foo.run`.
//
c0d33ngr marked this conversation as resolved.
Show resolved Hide resolved
//// SNIPPET:TREE
// ----
// build.mill
// src/
// Foo.scala
// ...
// test/
// src/
// FooTests.scala
// out/
// compile.json
// compile.dest/
// ...
// test/
// compile.json
// compile.dest/
// ...
// ----
//
c0d33ngr marked this conversation as resolved.
Show resolved Hide resolved

//// SNIPPET:END

//
// You can download this example project using the *download* link above
// if you want to try out the commands below yourself. The only requirement is
// that you have some version of the JVM installed; the `./mill` script takes
// care of any further dependencies that need to be downloaded.
//
// The source code for this module lives in the `src/` folder.
// Output for this module (compiled files, resolved dependency lists, ...)
// lives in `out/`.
//
//// SNIPPET:DEPENDENCIES
//
//
/** Usage

> ./mill resolve _ # List what tasks are available to run
assembly
...
clean
...
compile
...
run
...
show
...
inspect
...

> ./mill run --text hello
c0d33ngr marked this conversation as resolved.
Show resolved Hide resolved
...
#1 [info] Linking native code (immix gc, none lto) (185 ms)
#1 [info] Postprocessing (0 ms)
#1 [info] Total (24202 ms)
[90/1] run
<h1>hello</h1>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The syntax here looks wrong; are the example tests actually being run?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might need to register this folder in the example/package.mill. Please follow the existing code that's already there, and follow instructions in the readme for how to run them, and verify thst you can run the tests and that they pass

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay


> ./mill nativeLink # Build and link native binary
...
#2 [info] done compiling
#2 [85/85] nativeLink
#2 [info] Linking (multithreadingEnabled=true, disable if not used) (2509 ms)
#2 [info] Discovered 1351 classes and 10597 methods after classloading
#2 [info] Checking intermediate code (quick) (260 ms)
#2 [info] Multithreading was not explicitly enabled - initial class loading has not detected any usage of system threads. Multithreading support will be disabled to improve performance.
#2 [info] Linking (multithreadingEnabled=false) (2037 ms)
#2 [info] Discovered 1182 classes and 9352 methods after classloading
#2 [info] Checking intermediate code (quick) (32 ms)
#2 [info] Discovered 1162 classes and 7192 methods after optimization
#2 [info] Optimizing (debug mode) (6107 ms)
...

> ./mill releaseMode # Specifies the `releaseMode`. Default releaseMode is set to Debug.
...
#1 [2/2] releaseMode

> ./mill nativeOptimize # Set native optimization. The Default value is true.
...
#2 [info] done compiling
#1 [2/2] nativeOptimize

> ./mill nativeIncrementalCompilation # Set native incremental compilation. It recompiles only changed files. Default is false.
c0d33ngr marked this conversation as resolved.
Show resolved Hide resolved
[build.mill] #2 [55/59] compile
...
#2 [info] done compiling
#1 [1/1] nativeIncrementalCompilation

> ./mill nativeLinkingOptions # Set path to native linking options. Default path is `"-L/usr/local/lib`.
[build.mill] #1 [55/59] compile
...
#1 [info] done compiling
#1 [7/7] nativeLinkingOptions

*/

//// SNIPPET:END

//
// You can run `+mill resolve __+` to see a full list of the different tasks that
// are available, `+mill resolve _+` to see the tasks within `foo`,
// `mill inspect compile` to inspect a task's doc-comment documentation or what
// it depends on, or `mill show foo.scalaVersion` to show the output of any task.
//
// The most common *tasks* that Mill can run are cached *targets*, such as
// `compile`, and un-cached *commands* such as `foo.run`. Targets do not
// re-evaluate unless one of their inputs changes, whereas commands re-run every
// time.

25 changes: 25 additions & 0 deletions example/scalalib/native/1-simple/src/Foo.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package foo

import scala.scalanative.libc._
import scala.scalanative.unsafe._
import mainargs.{main, ParserForMethods}
import fansi._

object Foo {

def generateHtml(text: String): CString = {
val colored = Console.RED + "<h1>" + text + "</h1>" + Console.RESET + "\n"
c0d33ngr marked this conversation as resolved.
Show resolved Hide resolved

implicit val z: Zone = Zone.open
val cResult = toCString(colored)
z.close()
cResult
}

@main
def main(text: String) = {
stdio.printf(generateHtml(text))
}

def main(args: Array[String]): Unit = ParserForMethods(this).runOrExit(args)
}
c0d33ngr marked this conversation as resolved.
Show resolved Hide resolved
21 changes: 21 additions & 0 deletions example/scalalib/native/1-simple/test/src/FooTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package foo

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

object FooTests extends TestSuite {
def tests = Tests {
test("simple one") {
val result = Foo.generateHtml("hello")
val colored = Console.RED + "<h1>hello</h1>" + Console.RESET + "\n"
assert(fromCString(result) == colored)
fromCString(result)
}
test("simple two") {
val result = Foo.generateHtml("hello world")
val colored = Console.RED + "<h1>hello world</h1>" + Console.RESET + "\n"
assert(fromCString(result) == colored)
fromCString(result)
}
}
}
76 changes: 76 additions & 0 deletions example/scalalib/native/2-interop/build.mill
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
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")
}
}


// This is an example of how to use Mill to compile C code together with your Scala Native
// code.
//
// The above build expect the following project layout:
//
// ----
// build.mill
// src/
// foo/
// HelloWorld.scala
//
// native-src/
// HelloWorld.c
//
// test/
// src/
// foo/
// HelloWorldTests.scala
// ----
//
// This example is pretty minimal, but it demonstrates the core principles, and
// can be extended if necessary to more elaborate use cases. The `native*` tasks
// can also be extracted out into a `trait1 for re-use if you have multiple
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

`trait

?

// `ScalaNativeModule`s that need native C components.

/** Usage

> ./mill nativeCompiled
...
#1 [info] done compiling
#1 [1/1] nativeCompiled

> ./mill run
...
#2 [info] Compiling to native code (4749 ms)
#2 [info] Linking with [pthread, dl, HelloWorld]
#2 [info] Linking native code (immix gc, none lto) (117 ms)
#2 [info] Postprocessing (0 ms)
#2 [info] Total (11344 ms)
[90/1] run
Running HelloWorld function
Done...
Reversed: !dlroW ,olle

> ./mill test


*/
15 changes: 15 additions & 0 deletions example/scalalib/native/2-interop/native-src/HelloWorld.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#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;
}
21 changes: 21 additions & 0 deletions example/scalalib/native/2-interop/src/foo/HelloWorld.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
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
}

19 changes: 19 additions & 0 deletions example/scalalib/native/2-interop/test/src/HelloWorldTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
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)
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include <string.h>
#include "bar.h"

// Function to count the length of a string
int stringLength(const char* str) {
return strlen(str);
}

7 changes: 7 additions & 0 deletions example/scalalib/native/3-multi-module/bar/native-src/bar.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#ifndef BAR_H
#define BAR_H

// Declaration of the function to count string length
int stringLength(const char* str);

#endif
26 changes: 26 additions & 0 deletions example/scalalib/native/3-multi-module/bar/src/Bar.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package bar

import scala.scalanative.libc._
import scala.scalanative.unsafe._

object Bar {
def main(args: Array[String]): Unit = {
println("Running HelloWorld function")
implicit val z: Zone = Zone.open
val result = toCString(args(0))
val barValue = HelloWorldBar.stringLength(result)
z.close()
stdio.printf(c"Bar value: Argument length is %i\n", barValue)
println("Done...")
}
}

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

Loading
Loading