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

Conversation

c0d33ngr
Copy link

@c0d33ngr c0d33ngr commented Oct 3, 2024

This is a draft to close issue #3647

@c0d33ngr c0d33ngr marked this pull request as draft October 3, 2024 09:16
@c0d33ngr
Copy link
Author

c0d33ngr commented Oct 3, 2024

Hi @lihaoyi something like this or I have to use scalatags and mainargs?

@lihaoyi
Copy link
Member

lihaoyi commented Oct 3, 2024

Let's use the same scalatags/mainargs example as we did in scalalib/basic/1-simple/.

You can get rid of most of the docs that are already part of scalalib/basic/1-simple/. The point of this page is to show what's unique about scala native, so the docs should highlight the differences and unique features of it: e.g. the compile to binary, the different optimization levels, etc.

@c0d33ngr
Copy link
Author

c0d33ngr commented Oct 3, 2024

Alright, it's noted

@c0d33ngr c0d33ngr changed the title Add scalanative doc for 1-simple Draft to add scalanative doc Oct 3, 2024
@c0d33ngr
Copy link
Author

c0d33ngr commented Oct 4, 2024

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

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

  object test extends ScalaNativeTests {
    def ivyDeps = Agg(ivy"com.lihaoyi::utest:0.8.4")
    def testFramework = "utest.runner.Framework"
  }
}
#makefile
all : 
	gcc -m64 -shared -c native-src/HelloWorld.c -o target/libstub.so

@lihaoyi good day, I've some few questions to ask

Is it possible for me to run the commands in makefile from build.mill config?
Also how get the full file path of where am currently working in?

When I replace the def nativeLinkingOptions with the full path to the choice location to store the .so file, it worked. But before that I had to run make from terminal to generate the .so file in the location

That's why I asked whether it's possible to run the comments in makefile from build.mill

Lastly, is this what you want for the 2-interop code example?

@lihaoyi
Copy link
Member

lihaoyi commented Oct 4, 2024

Is it possible for me to run the commands in makefile from build.mill config?

Sure, os.call(("make, "blah")). Though I suggest if you know what you want to call, you can os.call gcc directly rather than going through make

Also how get the full file path of where am currently working in?

T.dest or millSourcePath depending on what where am currently working in means

When I replace the def nativeLinkingOptions with the full path to the choice location to store the .so file, it worked. But before that I had to run make from terminal to generate the .so file in the location

Make an upstream task generate the SO file and return a PathRef for the downstream task to consume

Lastly, is this what you want for the 2-interop code example?

Yes the 2-interop example looks great. Perhaps let's make it return a value as well, so rather than printString we make it reverseString or something, just so people can see how that works? Other than that it looks great: a tiny C program, a tiny Scala facade, and a tiny Scala program all interacting. You just need to wire up gcc to compile the thing as part of Mill so we can avoid having to run make manually

@c0d33ngr c0d33ngr changed the title Draft to add scalanative doc Add Scala Native Examples Oct 4, 2024
@c0d33ngr
Copy link
Author

Okay. For this my draft PR,
1-simple works using both mill run and mill test thanks to the ::
2-interop works only using mill run. mill test still has the linking problem
3-multi-module both mill run and mill test has linking problem similar to 2-interop
4-common-config works well except that os.resource doesn't exist. I see other common-config examples and notice them trying to access the resources and custom-redources directories from mill config. I didn't do similar cause I don't know how to access mill resources directory path. How can someone get the path of resources of mill?

@lihaoyi what do you think?

@lihaoyi
Copy link
Member

lihaoyi commented Oct 15, 2024

I did a review pass. Please make sure you read through the original ticket and earlier comments, it doesn't make sense for me to keep repeating things I've already written

@c0d33ngr
Copy link
Author

Alright @lihaoyi I did the changes to the draft PR
You can review 1-simple, 2-interop and 3-common-config exapmles

About 3-multi-module, it seem to be the most complicated one in the examples code. The independent module(bar) works well but the one which depends on bar which is foo seem to have linking problem. I have done lots of debugging but don't seem to see what I could be doing wrong. Step below

mill bar.nativeCompiled
SUCCESS OUTPUT MESSAGE

LD_LIBRARY_PATH=/../example/scalalib/native/3-multi/bar/target mill bar.run hello
SUCCESS OUTPUT MESSAGE

mill foo.nativeCompiled
SUCCESS OUTPUT MESSAGE

LD_LIBRARY_PATH=/.../mill/example/scalalib/native/3-multi/bar/target:/.../mill/example/scalalib/native/3-multi/foo/target:$LD_LIBRARY_PATH mill foo.run --bar-text hello --foo-text world


#1 [info] Optimizing (debug mode) (4693 ms)
#1 [info] Produced 3 LLVM IR files
#1 [info] Generating intermediate code (3848 ms)
#1 [info] Compiling to native code (7827 ms)
#1 [info] Linking with [pthread, dl, HelloWorldBar, HelloWorldFoo]
#1 [error] /usr/bin/ld: cannot find -lHelloWorldBar: No such file or directory
#1 [error] clang: error: linker command failed with exit code 1 (use -v to see invocation)
#1 [info] Total (19741 ms)
1 targets failed
foo.nativeLink scala.scalanative.build.BuildException: Failed to link /workspaces/milling-two/sunday-work/mill/example/scalalib/native/3-multi/out/foo/nativeWorkdir.dest/native/foo.Foo

I have a question to ask also, if I want to make use of a file or directory path of a module in another module how to go about it in mill

@lihaoyi
Copy link
Member

lihaoyi commented Oct 21, 2024

You can read things from other modules' folders by declaring them as Task.Sources in the upstream module; you should never write things in other module's folders, as all writes should go to Task.dest.

For the scala-native thing, I have no idea. Try the scala-native discord channel?

Comment on lines 81 to 85
#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

@c0d33ngr
Copy link
Author

You can read things from other modules' folders by declaring them as Task.Sources in the upstream module; you should never write things in other module's folders, as all writes should go to Task.dest.

For the scala-native thing, I have no idea. Try the scala-native discord channel?

I needed to know so I can direct the location of the header file foo depend on, no writing to it

//
// 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

?

object bar extends MyModule {

// Compile C
def nativeCompiled = T {
Copy link
Member

Choose a reason for hiding this comment

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

You should be using the Task {} and Task.dest syntax in main


os.proc(
"gcc", "-m64", "-shared", "-fPIC",
"-I", "/workspaces/milling-two/sunday-work/mill/example/scalalib/native/3-multi/bar/native-src",
Copy link
Member

Choose a reason for hiding this comment

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

Hardcoded paths are wrong

Copy link
Author

Choose a reason for hiding this comment

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

Yes, I know. I can now replace it with what you informed me of how to use paths from one module in another one

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

os.proc(
Copy link
Member

Choose a reason for hiding this comment

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

This os.proc call should be moved into MyModule

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants