janet-lang / jpm

Janet Project Manager
MIT License
65 stars 21 forks source link

added :objects option to declare-native #66

Closed saikyun closed 1 year ago

saikyun commented 1 year ago

I wanted to ask if something like this makes sense, because it allows a project file like this: https://github.com/saikyun/freja-jaylib/blob/test-deps/project.janet

The main idea is that I can have raylib be compiled, and added as a dependency to (freja-)jaylib. This way people won't have to recompile raylib each time jaylib is modified.

Only missing thing in jpm is linking the .a-file, so I added the :objects-key.

I've only tried on macOS, but should work for linux. For the sake of windows, perhaps declare-native / :objects should deal with using the right suffix. I'm also guessing that for static builds the extra :objects would be needed as well.

What do you think @bakpakin? :) Should I continue in this direction, and make sure it works on windows & with static builds as well, or is there a better way to do this?

saikyun commented 1 year ago

Oh, maybe a bit more work is needed for static builds to work. I tried a bit but couldn't quite figure out what went wrong.

Is there something special needed in order to link .a-files when doing a static build?

Den lör 11 feb. 2023 16:47Calvin Rose @.***> skrev:

Merged #66 https://github.com/janet-lang/jpm/pull/66 into master.

— Reply to this email directly, view it on GitHub https://github.com/janet-lang/jpm/pull/66#event-8496813963, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAS46Z3RRSZW6ETEL3L44XLWW6YCFANCNFSM6AAAAAAUH5PEDE . You are receiving this because you authored the thread.Message ID: @.***>

saikyun commented 1 year ago

Okay, so what I'm finding is something like this: If I try to link with raylib.a when doing the static build, I get an error like this when trying to compile an exe using that jaylib:

linking build/test_build...
ld: in build/freja-jaylib.a(raylib.a), archive member 'raylib.a' with length 2181616 is not mach-o or llvm bitcode file 'build/freja-jaylib.a' for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I looked closer at how linking is done normally, and it seems that only .o-files are linked. I tried linking all the .o-files instead of the single .a-file, and now it works.

The question then is if there's a concise way of expressing "I want all the .o-files of this other native module". I guess I could store all the paths to the .o-files in the result of declare-native, and use that in the second declare-native. But it feels kinda messy.

bakpakin commented 1 year ago

Perhaps try adding a flag like "-l:raylib.a" to lflags?

bakpakin commented 1 year ago

More to the point, in most cases jpm isn't designed for the way you are trying to link things - what you should have are dynamic libraries for raylib, and dynamic libraries for freja. When a user then uses create executable to finally using freja (and there for raylib by dependency), static versions of both libraries, along with any other user code, should be compiled together into one big executable. There is no benefit (that I can think of off hand) for having a dynamic library that has a static library bundled inside it.

saikyun commented 1 year ago

Aha, I see. I'll take another look. I have just recently understood what linkers do, so the subtleties between static / dynamic linking in multiple steps are beyond me. Thanks for giving me more clues. :)

bakpakin commented 1 year ago

So I'm noticing that default executables on linux created by (create-executable ...) do not have -rdynamic passed, meaning that they cannot themselves easily import dynamic libraries. You can change this adding -rdynamic to :lflags if the system is linux. I believe that may help your original use case for freja (a bundled executable that can load other janet native modules).

saikyun commented 1 year ago

Ah, yeah, I am using that flag. I think you gave me that tip over a year ago. :D

Linking with raylib.a made it possible to build the .exe, so thanks for that. :)

Just so we aren't talking past each other, when building (vanilla) jaylib, and using the jaylib.so by (use jaylib), do you mean that it in turn loads raylib.so rather than raylib.a? And when I build an executable from the same source, it will instead use jaylib.a and raylib.a?

EDIT: Wait, nevermind, I see now that regular jaylib never ends up with a raylib.a/.so.

Only reason I'm trying what I'm trying is to split the build steps between "building raylib" and "building jaylib" so that raylib doesn't need to be rebuilt each time. I don't mind if it ends up with only jaylib.a/.so. :)

saikyun commented 1 year ago

I just noticed that when modifying files in (vanilla) jaylib it acts as I want if I only modify files in :source, it's when I modify files in :headers that it recompiles all of raylib as well.

bakpakin commented 1 year ago

I just noticed that when modifying files in (vanilla) jaylib it acts as I want if I only modify files in :source, it's when I modify files in :headers that it recompiles all of raylib as well.

Yes, jpm will make each source file depend on all headers, rather than trying to determine which source file imports which headers.

saikyun commented 1 year ago

Okay. Is improving the incremental build time in those cases something that you'd be interested in having?

A couple of weeks ago I tried doing something using clang -MM which figures out local includes. It worked well with my limited testing: https://github.com/saikyun/jpm/blob/eef7ae1f9aeee40b730b44253ef3ec9a56645378/jpm/cc.janet#L101-L113

Not sure if a similar thing exists for cl. And maybe it's just too flaky overall.