Skip to content
Draft
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
1 change: 1 addition & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,4 @@ bazel_dep(name = "tar.bzl", version = "0.7.0")
bazel_dep(name = "score_tooling", version = "1.1.2")
bazel_dep(name = "aspect_rules_lint", version = "2.3.0")
bazel_dep(name = "buildifier_prebuilt", version = "8.5.1")
bazel_dep(name = "rules_pkg", version = "1.2.0")
4 changes: 3 additions & 1 deletion MODULE.bazel.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

69 changes: 69 additions & 0 deletions rules/qnx/common/attrs.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# *******************************************************************************
# Copyright (c) 2026 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************

"""
Common attribute definitions for QNX image generation Bazel rules.

This module defines a shared attribute dictionary (`COMMON_RULES_ATTRS`) used
across rules responsible for generating QNX IFS images. These attributes
standardize how input files, build definitions, and output artifacts are
specified and handled within the rule implementations.

Attributes:
all_files (label_list, mandatory):
Collection of input targets contributing to the filesystem image.
This may include DUI files, `rules_pkg` outputs, and regular files.

build_file (label, mandatory):
Label pointing to the main QNX build file (entry point). Must resolve
to a single file.

extension (string, default = "ifs"):
File extension for the generated IFS image. This exists primarily as a
workaround for limitations in IPNext startup code when interpreting IFS
images. This attribute is expected to be deprecated or replaced by a
toolchain-based configuration in the future to ensure consistency.

extra_build_files (label_list, default = []):
Additional build files to be included after the main `build_file`.
These are appended in order and allow modularization of build logic.

out (string, default = ""):
Optional explicit name for the output file (must not include path
components). If not provided, the output filename is derived as:
`<rule_name>.<extension>`.
"""

COMMON_RULES_ATTRS = {
"all_files": attr.label_list(
mandatory = True,
),
"build_file": attr.label(
allow_single_file = True,
doc = "Single label that points to the main build file (entrypoint)",
mandatory = True,
),
"extension": attr.string(
default = "ifs",
doc = "Extension for the generated IFS image. Manipulating this extensions is a workaround for IPNext startup code limitation, when interpreting ifs images. This attribute will either disappear or will be replaced by toolchain configuration in order to keep output files consistent.",
),
"extra_build_files": attr.label_list(
allow_files = True,
default = [],
doc = "Additional build files to be included after the main build_file.",
),
"out": attr.string(
default = "",
doc = "Optional explicit output filename (no path). If empty, uses name + '.' + extension.",
),
}
120 changes: 120 additions & 0 deletions rules/qnx/common/common.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# *******************************************************************************
# Copyright (c) 2026 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************

""" TODO: Write docstrings
"""

def prep_output(ctx):
"""
Determines and declares the output file for the QNX image build action.

This function resolves the output filename based on rule attributes. If
`ctx.attr.out` is explicitly provided, it is used directly; otherwise, the
filename is derived from the rule name and the specified extension. The
function validates that the resulting filename does not contain any path
components, ensuring it represents a single artifact in the current package.
It then declares the output file via `ctx.actions.declare_file`.

Args:
ctx: The rule context (`RuleContext`) provided by Bazel. It is expected
to expose the following attributes:
- ctx.attr.out: Optional explicit output filename.
- ctx.attr.name: Name of the rule, used as a fallback prefix.
- ctx.attr.extension: File extension used when `out` is not provided.

Returns:
File: A declared output `File` object representing the generated QNX
image artifact.

Raises:
Fail: If the resolved output name contains path separators (i.e., is not
a simple filename).
"""
out_name = ctx.attr.out if ctx.attr.out else "{}.{}".format(ctx.attr.name, ctx.attr.extension)
if "/" in out_name:
fail("qnx_ifs.out must be a filename without path components, got: {}".format(out_name))

return ctx.actions.declare_file(out_name)

def prep_inputs(ctx):
"""
Prepares and aggregates input files required for QNX image generation.

This function invokes `gen_image_definition` to resolve the QNX image
definition artifacts, including the main build file, any included build
files, and filesystem contents. It then appends these artifacts to the
`inputs` collection, which is intended to be consumed by a downstream
Bazel action. Additionally, it returns the path to the main build file,
which typically serves as the entry point for the image generation process.

Args:
ctx: The rule context (`RuleContext`) provided by Bazel. It is expected
to expose the following attributes:
- ctx.attr.all_files: Combined set of source inputs (e.g., DUI,
rules_pkg outputs, and regular files).
- ctx.file.build_file: Optional main build file.
- ctx.files.extra_build_files: Additional build files to be included.

Returns:
A tuple containing:
- main_build_file.path (str): Filesystem path to the main QNX build
file used as the entry point.
- inputs (list[File]): List of input `File` objects including the
main build file, additional build files, and filesystem contents.
"""
main_build_file, build_files, fs_contents = gen_image_definition(
ctx,
srcs = ctx.attr.all_files,
extra_build_file = ctx.file.build_file,
extra_build_files = ctx.files.extra_build_files,
)

inputs.append(main_build_file)
inputs.extend(build_files)
inputs.extend(fs_contents)

return main_build_file.path, inputs

def gen_image(ctx, inputs, outputs, arguments, image_tc_type):
"""
Executes an image generation action by invoking a selected tool.

This function registers a `ctx.actions.run` action that uses the specified
executable and environment from `tool_info` to transform `inputs` into
`outputs`. It then returns a `DefaultInfo` provider exposing the generated
output files.

Args:
ctx: The rule context (`RuleContext`) provided by Bazel. Used to declare
and register build actions.
inputs: A sequence or depset of input `File` objects required by the
action.
outputs: A list of output `File` objects that will be generated by the
action.
arguments: A list of command-line arguments passed to the executable.
image_tc_type: .

Returns:
A list containing a single `DefaultInfo` provider, where `files` is a
depset of the generated output files.
"""
tool_info = ctx.toolchains[image_tc_type].imagefs_toolchain_info
ctx.actions.run(
outputs = outputs,
inputs = inputs,
arguments = arguments,
executable = tool_info.executable,
env = tool_info.env,
tools = tool_info.tools,
)
return [DefaultInfo(files = depset(outputs))]
122 changes: 122 additions & 0 deletions rules/qnx/common/pkg.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# *******************************************************************************
# Copyright (c) 2026 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************

"""
Supports QNX zzzzzzzzzzzzzzzzzzzzzzzzzzzzFS image generation from rules_pkg providers. Filters rules_pkg
targets from srcs and generates QNX build file entries from their contents.
"""

load("@bazel_skylib//lib:paths.bzl", "paths")
load("@rules_pkg//pkg:providers.bzl", "PackageDirsInfo", "PackageFilegroupInfo", "PackageFilesInfo", "PackageSymlinkInfo")

_DEFAULT_FILE_MODE = "0644"
_DEFAULT_DIR_MODE = "0755"
_DEFAULT_LINK_MODE = "0777"
_DEFAULT_UID = 0
_DEFAULT_GID = 0
_FIXED_MTIME = "2021-03-22-20:20:00"

def _get_pkg_attr(attributes, field, default):
"""Get a single field from a pkg_attributes dict, defaulting if absent or None."""
if attributes == None:
return default
value = attributes.get(field)
if value == None:
return default
return value

def qnx_build_inputs_from_pkg(ctx, pkg_srcs, per_file_attrs = None):
"""Generate QNX build file and collect contents from rules_pkg sources.

Args:
ctx: Rule context.
pkg_srcs: List of targets providing rules_pkg providers.
per_file_attrs: Optional list of extra attributes for file entries
(e.g. ["-optional"]).
Returns:
fs_contents: List of Files to include in the image.
build_file: Generated QNX build file.
"""
content = "[mtime={}]\n".format(_FIXED_MTIME)
fs_contents = []
extra_attrs_str = ""
if per_file_attrs:
extra_attrs_str = " ".join(per_file_attrs) + " "

for src in pkg_srcs:
# Normalize to lists of (provider, label) tuples regardless of source type
if PackageFilegroupInfo in src:
pfgi = src[PackageFilegroupInfo]
files_list = pfgi.pkg_files
dirs_list = pfgi.pkg_dirs
symlinks_list = pfgi.pkg_symlinks
elif (
PackageFilesInfo in src or
PackageDirsInfo in src or
PackageSymlinkInfo in src
):
# src.label is included in the manually-constructed tuples to match the (provider, label) structure from PackageFilegroupInfo.
files_list = [(src[PackageFilesInfo], src.label)] if PackageFilesInfo in src else []
dirs_list = [(src[PackageDirsInfo], src.label)] if PackageDirsInfo in src else []
symlinks_list = [(src[PackageSymlinkInfo], src.label)] if PackageSymlinkInfo in src else []
else:
fail("Target {} does not provide any rules_pkg provider".format(src.label))

# Process directories
for pdi, _label in dirs_list:
mode = _get_pkg_attr(pdi.attributes, "mode", _DEFAULT_DIR_MODE)
uid = _get_pkg_attr(pdi.attributes, "uid", _DEFAULT_UID)
gid = _get_pkg_attr(pdi.attributes, "gid", _DEFAULT_GID)
for dir_path in pdi.dirs:
dir_path = paths.normalize(dir_path)
content += "[type=dir uid={} gid={} perms={}] /{}\n".format(
uid,
gid,
mode,
dir_path,
)

# Process files
for pfi, _label in files_list:
mode = _get_pkg_attr(pfi.attributes, "mode", _DEFAULT_FILE_MODE)
uid = _get_pkg_attr(pfi.attributes, "uid", _DEFAULT_UID)
gid = _get_pkg_attr(pfi.attributes, "gid", _DEFAULT_GID)
for dest, src_file in sorted(pfi.dest_src_map.items()):
content += "[{}uid={} gid={} perms={}] /{}={}\n".format(
extra_attrs_str,
uid,
gid,
mode,
dest,
src_file.path,
)
fs_contents.append(src_file)

# Process symlinks
for psi, _label in symlinks_list:
mode = _get_pkg_attr(psi.attributes, "mode", _DEFAULT_LINK_MODE)
uid = _get_pkg_attr(psi.attributes, "uid", _DEFAULT_UID)
gid = _get_pkg_attr(psi.attributes, "gid", _DEFAULT_GID)
dest = paths.normalize(psi.destination)
content += "[type=link uid={} gid={} perms={}] /{}={}\n".format(
uid,
gid,
mode,
dest,
psi.target,
)

build_file = ctx.actions.declare_file("{}_pkg_content.build".format(ctx.attr.name))
ctx.actions.write(build_file, content)

return fs_contents, build_file
Loading
Loading