diff --git a/docs/modules/ROOT/pages/scalalib/native-examples.adoc b/docs/modules/ROOT/pages/scalalib/native-examples.adoc
new file mode 100644
index 00000000000..90d11b01714
--- /dev/null
+++ b/docs/modules/ROOT/pages/scalalib/native-examples.adoc
@@ -0,0 +1,30 @@
+= Scala Native Examples
+:page-aliases: Scala_Native_Examples.adoc
+
+++++
+
+++++
+
+
+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[]
+
diff --git a/example/package.mill b/example/package.mill
index 9544d316c76..4b45f096411 100644
--- a/example/package.mill
+++ b/example/package.mill
@@ -53,6 +53,7 @@ object `package` extends RootModule with Module {
object publishing extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "publishing"))
object module extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "module"))
object web extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "web"))
+ object native extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "native"))
}
object depth extends Module {
diff --git a/example/scalalib/native/1-simple/build.mill b/example/scalalib/native/1-simple/build.mill
new file mode 100644
index 00000000000..6dc53fe97ca
--- /dev/null
+++ b/example/scalalib/native/1-simple/build.mill
@@ -0,0 +1,56 @@
+//// 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::mainargs::0.7.6"
+ )
+
+ object test extends ScalaNativeTests {
+ def ivyDeps = Agg(ivy"com.lihaoyi::utest::0.8.4")
+ def testFramework = "utest.runner.Framework"
+ }
+}
+//// SNIPPET:END
+//
+//
+//// SNIPPET:DEPENDENCIES
+//
+//
+/** Usage
+
+
+> ./mill nativeLink # Build and link native binary
+...
+Linking native code (immix gc, none lto) ...
+
+> ./mill releaseMode # Generate some files in `out` directory about the `releaseMode`. Default releaseMode is set to Debug.
+
+> ./mill nativeOptimize # Generate some files in `out` directory about native optimization. The Default value is true.
+
+> ./mill nativeIncrementalCompilation # Generate config files in `out` directory about native incremental compilation. Default is false.
+
+> ./mill nativeLinkingOptions # Generate config files in `out` directory about the path in native linking options. Default path is `"-L/usr/local/lib`.
+
+
+*/
+
+//// 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.
+
+
diff --git a/example/scalalib/native/1-simple/src/Foo.scala b/example/scalalib/native/1-simple/src/Foo.scala
new file mode 100644
index 00000000000..a692bed07a1
--- /dev/null
+++ b/example/scalalib/native/1-simple/src/Foo.scala
@@ -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 = "
" + text + "
\n"
+
+ implicit val z: Zone = Zone.open
+ val cResult = toCString(html)
+ z.close()
+ cResult
+ }
+
+ @main
+ def main(text: String) = {
+ stdio.printf(generateHtml(text))
+ }
+
+ def main(args: Array[String]): Unit = ParserForMethods(this).runOrExit(args)
+}
+
diff --git a/example/scalalib/native/1-simple/test/src/FooTests.scala b/example/scalalib/native/1-simple/test/src/FooTests.scala
new file mode 100644
index 00000000000..33ce63fc225
--- /dev/null
+++ b/example/scalalib/native/1-simple/test/src/FooTests.scala
@@ -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) == "hello
\n")
+ fromCString(result)
+ }
+ test("simple two") {
+ val result = Foo.generateHtml("hello world")
+ assert(fromCString(result) == "hello world
\n")
+ fromCString(result)
+ }
+ }
+}
+
diff --git a/example/scalalib/native/2-interop/build.mill b/example/scalalib/native/2-interop/build.mill
new file mode 100644
index 00000000000..97baedc4a14
--- /dev/null
+++ b/example/scalalib/native/2-interop/build.mill
@@ -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
+// `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
+
+
+*/
diff --git a/example/scalalib/native/2-interop/native-src/HelloWorld.c b/example/scalalib/native/2-interop/native-src/HelloWorld.c
new file mode 100644
index 00000000000..e5240b7d041
--- /dev/null
+++ b/example/scalalib/native/2-interop/native-src/HelloWorld.c
@@ -0,0 +1,15 @@
+#include
+#include
+#include
+
+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;
+}
diff --git a/example/scalalib/native/2-interop/src/foo/HelloWorld.scala b/example/scalalib/native/2-interop/src/foo/HelloWorld.scala
new file mode 100644
index 00000000000..55bf43573bd
--- /dev/null
+++ b/example/scalalib/native/2-interop/src/foo/HelloWorld.scala
@@ -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
+}
+
diff --git a/example/scalalib/native/2-interop/test/src/HelloWorldTests.scala b/example/scalalib/native/2-interop/test/src/HelloWorldTests.scala
new file mode 100644
index 00000000000..e88bda99693
--- /dev/null
+++ b/example/scalalib/native/2-interop/test/src/HelloWorldTests.scala
@@ -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)
+ }
+ }
+}
+
diff --git a/example/scalalib/native/3-multi-module/bar/native-src/HelloWorldBar.c b/example/scalalib/native/3-multi-module/bar/native-src/HelloWorldBar.c
new file mode 100644
index 00000000000..1a1d142c369
--- /dev/null
+++ b/example/scalalib/native/3-multi-module/bar/native-src/HelloWorldBar.c
@@ -0,0 +1,8 @@
+#include
+#include "bar.h"
+
+// Function to count the length of a string
+int stringLength(const char* str) {
+ return strlen(str);
+}
+
diff --git a/example/scalalib/native/3-multi-module/bar/native-src/bar.h b/example/scalalib/native/3-multi-module/bar/native-src/bar.h
new file mode 100644
index 00000000000..d683bb4d09a
--- /dev/null
+++ b/example/scalalib/native/3-multi-module/bar/native-src/bar.h
@@ -0,0 +1,7 @@
+#ifndef BAR_H
+#define BAR_H
+
+// Declaration of the function to count string length
+int stringLength(const char* str);
+
+#endif
diff --git a/example/scalalib/native/3-multi-module/bar/src/Bar.scala b/example/scalalib/native/3-multi-module/bar/src/Bar.scala
new file mode 100644
index 00000000000..2d6155e0b0f
--- /dev/null
+++ b/example/scalalib/native/3-multi-module/bar/src/Bar.scala
@@ -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
+}
+
diff --git a/example/scalalib/native/3-multi-module/bar/test/src/BarTests.scala b/example/scalalib/native/3-multi-module/bar/test/src/BarTests.scala
new file mode 100644
index 00000000000..cb5585b99e2
--- /dev/null
+++ b/example/scalalib/native/3-multi-module/bar/test/src/BarTests.scala
@@ -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
+ }
+ }
+}
+
diff --git a/example/scalalib/native/3-multi-module/build.mill b/example/scalalib/native/3-multi-module/build.mill
new file mode 100644
index 00000000000..7f6c48fa7df
--- /dev/null
+++ b/example/scalalib/native/3-multi-module/build.mill
@@ -0,0 +1,91 @@
+//// SNIPPET:BUILD
+package build
+import mill._, scalalib._, scalanativelib._
+
+trait MyModule extends 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")
+}
+
+object foo extends MyModule {
+ def moduleDeps = Seq(bar)
+
+ def ivyDeps = Agg(ivy"com.lihaoyi::mainargs::0.7.6")
+
+ // Compile C
+ def nativeCompiled = T {
+ os.makeDir.all(millSourcePath / "target")
+
+ os.proc(
+ "gcc", "-m64", "-shared", "-fPIC",
+ "-I", "/workspaces/milling-two/sunday-work/mill/example/scalalib/native/3-multi/bar/native-src",
+ millSourcePath.toString + "/native-src/HelloWorldFoo.c",
+ "-o", millSourcePath.toString + "/target/libHelloWorldFoo.so")
+ .call(stdout = os.Inherit)
+
+ PathRef(T.dest / "target/libHelloWorldFoo.so")
+ }
+}
+
+object bar extends MyModule {
+
+ // Compile C
+ def nativeCompiled = T {
+ os.makeDir.all(millSourcePath / "target")
+
+ os.proc(
+ "gcc", "-m64", "-shared", "-fPIC",
+ millSourcePath.toString + "/native-src/HelloWorldBar.c",
+ "-o", millSourcePath.toString + "/target/libHelloWorldBar.so")
+ .call(stdout = os.Inherit)
+
+ PathRef(T.dest / "target/libHelloWorldBar.so")
+ }
+
+}
+//// SNIPPET:END
+
+// 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:
+//
+//// SNIPPET:TREE
+//
+// ----
+// build.mill
+// bar/
+// native-src/
+// bar.h
+// HelloWorldBar.c
+// src/
+// Bar.scala
+// test/
+// src/
+// BarTests.scala
+// foo/
+// native-src/
+// HelloWorldFoo.c
+// src/
+// Foo.scala
+//
+// ----
+//
+//// SNIPPET:END
+
+
diff --git a/example/scalalib/native/3-multi-module/foo/native-src/HelloWorldFoo.c b/example/scalalib/native/3-multi-module/foo/native-src/HelloWorldFoo.c
new file mode 100644
index 00000000000..e8b9da8d2a1
--- /dev/null
+++ b/example/scalalib/native/3-multi-module/foo/native-src/HelloWorldFoo.c
@@ -0,0 +1,24 @@
+#include
+#include "bar.h"
+
+// Function to count vowels in a string
+int vowelCount(const char* str) {
+ int count = 0;
+ for (int i = 0; str[i] != '\0'; i++) {
+ char c = str[i];
+ if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' ||
+ c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U') {
+ count++;
+ }
+ }
+ return count;
+}
+
+// Function in foo that uses bar's string length and foo's vowel count functions
+int vowelDensity(const char* str) {
+ int length = stringLength(str); // Call bar's function for string length
+ int vowels = vowelCount(str); // Call foo's own function for vowel count
+
+ return (length > 0) ? (vowels * 100) / length : 0; // Return vowel density as percentage
+}
+
diff --git a/example/scalalib/native/3-multi-module/foo/src/Foo.scala b/example/scalalib/native/3-multi-module/foo/src/Foo.scala
new file mode 100644
index 00000000000..564c3418085
--- /dev/null
+++ b/example/scalalib/native/3-multi-module/foo/src/Foo.scala
@@ -0,0 +1,31 @@
+package foo
+
+import scala.scalanative.libc._
+import scala.scalanative.unsafe._
+import mainargs.{main, ParserForMethods, arg}
+
+object Foo {
+ @main
+ def main(@arg(name = "foo-text") fooText: String,
+ @arg(name = "bar-text") barText: String): Unit = {
+
+ implicit val z: Zone = Zone.open
+ val cFooText = toCString(fooText)
+ val cBarText = toCString(barText)
+ z.close()
+
+ stdio.printf(c"Foo.value: The vowel density of '%s' is %d\n", cFooText, HelloWorldFoo.vowelDensity(cFooText))
+ stdio.printf(c"Bar.value: The string length of '%s' is %d\n", cBarText, bar.HelloWorldBar.stringLength(cBarText))
+ }
+
+ def main(args: Array[String]): Unit = ParserForMethods(this).runOrExit(args)
+}
+
+@extern
+@link("HelloWorldFoo")
+// Arbitrary object name
+object HelloWorldFoo {
+ // Name and signature of C function
+ def vowelDensity(str: CString): CInt = extern
+}
+
diff --git a/example/scalalib/native/4-common-config/build.mill b/example/scalalib/native/4-common-config/build.mill
new file mode 100644
index 00000000000..01656860367
--- /dev/null
+++ b/example/scalalib/native/4-common-config/build.mill
@@ -0,0 +1,59 @@
+// This example shows some of the common tasks you may want to override on a
+// `ScalaNativeModule`: specifying the `releaseMode`, `nativeLinkStubs`
+// `nativeIncrementalCompilation, `nativeLinkingOptions` and `nativeWorkdir`.
+
+
+//// SNIPPET:BUILD
+package build
+import mill._, scalalib._, scalanativelib._, scalanativelib.api._
+
+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"
+ )
+
+ // Set the releaseMode to ReleaseFast.
+ def releaseMode: T[ReleaseMode] = T { ReleaseMode.ReleaseFast }
+
+ // Set linking of `@stub` methods to true.
+ def nativeLinkStubs: T[Boolean] = T { true }
+
+ // Set incremental compilation to true
+ def nativeIncrementalCompilation: T[Boolean] = T { true }
+
+ // Set nativeLinkingOptions path to a directory named `target`.
+ def nativeLinkingOptions = Seq("-L" + millSourcePath.toString + "/target")
+
+ // Set nativeWorkdir directory to `newDir`
+ def nativeWorkdir = T { T.dest / "newDir" }
+}
+//// SNIPPET:END
+
+//
+
+/** Usage
+
+> ./mill run
+...
+Foo.value: hello
+
+> ./mill show releaseMode
+"mill.scalanativelib.api.ReleaseMode.ReleaseFast"
+
+> ./mill show nativeLinkStubs
+true
+
+> ./mill show nativeIncrementalCompilation
+true
+
+> ./mill show nativeLinkingOptions
+...
+
+> ./mill show nativeWorkdir
+...
+*/
+
diff --git a/example/scalalib/native/4-common-config/src/Foo.scala b/example/scalalib/native/4-common-config/src/Foo.scala
new file mode 100644
index 00000000000..05b82b0f561
--- /dev/null
+++ b/example/scalalib/native/4-common-config/src/Foo.scala
@@ -0,0 +1,24 @@
+package foo
+
+import scala.scalanative.libc._
+import scala.scalanative.unsafe._
+import fansi._
+
+object Foo {
+
+ def generateHtml(text: String): CString = {
+ val colored = Console.RED + "" + text + "
" + Console.RESET
+
+ implicit val z: Zone = Zone.open
+ val cResult = toCString(colored)
+ z.close()
+ cResult
+ }
+
+ val value = generateHtml("hello")
+
+ def main(args: Array[String]): Unit = {
+ stdio.printf(c"Foo.value: %s\n", Foo.value)
+ }
+}
+