Top
Best
New

Posted by randerson_112 3 hours ago

Show HN: I built a Cargo-like build tool for C/C++(github.com)
I love C and C++, but setting up projects can sometimes be a pain.

Every time I wanted to start something new I'd spend the first hour writing CMakeLists.txt, figuring out find_package, copying boilerplate from my last project, and googling why my library isn't linking. By the time the project was actually set up I'd lost all momentum.

So, I built Craft - a lightweight build and workflow tool for C and C++. Instead of writing CMake, your project configuration goes in a simple craft.toml:

  [project]
  name = "my_app"
  version = "0.1.0"
  language = "c"
  c_standard = 99

  [build]
  type = "executable"
Run craft build and Craft generates the CMakeLists.txt automatically and builds your project. Want to add dependencies? That's just a simple command:

  craft add --git https://github.com/raysan5/raylib --links raylib
  craft add --path ../my_library
  craft add sfml
Craft will clone the dependency, regenerate the CMake, and rebuild your project for you.

Other Craft features: craft init - adopt an existing C/C++ project into Craft or initialize an empty directory. craft template - save any project structure as a template to be initialized later. craft gen - generate header and source files with starter boilerplate code. craft upgrade - keeps itself up to date.

CMakeLists.extra.cmake for anything that Craft does not yet handle.

Cross platform - macOS, Linux, Windows.

It is still early (I just got it to v1.0.0) but I am excited to be able to share it and keep improving it.

Would love feedback. Please also feel free to make pull requests if you want to help with development!

77 points | 74 comments
adev_ 2 hours ago|
Feedback of someone who is used to manage large (>1500) software stack in C / C++ / Fortran / Python / Rust / etc:

- (1) Provide a way to compile without internet access and specify the associated dependencies path manually. This is absolutely critical.

Most 'serious' multi-language package managers and integration systems are building in a sandbox without internet access for security reasons and reproducibility reasons.

If your build system does not allow to build offline and with manually specified dependencies, you will make life of integrators and package managers miserable and they will avoid your project.

(2) Never ever build in '-03 -march=native' by default. This is always a red flag and a sign of immaturity. People expect code to be portable and shippable.

Good default options should be CMake equivalent of "RelWithDebInfo" (meaning: -O2 -g -DNDEBUG ).

-O3 can be argued. -march=native is always always a mistake.

- (3) Allow your build tool to be built by an other build tool (e.g CMake).

Anybody caring about reproducibility will want to start from sources, not from a pre-compiled binary. This also matter for cross compilation.

- (4) Please offer a compatibility with pkg-config (https://en.wikipedia.org/wiki/Pkg-config) and if possible CPS (https://cps-org.github.io/cps/overview.html) for both consumption and generation.

They are what will allow interoperability between your system and other build systems.

- (5) last but not least: Consider seriously the cross-compilation use case.

It is common in the world of embedded systems to cross compile. Any build system that does not support cross-compilation will be de facto banned from the embedded domain.

tgma 48 minutes ago|||
> -march=native is always always a mistake

Gentoo user: hold my beer.

jjmarr 38 minutes ago|||
It's also an option on NixOS but I haven't managed to get it working unlike Gentoo.
CarVac 35 minutes ago|||
Gentoo binaries aren't shipped that way
moralestapia 1 hour ago||
>15000

15000 what?

adev_ 1 hour ago||
1500 C/C++ individual software components.

The 15000 was a typo on my side. Fixed.

moralestapia 1 hour ago||
I see, thanks. I didn't mind the number it just wasn't clear what was it about.
looneysquash 2 hours ago||
Besides Cargo, you might want to take a look at Python's pyproject.toml standard. https://packaging.python.org/en/latest/guides/writing-pyproj...

It's similar, but designed for an existing ecosystem. Cargo is designed for `cargo`, obviously.

But `pyproject.toml` is designed for the existing tools to all eventually adopt. (As well as new tools, of course.)

randerson_112 17 minutes ago||
Thank you everyone for the feedback so far! I just wanted to say that I understand this is not a fully cohesive and functional project for every edge case. This is the first day of releasing it to the public and it is only the beginning of the journey. I do not expect to fully solve a problem of this scale on my own, Craft is open source and open to the community for development. I hope that as a community this can grow into a more advanced and widely adopted tool.
gavinray 2 hours ago||
The least painful C/C++ build tool I've used is xmake

https://github.com/xmake-io/xmake

The reason why I like it (beyond ease-of-use) is that it can spit out CMakeLists.txt and compile_commands.json for IDE/LSP integration and also supports installing Conan/vcpkg libraries or even Git repos.

    set_project("myapp")
    set_languages("c++20")

    add_requires("conan::fmt/11.0.2", {alias = "fmt"})
    add_requires("vcpkg::fmt", {alias = "fmt"})
    add_requires("git://github.com/fmtlib/fmt v11.0.2", {alias = "fmt"})

    target("myapp")
        set_kind("binary")
        add_files("src/*.cpp")
        add_packages("fmt")
Then you use it like

  # Generate compile_commands.json and CMakeLists.txt
  $ xmake project -k compile_commands
  $ xmake project -k cmake

  # Build + run
  $ xmake && xmake run myapp
ethin 2 hours ago||
I would happily switch to it in a heartbeat if it was a lot more well-documented and if it supported even half of what CMake does.

As an example of what I mean, say I want to link to the FMOD library (or any library I legally can't redistribute as an SDK). Or I want to enable automatic detection on Windows where I know the library/SDK is an installer package. My solution, in CMake, is to just ask the registry. In XMake I still can't figure out how to pull this off. I know that's pretty niche, but still.

The documentation gap is the biggest hurtle. A lot of the functions/ways of doing things are poorly documented, if they are at all. Including a CMake library that isn't in any of the package managers for example. It also has some weird quirks: automatic/magic scoping (which is NOT a bonus) along with a hack "import" function instead of using native require.

All of this said, it does work well when it does work. Especially with modules.

delta_p_delta_x 2 hours ago|||
Agreed, xmake seems very well-thought-out, and supports the most modern use-cases (C++20 named modules, header unit modules, and `import std`, which CMake still has a lot of ceremony around). I should switch to it.
IshKebab 54 minutes ago||
I've had some experience with this but it seems to be rather slow, very niche and tbh I can't see a reason to use it over CMake.
looneysquash 2 hours ago||
Nice. I have been thinking of making something similar. Now hopefully I don't have to!

Not sure how big your plans are.

My thoughts would be to start as a cmake generator but to eventually replace it. Maybe optionally.

And to integrate suppoet for existing package managers like vcpkg.

At the same time, I'd want to remain modular enough that's it's not all or nothing. I also don't like locking.

But right now package management and build system are decoupled completely. And they are not like that in other ecosystems.

For example, Cmake can use vcpkg to install a package but then I still have to write more cmake to actually find and use it.

psyclobe 2 hours ago|
> For example, Cmake can use vcpkg to install a package but then I still have to write more cmake to actually find and use it.

I have this solved at our company. We have a tool built on top of vcpkg, to manage internal + external dependencies. Our cmake linker logic leverages the port names and so all you really do is declare your manifest file (vcpkg.json) then declare which one of them you will export publicly.

Everything after that is automatic including the exported cmake config for your library.

wild_pointer 16 minutes ago||
What about cmkr?

https://cmkr.build/

thegrim33 9 minutes ago||
Project description is AI generated, even the HN post is AI generated, why should I spend any energy looking into your project when all you're doing is just slinging AI slop around and couldn't be bothered to put any effort in yourself?
lgtx 2 hours ago||
The installation instructions being a `curl | sh` writing to the user's bashrc does not inspire confidence.
ori_b 2 hours ago||
They did say it was inspired by cargo, which is often installed using rustup as such:

    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
bikelang 2 hours ago|||
I don’t love this approach either (what a security nightmare…) - but it is easy to do for users and developers alike. Having to juggle a bunch of apt-like repositories for different distros is a huge time sink and adds a bunch of build complexity. Brew is annoying with its formulae vs tap vs cask vs cellar - and the associated ruby scripting… And then there’s windows - ugh.

I wish there was a dead simple installer TUI that had a common API specification so that you could host your installer spec on your.domain.com/install.json - point this TUI at it and it would understand the fine grained permissions required, handle required binary signature validation, manifest/sbom validation, give the user freedom to customize where/how things were installed, etc.

maccard 1 hour ago|||
Given you're about to run a binary, it's no worse than that.
hyperhopper 57 minutes ago||
It is definitely worse. At leas a binary is constant, on your system, can be analyzed. Curl|sh can give you different responses than just curling. Far far worse
uecker 2 hours ago|||
This is fitting for something simulating cargo, which is a huge supply chain risk itself.
jjgreen 2 hours ago||
[flagged]
Bjartr 2 hours ago||
If you'd just left off "to fuck" you'd end up way less downvoted, if it even happened at all.
jjgreen 2 hours ago|||
With fucks, without fucks, in iambic pentameter, anything vaguely critical of Rust will be downvoted. As you can see.
KPGv2 2 hours ago|||
[flagged]
jvanderbot 2 hours ago||
Knowing the reason something is considered bad does not immediately change that fact that it is considered bad.

Social / emotional signals still exist around that word.

seniorThrowaway 2 hours ago||
Having to work around a massive C++ software project daily, I wish you luck. We use conan2, and while it can be very challenging to use, I've yet to find something better that can handle incorporating as dependencies ancient projects that still use autoconf or even custom build tooling. It's also very good at detecting and enforcing ABI compatibility, although there are still some gaps. This problem space is incredibly hard and improving it is a prime driver for the creation of many of the languages that came after C/C++
mgaunard 27 minutes ago|
I find that conan2 is mostly painful with ABI. Binaries from GCC are all backwards compatible, as are C++ standard versions. The exception is the C++11 ABI break.

And yet it will insist on only giving you binaries that match exactly. Thankfully there are experimental extensions that allow it to automatically fall back.

cherryteastain 2 hours ago|
Seems to solve a problem very similar to Conan or vcpkg but without its own package archive or build scripts. In general, unlike Cargo/Rust, many C/C++ projects dynamically link libraries and often require complex Makefile/shell script etc magic to discover and optionally build their dependencies.

How does craft handle these 'diamond' patterns where 2 dependencies may depend on versions of the same library as transitive dependencies (either for static or dynamic linking or as header-only includes) without custom build scripts like the Conan approach?

More comments...