Skip to content

Commit

Permalink
✨ Introduce C/C++
Browse files Browse the repository at this point in the history
Generalized c family language support as c. Cross compilation support
works the same way as rust. Uses Doxygen to generate html and manpages.
  • Loading branch information
abbec committed Apr 21, 2023
1 parent 68f0a5c commit 43ed8d2
Show file tree
Hide file tree
Showing 21 changed files with 804 additions and 26 deletions.
12 changes: 12 additions & 0 deletions c/Doxyfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
PROJECT_NAME = "@name@"
PROJECT_NUMBER = "@version@"
OUTPUT_DIRECTORY = "@outputDirectory@"
JAVADOC_AUTOBRIEF = YES
QT_AUTOBRIEF = YES
RECURSIVE = YES
GENERATE_LATEX = NO
GENERATE_MAN = YES
USE_MATHJAX = YES
MATHJAX_RELPATH = "./mathjax"
HTML_OUTPUT = "html"
GENERATE_HTML = YES
96 changes: 96 additions & 0 deletions c/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
let self =
args@{ base
, lib
, pkgs
, platforms ? { }
, extraAttrs ? { }
, generateDocs ? true
, components
, mathjax ? null
}:
let
mathjaxDefaultVersion = "3.2.2";
mathjax' =
if mathjax == null then
builtins.fetchurl
{
url = "https://cdn.jsdelivr.net/npm/mathjax@${mathjaxDefaultVersion}/es5/tex-svg.js";
sha256 = "sha256:10m80cpdhk1jqvqvkzy8qls7nmsra77fx7rrq4snk0s46z1msafl";
} else mathjax;

inner =
{ name
, pkgs
, stdenv ? pkgs.stdenv
, output ? null
, platformOverrides ? _: { }
}:
let
factory = pkgs.callPackage (import ./make-derivation.nix platformOverrides)
{
inherit base stdenv components;
targetName = name;
mathjax = mathjax';
};

finalPlatform = factory:
{
inherit name pkgs;
__functor = _self: factory;
overrideFactory = overrides:
finalPlatform (
factory.override overrides);
} // lib.optionalAttrs (output != null) { inherit output; };
in
(finalPlatform factory);

mkPlatform = lib.makeOverridable inner;

platforms' = {
_default = mkPlatform {
inherit pkgs;
name = "_default";
};
} // platforms;

createPlatformTargets = attrsOrFn:
lib.mapAttrs'
(name: platform: {
name = platform.output or name;
value = platform attrsOrFn;
})
platforms';

toComponent = componentFactory: targets:
componentFactory
({
name = targets._default.name;
version = targets._default.version;
} // targets // lib.optionalAttrs generateDocs {
docs.api = targets._default.doc;
});
in
extraAttrs // rec {
inherit mkPlatform;
platforms = platforms' // {
override = overridePlatforms;
};

name = "c/c++";
emoji = "🦕";
description = ''
C/C++
'';

overrideAttrs = f: self (args // {
extraAttrs = (extraAttrs // (f extraAttrs));
});
overridePlatforms = f: self (args // {
platforms = (platforms' // (f platforms'));
});
} // (
builtins.listToAttrs
(builtins.map
(t: { name = "mk${t}"; value = attrsOrFn: toComponent base."mk${t}" (createPlatformTargets attrsOrFn); })
[ "Library" "Client" "Service" ]));
in self
20 changes: 20 additions & 0 deletions c/doxygen-awesome.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{ stdenv, fetchFromGitHub }:
stdenv.mkDerivation rec {
name = "doxygen-awesome-css";
version = "2.2.0";
src = fetchFromGitHub {
owner = "jothepro";
repo = name;
rev = "v${version}";
sha256 = "sha256-hcZiHrlgeBLvEhzJk0iZ2GpSxVPn/mjt2Hkt6Mh00OU=";
};
installFlags = [ "PREFIX=$(out)" ];
postInstall = ''
echo "GENERATE_TREEVIEW = YES
DISABLE_INDEX = NO
FULL_SIDEBAR = NO
HTML_EXTRA_STYLESHEET = $out/share/doxygen-awesome-css/doxygen-awesome.css \
$out/share/doxygen-awesome-css/doxygen-awesome-sidebar-only.css
HTML_COLORSTYLE = LIGHT # required with Doxygen >= 1.9.5 ">"$out/Doxyfile"
'';
}
195 changes: 195 additions & 0 deletions c/make-derivation.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
platformOverrides:
{ base
, stdenv
, pkgs
, lib
, buildPackages
, substitute
, writeShellScriptBin
, components
, targetName
, doxygenOutputDir ? "doc"
, mathjax
, enableDoxygen ? true
, doxygenTheme ? null
, overrideAttrs ? _: { }
}:

attrsOrFn:

let
doxygenTheme' =
if doxygenTheme == null then
buildPackages.callPackage ./doxygen-awesome.nix { }
else doxygenTheme;

doxyfile = attrs: substitute {
name = "Doxyfile";
src = ./Doxyfile;
replacements = [
"--subst-var-by"
"name"
attrs.name
"--subst-var-by"
"version"
attrs.version
"--subst-var-by"
"outputDirectory"
attrs.doxygenOutputDir or doxygenOutputDir
];
};


wrappedDoxygen = outputDir: doxyfiles: writeShellScriptBin "doxygen" ''
set -euo pipefail
doxyfiles=(${builtins.concatStringsSep " " doxyfiles})
: ''${componentDir=$PWD}
if [ -e "$componentDir/Doxyfile" ]; then
doxyfiles+=("$componentDir/Doxyfile")
fi
if [[ $@ =~ "--print-generated-config" ]]; then
${buildPackages.bat}/bin/bat "''${doxyfiles[@]}"
exit 0
fi
# copy mathjax
mkdir -p "${outputDir}"/html/mathjax/
cp --no-preserve=mode "${mathjax}" "${outputDir}"/html/mathjax/MathJax.js
if [ $# -eq 0 ]; then
${buildPackages.doxygen}/bin/doxygen <(cat "''${doxyfiles[@]}")
else
${buildPackages.doxygen}/bin/doxygen "$@"
fi
'';

attrsOrFn' =
if builtins.isPath attrsOrFn then
import attrsOrFn
else
attrsOrFn;

attrsFn =
if builtins.isFunction attrsOrFn' then
attrsOrFn'
else
{}: attrsOrFn';

fn = args:
let
attrs' = (attrsFn args);
platformAttrs = attrs' // (platformOverrides attrs');
attrs = platformAttrs // (overrideAttrs platformAttrs);

in
(base.mkDerivation
({
inherit stdenv;
doCheck = true;
strictDeps = true;
outputs = [ "out" "doc" "man" ];
} // attrs // {
nativeBuildInputs = [
buildPackages.clang-tools
buildPackages.valgrind
]
++ attrs.nativeBuildInputs or [ ]
++ lib.optional enableDoxygen (
wrappedDoxygen
(attrs.doxygenOutputDir or doxygenOutputDir)
[
(doxyfile attrs)
"${doxygenTheme'}/Doxyfile"
]
);

shellInputs = [
pkgs.${attrs.debugger or "gdb"}
] ++ attrs.shellInputs or [ ];

lintPhase = attrs.lintPhase or ''
runHook preLint
if [ -z "''${dontCheckClangFormat:-}" ]; then
echo "🏟 Checking format in C/C++ files..."
${buildPackages.fd}/bin/fd --ignore-file=.gitignore --glob '*.[h,hpp,hh,cpp,cxx,cc,c]' --exec-batch clang-format -Werror -n --style=file
rc=$?
if [ $rc -eq 0 ]; then
echo "✅ perfectly formatted!"
fi
fi
runHook postLint
'';

shellCommands = {
build = {
# TODO: fix undef of buildPhase when we stop using mkShell
script = ''
: ''${NIX_LOG_FD:=} ''${buildFlags:=}
${if attrs ? buildPhase then "" else "unset buildPhase"}
phases="''${preConfigurePhases:-} configurePhase ''${preBuildPhases:-} buildPhase"
genericBuild
'';
description = "Build the component.";
};
format = {
script = ''
runHook preFormat
echo "🏟️ Formatting C++ files..."
${buildPackages.fd}/bin/fd --glob '*.[h,hpp,hh,cpp,cxx,cc,c]' --exec-batch clang-format --style=file -i "$@"
runHook postFormat
'';
description = "Format source code in the component.";
};
check = {
script = ''
: ''${NIX_LOG_FD:=} ''${buildFlags:=} ''${lintPhase:=echo 'no lintPhase'}
${if attrs ? buildPhase then "" else "unset buildPhase"}
phases="''${preConfigurePhases:-} configurePhase ''${preBuildPhases:-} buildPhase checkPhase lintPhase"
genericBuild
'';
description = "Run the checks and lints for the component.";
};
debug = {
script = ''${attrs.debugger or "gdb"} ${if attrs ? executable then "${attrs.executable} \"$@\"" else "$@"}'';
description = "Run ${attrs.debugger or "gdb"}${if attrs ? executable then " on ${attrs.executable}" else ""}.";
};
}
// lib.optionalAttrs (attrs ? executable) {
run = {
script = ''./${attrs.executable} "$@"'';
description = "Run ${attrs.executable}";
};
} // lib.optionalAttrs enableDoxygen {
show-doxyfile = {
script = "doxygen --print-generated-config";
description = "Show the combination of Doxygen files that will be used by Doxygen.";
};
docs = {
script = ''
doxygen && xdg-open ${attrs.doxygenOutputDir or doxygenOutputDir}/html/index.html
'';
description = "Show the documentation.";
};
}
// attrs.shellCommands or { };
}));

splicedComponents = base.mapComponentsRecursive
(_path: component:
if builtins.hasAttr targetName component then
# Promote the requested target to top-level on the component. This makes it
# possible to still use other targets on the components if needed. I.e. <component>
# will be the requested target whereas <component>.<other-target> will be the other
# target.
component.componentAttrs // component."${targetName}"
else
component
)
components;
in
lib.makeOverridable fn (
builtins.intersectAttrs
(builtins.functionArgs attrsFn)
(pkgs // splicedComponents // { inherit targetName; })
)
2 changes: 2 additions & 0 deletions docs/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
- [Overriding](./rust/override.md)
- [Python](./python/components.md)
- [Overriding](./python/override.md)
- [C/C++](./c/components.md)
- [Overriding](./c/override.md)
- [Terraform](./terraform/components.md)
- [Protobuf](./protobuf.md)
---
Expand Down
Loading

0 comments on commit 43ed8d2

Please sign in to comment.