-
-
Notifications
You must be signed in to change notification settings - Fork 408
Add Scala Native Examples #3657
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
Changes from all commits
3346097
52a38c4
367bcb0
a48faac
7df4367
cee766e
ef802d3
11a6e75
2783005
24b9543
b1015e7
8740b01
19ed282
2c9d4ec
61f53b7
9dc9053
4f3a61b
dfb0e5e
bd1f756
e41d609
69ffd7d
7cada73
bf2edb2
8dc0853
a367ead
55c143f
c37e9a0
24729ea
f047193
3919933
950440a
dda37bf
ca18f5f
7cbf92e
82ed7c0
ba260e5
aed36a5
d98e8c2
45fb020
1c5e946
8659821
6b5b1bf
956f223
f2c0e9e
5768cbd
7e312b2
8d3e300
495c44f
4501c2e
9242768
76bb361
a599da6
9ac2cc1
084dfb4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 scala-native application that calls C function within it, | ||
as well as example of two modules with a scala-native application. | ||
|
||
== 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[] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package build | ||
import mill._, scalalib._, scalanativelib._ | ||
|
||
object `package` extends RootModule with ScalaNativeModule { | ||
def scalaVersion = "3.3.4" | ||
def scalaNativeVersion = "0.5.5" | ||
|
||
// You can have arbitrary numbers of third-party dependencies | ||
def ivyDeps = Agg( | ||
ivy"com.lihaoyi::mainargs::0.7.6" | ||
) | ||
|
||
object test extends ScalaNativeTests with TestModule.Utest{ | ||
def ivyDeps = Agg(ivy"com.lihaoyi::utest::0.8.4") | ||
def testFramework = "utest.runner.Framework" | ||
} | ||
} | ||
|
||
// This example demonstrates a simple Scala program that generates HTML content | ||
// from user-provided text and prints it to the standard output, utilizing Scala | ||
// Native for native integration and `mainargs` for command-line argument parsing. | ||
|
||
/** Usage | ||
|
||
> ./mill run --text hello | ||
<h1>hello</h1> | ||
|
||
> ./mill show nativeLink # Build and link native binary | ||
".../out/nativeLink.dest/out" | ||
|
||
> ./out/nativeLink.dest/out --text hello # Run the executable | ||
<h1>hello</h1> | ||
|
||
*/ |
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} | ||
|
||
object Foo { | ||
|
||
def generateHtml(text: String): CString = { | ||
val html = "<h1>" + text + "</h1>\n" | ||
|
||
implicit val z: Zone = Zone.open() | ||
val cResult = toCString(html) | ||
cResult | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alright... Noted I'm into it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Hello @sideeffffect Here's is the updated code. Are they good to go? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @c0d33ngr please open a new PR with these changes, it's easier to discuss individual code changes on the PR, and when we're done we can just click the merge button :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alright.. |
||
|
||
} | ||
|
||
@main | ||
def main(text: String) = { | ||
stdio.printf(generateHtml(text)) | ||
} | ||
|
||
def main(args: Array[String]): Unit = ParserForMethods(this).runOrExit(args) | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package foo | ||
|
||
import scala.scalanative.unsafe._ | ||
import utest._ | ||
|
||
object FooTests extends TestSuite { | ||
def tests = Tests { | ||
test("simple one") { | ||
val result = Foo.generateHtml("hello") | ||
assert(fromCString(result) == "<h1>hello</h1>\n") | ||
fromCString(result) | ||
} | ||
test("simple two") { | ||
val result = Foo.generateHtml("hello world") | ||
assert(fromCString(result) == "<h1>hello world</h1>\n") | ||
fromCString(result) | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package build | ||
import mill._, scalalib._, scalanativelib._ | ||
|
||
object `package` extends RootModule with ScalaNativeModule { | ||
def scalaVersion = "3.3.4" | ||
def scalaNativeVersion = "0.5.5" | ||
|
||
object test extends ScalaNativeTests { | ||
def ivyDeps = Agg(ivy"com.lihaoyi::utest::0.8.4") | ||
def testFramework = "utest.runner.Framework" | ||
} | ||
|
||
} | ||
|
||
|
||
// 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 | ||
// | ||
// resources/ | ||
// scala-native/ | ||
// HelloWorld.c | ||
// | ||
// test/ | ||
// src/ | ||
// foo/ | ||
// HelloWorldTests.scala | ||
// ---- | ||
// | ||
// *Note:* C/C++ source files need to be in `resources/scala-native` directory so | ||
// It can be linked and compiled successfully. More info from Scala Native doc | ||
// https://scala-native.org/en/stable/user/native.html#using-libraries-with-native-code[here] | ||
// and also Scala user forum https://users.scala-lang.org/t/how-to-test-scala-native-code-interop-with-c/10314/3?u=c0d33ngr[here] | ||
// | ||
// This example is pretty minimal, but it demonstrates the core principles, and | ||
// can be extended if necessary to more elaborate use cases. | ||
|
||
/** Usage | ||
|
||
> ./mill run | ||
Running HelloWorld function | ||
Done... | ||
Reversed: !dlroW ,olleH | ||
|
||
> ./mill test | ||
Tests: 1, Passed: 1, Failed: 0 | ||
|
||
*/ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
#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)); // +1 for null terminator | ||
|
||
if (reversed == NULL) { | ||
return NULL; // handle malloc failure | ||
} | ||
|
||
for (int i = 0; i < length; i++) { | ||
reversed[i] = str[length - i - 1]; | ||
} | ||
reversed[length] = '\0'; // Null-terminate the string | ||
|
||
return reversed; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package foo | ||
|
||
import scala.scalanative.libc._ | ||
import scala.scalanative.unsafe._ | ||
import scala.scalanative.unsigned._ | ||
|
||
object Main { | ||
def main(args: Array[String]): Unit = { | ||
println("Running HelloWorld function") | ||
val reversedStr = HelloWorld.reverseString(c"Hello, World!") | ||
println("Reversed: " + fromCString(reversedStr)) | ||
stdlib.free(reversedStr) // Free the allocated memory | ||
println("Done...") | ||
} | ||
} | ||
|
||
// Define the external module, the C library containing our function "reverseString" | ||
@extern | ||
object HelloWorld { | ||
def reverseString(str: CString): CString = extern | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package foo | ||
|
||
import utest._ | ||
import scala.scalanative.unsafe._ | ||
import scala.scalanative.libc.stdlib | ||
|
||
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) | ||
|
||
stdlib.free(result) // Free memory after the test | ||
} | ||
} | ||
} | ||
|
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); | ||
} | ||
|
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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
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) | ||
stdio.printf(c"Bar value: Argument length is %i\n", barValue) | ||
println("Done...") | ||
} | ||
} | ||
|
||
// Define the external module, the C library containing our function "stringLength" | ||
@extern | ||
// Arbitrary object name | ||
object HelloWorldBar { | ||
// Name and signature of C function | ||
def stringLength(str: CString): CInt = extern | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package bar | ||
|
||
import utest._ | ||
import scala.scalanative.unsafe._ | ||
|
||
object BarTests extends TestSuite { | ||
def tests = Tests { | ||
test("simple one") { | ||
val result = HelloWorldBar.stringLength(c"hello") | ||
assert(result == 5) | ||
result | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package build | ||
import mill._, scalalib._, scalanativelib._ | ||
|
||
trait MyModule extends ScalaNativeModule { | ||
def scalaVersion = "3.3.4" | ||
def scalaNativeVersion = "0.5.5" | ||
|
||
object test extends ScalaNativeTests { | ||
def ivyDeps = Agg(ivy"com.lihaoyi::utest::0.8.4") | ||
def testFramework = "utest.runner.Framework" | ||
} | ||
} | ||
|
||
object foo extends MyModule { | ||
def moduleDeps = Seq(bar) | ||
|
||
def ivyDeps = Agg(ivy"com.lihaoyi::mainargs::0.7.6") | ||
} | ||
|
||
object bar extends MyModule | ||
|
||
// This example contains a simple Mill build with two modules, `foo` and `bar`. | ||
// We don't mark either module as top-level using `extends RootModule`, so | ||
// running tasks needs to use the module name as the prefix e.g. `foo.run` or | ||
// `bar.run`. You can define multiple modules the same way you define a single | ||
// module, using `def moduleDeps` to define the relationship between them. | ||
// | ||
// Note that we split out the `test` submodule configuration common to both | ||
// modules into a separate `trait MyModule`. This lets us avoid the need to | ||
// copy-paste common settings, while still letting us define any per-module | ||
// configuration such as `ivyDeps` specific to a particular module. | ||
// | ||
// The above builds expect the following project layout: | ||
// | ||
// ---- | ||
// build.mill | ||
// bar/ | ||
// resources/ | ||
// scala-native/ | ||
// bar.h | ||
// HelloWorldBar.c | ||
// src/ | ||
// Bar.scala | ||
// test/ | ||
// src/ | ||
// BarTests.scala | ||
// foo/ | ||
// resources/ | ||
// scala-native/ | ||
// bar.h | ||
// HelloWorldFoo.c | ||
// src/ | ||
// Foo.scala | ||
// | ||
// ---- | ||
// | ||
// *Note:* C/C++ source files need to be in `resources/scala-native` directory so | ||
// It can be linked and compiled successfully. More info from Scala Native doc | ||
// https://scala-native.org/en/stable/user/native.html#using-libraries-with-native-code[here] | ||
// and also Scala user forum https://users.scala-lang.org/t/how-to-test-scala-native-code-interop-with-c/10314/3?u=c0d33ngr[here] | ||
|
||
/** Usage | ||
|
||
> ./mill bar.run hello | ||
Running HelloWorld function | ||
Done... | ||
Bar value: Argument length is 5 | ||
|
||
> ./mill bar.test | ||
Tests: 1, Passed: 1, Failed: 0 | ||
|
||
> ./mill foo.run --bar-text hello --foo-text world | ||
Foo.value: The vowel density of 'world' is 20 | ||
Bar.value: The string length of 'hello' is 5 | ||
|
||
*/ |
Uh oh!
There was an error while loading. Please reload this page.