{"id":20134186,"url":"https://github.com/poly2it/kein","last_synced_at":"2025-09-17T21:17:46.246Z","repository":{"id":260510052,"uuid":"881499166","full_name":"poly2it/kein","owner":"poly2it","description":"Es gibt kein Build-System!","archived":false,"fork":false,"pushed_at":"2025-02-10T23:14:22.000Z","size":98,"stargazers_count":5,"open_issues_count":8,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-09T17:14:41.178Z","etag":null,"topics":["build-library","build-system","build-tool","c","compilation-database","deterministic","experimental","kein","nix","nix-flake"],"latest_commit_sha":null,"homepage":"","language":"Nix","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/poly2it.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-10-31T17:37:05.000Z","updated_at":"2025-02-10T23:14:25.000Z","dependencies_parsed_at":"2025-01-13T21:25:05.232Z","dependency_job_id":"9f416b58-18fb-4b06-9860-155e8bf1533c","html_url":"https://github.com/poly2it/kein","commit_stats":null,"previous_names":["poly2it/kein"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/poly2it%2Fkein","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/poly2it%2Fkein/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/poly2it%2Fkein/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/poly2it%2Fkein/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/poly2it","download_url":"https://codeload.github.com/poly2it/kein/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248074923,"owners_count":21043490,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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-library","build-system","build-tool","c","compilation-database","deterministic","experimental","kein","nix","nix-flake"],"created_at":"2024-11-13T21:06:41.942Z","updated_at":"2025-09-17T21:17:41.177Z","avatar_url":"https://github.com/poly2it.png","language":"Nix","readme":"\u003cp align=\"center\"\u003e\u003cimage width=\"40%\" src=\"./doc/logo.svg\"/\u003e\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\u003ci\u003eKein\u003c/i\u003e is a contemporary build system centered around Nix.\u003c/p\u003e\n\n## Pitch\nSetting up a flake with a C program runnable using `nix run` on x86_64 and\naarch64 Linux and Darwin:\n```nix\n{\n  description = \"My kein flake\";\n\n  inputs = {\n    nixpkgs.url = \"github:nixos/nixpkgs?ref=nixos-unstable\";\n    kein = {\n      url = \"github:poly2it/kein\";\n      inputs.nixpkgs.follows = \"nixpkgs\";\n    };\n  };\n\n  outputs = { kein, ... }: kein.flakeFromKeinexpr {\n    bin = {\n      main = ./main.c;\n    };\n  };\n}\n```\n\nKein is currently in unstable alpha.\n\n## Rationale\nFirst and foremost, I do not think any of the existing build systems are\ngood enough. Makefiles have been my go-to option for configuring project builds\nup until now. They mostly suffice and are much faster, simpler and less\nabstract than CMake or Bazel builds, but still have some hurdles.\n\n- Third-party software is required to achieve basic functionality like\n  automatic rebuilds of files dependent on modified headers.\n- The syntax gets cluttery fast.\n- They require too much boilerplate.\n- People resort to nonstandard implementations to resolve the complications.\n- Effort is required to have them work with Nix.\n\nAdditionally, beyond the scope of Makefiles, other traits may be sought after in\nnew build systems, like determinism, better caching and more options for\nbuild-time programmability.\n\nNix already offers wrappers for building projects using existing build systems,\nbut that forms another abstraction, and the subordinate issues are not resolved.\nThe pain points still don't end, as Nix is not compatible with the mutable\nstores used in traditional build systems. A build which fails at 95% has to\nrestart from zero for every attempt at a patch. In practice, the wrappers are\nnot used in development, only in publishing. Developers use dev shells to\nwork outside of the deterministic build environment. After achieving a\nsuccessful build in the dev shell, Nix may be set up to wrap the build system\nto verify determinism. This solution does not allow Nix to provide any value to\nnew projects built with Nix in mind.\n\nKein is not a build system in the same sense as the aforementioned. Kein\nprovides build-oriented interfaces around Nix to allow building all parts of\nyour programs for Nix directly. Every object is built separately and stored\nindefinitely as a derivation, allowing fast iteration times. Nix builds your\nprojects without a build system.\n\n## Documentation\nA minimal flake looks like this (the same as seen before):\n```nix\n{\n  description = \"My kein flake\";\n\n  inputs = {\n    nixpkgs.url = \"github:nixos/nixpkgs?ref=nixos-unstable\";\n    kein = {\n      url = \"github:poly2it/kein\";\n      inputs.nixpkgs.follows = \"nixpkgs\";\n    };\n  };\n\n  outputs = { kein, ... }: kein.flakeFromKeinexpr {\n    bin = {\n      main = ./main.c;\n    };\n  };\n}\n```\n\nHenceforth, the attribute set used as the argument to kein.flakeFromKeinexpr\nis called a Kein expression, or `keinexpr`. A Kein expression looks like the\nfollowing:\n\n```nix\n{\n  bin = {\n    main = ./main.c\n  };\n}\n```\n\n`bin` in these expressions map to the `bin` directory in the result derivation\nif you `nix build` the flake.\n\nHere is a more complex Kein expression:\n\n```nix\n{\n  meta = { lib, ... }: {\n    name = \"rayprogram\";\n    license = lib.licenses.lgpl3;\n  };\n  licenseFile = ../../LICENSE;\n  distributedFiles = [\n    ./NOTES.txt\n  ];\n  lib = { pkgs, gcc, ... }: {\n    \"utils.so\" =\n      [./utils.c]\n      |\u003e gcc.include pkgs.raylib\n      |\u003e gcc.link \"raylib\";\n  };\n  bin = { pkgs, gcc, ... }: {\n    rayprogram =\n      [./main.c]\n      |\u003e gcc.include pkgs.raylib\n      |\u003e gcc.link \"raylib\"\n      |\u003e gcc.link \"utils\"\n      |\u003e gcc.setPositionIndependent true;\n  };\n}\n```\n\nBuilding [the above example](examples/raylib), we get the following directory\nstructure:\n\n```\nresult\n├── bin\n│   └── rayprogram\n├── lib\n│   └── utils.so\n├── LICENSE\n└── NOTES.txt\n```\n\nThe following sections should suffice to explain everything which is going on\nin that expression, if it's not clear already.\n\n### Project Derivation\nKein flakes automatically get a \"default\" derivation, a project derivation,\nwhich contains the files specified in the Kein expression. By default, the main\nprogram is the same as the name (`meta.name`) of the kein expression.\n\n### Backend selection\n#### Inferred backend\nThe backend formula will by default be inferred by the constraint expressions\nused in the linkage expression.\n\n#### Explicit backend\nThe backend forumla can be written expressly using `\u003cbackend\u003e` as a functor.\n\n```nix\n./main.c |\u003e gcc\n```\n\n### Constraint API\nThe compilation of an output is configured via constraints. A constraint takes\na `constraintExpr`, that is either another constraint, a path or a list of\n`constraintExprs`, and outputs a new `constraint` depending on the constraint\nfunction used. A constraint may represent a single compilation unit, or a\ncollection of units, for example when linking multiple units via the GCC\nbackend.\n\nDifferent backends have different constraints. To access a backend, write your\noutput (bin, lib, etc.) as a function taking a set:\n\n```nix\nbin = { gcc, ... }: {\n  main = ./main.c;\n};\n```\n\n`gcc` is now the API for the `gcc` backend. We are additionally given optional\naccess to `pkgs` and `system`. We can now set compilation options, for example\nincluding the raylib headers and linking raylib:\n\n```nix\nbin = { gcc, pkgs, ... }: {\n  main =\n    [./main.c]\n    |\u003e gcc.include pkgs.raylib\n    |\u003e gcc.link \"raylib\";\n};\n```\n\n**Constraints acting on lists of constraint expressions propagate to the\nlinking stage**. Each subordinate item is considered translation unit in the\nGCC backend. Constraints acting on these also propagate to all inner\nconstraints. To except an inner `constraintExpr`, the inverse, or another value\non the excepted expression:\n\n```nix\nbin = { gcc, pkgs, ... }: {\n  main =\n    [\n      ./a.c\n      (./b.c |\u003e gcc.setPositionIndependent false)\n    ]\n    |\u003e gcc.setPositionIndependent true\n    |\u003e gcc.include pkgs.raylib\n    |\u003e gcc.link \"raylib\";\n};\n```\n\nThe only constraints which propagate outwards from lists of constraint\nexpressions are `include` and `link`.\n\n### Metadata and Special Files\nMetadata can be added to Kein expressions to attach information to build inputs.\nThe data is added to the meta top-level attribute, which is either a set or\nfunction taking an attribute set containing `lib` and `pkgs`:\n\n```nix\nmeta = { lib, ... }: {\n  name = \"A Name\";\n  license = lib.licenses.lgpl3;\n};\n```\n\n#### Special Files\nA license file can be added to the top-level attribute `licenseFile`. It will\nbe added to the project derivations. Multiple licenses can be attached as a list\nin `licenseFiles`. Other files can be added to `distributedFiles`:\n\n```nix\nlicenseFile = ./LICENSE;\ndistributedFiles = [\n  ./NOTES.txt\n];\n```\n\n### `gcc.include \u003cpackage/packages\u003e`\nWhere package is a derivation, makes its `include` directory searchable during\nobject compilation, and `lib` searchable during linkage.\n\n### `gcc.link \u003cname/names\u003e`\nLinks `name` as in `-l\u003cname\u003e` during compilation. If `name` can be found as an\noutput in the top-level `lib` section in a Kein expression, that library will be\nlinked instead. To exemplify:\n\n```nix\n{\n  lib = { pkgs, gcc, ... }: {\n    \"applex.a\" = ./applex.c;\n    \"banane.so\" = ./banane.c;\n  };\n  bin = { pkgs, gcc, ... }: {\n    main =\n      ./main.c\n      |\u003e gcc.link \"applex\"\n      |\u003e gcc.link \"banane\";\n  };\n}\n```\n\n### `gcc.define \u003ckey\u003e \u003cvalue\u003e`\nDefines `key` as a compile-time macro `name`.\n\n### `gcc.setPositionIndependent \u003cbool\u003e`\nSets the `positionIndependent` constraint to `bool`. If the unit is compiled to\nan executable, `-fPIE` will be used. If the unit is compiled to an archive\n`-fPIC` is used.\n\n### `gcc.setOptimizeLevel \u003cvalue\u003e`\nSets the optimization level to `value`. Equivalent to `-O\u003cvalue\u003e`.\n\n### `gcc.enableDebugging \u003cbool\u003e`\nDecides whether debug symbols should be enabled. Equivalent to `-g`.\n\n### `gcc.setDebuggingTarget \u003cvalue\u003e`\nSelects the target debugging format used, and enables debugging.\n\n### `gcc.setDebuggingLevel \u003cvalue\u003e`\nSets the debugging level emitted from GCC.\n\n### `gcc.sanitizeAddresses \u003cbool\u003e`\nDecides whether AddressSanitizer should be enabled and set to sanitise\naddresses.\n\n### `gcc.sanitizeKernelAddresses \u003cbool\u003e`\nDecides whether Linux Kernel Sanitizers should be enabled.\n\n### `gcc.sanitizeThreads \u003cbool\u003e`\nDecides whether ThreadSanitizer should be enabled.\n\n### `gcc.sanitizeUndefinedBehaviour \u003cbool\u003e`\nDecides whether UndefinedBehaviourSanitizer should be enabled.\n\n### `gcc.sanitizeLeaks \u003cbool\u003e`\nDecides whether LeakSanitizer should be enabled.\n\n### `gcc.sanitizePointerComparisons \u003cbool\u003e`\nDecides whether AddressSanitizer should be set to sanitise pointer comparisons\nbetween unrelated objects. Will also enable `sanitizeAddresses`.\n\n### `gcc.sanitizePointerSubtraction \u003cbool\u003e`\nDecides whether AddressSanitizer should be set to sanitise pointer subtraction.\nWill also enable `sanitizeAddresses`.\n\n### `gcc.setStandard \u003cvalue\u003e`\nSet the language standard revision to `value`. Equivalent to `-std=\u003cvalue\u003e`.\n\n### `gcc.debug`\nEnables an assortent of options tailored towards debuggable builds. Includes\nAddressSanitizer.\n\n### `gcc.setArguments \u003carguments\u003e`\nAllows you to include any supported GCC command-line argument. The support for\nthis option is considered second-class. Kein resolves option dependencies and\nother niceties when declaring options using constraints. Regardless, this\noption can be quite useful, especially for bespoke operations.\n\n```nix\n{\n  bin = { gcc, ... }: {\n    main = [./main.c] |\u003e gcc.setArguments {\n      cOptions.std = \"iso9899:1999\";\n      codeGenerationFlags.PIC = true;\n      instrumentationFlags.sanitize = [\"address\" \"pointer-compare\"];\n    };\n  };\n}\n```\n\n`arguments` is an attribute set of arguments divided into categories. The\ncategories are as follows:\n\n#### Flags (`-f...`)\n- `overallFlags`\n- `cFlags`\n- `cppFlags`\n- `objcObjcppFlags`\n- `diagnosticFlags`\n- `debuggingFlags`\n- `instrumentationFlags`\n- `optimizationFlags`\n- `preprocessorFlags`\n- `linkerFlags`\n- `codeGenerationFlags`\n\n#### Debugging (`-g...`)\n\n#### Options (`-...`)\n- `overallOptions`\n- `cOptions`\n- `warningOptions`\n- `optimizationOptions`\n- `instrumentationOptions`\n- `preprocessorOptions`\n- `assemblerOptions`\n- `linkerOptions`\n- `directoryOptions`\n\n#### Warnings (`-W...`)\n- `overallWarnings`\n- `cObjcWarnings`\n\nThe specific category in which an argument is found can be found in the [source\ncode for command generation](backends/gcc/arguments/to_command.nix). The names\nfor the arguments directly reflect the actual GCC arguments, with some\nquality-of-life exceptions. Addition signs, as in *-c++*, are replaced with\n*p*s, as in *-cpp*. Common prefixes in categories are removed;\n`-ftree-vectorize` becomes `tree-vectorize`.\n\nFour types are supported for arguments: `null` discards the argument; `string`\ncreates an appropriate value binding to the value; `list` does as the string;\nbut repeats the argument for every value; and `bool` includes the argument as-is\nif the value is true, or discards the argument otherwise.\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpoly2it%2Fkein","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpoly2it%2Fkein","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpoly2it%2Fkein/lists"}