{"id":23773033,"url":"https://github.com/threadexio/cbundl","last_synced_at":"2026-03-08T00:32:36.460Z","repository":{"id":269181685,"uuid":"906658593","full_name":"threadexio/cbundl","owner":"threadexio","description":"webpack but for C code.","archived":false,"fork":false,"pushed_at":"2025-06-14T13:34:55.000Z","size":200,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-11-08T01:22:20.070Z","etag":null,"topics":["build-tool","bundle","bundler","c","cpp","preprocessing","preprocessor"],"latest_commit_sha":null,"homepage":"https://crates.io/crates/cbundl","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/threadexio.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-12-21T14:28:50.000Z","updated_at":"2025-06-14T13:34:58.000Z","dependencies_parsed_at":null,"dependency_job_id":"0321597d-c477-4ff3-800a-9822f2911e16","html_url":"https://github.com/threadexio/cbundl","commit_stats":null,"previous_names":["threadexio/cbundl"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/threadexio/cbundl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/threadexio%2Fcbundl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/threadexio%2Fcbundl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/threadexio%2Fcbundl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/threadexio%2Fcbundl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/threadexio","download_url":"https://codeload.github.com/threadexio/cbundl/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/threadexio%2Fcbundl/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30238882,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-08T00:30:53.000Z","status":"ssl_error","status_checked_at":"2026-03-08T00:30:44.061Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["build-tool","bundle","bundler","c","cpp","preprocessing","preprocessor"],"created_at":"2025-01-01T05:22:04.247Z","updated_at":"2026-03-08T00:32:36.441Z","avatar_url":"https://github.com/threadexio.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"[crates-io]: https://crates.io/crates/cbundl\n[gh-actions]: https://github.com/threadexio/cbundl/actions/workflows/ci.yaml\n[gh-releases]: https://github.com/threadexio/cbundl/releases\n[license]: https://github.com/threadexio/cbundl/blob/master/LICENSE\n[art-license]: https://github.com/threadexio/cbundl/blob/master/assets/LICENSE\n\n[license-badge]: https://img.shields.io/github/license/threadexio/cbundl?style=for-the-badge\u0026logo=github\u0026label=license\u0026labelColor=%230e202e\u0026color=%23d6e4ef\n[tests-badge]: https://img.shields.io/github/actions/workflow/status/threadexio/cbundl/ci.yaml?branch=master\u0026style=for-the-badge\u0026logo=artifacthub\u0026labelColor=%230e202e\n[version-badge]: https://img.shields.io/crates/v/cbundl?style=for-the-badge\u0026logo=buffer\u0026labelColor=%230e202e\u0026color=%23354c5d\n\n[cc-by-nc-4-int]: https://creativecommons.org/licenses/by-nc/4.0\n\n\u003cdiv align=\"center\"\u003e\n\u003cimg src=\"https://raw.githubusercontent.com/threadexio/cbundl/refs/heads/master/assets/logo.svg\" width=\"50%\" alt=\"logo\"\u003e\n\n# cbundl\n\n\u003cp\u003e\n\n[`webpack`](https://webpack.js.org) but for C code.\n\n\u003c/p\u003e\n\n[![license-badge]][license]\n[![tests-badge]][gh-actions]\n[![version-badge]][crates-io]\n\n\u003c/div\u003e\n\n\u003cbr\u003e\n\nA simple tool that makes self-contained abominations of C code called bundles. It takes many `.c` and `.h` files and ~~concatenates~~ figures out the dependencies between them. Then, using patented (not yet) dependency finding algorithms, it arranges the source code in the files in the correct order so as to produce a single `.c` file that contains everything. In other words, given a bunch of header and implementation files, this tool can produce a single `.c` file that (hopefully) compiles and works the same way as just compiling each translation unit by itself and then linking them together.\n\n## Table of Contents\n\n* [Usage](#usage)\n  * [Directives](#directives)\n    * [bundle](#bundle)\n    * [impl](#impl)\n  * [Configuration](#configuration)\n  * [Workflow](#workflow)\n* [Installation](#installation)\n  * [cargo](#cargo)\n  * [nix](#nix)\n  * [manually](#manually)\n* [Building](#building)\n* [License](#license)\n\n## Usage\n\nThe tool operates as a simple preprocessor. Like the C preprocessor, the tool has its own directives that instruct it how to bundle code together. Enough words. Here is an example:\n\nConsider the following C code (don't worry about the `// cbundl` comments for now):\n\n---\n\n`frob.h`\n\n```c\n#ifndef _FOO_H\n#define _FOO_H\n\nstruct frobinator {\n  int frob_count;\n};\n\nvoid frobinate(struct frobinator* frob);\n\n#endif\n\n// cbundl: impl=frob.c\n```\n\n---\n\n`frob.c`\n\n```c\n// cbundl: bundle\n#include \"frob.h\"\n\n#include \u003cstdio.h\u003e\n\nstatic void update_frob_count(int *frob_count) {\n  *frob_count += 1;\n}\n\nvoid frobinate(struct frobinator* frob) {\n  printf(\"frobbed!\\n\");\n  update_frob_count(\u0026frob-\u003efrob_count);\n}\n```\n\n---\n\n`main.c`\n\n```c\n#include \u003cstdio.h\u003e\n\n// cbundl: bundle\n#include \"frob.h\"\n\nint main() {\n  struct frobinator f = {0};\n\n  for (int i = 0; i \u003c 10; i++)\n    frobinate(\u0026f);\n\n  printf(\"enough...\\n\");\n  return 0;\n}\n```\n\nTo compile and run this project you would have to do something along the lines of:\n\n```bash\n$ cc main.c frob.c -o frob\n```\n\nWhich would get you a binary. Now let's just assume that you are in a fictional universe where for whatever reason your code will be compiled with a fixed command along the lines of:\n\n```bash\n$ cc main.c -o frob\n```\n\nWell... that won't work because the linker complains that we did not give it any implementation for `frobinate()`. We cannot change the compilation command. But we _can_ change what code goes in the compiler. If we insert a pre-processing step on our code _before_ it is even sent to the compiler we could theoretically include the implementation of `frobinate()` directly in `main.c`. This way we would end up with a self-contained translation unit which the compiler (and linker) will be happy to assemble into an executable. We can use `cbundl` for exactly this.\n\n```bash\n$ cbundl main.c -o final.c\n```\n\nThe above command will parse `main.c` and figure out what dependencies it has. In this example, `main.c` wants `stdio.h` and `frob.h`. Notice the comment above the `#include \"frob.h\"`. Comments that begin with `// cbundl:` are called \"directives\" and give special instructions to `cbundl`. The directive above `frob.h` tells `cbundl` that, to build the final bundle, it needs to include `frob.h`. The directive at the end of `frob.h` tells `cbundl` that the implementation for at least one of the symbols declared by `frob.h` lives in `frob.c`. This tells `cbundl` to include `frob.c` inside the resulting bundle. That's it. That's the entire tool :clap:. The file `final.c` then contains:\n\n```c\n// My amazing header text!\n\n/**\n *\n *                        )                (    (\n *                    ( /(    (           )\\ ) )\\\n *                (   )\\())  ))\\   (     (()/(((_)\n *                 )\\ ((_)\\  /((_)  )\\ )   ((_))_\n *               ((_)| |(_)(_))(  _(_/(   _| || |\n *              / _| | '_ \\| || || ' \\))/ _` || |\n *              \\__| |_.__/ \\_,_||_||_| \\__,_||_|\n *\n *                cbundl X.X.X-release (XXXXXXX)\n *             https://github.com/threadexio/cbundl\n *\n *      Generated at: XXX XX XXX XXX XX:XX:XX (UTC+XX:XX)\n *\n *\n * Use a gun. And if that don't work...\n *                                       use more gun.\n *   - Dr. Dell Conagher\n *\n */\n\n/**\n * bundled from \"frob.h\"\n */\n\n#ifndef _FOO_H\n#define _FOO_H\n\nstruct frobinator {\n  int frob_count;\n};\n\nvoid frobinate(struct frobinator* frob);\n\n#endif\n\n/**\n * bundled from \"main.c\"\n */\n\n#include \u003cstdio.h\u003e\n\nint main() {\n  struct frobinator f = {0};\n\n  for (int i = 0; i \u003c 10; i++) frobinate(\u0026f);\n\n  printf(\"enough...\\n\");\n  return 0;\n}\n\n/**\n * bundled from \"frob.c\"\n */\n\n#include \u003cstdio.h\u003e\n\nstatic void update_frob_count(int* frob_count) {\n  *frob_count += 1;\n}\n\nvoid frobinate(struct frobinator* frob) {\n  printf(\"frobbed!\\n\");\n  update_frob_count(\u0026frob-\u003efrob_count);\n}\n```\n\nThe compiler is now happy to make us our binary. :smiley: Congratulations, you now know everything about this tool. :clap: And because we were good programmer boys, girls and everything in between, `cbundl` will also pass the resulting bundle code through a code formatter of our choice (`clang-format` by default) so its nice and pretty.\n\n\u003cdetails\u003e\n\u003csummary\u003eCommand line arguments\u003c/summary\u003e\n\n```\nUsage: cbundl [OPTIONS] \u003cpath\u003e\n\nArguments:\n  \u003cpath\u003e\n          Path to the entry source file.\n\nOptions:\n      --no-config\n          Don't load any configuration file. (Overrides `--config`)\n\n      --config \u003cpath\u003e\n          Specify an alternate configuration file.\n\n          [default: .cbundl.toml cbundl.toml]\n\n      --deterministic[=\u003cboolean\u003e]\n          Output a deterministic bundle.\n\n          [possible values: yes, no]\n\n  -o, --output \u003cpath\u003e\n          Specify where to write the resulting bundle.\n\n          [default: -]\n\n      --no-banner[=\u003cboolean\u003e]\n          Don't output the banner at the top of the bundle.\n\n          [possible values: yes, no]\n\n      --no-format[=\u003cboolean\u003e]\n          Don't pass the resulting bundle through the formatter.\n\n          [possible values: yes, no]\n\n      --formatter \u003cexe\u003e\n          Code formatter. Must format the code from stdin and write it to stdout.\n\n          [default: clang-format]\n\n  -h, --help\n          Print help (see a summary with '-h')\n\n  -V, --version\n          Print version\n```\n\n\u003c/details\u003e\n\n### Directives\n\nDirectives are special single-line comments (`//`, not `/* */`) that give instructions to `cbundl`.\n\nThe format of directives is as follows:\n\n```c\n// cbundl: \u003cbody\u003e\n```\n\nThe directive means different things depending on what `\u003cbody\u003e` is. At this time, only 2 different directives exist:\n\n* `bundle`\n* `impl`\n\n#### bundle\n\n**Format:** `// cbundl: bundle`\n\nThe bundle directive must always appear exactly above a local `#include`, without any other comments or code in between. It informs `cbundl` of a dependency relation between the current file and the `#include`d file. An intuitive way to think about it, is that the current file \"wants\" the `#include`d file. Any `#include`s annotated with a bundle directive will not appear in the bundle. Additionally, any `#include`s not annotated with a bundle directive will be left as-is. This allows you to create a kind of semi-bundle where even the final bundle includes local files. I can't imagine where that would be useful, but you _can_ do it.\n\n#### impl\n\n**Format:** `// cbundl: impl=\u003cpath\u003e`\n\nThe `impl` directive, also called an implementation directive, informs `cbundl` that the current file is implemented by the file specified by `\u003cpath\u003e`. This directive can appear any number of times in the file (if the implementation is split across many other files). It can also appear anywhere in the file, but convention is that `impl` directives appear only at either the start or the end of the file. Just like `#include`-ing `.c` files, using an implementation directive that points to a `.h` file is generally considered bad practice.\n\n### Configuration\n\n`cbundl` can be configured via a configuration file. The configuration file exposes fine-grained settings for `cbundl` not available through the command line. By default, `cbundl` looks for configuration files named `.cbundl.toml` or `cbundl.toml` (in that order), though a custom configuration file can be specified via `--config`. Alternatively, `--no-config` tells `cbundl` to ignore any configuration files.\n\n\u003e [!NOTE]\n\u003e Command line flags always take priority over the configuration file.\n\nConfiguration files for `cbundl` are written in [TOML](https://toml.io/en). An example configuration is given in [`cbundl.toml`](./cbundl.toml).\n\n### Workflow\n\nOk that's all cool and all but how do I integrate it into my workflow? I'm glad you asked. Simple, instead of running just:\n\n```bash\n$ cc ...\n```\n\nYou do:\n\n```bash\n$ cbundl main.c \u003e bundle.c\n$ cc bundle.c -o main\n```\n\nJust make the bundle with `cbundl` and compile the bundle instead of your source files. You can also write a simple `Makefile` that does this:\n\n```make\nbuild:\n  cbundl main.c \u003e bundle.c\n  cc bundle.c -o main\n```\n\n## Installation\n\n`cbundl` provides pre-built release binaries in [Releases][gh-releases] for all 3 major desktop platforms.\n\n\u003e [!NOTE]\n\u003e Those binaries are built in [Github Actions][gh-actions]. However if you still don't trust the binaries, I don't blame you. Proceed to the [Building](#building) section.\n\n### cargo\n\nIf you happen to have `cargo` installed you can simply do:\n\n```bash\n$ cargo install cbundl\n```\n\n### nix\n\nIf you happen to have `nix` with flakes enabled, you can do:\n\n```bash\n$ nix run 'github:threadexio/cbundl/master'\n# or\n$ nix run 'github:threadexio/cbundl/vX.X.X'\n```\n\n\u003e [!NOTE]\n\u003e The above will run `cbundl` from the `master` branch. You should generally use the second form to pin down exactly which version you want.\n\nDirectly without even cloning the repository. Isn't Nix great?\n\nIf you want to install `cbundl` permanently, you can add the flake to your system configuration.\n\n### manually\n\n`cbundl` is a standalone binary. This means you can very easily install it only for your own user. The following will download the latest linux binary from [Releases][gh-releases] into `~/.bin`.\n\n```bash\n$ mkdir -p ~/.bin\n$ curl --proto '=https' --tlsv1.2 -sSfL 'https://github.com/threadexio/cbundl/releases/latest/download/cbundl-linux' -o ~/.bin/cbundl\n$ chmod +x ~/.bin/cbundl\n```\n\nYou can then add `~/.bin` to `PATH` so you can use the tool like any other command.\n\n* Temporarily\n\n```bash\n$ export PATH=\"$HOME/.bin:$PATH\"\n```\n\n* Permanently\n\n```bash\n$ echo -e '\\nexport PATH=\"$HOME/.bin:$PATH\"\\n' \u003e\u003e ~/.profile\n$ exec bash\n```\n\nIf all goes well, you should then be able to do `cbundl --version`.\n\nYou could however not do any of that and simply download it somewhere and use the full path to run it.\n\n## Building\n\nIronic that a C source code pre-processing tool is not written in C, isn't it? Anyway, as `cbundl` is written in a modern language called \"Rust\", you don't have to fiddle with finicky `Makefile`s or an esoteric `cmake` setup to get the damn thing to build. Simply do:\n\n```bash\n$ cargo build # for the debug build\n# or\n$ cargo build --release # for the release build\n```\n\nThen you can run `cbundl` through `cargo` with `cargo run` or by running it directly from `target/debug/cbundl` or `target/release/cbundl`, depending on which you built.\n\n## License\n\n* All code and contributions in this repository are licensed under the Apache 2.0 license, a copy of which can be found [here][license].\n* All artwork in this repository is licensed under [Creative Commons Attribution-NonCommercial 4.0 International][cc-by-nc-4-int]. A copy of the license can be found [here][art-license].\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthreadexio%2Fcbundl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthreadexio%2Fcbundl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthreadexio%2Fcbundl/lists"}