Skip to content
Open
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
213 changes: 193 additions & 20 deletions renode/extensions.bzl
Original file line number Diff line number Diff line change
@@ -1,18 +1,116 @@
load("@bazel_tools//tools/build_defs/repo:local.bzl", "new_local_repository")

_DEFAULT_PORTABLE_RENODE = {
"name": "renode_default_toolchain",
"url": "https://builds.renode.io/renode-1.15.3+20250530git063124cbc.linux-portable-dotnet.tar.gz",
"sha256": "f4c7a4c5b7d852c633be0a309698cae34c3e0c4c801678d60c314d82597b26d4",
# Default portable Renode builds for different platforms
_DEFAULT_LINUX_X64 = {
"name": "renode_linux_x64",
"url": "https://github.com/renode/renode/releases/download/v1.16.0/renode-1.16.0.linux-portable-dotnet.tar.gz",
"sha256": "", # Will be filled after verification
"os": "linux",
"cpu": "x86_64",
}

_DEFAULT_LINUX_ARM64 = {
"name": "renode_linux_arm64",
"url": "https://github.com/renode/renode/releases/download/v1.16.0/renode-1.16.0.linux-arm64-portable-dotnet.tar.gz",
"sha256": "",
"os": "linux",
"cpu": "aarch64",
}

_DEFAULT_MACOS_ARM64 = {
"name": "renode_macos_arm64",
"url": "https://github.com/renode/renode/releases/download/v1.16.0/renode-1.16.0-dotnet.osx-arm64-portable.dmg",
"sha256": "",
"os": "macos",
"cpu": "aarch64",
}

_DEFAULT_MACOS_X64 = {
"name": "renode_macos_x64",
"url": "https://github.com/renode/renode/releases/download/v1.16.0/renode_1.16.0.dmg",
"sha256": "",
"os": "macos",
"cpu": "x86_64",
}

def _portable_renode_toolchain_repository_impl(repository_ctx):
"""Creates a repository with a single Renode runtime"""
"""Creates a repository with a single Renode runtime from tar.gz"""
repository_ctx.report_progress("Downloading Renode...")
repository_ctx.download_and_extract(
repository_ctx.attr.url,
sha256 = repository_ctx.attr.sha256,
if repository_ctx.attr.sha256:
repository_ctx.download_and_extract(
repository_ctx.attr.url,
sha256 = repository_ctx.attr.sha256,
)
else:
repository_ctx.download_and_extract(
repository_ctx.attr.url,
)
repository_ctx.file(
"BUILD.bazel",
content = repository_ctx.attr.build_file_content,
)

def _dmg_renode_toolchain_repository_impl(repository_ctx):
"""Creates a repository with a single Renode runtime from DMG (macOS)"""
repository_ctx.report_progress("Downloading Renode DMG...")

# Download the DMG file
dmg_path = "renode.dmg"
if repository_ctx.attr.sha256:
repository_ctx.download(
repository_ctx.attr.url,
output = dmg_path,
sha256 = repository_ctx.attr.sha256,
)
else:
repository_ctx.download(
repository_ctx.attr.url,
output = dmg_path,
)

# Mount, copy, and unmount the DMG
repository_ctx.report_progress("Extracting Renode from DMG...")

# Create mount point
mount_point = "dmg_mount"
repository_ctx.execute(["mkdir", "-p", mount_point])

# Mount DMG (read-only, no browse)
result = repository_ctx.execute([
"hdiutil", "attach", dmg_path,
"-mountpoint", mount_point,
"-nobrowse", "-readonly", "-noverify"
])
if result.return_code != 0:
fail("Failed to mount DMG: " + result.stderr)

# Find and copy the Renode.app or renode directory
# The DMG typically contains a Renode.app bundle
result = repository_ctx.execute(["ls", mount_point])
repository_ctx.report_progress("DMG contents: " + result.stdout)

# Copy contents - handle both .app bundle and direct directory
# Create the renode directory to match expected structure
repository_ctx.execute(["mkdir", "-p", "renode"])

# Try to copy from Renode.app/Contents/MacOS structure
cp_result = repository_ctx.execute([
"sh", "-c",
"if [ -d '%s/Renode.app/Contents/MacOS' ]; then " % mount_point +
"cp -R '%s/Renode.app/Contents/MacOS/'* renode/; " % mount_point +
"elif [ -d '%s/Renode' ]; then " % mount_point +
"cp -R '%s/Renode/'* renode/; " % mount_point +
"else " +
"cp -R '%s/'* renode/ 2>/dev/null || true; " % mount_point +
"fi"
])

# Unmount DMG
repository_ctx.execute(["hdiutil", "detach", mount_point, "-force"])

# Clean up
repository_ctx.execute(["rm", "-rf", mount_point, dmg_path])

repository_ctx.file(
"BUILD.bazel",
content = repository_ctx.attr.build_file_content,
Expand All @@ -26,53 +124,96 @@ _COSIMULATION_DIRECTORY = "%s"

def _renode_impl(module_ctx):
toolchains = []
toolchain_configs = [] # List of (name, os, cpu) tuples
build_file = module_ctx.read(Label("//renode:renode.BUILD.bazel"))

# Build file for tar.gz portable builds (Linux)
build_file_portable = _renode_build_file_variables(
"renode_*/",
"renode_*/plugins/**/IntegrationLibrary/",
) + build_file

# Build file for DMG builds (macOS) - different directory structure
build_file_macos = _renode_build_file_variables(
"renode/",
"renode/plugins/**/IntegrationLibrary/",
) + build_file

# Process user-specified portable downloads
for mod in module_ctx.modules:
for tag in mod.tags.download_portable:
toolchains.append(tag.name)
portable_renode_repository(
name = tag.name,
url = tag.url,
sha256 = tag.sha256,
build_file_content = build_file_portable,
)
os = tag.os if tag.os else "linux"
cpu = tag.cpu if tag.cpu else "x86_64"
toolchain_configs.append("%s:%s:%s" % (tag.name, os, cpu))

if os == "macos":
dmg_renode_repository(
name = tag.name,
url = tag.url,
sha256 = tag.sha256,
build_file_content = build_file_macos,
)
else:
portable_renode_repository(
name = tag.name,
url = tag.url,
sha256 = tag.sha256,
build_file_content = build_file_portable,
)

# Process local builds
build_file_local_build = _renode_build_file_variables(
"",
"src/Plugins/**/IntegrationLibrary/",
) + build_file
for mod in module_ctx.modules:
for tag in mod.tags.local_build:
toolchains.append(tag.name)
os = tag.os if tag.os else "linux"
cpu = tag.cpu if tag.cpu else "x86_64"
toolchain_configs.append("%s:%s:%s" % (tag.name, os, cpu))
new_local_repository(
name = tag.name,
path = tag.path,
build_file_content = build_file_local_build,
)

# If no toolchains specified, create defaults for common platforms
if len(toolchains) == 0:
toolchains.append(_DEFAULT_PORTABLE_RENODE["name"])
# Add Linux x64 (most common CI platform)
toolchains.append(_DEFAULT_LINUX_X64["name"])
toolchain_configs.append("%s:linux:x86_64" % _DEFAULT_LINUX_X64["name"])
portable_renode_repository(
name = _DEFAULT_LINUX_X64["name"],
url = _DEFAULT_LINUX_X64["url"],
sha256 = _DEFAULT_LINUX_X64["sha256"],
build_file_content = build_file_portable,
**_DEFAULT_PORTABLE_RENODE
)

# Add macOS ARM64 (Apple Silicon)
toolchains.append(_DEFAULT_MACOS_ARM64["name"])
toolchain_configs.append("%s:macos:aarch64" % _DEFAULT_MACOS_ARM64["name"])
dmg_renode_repository(
name = _DEFAULT_MACOS_ARM64["name"],
url = _DEFAULT_MACOS_ARM64["url"],
sha256 = _DEFAULT_MACOS_ARM64["sha256"],
build_file_content = build_file_macos,
)

renode_toolchains_repository(
name = "renode_toolchains",
toolchains = toolchains,
toolchain_configs = toolchain_configs,
)

# Toolchain template with configurable platform constraints
_RENODE_TOOLCHAIN_TEMPLATE = """\
toolchain(
name = "{name}",
target_compatible_with = [
"@platforms//os:linux",
"@platforms//cpu:x86_64",
"@platforms//os:{os}",
"@platforms//cpu:{cpu}",
],
toolchain = "{impl}",
toolchain_type = "@rules_renode//renode:toolchain_type",
Expand All @@ -82,9 +223,23 @@ toolchain(

def _renode_toolchains_repository(repository_ctx):
build_file = []
for toolchain_repo in repository_ctx.attr.toolchains:
toolchain_configs = repository_ctx.attr.toolchain_configs

for i, toolchain_repo in enumerate(repository_ctx.attr.toolchains):
if i < len(toolchain_configs):
config = toolchain_configs[i]
# config is "name:os:cpu" format
parts = config.split(":")
os = parts[1] if len(parts) > 1 else "linux"
cpu = parts[2] if len(parts) > 2 else "x86_64"
else:
os = "linux"
cpu = "x86_64"

build_file.append(_RENODE_TOOLCHAIN_TEMPLATE.format(
name = toolchain_repo,
os = os,
cpu = cpu,
impl = "@%s//:toolchain_impl" % toolchain_repo,
))

Expand All @@ -101,12 +256,26 @@ portable_renode_repository = repository_rule(
},
)

dmg_renode_repository = repository_rule(
implementation = _dmg_renode_toolchain_repository_impl,
attrs = {
"url": attr.string(
mandatory = True,
),
"sha256": attr.string(),
"build_file_content": attr.string(),
},
)

renode_toolchains_repository = repository_rule(
implementation = _renode_toolchains_repository,
attrs = {
"toolchains": attr.string_list(
mandatory = True,
),
"toolchain_configs": attr.string_list(
doc = "List of 'name:os:cpu' configs matching toolchains order",
),
},
)

Expand All @@ -116,14 +285,18 @@ _download_portable = tag_class(
"name": attr.string(),
"url": attr.string(),
"sha256": attr.string(),
"os": attr.string(doc = "Target OS: linux or macos"),
"cpu": attr.string(doc = "Target CPU: x86_64 or aarch64"),
},
)

_local_build = tag_class(
doc = "Copies a localy built Renode to a repository of the passed name, it isn't hermetic",
doc = "Copies a locally built Renode to a repository of the passed name, it isn't hermetic",
attrs = {
"name": attr.string(),
"path": attr.string(),
"os": attr.string(doc = "Target OS: linux or macos"),
"cpu": attr.string(doc = "Target CPU: x86_64 or aarch64"),
},
)

Expand Down