Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions core/MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,12 @@ module(

bazel_dep(name = "platforms", version = "0.0.4")
bazel_dep(name = "bazel_skylib", version = "1.0.3")

nix_repo = use_extension("//extensions:repository.bzl", "nix_repo")
use_repo(nix_repo, "nixpkgs_repositories")
nix_repo.override(name = "nixpkgs")
nix_repo.github(
name = "nixpkgs",
tag = "22.11",
sha256 = "ddc3428d9e1a381b7476750ac4dbea7a42885cbbe6e1af44b21d6447c9609a6f",
)
Empty file added core/extensions/BUILD.bazel
Empty file.
223 changes: 223 additions & 0 deletions core/extensions/package.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
"""Defines the nix_pkg module extension.
"""

load("//:nixpkgs.bzl", "nixpkgs_package")
load("//:util.bzl", "fail_on_err")
load("//private:module_registry.bzl", "registry")
load("@bazel_skylib//lib:dicts.bzl", "dicts")
load("@bazel_skylib//lib:partial.bzl", "partial")
load("@nixpkgs_repositories//:defs.bzl", "nix_repo")

_ACCESSOR = '''\
def nix_pkg(module_name, name, label):
"""Access a Nix package imported with `nix_pkg`.

Args:
module_name: `String`; Name of the calling Bazel module.
This is needed until Bazel offers unique module identifiers,
see [#17652][bazel-17652].
name: `String`; Name of the package.
label: `String`; Target within the package.
A string representation of a label under the package's external
workspace.

Returns:
`Label`; The resolved label to the target within the package's external workspace.

[bazel-17652]: https://github.com/bazelbuild/bazel/issues/17652
"""
resolved = _fail_on_err(
_get_repository(module_name, name),
prefix = "Invalid Nix repository, you must use the nix_repo extension and request a global repository or register a local repository: ",
)
return resolved.relative(label)
'''

def _name_from_attr(attr):
"""Generate a global workspace name from an attribute path.
"""
return attr

def _attr_pkg(attr):
return partial.make(
nixpkgs_package,
attribute_path = attr.attr,
repository = nix_repo("rules_nixpkgs_core", "nixpkgs"),
)

def _local_attr_pkg(key, local_attr):
kwargs = {}

if bool(local_attr.attr):
kwargs["attribute_path"] = local_attr.attr
else:
kwargs["attribute_path"] = local_attr.name

repo_set = bool(local_attr.repo)
repos_set = bool(local_attr.repos)

if repo_set and repos_set:
fail("Duplicate Nix repositories. Specify at most one of `repo` and `repos`.")
elif repo_set:
kwargs["repository"] = nix_repo(key, local_attr.repo)
elif repos_set:
kwargs["repositories"] = {
name: nix_repo(key, repo)
for name, repo in local_attr.repos.items()
}
else:
kwargs["repository"] = nix_repo(key, "nixpkgs")

build_file_set = bool(local_attr.build_file)
build_file_content_set = bool(local_attr.build_file_content)

if build_file_set and build_file_content_set:
fail("Duplicate BUILD file. Specify at most one of `build_file` and `build_file_contents`.")
elif build_file_set:
kwargs["build_file"] = local_attr.build_file
elif build_file_content_set:
kwargs["build_file"] = local_attr.build_file_content

return partial.make(
nixpkgs_package,
**kwargs
)

def _nix_pkg_impl(module_ctx):
r = registry.make()

for mod in module_ctx.modules:
key = fail_on_err(registry.add_module(r, name = mod.name, version = mod.version))

for attr in mod.tags.attr:
name = _name_from_attr(attr.attr)
fail_on_err(
registry.use_global_repo(r, key = key, name = name),
prefix = "Cannot use unified Nix package: ",
)
if not registry.has_global_repo(r, name = name):
fail_on_err(
registry.add_global_repo(
r,
name = name,
repo = _attr_pkg(attr),
),
prefix = "Cannot define unified Nix package: ",
)

for local_attr in mod.tags.local_attr:
fail_on_err(
registry.add_local_repo(
r,
key = key,
name = local_attr.name,
repo = _local_attr_pkg(key, local_attr),
),
prefix = "Cannot use Nix package: ",
)

for repo_name, repo in registry.get_all_repositories(r).items():
partial.call(repo, name = repo_name)

fail_on_err(
registry.hub_repo(r, name = "nixpkgs_packages", accessor = _ACCESSOR),
prefix = "Failed to generate `nixpkgs_packages`: ",
)

_ATTR_ATTRS = {
"attr": attr.string(
doc = "The attribute path of the package to import.",
mandatory = True,
),
}

_COMMON_ATTRS = {
"name": attr.string(
doc = "A unique name for this package. The name must be unique within the requesting module.",
mandatory = True,
),
"attr": attr.string(
doc = "The attribute path of the package to import. Defaults to `name`.",
mandatory = False,
),
}

_REPO_ATTRS = {
"repo": attr.string(
doc = """\
The Nix repository to use.
Equivalent to `repos = {"nixpkgs": repo}`.
Specify at most one of `repo` or `repos`.
""",
mandatory = False,
),
"repos": attr.string_dict(
doc = """\
The Nix repositories to use. The dictionary keys represent the names of the
`NIX_PATH` entries. For example, `repositories = { "myrepo" : "somerepo" }`
would replace all instances of `<myrepo>` in the Nix code by the path to the
Nix repository `somerepo`. See the [relevant section in the nix
manual](https://nixos.org/nix/manual/#env-NIX_PATH) for more information.
Specify at most one of `repo` or `repos`.
""",
mandatory = False,
),
}

_BUILD_ATTRS = {
"build_file": attr.label(
doc = """\
The file to use as the `BUILD` file for the external workspace generated for this package.

Its contents are copied into the file `BUILD` in root of the nix output folder. The Label does not need to be named `BUILD`, but can be.

For common use cases we provide filegroups that expose certain files as targets:

<dl>
<dt><code>:bin</code></dt>
<dd>Everything in the <code>bin/</code> directory.</dd>
<dt><code>:lib</code></dt>
<dd>All <code>.so</code>, <code>.dylib</code> and <code>.a</code> files that can be found in subdirectories of <code>lib/</code>.</dd>
<dt><code>:include</code></dt>
<dd>All <code>.h</code>, <code>.hh</code>, <code>.hpp</code> and <code>.hxx</code> files that can be found in subdirectories of <code>include/</code>.</dd>
</dl>

If you need different files from the nix package, you can reference them like this:
```
package(default_visibility = [ "//visibility:public" ])
filegroup(
name = "our-docs",
srcs = glob(["share/doc/ourpackage/**/*"]),
)
```
See the bazel documentation of [`filegroup`](https://docs.bazel.build/versions/master/be/general.html#filegroup) and [`glob`](https://docs.bazel.build/versions/master/be/functions.html#glob).
Specify at most one of `build_file` or `build_file_content`.
""",
mandatory = False,
),
"build_file_content": attr.string(
doc = """\
Like `build_file`, but a string of the contents instead of a file name.
Specify at most one of `build_file` or `build_file_content`.
""",
mandatory = False,
),
}

_attr_tag = tag_class(
attrs = _ATTR_ATTRS,
doc = "Import a globally unified Nix package. If multiple Bazel modules import the same nixpkgs attribute, then they will all use the same external Bazel repository that imports the Nix package.",
)

_local_attr_tag = tag_class(
attrs = dicts.add(_COMMON_ATTRS, _REPO_ATTRS, _BUILD_ATTRS),
doc = "Import a Nix package by attribute path.",
)

nix_pkg = module_extension(
_nix_pkg_impl,
tag_classes = {
"attr": _attr_tag,
"local_attr": _local_attr_tag,
},
)
Loading