feat: doxygen compilation from source (wip)
diff --git a/doxygen/doxygen.bzl b/doxygen/doxygen.bzl
index 02e0828..e635c31 100644
--- a/doxygen/doxygen.bzl
+++ b/doxygen/doxygen.bzl
@@ -2,7 +2,7 @@
 
 def _expand_make_variables(string, ctx):
     """Replace make variables in a string with their values.
-    
+
     Args:
         string: The string to expand.
         ctx: The context object.
@@ -44,7 +44,7 @@
         outputs = outs,
         arguments = [doxyfile.path] + ctx.attr.doxygen_extra_args,
         progress_message = "Running doxygen",
-        executable = ctx.executable._executable,
+        executable = ctx.executable.executable,
     )
 
     return [
@@ -104,7 +104,7 @@
             doc = "The dot executable to use. Must refer to an executable file.",
         ),
         "doxygen_extra_args": attr.string_list(default = [], doc = "Extra arguments to pass to the doxygen executable."),
-        "_executable": attr.label(
+        "executable": attr.label(
             executable = True,
             cfg = "exec",
             allow_single_file = True,
@@ -1113,7 +1113,7 @@
     _add_generic_configuration(configurations, "MSCFILE_DIRS", mscfile_dirs)
 
     if doxyfile_template:
-        kwargs["doxyfile_template"] = doxyfile_template 
+        kwargs["doxyfile_template"] = doxyfile_template
 
     _doxygen(
         name = name,
diff --git a/examples/MODULE.bazel b/examples/MODULE.bazel
index f74aa16..b532738 100644
--- a/examples/MODULE.bazel
+++ b/examples/MODULE.bazel
@@ -9,6 +9,43 @@
 bazel_dep(name = "aspect_bazel_lib", version = "2.10.0")
 bazel_dep(name = "bazel_skylib", version = "1.7.1")
 bazel_dep(name = "rules_foreign_cc", version = "0.14.0")
+bazel_dep(name = "libconfig", version = "1.7.3")
+bazel_dep(name = "rules_flex", version = "0.4")
+bazel_dep(name = "rules_bison", version = "0.4")
+
+github_archive = use_repo_rule("//:git_archive.bzl", "github_archive")
+
+github_archive(
+    name = "doxygen_source",
+    build_file = "//:doxygen_source.BUILD.bazel",
+    commit = "200cec9b87fa544f39deeb96966568985f40aea8",
+    repository = "doxygen/doxygen",
+    sha256 = "6aa71da24aa7b8fc8f9eba5a6651b1fb47c569c68d3e226b011d39a15087c2fb",
+)
+
+bison = use_extension(
+    "@rules_bison//bison/extensions:bison_repository_ext.bzl",
+    "bison_repository_ext",
+)
+bison.repository(
+    name = "bison",
+    version = "3.3.2",
+    extra_copts = ["-O3"],
+)
+use_repo(bison, "bison")
+register_toolchains("@bison//:toolchain")
+
+flex = use_extension(
+    "@rules_flex//flex/extensions:flex_repository_ext.bzl",
+    "flex_repository_ext",
+)
+flex.repository(
+    name = "flex",
+    version = "2.6.4",
+    extra_copts = ["-O3"],
+)
+use_repo(flex, "flex")
+register_toolchains("@flex//:toolchain")
 
 doxygen_extension = use_extension("@rules_doxygen//:extensions.bzl", "doxygen_extension")
 
diff --git a/examples/doxygen_source.BUILD.bazel b/examples/doxygen_source.BUILD.bazel
new file mode 100644
index 0000000..5f7ca4e
--- /dev/null
+++ b/examples/doxygen_source.BUILD.bazel
@@ -0,0 +1,35 @@
+load("@rules_foreign_cc//foreign_cc:defs.bzl", "cmake")
+
+filegroup(
+    name = "all_srcs",
+    srcs = glob(["**"]),
+    visibility = ["//visibility:public"],
+)
+
+cmake(
+    name = "doxygen",
+    lib_source = ":all_srcs",
+    out_binaries = ["doxygen"],
+    visibility = ["//visibility:public"],
+    env = {
+        "FLEX": "$$EXT_BUILD_DEPS/flex/bin/flex/bin",
+    },
+    build_data = [
+        "@rules_flex//flex:current_flex_toolchain",
+        "@rules_bison//bison:current_bison_toolchain",
+    ],
+)
+
+genrule(
+    name = "doxygen_binary",
+    srcs = [":doxygen"],
+    outs = ["doxygen_bin"],
+    cmd = "cp $$1 $@",
+    visibility = ["//visibility:public"],
+)
+
+filegroup(
+    name = "doxygen_source",
+    srcs = [":doxygen_binary"],
+    visibility = ["//visibility:public"],
+)
diff --git a/examples/git_archive.bzl b/examples/git_archive.bzl
new file mode 100644
index 0000000..745a5ac
--- /dev/null
+++ b/examples/git_archive.bzl
@@ -0,0 +1,93 @@
+"""Repository rules for downloading archives from GitHub and GitLab"""
+
+def workspace_and_buildfile(ctx):
+    """Utility function for writing WORKSPACE and, if requested, a BUILD file.
+
+    This rule is intended to be used in the implementation function of a
+    repository rule.
+    It assumes the parameters `name`, `build_file`, `build_file_content`,
+    `workspace_file`, and `workspace_file_content` to be
+    present in `ctx.attr`; the latter four possibly with value None.
+
+    Args:
+      ctx: The repository context of the repository rule calling this utility
+        function.
+    """
+    if ctx.attr.build_file and ctx.attr.build_file_content:
+        ctx.fail("Only one of build_file and build_file_content can be provided.")
+
+    if ctx.attr.workspace_file and ctx.attr.workspace_file_content:
+        ctx.fail("Only one of workspace_file and workspace_file_content can be provided.")
+
+    if ctx.attr.workspace_file:
+        ctx.file("WORKSPACE", ctx.read(ctx.attr.workspace_file))
+    elif ctx.attr.workspace_file_content:
+        ctx.file("WORKSPACE", ctx.attr.workspace_file_content)
+    else:
+        ctx.file("WORKSPACE", "workspace(name = \"{name}\")\n".format(name = ctx.name))
+
+    if ctx.attr.build_file:
+        ctx.file("BUILD.bazel", ctx.read(ctx.attr.build_file))
+    elif ctx.attr.build_file_content:
+        ctx.file("BUILD.bazel", ctx.attr.build_file_content)
+
+def _github_archive_impl(repository_ctx):
+    """A rule to be called in the WORKSPACE that adds an external from github using a workspace rule.
+
+    The required name= is the rule name and so is used for @name//... labels when referring to this archive from BUILD files.
+
+    The required commit= is the git hash to download.
+    When the git project is also a git submodule in CMake, this should be kept in sync with the git submodule commit used there.
+    This can also be a tag.
+
+    The required sha256= is the checksum of the downloaded archive.
+    When unsure, you can omit this argument (or comment it out) and then the checksum-mismatch error message message will offer a suggestion.
+
+    The optional build_file= is the BUILD file label to use for building this external.
+    When omitted, the BUILD file(s) within the archive will be used.
+
+    The optional local_repository_override= can be used for temporary local testing;
+    instead of retrieving the code from github, the code is retrieved from the local filesystem path given in the argument.
+
+    Args:
+        repository_ctx: The context object for the repository rule.
+    """
+    if repository_ctx.attr.build_file and repository_ctx.attr.build_file_content:
+        fail("Only one of build_file and build_file_content can be provided.")
+    if repository_ctx.attr.workspace_file and repository_ctx.attr.workspace_file_content:
+        fail("Only one of workspace_file and workspace_file_content can be provided.")
+
+    repository = repository_ctx.attr.repository
+    commit = repository_ctx.attr.commit
+
+    urls = ["https://github.com/%s/archive/%s.tar.gz" % (repository, commit)]
+
+    repository_split = repository.split("/")
+    if len(repository_split) != 2:
+        fail("The repository must be formatted as 'organization/project'. Got: %s" % repository)
+    _, project = repository_split
+
+    # Github archives omit the "v" in version tags, for some reason.
+    strip_commit = commit.removeprefix("v")
+    strip_prefix = project + "-" + strip_commit
+
+    repository_ctx.download_and_extract(
+        urls,
+        sha256 = repository_ctx.attr.sha256,
+        stripPrefix = strip_prefix,
+    )
+    workspace_and_buildfile(repository_ctx)
+
+github_archive = repository_rule(
+    implementation = _github_archive_impl,
+    local = True,
+    attrs = {
+        "repository": attr.string(mandatory = True, doc = "The github repository to download from."),
+        "commit": attr.string(mandatory = True, doc = "The git commit hash to download."),
+        "sha256": attr.string(default = "0" * 64, doc = "The sha256 checksum of the downloaded archive."),
+        "build_file": attr.label(doc = "The BUILD file label to use for building this external."),
+        "build_file_content": attr.string(doc = "The content for the BUILD file for this repository."),
+        "workspace_file": attr.label(doc = "The file to use as the `WORKSPACE` file for this repository."),
+        "workspace_file_content": attr.string(doc = "The content for the WORKSPACE file for this repository."),
+    },
+)
diff --git a/examples/source/BUILD.bazel b/examples/source/BUILD.bazel
new file mode 100644
index 0000000..4f51bf7
--- /dev/null
+++ b/examples/source/BUILD.bazel
@@ -0,0 +1,12 @@
+load("@doxygen//:doxygen.bzl", "doxygen")
+
+doxygen(
+    name = "doxygen",
+    srcs = glob([
+        "*.h",
+        "*.cpp",
+    ]),
+    executable = "@doxygen_source//:doxygen_source",
+    project_brief = "Example project for doxygen",
+    project_name = "base",
+)
diff --git a/examples/source/README.md b/examples/source/README.md
new file mode 100644
index 0000000..c7f1c01
--- /dev/null
+++ b/examples/source/README.md
@@ -0,0 +1,8 @@
+# Basic usage example
+
+This is a basic example of how to use the rules.
+Move to the parent directory and run the following command:
+
+```bash
+bazel build //base:doxygen
+```
diff --git a/examples/source/lib.cpp b/examples/source/lib.cpp
new file mode 100644
index 0000000..e9c08bc
--- /dev/null
+++ b/examples/source/lib.cpp
@@ -0,0 +1,9 @@
+/**
+ * @file lib.cpp
+ * @author Ernesto Casablanca (casablancaernesto@gmail.com)
+ * @copyright 2024
+ */
+
+#include "lib.h"
+
+int add(int a, int b) { return a + b; }
\ No newline at end of file
diff --git a/examples/source/lib.h b/examples/source/lib.h
new file mode 100644
index 0000000..9608ca2
--- /dev/null
+++ b/examples/source/lib.h
@@ -0,0 +1,18 @@
+/**
+ * @file lib.h
+ * @author Ernesto Casablanca (casablancaernesto@gmail.com)
+ * @copyright 2024
+ */
+#pragma once
+
+/**
+ * @brief Add two integers
+ *
+ * Who knows what the result will be?
+ * @note This function is very complex. Use it with caution.
+ * @warning The result can be greater than the maximum value that can be stored!
+ * @param a First integer
+ * @param b Second integer
+ * @return Sum of a and b
+ */
+int add(int a, int b);
\ No newline at end of file