Skip to content
Open
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
platforms/**/*.xml
platforms/**/*.bin
platforms/**/.built
platforms/**/*.xml
54 changes: 41 additions & 13 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
TOPDIR := $(PWD)
PARTITIONS := $(wildcard platforms/*/*/partitions.conf)
PARTITIONS_XML := $(patsubst %.conf,%.xml, $(PARTITIONS))
PLATFORMS := $(patsubst %/partitions.conf,%/gpt, $(PARTITIONS))
STAMPS := $(patsubst %/partitions.conf,%/.built, $(PARTITIONS))

CONTENTS_XML_IN := $(wildcard platforms/*/*/contents.xml.in)
CONTENTS_XML := $(patsubst %.xml.in,%.xml, $(CONTENTS_XML_IN))
Expand All @@ -13,16 +12,35 @@ BUILD_ID ?=

.PHONY: all check clean lint integration

all: $(PLATFORMS) $(PARTITIONS_XML) $(CONTENTS_XML)
all: $(STAMPS) $(CONTENTS_XML)

%/gpt: %/partitions.xml
cd $(shell dirname $^) && $(TOPDIR)/ptool.py -x partitions.xml
# Generate partitions.xml and GPT binaries from partitions.conf
# - single-disk: writes partitions.xml in the platform dir, runs ptool there
# - multi-disk: writes partitions0.xml, partitions1.xml, ... in the platform dir,
# runs ptool in disk0/, disk1/, ... subdirectories
%/.built: %/partitions.conf
$(TOPDIR)/gen_partition.py -i $< -o $*/partitions.xml
@if [ -f $*/partitions.xml ]; then \
(cd $* && $(TOPDIR)/ptool.py -x partitions.xml); \
else \
i=0; \
while [ -f $*/partitions$${i}.xml ]; do \
mkdir -p $*/disk$${i}; \
(cd $*/disk$${i} && $(TOPDIR)/ptool.py -x ../partitions$${i}.xml); \
i=$$((i+1)); \
done; \
fi
@touch $@

%/partitions.xml: %/partitions.conf
$(TOPDIR)/gen_partition.py -i $^ -o $@

%/contents.xml: %/partitions.xml %/contents.xml.in
$(TOPDIR)/gen_contents.py -p $< -t $@.in -o $@ $${BUILD_ID:+ -b $(BUILD_ID)}
%/contents.xml: %/contents.xml.in %/.built
# default to partitions.xml from same dir; for multi-disk use partitions0.xml
# with an explicit file prefix of disk0 for gen_contents
@partxml="$*/partitions.xml"; prefix=""; \
if [ ! -f "$$partxml" ]; then \
partxml="$*/partitions0.xml"; prefix="disk0"; \
fi; \
$(TOPDIR)/gen_contents.py -p "$$partxml" -t $@.in -o $@ \
$${prefix:+ -f $$prefix} $${BUILD_ID:+ -b $(BUILD_ID)}

lint:
# W605: invalid escape sequence
Expand All @@ -33,8 +51,16 @@ lint:
pycodestyle --ignore=E501 gen_contents.py

integration: all
# make sure generated output has created expected files
tests/integration/check-missing-files platforms/*/*/*.xml
# check files mentioned in generated XML outputs
tests/integration/check-missing-files \
$$(find platforms \
-name 'contents.xml' -o \
-name 'partitions.xml' -o \
-name 'patch*.xml' -o \
-name 'rawprogram*.xml' -o \
-name 'wipe_*.xml')
# test %include and multi-disk features
tests/integration/check-include-multidisk

check: lint integration

Expand All @@ -43,4 +69,6 @@ install: $(BINS)
install -m 755 $^ $(DESTDIR)$(PREFIX)/bin

clean:
@rm -f platforms/*/*/*.xml platforms/*/*/*.bin
@rm -f platforms/*/*/*.xml platforms/*/*/*.bin platforms/*/*/*/*.xml platforms/*/*/*/*.bin
@rm -f platforms/*/*/.built
@rm -rf platforms/*/*/disk[0-9]*/
29 changes: 23 additions & 6 deletions gen_contents.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
def usage():
print(
(
"\n\tUsage: %s -t <template> -p <partitions_xml_path> -o <output> \n\tVersion 0.1\n"
"\n\tUsage: %s -t <template> -p <partitions_xml_path> -o <output> [-f <file_prefix>]\n\tVersion 0.1\n"
% (sys.argv[0])
)
)
Expand All @@ -31,7 +31,7 @@ def ParseXML(XMLFile):
return None


def UpdateMetaData(TemplateRoot, PartitionRoot, BuildId):
def UpdateMetaData(TemplateRoot, PartitionRoot, BuildId, FilePrefix=""):
ChipIdList = TemplateRoot.findall("product_info/chipid")
DefaultStorageType = None
for ChipId in ChipIdList:
Expand All @@ -54,8 +54,10 @@ def _add_file_elements(parent_element, pathname, file_path_flavor=None):
"""Helper function to add file_name and file_path sub-elements."""
file_name_text = os.path.basename(pathname)
file_path_text = os.path.dirname(pathname)
if not file_path_text: # no directory, use explicit . as current dir
file_path_text = "."
if not file_path_text:
file_path_text = FilePrefix if FilePrefix else "."
elif FilePrefix:
file_path_text = FilePrefix + "/" + file_path_text

new_file_name = ET.SubElement(parent_element, "file_name")
new_file_name.text = file_name_text
Expand Down Expand Up @@ -133,7 +135,8 @@ def _add_file_elements(parent_element, pathname, file_path_flavor=None):
usage()
try:
build_id = ""
opts, rem = getopt.getopt(sys.argv[1:], "t:p:o:b:")
file_prefix_override = None
opts, rem = getopt.getopt(sys.argv[1:], "t:p:o:b:f:")
for opt, arg in opts:
if opt in ["-t"]:
template = arg
Expand All @@ -143,6 +146,8 @@ def _add_file_elements(parent_element, pathname, file_path_flavor=None):
output_xml = arg
elif opt in ["-b"]:
build_id = arg
elif opt in ["-f"]:
file_prefix_override = arg
else:
usage()
except Exception as argerr:
Expand All @@ -155,7 +160,19 @@ def _add_file_elements(parent_element, pathname, file_path_flavor=None):
print("Selected Partition XML: " + partition_xml)
partition_root = ParseXML(partition_xml)

UpdateMetaData(xml_root, partition_root, build_id)
# compute file prefix: relative path from output dir to partitions.xml dir
# e.g., output=spinor-nvme/contents.xml, partition=spinor-nvme/disk0/partitions.xml
# gives prefix "disk0". Can be overridden with -f.
if file_prefix_override is not None:
file_prefix = file_prefix_override
else:
output_dir = os.path.dirname(os.path.abspath(output_xml))
partition_dir = os.path.dirname(os.path.abspath(partition_xml))
file_prefix = os.path.relpath(partition_dir, output_dir)
if file_prefix == ".":
file_prefix = ""

UpdateMetaData(xml_root, partition_root, build_id, file_prefix)

OutputTree = ET.ElementTree(xml_root)
ET.indent(OutputTree, space="\t", level=0)
Expand Down
99 changes: 73 additions & 26 deletions gen_partition.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

import getopt
import re
import os
import sys
import xml.etree.ElementTree as ET
from collections import OrderedDict
Expand Down Expand Up @@ -68,15 +69,42 @@ def usage():
}

##################################################################
# store entries read from input file
disk_entry = None
partition_entries = []
# store partition image map passed from command line
partition_image_map = {}
input_file = None
output_xml = None


def expand_includes(filepath, include_stack=None):
"""Read a file and expand %include directives recursively.

Returns a list of stripped lines.
Paths in %include are resolved relative to the including file's directory.
Raises ValueError on circular includes or missing files.
"""
if include_stack is None:
include_stack = []
filepath = os.path.realpath(filepath)
if not os.path.exists(filepath):
raise ValueError("File not found: %s" % filepath)
if filepath in include_stack:
raise ValueError("Circular include detected: %s\nInclude stack: %s" % (
filepath, " -> ".join(include_stack)))
# make a copy, instead of mutating as we're recursing
include_stack = include_stack + [filepath]
base_dir = os.path.dirname(filepath)
lines = []
with open(filepath) as f:
for raw_line in f:
stripped = raw_line.strip()
if stripped.startswith('%include '):
inc_path = stripped[len('%include '):]
inc_full = os.path.join(base_dir, inc_path)
lines.extend(expand_includes(inc_full, include_stack))
else:
lines.append(stripped)
return lines

def disk_options(argv):
disk_params = disk_params_defaults.copy()
for opt, arg in argv:
Expand Down Expand Up @@ -231,7 +259,6 @@ def generate_partition_xml(disk_params, partitions, output_xml):

###############################################################################
# main
disk_entry_err_msg = "contains more than one --disk entries"

if len(sys.argv) < 3:
usage()
Expand All @@ -257,30 +284,50 @@ def generate_partition_xml(disk_params, partitions, output_xml):
except Exception as argerr:
print(str(argerr))
usage()
f = open(input_file)
line = f.readline()
while line:
if not re.search(r"^\s*#", line) and not re.search(r"^\s*$", line):
line = line.strip()
if re.search("^--disk", line):
if disk_entry is None:
disk_entry = line
else:
print("%s %s" % (sys.argv[1], disk_entry_err_msg))
print("%s\n%s" % (disk_entry, line))
sys.exit(1)
elif re.search("^--partition", line):
partition_entries.append(line)
else:
print("Ignoring %s" % (line))
line = f.readline()
f.close()

# Parse expanded lines into disk sections
disk_sections = [] # list of (disk_line, [partition_lines])
current_disk = None
current_partitions = []

for line in expand_includes(input_file):
if re.search(r"^\s*#", line) or re.search(r"^\s*$", line):
continue
if re.search("^--disk", line):
if current_disk is not None:
disk_sections.append((current_disk, current_partitions))
current_partitions = []
current_disk = line
elif re.search("^--partition", line):
current_partitions.append(line)
else:
print("Ignoring %s" % (line))
if current_disk is not None:
disk_sections.append((current_disk, current_partitions))

if not disk_sections:
print("Error: no --disk entry found in %s" % input_file)
sys.exit(1)

if output_xml:
if len(disk_sections) == 1:
disk_params = parse_disk_entry(disk_sections[0][0])
partitions = parse_partition_entries(disk_sections[0][1])
generate_partition_xml(disk_params, partitions, output_xml)
else:
# Multi-disk: derive output filenames by inserting an index before
# the extension, e.g. partitions.xml -> partitions0.xml, partitions1.xml
base, ext = os.path.splitext(output_xml)
for idx, (disk_line, part_lines) in enumerate(disk_sections):
disk_params = parse_disk_entry(disk_line)
partitions = parse_partition_entries(part_lines)
out_path = "%s%d%s" % (base, idx, ext)
generate_partition_xml(disk_params, partitions, out_path)
else:
print("Error: -o is required")
sys.exit(1)
except Exception as e:
print("Error: ", e)
sys.exit(1)

disk_params = parse_disk_entry(disk_entry)
partitions = parse_partition_entries(partition_entries)
generate_partition_xml(disk_params, partitions, output_xml)

sys.exit(0)
Original file line number Diff line number Diff line change
Expand Up @@ -60,41 +60,41 @@
<download_file>
<fastboot_complete>{partition_name}</fastboot_complete>
<file_name>{image_name}</file_name>
<file_path>.</file_path>
<file_path>disk0</file_path>
</download_file>
<partition_file>
<storage_type>{storage_type}</storage_type>
<file_name>{partition_file_name}</file_name>
<file_path flavor="default">.</file_path>
<file_path flavor="default">disk0</file_path>
</partition_file>
<partition_patch_file>
<storage_type>{storage_type}</storage_type>
<file_name>{partition_patch_file_name}</file_name>
<file_path flavor="default">.</file_path>
<file_path flavor="default">disk0</file_path>
</partition_patch_file>
<download_file storage_type="nvme" fastboot_complete="efi">
<file_name>efi.bin</file_name>
<file_path flavor="qcom_server">../</file_path>
<file_path flavor="qcom_server">disk1</file_path>
</download_file>
<download_file storage_type="nvme" fastboot_complete="rootfs">
<file_name>rootfs.img</file_name>
<file_path flavor="qcom_server">../</file_path>
<file_path flavor="qcom_server">disk1</file_path>
</download_file>
<download_file storage_type="nvme" flavor="qcom_server">
<file_name>gpt_main0.bin</file_name>
<file_path flavor="qcom_server">../</file_path>
<file_path flavor="qcom_server">disk1</file_path>
</download_file>
<download_file storage_type="nvme" flavor="qcom_server">
<file_name>gpt_backup0.bin</file_name>
<file_path flavor="qcom_server">../</file_path>
<file_path flavor="qcom_server">disk1</file_path>
</download_file>
<partition_file storage_type="nvme" flavor="qcom_server">
<file_name>rawprogram0.xml</file_name>
<file_path flavor="qcom_server">../</file_path>
<file_path flavor="qcom_server">disk1</file_path>
</partition_file>
<partition_patch_file storage_type="nvme" flavor="qcom_server">
<file_name>patch0.xml</file_name>
<file_path flavor="qcom_server">../</file_path>
<file_path flavor="qcom_server">disk1</file_path>
</partition_patch_file>
</build>
</builds_flat>
Expand Down
8 changes: 8 additions & 0 deletions platforms/glymur-crd/spinor-nvme/partitions.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright (c) 2026 Qualcomm Innovation Center, Inc. All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause-Clear
#
# Combined NVMe + SPINOR partition layout for Glymur CRD.
# NHLOS firmware on SPINOR, HLOS (Linux) on NVMe.

%include ../spinor/partitions.conf
%include ../nvme/partitions.conf
Loading
Loading