-
Notifications
You must be signed in to change notification settings - Fork 45
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
Investigate implementing header-translator
on top of Swift's ClangImporter
#345
Comments
I've started some initial work in this direction here. Right now the plan is to start building out the bindings to I originally tried using autocxx (which uses a modified It would have been nice if Note that I haven't tested this on macOS yet so you would need to modify the |
Cool! I think I was planning on just rewriting |
There's still a lot left to do before this this thing is usable but there's at least enough in place now to show that we can create a |
As part of this, I've done a small investigation into the current state of tools for automatic binding generation - that is, tools where you do zero or minimal configuration in the host language, and instead let a generator handle the tedious part. Note that many of these use a helper library like Bindings for calling Rust from another language:
Bindings for calling another language/framework from Rust:
|
To me, it is very apparent that bindgen is lacking, even for C projects, and I think the reason is that they are severely limited by libclang. So what if we made a tool for the entire community, built directly upon clang's AST, such that all attributes are available to use (and additional support could be added through attributes instead of special comments)? I first need to investigate what's possible with clang's debug AST output, since that would be a lot simpler, but it is very likely that we'll need to use clang's AST directly to do the binding generation. CppSharp is a good example of doing this. |
Ooh, just found |
I also agree with this based on my experience. And yes, For the work I've been doing on generating bindings for Later, I tried using it again and had a bit more success, but still had to implement a lot of manual fixes or additions with plain Now I've settled on just factoring out that support code into a separate crate and just using plain
I've started thinking along the same lines as I've been working on the I think it would be good to (try to) provide a better alternative than what is available with the current implementation that most projects are using based on
I think this would be an ideal goal. I'm not entirely sure how realistic it will be in the end for
That looks promising. Part of what I've been working on with the Currently I'm focusing on finishing the In any case, if you manage to start building something based on the AST definitions from the One thing to note is that apparently the |
Yeah, several parts of the project turned out to be a little more difficult to figure out than I expected and I've started over a few times so it's taken a bit longer than expected to get to a point where it is starting to look like something usable.
Sounds good. Regarding compilation times, I think it's possible to get things set up to where the overall project compiles reasonably quickly. Once the So the heaviest part of the build process is fetching, configuring, and building the libs on the C++ side. Early on, I experimented with pre-packaging all that stuff up in some Docker containers so that at least the CI could be relatively quick (only needing to fetch and recompile the Rust code for each test run). The containers came out to about 6GB with everything built in debug mode with symbols, which isn't too bad. (Although currently I'm compiling those libs in release mode and only care about debug/release on the Rust side). It's possible to do something similar for the main build process, perhaps also combining with something like That would probably also make more sense if the plan is to publish the actual tool as a binary through In any case, I think keeping the build times manageable (in a portable way) is feasible with a little bit of planning.
That seems reasonable. I also think the build script is probably the more flexible approach as well. At least, I would start with that before trying to build a proc-macro version. |
Linking bindgen issues rust-lang/rust-bindgen#1740 and rust-lang/rust-bindgen#297. An issue discovered there is that it's difficult to bundle clang for Windows, so perhaps the build script / proc-macro solution is out of the question once we stop using libclang. But I consider that bad practice anyhow, since it vastly increases compile time and requires the entire C compiler to run just to get header information, so probably not worth it to try to ever support. Rather, I'd want to put a lot of work into the diffing engine! |
Yeah... I think I tried early on to get the bindings for But even if we can't realistically support Windows, I'm not sure why that should preclude supporting a build-script or proc-macro approach. Wouldn't it be okay (if non-ideal) to just say that Windows is unsupported and fail in that case?
I'm not entirely sure I understand what you mean here. At least, in order to use Or are you thinking of more along the lines of keeping the tools separate, where the diffing part is a distinct tool from whatever ends up using the In any case, I think there may be some way for us to handle the Windows situation but I haven't thought about it too much yet. At the very least, we could probably support it in a somewhat indirect way through WSL (since WSL can still access the Windows FS). In fact, I'm developing the bindings in a WSL instance on my current machine. |
I think I was speaking of a future where we were merged with And then I was arguing that having a build script generate the bindings for a library is a bad idea for everyone in the ecosystem, since it forces users of the library (not just creators of the library) to have have clang installed/compile parts of LLVM, and that I really think the "run the generator once and commit/ Basically: Just me telling myself that the |
Generally, I think what you suggest here is ideal, if possible, in that it would be nice for users to not have to need additional toolchain artifacts installed (which maybe difficult to set up) in order to consume the library. For Objective-C oriented libraries, I think this is probably fine, and I think For cases where this new tooling may be used for C++ libraries, however, there is a potential complication that may make it more difficult to achieve. Specifically, one of the differences between This is in contrast to But in order for Unfortunately, since C++ doesn't have a stable ABI, most of the those size and alignment details will be implementation specific (i.e., dependent upon the user's build environment), and may event vary between toolchain versions. So this is, I think, at least one reason why |
Damn, that's really unfortunate! I guess this could be resolved by working on giving C++ a stable ABI, but that's far out of scope, so it probably makes sense for us to only target Objective-C and plain C. |
Yeah, that might be a more realistic goal. And in any case, there'd still be room for these tools to adopt whatever we manage to put together if they want to try to switch over from There is one other possibility I was thinking of though after I wrote that comment: we might be able to use a mechanism like the diffing you have in mind for the Objective-C platform stuff for this same purpose on the C++ side. I'm not sure how many targets would be involved, but there is only a (reasonably small) finite number of possible targets to consider. And there's also some support crates in the ecosystem to help with some of this stuff, like link-cplusplus, which lets you choose between |
@madsmtm Could you provide a few small example header files with the kind of data that the current header translator has trouble dealing with and that you are hoping the Asking because I've just gotten to the point now where I'm able to scan through decls after the I have the implementation up at https://github.com/silvanshade/cxx-swift, although not updated with the most recent changes yet. It's usable, but requires manually checking out several of the dependencies from my other repos, and then manually compiling the llvm/clang/swift toolchain (the steps of which are more or less still described at https://github.com/silvanshade/framework-translator). Also if you try to build the There's still a lot of basic things left to be done but I think the EDIT: You can see how the module loading and access to the clang decls works starting with this test. The test creates two example modules with the following data:
int
foo();
int
baz();
int
qux();
struct s {
int n;
};
@interface TheClass
- (TheClass *)initWithSomeDatumX:(int)x andSomeDatumY:(int)y;
@end
@protocol TheProtocol
- (void)doSomethingWithTheClass:(TheClass *)someInstance;
@end
#define SQUARE(n) (n*n)
int
bar();
When run, it loads the modules
|
Things that are really hard with
It at least looks like the module loading part works, which is just wonderful news! Though I may be mistaken, it seems like the order of things in the file gets jumbled, which is unfortunate, but if not avoidable then we'll have to live with it. |
So a test case exercising a few of these could be something like: // A comment
//* A doc comment */
__attribute__((enum_extensibility(open))) enum TestEnumExtensibility: int {
TestEnumValue = 1,
TestEnumValueExpression = 1 + 1,
TestEnumValueExpressionComplexExpression = TestEnumValue + TestEnumValueExpression,
} __attribute__((swift_name("MyTestEnum")));
@interface TestClassWithGeneric<__covariant GenericType>
- (void) methodTakingComplexFunction: (NSInteger (__attribute__((noescape)) *)(GenericType, void * _Nullable)) myFn;
@property const char* propertyWithAttribute __attribute__((objc_returns_inner_pointer));
- (instancetype)initDesignated __attribute__((objc_designated_initializer));
@end If we can also somehow make use of the information in MappedTypes.def and CFDatabase.def in the future, instead of having to reimplement that, that'd be nice! |
Thanks, that should give me a better idea of which parts of the API to focus on.
Yeah, I think there isn't any guarantee about the order in which the entries are presented from the Swift lookup tables. It is theoretically possible to use the AST walker/visitor functionality instead (either on the Swift AST side, or the Clang AST side), but there are number of problems with that approach. First, just having a faithful encoding of the C++ API for that functionality available in Rust through FFI has turned out to be more difficult than I expected, mostly due to various problems encoding the corresponding virtual class hierarchy and its heavy use of templates, so I put aside efforts to add bindings for that for the time being. But I'm not sure if the declarations as they would be synthesized in the Swift module would actually maintain the order from the C headers or not, and I'm also not sure if that is true of the Clang modules (synthesized from the It may be that the only guaranteed way to maintain the order of the declarations as they appear in the headers is to traverse the AST of the headers themselves, in which case I think we kind of lose the advantage of working with modules to begin with, although we should still have more fine-grained control over accessing the AST than we do now with In the end though, it may not really matter much for us either way: The clang modules will have already been fully loaded by the time the lookup tables are available, which means that as we iterate through the individual declaration, we can always obtain the related entities (e.g., class for the method, or whatever) by using the accessor methods for If we want the generated code to roughly follow the same order as the declarations in the C headers, we could re-create the original ordering by looking up the source location for each decl we encounter and using that as a sorting key. |
I am not sure I've said this before, but I honestly think you may have be shooting yourself in the foot by going the C++ -> Rust route with this; I suspect the project would have been a lot easier to just write in plain C++. But done is done, and I get that the other may be more fun (for me too), so I won't say anything else about it. What I'm using as a sort of base-line is that XCode has a (somewhat hidden) feature where you can view a Swift-translated version of header files; I don't think that part is open-source, but it tells me that everything there is definitely possible to do. And this feature retains the ordering of items correctly. But again, the ordering is not that important, it's just a nicety when debugging, and makes it easier to understand the source, but definitely something that we can add later. So don't worry about it for now! |
It's a fair point. Certainly, building the FFI to the C++ API is very complex and has required a lot of effort. But on the other hand, when I started this attempt, I didn't nearly have the required C++ knowledge to have built such a tool. Early on, even reading some parts of the C++ source was difficult. I think having the goal in mind to understand things well enough to translate to Rust forced me to learn a lot of very subtle aspects of C++ programming. Along the way I managed to learn enough to be able to fix some long-standing bugs in In the end, although it has taken longer, I think it may lead to a better overall outcome. I learned more than I would have otherwise. And I'm still hoping that whatever we build might be useful for people that need something with more features than But having said that, I'm not opposed to the idea of writing something in pure C++ now if it turns out we run into insurmountable issues with interfacing the API with Rust.
I believe I have seen this part in the code base you are referring to actually. Take a look at In fact, in this part, it looks like they are even using the method I suggest (i.e., sorting the decls by source location): // Sort imported declarations in source order.
std::sort(ClangDecls.begin(), ClangDecls.end(),
[&](Decl *LHS, Decl *RHS) -> bool {
return ClangSM.isBeforeInTranslationUnit(
getEffectiveClangNode(LHS).getLocation(),
getEffectiveClangNode(RHS).getLocation());
}); |
Huh. Well, let's do that then!
Yeah, that's partially also why I haven't started, so a good long look at your Rust code would probably help me along too. I'll try to get to it in the coming week, and definitely will get started before August is over (my new computer is arriving in a few weeks, so I'm a little reluctant to do a lot of setup I'll just have to redo... But then again, I might learn something so I can do it better next time). |
I've updated the cxx-swift repo with instructions on how to build for both macOS and Linux, and I've also included a devcontainer which makes it easy to launch a pre-built environment that allows one to avoid all the long and complicated build steps and instead just able to clone the repo and quickly get started with development. At this point the crate is starting to become usable and I'll probably start adding more functionality a lot more quickly now. Over the next week I plan to get the CI set up and work on some small refactorings for the lower level libraries and then focus on adding bindings for the stuff you mentioned in the previous post with the example you gave. I think what I'll do is try to build a small proof of concept tool in the framework-translator (which is currently completely outdated, so ignore the README) using I'd also like to perhaps experiment at some point with generating the final rust code directly from the translator rather than producing the macro output as an intermediate step. I have most of the code needed for that already written from the proc-macro attempt I made awhile ago, and it would be interesting to see what kind of difference that might make. Anyway, let me know if you have any issues trying to build or questions about using the crate whenever you start looking into that. Hopefully I'll have some more in-depth example code to show soon. |
Just wanted to give an update on the I've been focusing on refactoring and refining the build process and parts of the lower-level crates in order to enable a smooth installation process and CI pipeline. That has been going well but has taken a bit longer than I expected due to the overall complexity of Swift toolchain build process and related factors. (I've had to take a bit of a detour and learn more about CMake than I ever intended, for instance). As I made progress toward that, and started to enable CI for a few of the crates, I realized it would be a good idea to make a few more radical refactorings to the build process now in order to make things much more robust to future changes and easier to maintain across platforms. My current thinking is to scrap the Docker devcontainer stuff entirely both for the "easy" development option and for CI and instead switch over to using homebrew "bottles" to package up the pre-built dependencies. This should be feasible since homebrew already has formulas for both LLVM 16 (with clang) and Swift 5.8.1 on both macOS and Linux. So it's just a matter of adapting the current formulas for those for this specialized use case and installing the built artifacts in a different prefix. This has a number of advantages over the previous approach I was using. One major advantage being that the development process on macOS (which would realistically be our primary platform) will be much smoother and the "easy" development option simply requires a Another advantage is that it should be easy enough to be able to Unfortunately, that also means it's going to be a little longer until the crates are ready to start iterating on adding in more features again. It will be worth it though I think, and based on the progress toward that goal, I'm hoping that things will be back on track by the beginning of September. I'll update this thread again once there's more news about that. |
For my part, I'll either install things a bit manually, or use I've begun on setting your repos and such up locally, could you tell me about the |
Sounds good. Just as a word of caution, there might be a bit of breakage over the next few days still but the build-system rewriting is going pretty well so things should be back on track soon. As far as Originally, I started working on it because I had fixed some soundness issues in I needed that for the But at that point I decided to just refactor most of the rest of the code, removing some functionality that I didn't feel was needed (e.g., regarding different flag types) and making some other parts safer and perhaps a little more obvious. I also ended up changing the macro interface a little. Overall, it works more or less the same way as The It uses a phased approach where you declare some data about the C++ classes that will be used in a series of For instance, if you have some crate called You can see an example of the sort of information it generates here:
Recently I've been extending Most of the important basic traits area covered pretty well now. I have plans to auto generate |
Cool, thanks for the explanation, I'll probably come back with more questions as I get further along. And no worries about the breakage, I still need to learn how LLVM, Clang and Swift works, and read a bunch of C++, which is going to take me a while anyhow. A recommendation though: Put everything that you can in one repository, makes it much easier to maintain (and for others to contribute). Btw, I'm glad to see that you have such a focus on soundness, that's really important to me as well. |
Yeah, that's a good suggestion. I've kind of gone back and forth with regard to how to structure the crates. Originally, I did have most everything in a single repo, but I started to feel overwhelmed with the complexity of it. Refactoring was often difficult. Breaking things up into separate repos forced me to clean up the messier parts and modularize the organization a lot. In the end, I think it helped clarify the design goals also. But I admit things are too scattered around right now. I will try to consolidate more soon. I'm also thinking about putting all of remaining central crates under an organization just to help with the visibility. I've put it off for a bit since a few of the crates I've created recently ended up becoming redundant and unused shortly after I created them and I didn't want to clutter the main repos too much with the churn from that. But since the design is starting to settle, I'll focus more on consolidation again. |
Another status update regarding the I'm still making progress on the refactoring with respect to the build process and related issues like portability and maintainability. Things have again taken longer than expected due to unanticipated complexity, which is largely unavoidable just given the nature of interfacing with the LLVM, Clang, and Swift toolchains, regardless of the choice of Rust or C++ for implementation. What I've done is switched from the more hand-crafted This has several advantages, including the fact that we can tie directly into the CMake build scripts for the Previously I was hardcoding a lot of that information in the Additionally, I've written some high-level CMake modules that encapsulate common functionality needed for those crates, which also handles things like detection of additional development tools, to aid in a comfortable development and CI workflow. After a lot of experimentation, I've also switched away from trying to rely on packagers like Homebrew and Scoop for prebuilt artifacts and have instead decided to focus on using vcpkg for that. This has a number of advantages, including the fact that it integrates seamlessly with CMake, and also provides an easy way to fetch all of the prerequisites needed to build various libraries (like The dependencies can be fetched local to the specific crate (using a Importantly, The end result of the refactoring to using CMake more directly along with The overall workflow will be much simpler for new contributors and easier for us to maintain since the compilation environment will be consistent and isolated from many differences due to platform and packager configuration choices. I'm still in the process of putting all of the pieces together and updating the current repos but am hoping that should be ready before too much longer. I probably won't have the full caching solution in place for a while longer still since that will require more experimentation. But I'm comfortable moving forward with the rest now, knowing that robust caching functionality is available. |
Thought I should give another progress update on this: I spent the last few weeks doing a lot of experimentation along the lines I described in the last post. I managed to get most of the However, in the end I wasn't quite satisfied with that solution. The first problem is that I discovered the For example, I explored building a multi-stage bootstrapped toolchain, statically linked, and distributed with a complete set of LLVM toolchain runtime artifacts (e.g., But ultimately that led to a larger distribution than I was happy with, and even still, there remained some overall fragility in other parts of the vcpkg caching solution, which is just kind of unavoidable due to ABI compatibility issues across so many parts of the host environment. The possibility that a user might still experience a build time of a couple dozen minutes, versus only a few minutes, without much idea of why, wasn't really acceptable to me. This is made worse by the fact that you can't really report detailed information interactively during Another issue I encountered is that the I also found that, even with everything fully cached, there was still a few minutes of overhead just to assemble and initialize the toolchain, just due to all of internal moving parts of this approach (e.g., fetching and bootstrapping I eventually went back to the drawing board and started over from scratch, this time using a much simpler, but less isolated approach. What I'm doing now is using CMake's ExternalProject functionality to accomplish something roughly similar to what a from-scratch Currently I have this working on macOS, Linux, and Windows, producing a package with all of the headers and libraries from Clang, LLVM, and Swift. The build is heavily optimized to build exactly the components we need, and nothing else, and produces an archived, location-independent package around 250MB. The libraries are also statically linked and compiled with thin-LTO, so we can use them with Rust's linker-plugin-based LTO for cross-language LTO, so we can essentially be just as efficient as a native C++ implementation. Getting this to work took a bit more effort that I anticipated, since the Swift libraries are not quite designed (with respect to the provided CMake configuration) to be exported and used in a relocatable fashion outside of the The next step is to clean up a few rough edges in the build script, then start building and providing these packages built from CI and hosted as releases for the associated GitHub repo. I plan to provide packages for Linux (GNU), macOS (Darwin), and Windows (MSVC) OS/ABIs, individually for Once that is accomplished, I will modify the One potential additional complication, however, has to do with linking these libraries since they are compiled with The It's taken quite a bit of effort to get to this point but it appears that we can have an essentially transparent process for installing and building all of this stuff without the user having to worry about configuring anything outside of their usual Rust environment. Although it has taken a lot longer than I had hoped, I think it will be worth the effort to have gotten here since it makes the idea of a more capable replacement for |
Another update: The toolchains build scripts are now up at https://github.com/llvmup/toolchains I have the CI workflow building the toolchains automatically: https://github.com/llvmup/toolchains/actions/runs/6668236820/job/18123391704 The builds take awhile until the ccache is warmed up but should be faster the second time around. I should have the release tarballs being deployed in the next few days. These should look like the following:
Once that's ready, I will update the EDIT: some of the distributions are uploaded now under releases: https://github.com/llvmup/toolchains/releases |
I hadn't properly read through your comments here before now, wow, what a ride you've been on with so many build systems, settling on CMake in the end sounds like the correct decision. Really, thank you so much for doing this work, it is so nice that you have such a huge focus on making this installation path seamless for new contributors!
While the user-experience is slightly nicer, I think it's a bad idea to download things in build scripts, since it makes it harder for people to do e.g. security verification, you have to pull in extra dependencies to fetch and unpack the package from the network, and it breaks things like Cargo's I see that you've created All of that said though, have you considered shipping the tarballs via. crates.io instead? E.g. something like a set of llvm-prebuilt-aarch64-apple-darwin/
include/
.. snip ..
lib/
.. snip ..
build.rs
Cargo.toml
llvm-prebuilt-x86_64-apple-darwin/
llvm-prebuilt-x86_64-linux-gnu/
.. snip .. This way, we could also potentially in the future support building from source (although this would mostly be an option for the pedantic, building from source will always take a long time) (the If you think this is possible, then I'll go and ask the crates.io team how they feel about us uploading fairly huge binary files to their servers. Related: There was the whole serde_derive shipping a binary debacle a while ago, which highlights that if we're to ship any binaries, the build steps must be 100% reproducible.
I'm pretty sure
This would be for compiling C++ files required by
That sounds awesome, I'm all for |
Thanks. I wish the process to get this put together hadn't taken quite so long but honestly I couldn't really find another way to build something maintainable and portable around the LLVM ecosystem libraries.
That's a good point. One possibility I've been considering is allowing the dependencies to be fetched automatically by default, but allowing that behavior to be overridden (either through environment variables, or feature flags), to disable binary downloading (or to provide a different fetch location, perhaps from a local source). There are two main reasons I can think of for keeping that the default:
I'm not entirely convinced either are that important, but the latter would sure be convenient. Building and hosting the libraries locally isn't a huge problem, but I couldn't figure out how to get I'd also mention that if this were to be the default, there would definitely be a Security section in the README and docs noting that -- especially for production use -- it would be better for users to disable that and either fetch the binaries themselves or (even better) build the artifacts locally and use those.
I'm open to this possibility. One downside is that, while most of the llvmup library portion is working now, I haven't done much work on the CLI yet, so that will take a little longer. What I have working now:
What remains is to do some refactoring to allow the manifest processing to combine the processed results for multiple platforms (currently it only handles one at a time), and there's also some bugs I'm trying to resolve in the component installer.
Hmm, I hadn't thought of that. It's an interesting idea, and could make some things simpler though I suppose. With the
If you are willing to contact them and inquire about that, I think that would be fine, at least just to know their stance on the issue. So I guess what you would want to ask them is whether it would be feasible to upload a crate containing the (patched) LLVM and Swift source trees?
Yeah, definitely relevant. I missed that originally but ran across some of the discussion a few days ago. Unfortunately, this is potentially a problem here too, and I created an issue about it: llvmup/toolchains#9 I think we could theoretically achieve 100% reproducible builds for Linux, but I'm much less confident about the ability to do that for macOS and Windows. And even for Linux, it would still be a lot of additional work to accomplish.
Oh, interesting. I wonder why the docs (particularly about linker-plugin-lto) don't mention that. In any case, I'll see if we can just use that.
That was the idea, yeah. Theoretically, it would make cross-compiling easier, and it's a relatively compact distribution. However, I since decided against using The problem I ran into is that it seems that I'm not sure if it's just ignoring the respective arguments (e.g., So what I opted for instead is to just ship the As long as the user has a working Rust toolchain (for env Also these
That would be really nice. |
There's one other perspective to consider that I forgot to mention with regard to download-during-build vs fetch-via-cli for the artifacts: Unfortunately, because the LLVM C++ libraries don't have a stable API, we will probably need to update the LLVM components more frequently than one might expect. This is especially going to be true across major LLVM versions, but at the moment, I even expect that we will need to update the libraries for each LLVM minor release, and then I've also provisioned for tweak releases (the I think I was a little concerned that requiring the user to manually update the llvmup components via the CLI almost every time they update their cxx-* cargo dependencies might be too much of an annoyance. Any thoughts on that? |
I don't think that's true, docs.rs doesn't actually allow network access. Don't worry about that part though, regardless of what we choose I can help you with setting it up so that docs.rs still works, they have several flags for that (
Yup!
I'll both ask them about uploading (fairly) huge source trees, and (fairly) huge prebuilt binaries. EDIT: Done, see rust-lang/crates.io#7680.
Good point! Perhaps let's table that for now, and see if the crates.io path is feasible. |
Ah, that's unfortunate, but not unreasonable. I probably should have checked that first. Anyway, given that, I'll stop working on the automatic download approach.
I think I'll expose a bare minimum interface for downloading through the CLI at least as a short term solution. I can re-use most of the automatic downloading logic. It's mostly that I'm hesitant to put a lot of effort into a full But if the crates.io path pans out, I'm definitely open to trying to make that work. |
Hi, I've been looking to improve the MacOS development experience with Rust. What is the status of this issue at the moment? From what I've gathered the opinion of large prebuilts on crates.io is leaning towards no. |
No status update on this specific issue, we're still somewhat stuck on figuring out a distribution mechanism for prebuilt LLVM binaries. Personally I'm leaning towards a combination of: system-provided when possible (e.g. integrate with the package manager's version of LLVM), provide prebuilt binaries of In any case, I'd suggest you open a separate issue, this one is quite large, and it can be hard to track what in particular you are having trouble with regarding the MacOS development experience with Rust. |
I have some (new) thoughts on this since the last update I posted about the llvmupFirst, I think that relying on system-provided is going to be difficult for several different reasons:
I think it would certainly be fine to include detailed instructions on how to use system-provided but I think if that's the default option many people will probably encounter too much trouble to even get started. Also I would note that I did kind of explore this approach early on when I was looking at building around As for
The JSON manifest files that ship with the llvmup tarballs contain this build info (2) which has all the details like library names, build options, dependencies (for topological sorting in build.rs), etc. So even if another distribution option were used, the information from (2) is still necessary to smoothly build the It's been awhile since I updated I'm thinking now that maybe the best option would be to just distribute the LLVM components as a Nix flake. The flake would still use the This might make the whole thing more niche, but maybe it's worth it. There's already some precedent for this kind of thing and I see more and more large projects are providing flakes. Mozilla has done it for awhile. My impression anyway is that offering the artifacts via a flake might get more traction than just asking people to download random binaries. And this way they could also build them locally in a reproducible fashion. The downside is this isn't a great option for Windows (native) users until Nix maybe becomes available there. But we could still provide the raw tarballs in that case since the cxx-clang and related cratesAs for the rest of the crates providing the Rust interface to I revamped moveref and added full documentation and test coverage. I also completely redesigned cxx-auto but haven't updated the repo yet. Previously, the way it worked is it used a combination of C++ template metaprogramming and code generation with This worked well with regard to the end result, but the problem is that it was really slow. It required creating an entirely separate pre-build crate to interface with the results from the C++ template metaprogramming, so that in the final crate's build.rs, So that required in practice building 2-3 separate crates, two of them with their own The way it works now is that I've managed to eliminate the need for the separate pre-build crate by instead, during the final crate's There's also no more JSON manifest files needed. All of the boilerplate configuration is done in the C++ template files. I haven't been able to fully test it yet but the individual parts are mostly working now and it seems fast. The other major issue with the I believe this is basically due to the fact that each of the binding files needed to import several LLVM headers, which repeatedly pulls in huge amounts of code for the C++ compiler to deal with. I recently refactored the C++ interfacing code to use C++ modules instead which I think should alleviate this once everything is up and running again. They behave basically like precompiled headers: we can wrap the individual headers in module interfaces which only need to be parsed and compiled to IR once and then loading is much, much faster everywhere else they are used. LLVM itself also has some sort of support for building directly using modules, which could potentially make things even better in this regard, but I haven't experimented with that yet. Both clang 18 and GCC 14 have good support for modules now so this seems viable. The one complication is that building C++ code that uses modules requires compiling the source to object files in the correct order, instead of the current status quo where the order doesn't matter. Unfortunately The Once that's done, I plan to update the |
In
header-translator
we have to do a bunch of hacks to account for the fact that a lot of things are not exposed to thelibclang
API. In particular the Swift attributes are desirable, e.g. the newNS_SWIFT_UI_ACTOR
will help us a lot in knowing which things requireMainThreadMarker
before access!So crazy idea: What if instead we forked/used Swift's ClangImporter and built directly upon their work instead? They already handle all the edge-cases, they have a system for importing
.apinotes
files, and so on.Though that would probably require more fluency in C++ than I really have, as well as the hard work of actually figuring out how to interface with it in a nice way, and probably also a beefier computer to compile all that code than the one I currently have :/
The text was updated successfully, but these errors were encountered: