This commit is contained in:
2025-08-22 16:51:27 +08:00
commit 1c754e754d
27 changed files with 784 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/build
*.kate-swp

53
Makefile Normal file
View File

@@ -0,0 +1,53 @@
#!/usr/bin/env make
##### Have default "all" target listed at the beginning
.PHONY: default
default: all
define stack_include
$$(info Makefile "$$(lastword $$(MAKEFILE_STACK))" includes "$1")
MAKEFILE_STACK += $1
CURRENT_MAKEFILE := $$(lastword $$(MAKEFILE_STACK))
CURRENT_DIR := $$(dir $$(CURRENT_MAKEFILE))
include $1
MAKEFILE_STACK := $$(wordlist 1,$$(shell expr $$(words $$(MAKEFILE_STACK)) - 1),$$(MAKEFILE_STACK))
CURRENT_MAKEFILE := $$(lastword $$(MAKEFILE_STACK))
CURRENT_DIR := $$(dir $$(CURRENT_MAKEFILE))
endef
MAKEFILE_STACK := $(dir .)$(lastword $(MAKEFILE_LIST))
CURRENT_MAKEFILE := $(lastword $(MAKEFILE_STACK))
CURRENT_DIR := $(dir $(CURRENT_MAKEFILE))
##### Setup out-of-tree build
# ROOT_DIR & WORK_DIR are all absolute path.
# As well as DIST_DIR & STAGING_DIR
ROOT_DIR := $(realpath .)
# O= must be an absolute path, otherwise unexpected behavior will occur when used in combination with make -C
ifdef O
WORK_DIR := $(realpath $(O))
else
WORK_DIR := $(ROOT_DIR)/build
endif
STAGING_DIR := $(WORK_DIR)/staging
DIST_DIR := $(WORK_DIR)/dist
# Collected targets
ROOTFS_TARGETS :=
BSP_TARGETS :=
# Makefile function definations
$(eval $(call stack_include,$(CURRENT_DIR)functions.mk))
# Load receipes
$(eval $(call stack_include,$(CURRENT_DIR)receipes/receipes.mk))
.PHONY: all_rootfs
all_rootfs: $(ROOTFS_TARGETS)
.PHONY: clean
clean:
rm -rf "$(WORK_DIR)"
.PHONY: .test
.test:
@echo $(MAKEFILE_STACK)

111
functions.mk Normal file
View File

@@ -0,0 +1,111 @@
########
dir_guard=@mkdir -p $(@D)
########
define distro_begin
ifdef DISTRO_NAME
$$(error Distro section "$$(DISTRO_NAME)" is not finished yet)
endif
DISTRO_NAME := $(1)
endef
########
define distro_end
ifndef DISTRO_NAME
$$(error No paired distro_begin found)
endif
ifdef RELEASE_NAME
$$(error Unfinished distro release "$$(DISTRO_NAME) $$(RELEASE_NAME)" found)
endif
undefine DISTRO_NAME
endef
########
define distro_release_begin
ifndef DISTRO_NAME
$$(error Not in distro section)
endif
ifdef RELEASE_NAME
$$(error Distro release "$$(DISTRO_NAME) $$(RELEASE_NAME)" is not finished yet)
endif
RELEASE_NAME := $(1)
endef
########
define distro_release_end
ifndef RELEASE_NAME
$$(error No paired distro_release_begin found)
endif
undefine RELEASE_NAME
endef
########
define add_receipe
ifndef RELEASE_NAME
$$(error Not within distro release section)
endif
# Cleanup and register new receipe
undefine RECEIPE_NAME
undefine ROOTFS_COMPRESSION
undefine EXTENDS
include $$(CURRENT_DIR)$1/receipe.mk
# Check name
ifndef RECEIPE_NAME
$$(error RECEIPE_NAME not defined)
endif
# Check compression method
ifeq ($$(ROOTFS_COMPRESSION),zstd)
ROOTFS_COMPRESSION_SUFFIX := tar.zst
ROOTFS_COMPRESSION_CMD := tar --use-compress-program=zstd -cf "$$$$$$$${OUTPUT_ARCHIVE}" -C "$$$$$$$${ROOTFS_DIR}" .
else
$$(error Unsupported compression mode)
endif
# Define global references
$$(DISTRO_NAME)_$$(RELEASE_NAME)_$$(RECEIPE_NAME)_DIR := $$(CURRENT_DIR)$1
$$(DISTRO_NAME)_$$(RELEASE_NAME)_$$(RECEIPE_NAME)_ARTIFACT := $(DIST_DIR)/$$(DISTRO_NAME)/$$(RELEASE_NAME)/$$(DISTRO_NAME)-$$(RELEASE_NAME)-$$(RECEIPE_NAME)-rootfs.$$(ROOTFS_COMPRESSION_SUFFIX)
ROOTFS_TARGETS += $$(DISTRO_NAME)-$$(RELEASE_NAME)-$$(RECEIPE_NAME)-rootfs
# Populate workdir
# Directory timestamp is not reliable so always resync staging dir
.PHONY: $(STAGING_DIR)/$$(DISTRO_NAME)/$$(RELEASE_NAME)/receipes/$$(RECEIPE_NAME)
$(STAGING_DIR)/$$(DISTRO_NAME)/$$(RELEASE_NAME)/receipes/$$(RECEIPE_NAME): $$(foreach depname,$$(EXTENDS),$$($$(DISTRO_NAME)_$$(RELEASE_NAME)_$$(depname)_DIR)/) $$(CURRENT_DIR)$1/
$$(dir_guard)
rm -rf "$$@"
@# Not preserving modification time, aka -t
rsync -rlpgoD --exclude receipe.mk $$+ $$@
# Define entry target
.PHONY: $$(DISTRO_NAME)-$$(RELEASE_NAME)-$$(RECEIPE_NAME)-rootfs
$$(DISTRO_NAME)-$$(RELEASE_NAME)-$$(RECEIPE_NAME)-rootfs: $$($$(DISTRO_NAME)_$$(RELEASE_NAME)_$$(RECEIPE_NAME)_ARTIFACT)
define GENERATE_TAR_TARGET
$$($$(DISTRO_NAME)_$$(RELEASE_NAME)_$$(RECEIPE_NAME)_ARTIFACT): $(STAGING_DIR)/$$(DISTRO_NAME)/$$(RELEASE_NAME)/receipes/$$(RECEIPE_NAME)
$$$$(dir_guard)
docker run --rm \
-v $(ROOT_DIR)/tools:/image-builder/tools:ro \
-v $$$$+:/image-builder/receipe:ro \
-v $$$$(dir $$$$@):/image-builder/output \
-it docker.io/openeuler/openeuler:24.03-lts \
/bin/sh -c ' \
mkdir /tmp/rootfs && \
cd /image-builder/tools && \
./build_rootfs.sh -i /image-builder/receipe -o /tmp/rootfs && \
export OUTPUT_ARCHIVE=/image-builder/output/$$$$(notdir $$$$@) && \
export ROOTFS_DIR=/tmp/rootfs && \
$$(ROOTFS_COMPRESSION_CMD)'
endef
$$(eval $$(GENERATE_TAR_TARGET))
# Reset registers again
undefine GENERATE_TAR_TARGET
undefine EXTENDS
undefine ROOTFS_COMPRESSION_CMD
undefine ROOTFS_COMPRESSION_SUFFIX
undefine ROOTFS_COMPRESSION
undefine RECEIPE_NAME
endef

View File

@@ -0,0 +1,23 @@
DISK_TABLE_TYPE=GUID
PARTITION_1_OFFSET=1MB
PARTITION_1_LENGTH=499MB
PARTITION_1_NAME="oerv-boot"
PARTITION_1_UUID=
PARTITION_1_TYPE=bls_boot
PARTITION_1_ATTRS=legacy_boot
PARTITION_1_USAGE=MOUNTPOINT
PARTITION_1_MOUNTPONT="/boot"
PARTITION_1_FILESYSTEM=vfat
PARTITION_1_FS_UUID=
PARTITION_2_OFFSET=
PARTITION_2_LENGTH=
PARTITION_2_NAME="oerv-root"
PARTITION_2_UUID=
PARTITION_2_TYPE=linux_root_riscv64
PARTITION_2_ATTRS=
PARTITION_2_USAGE=MOUNTPOINT
PARTITION_2_MOUNTPONT="/"
PARTITION_1_FILESYSTEM=ext4
PARTITION_1_FS_UUID=

View File

@@ -0,0 +1,3 @@
firmware-spacemit-k1
dracut
u-boot-menu

View File

@@ -0,0 +1,3 @@
# Provide esos.elf for early kernel direct filesystem lookup
install_items+= /lib/firmware/esos.elf

View File

@@ -0,0 +1,6 @@
U_BOOT_PROMPT="2"
U_BOOT_MENU_LABEL="OpenEuler GNU/Linux, kernel"
# tty enabled on both serial0 and fbdev
U_BOOT_PARAMETERS="rootwait rw earlycon=sbi console=tty0 console=ttyS0,115200 rd.multipath=0"
U_BOOT_ROOT=
U_BOOT_FDT_DIR="/dtb-"

View File

@@ -0,0 +1,13 @@
[bsp-common]
name=BSP-Common
type=rpm-md
baseurl=https://build-repo.tarsier-infra.isrc.ac.cn//home:/salimterryli:/bsp:/common/openeuler-24.03-sp2/
enabled=1
gpgcheck=0
[bsp-spacemit-k1]
name=BSP-K1
type=rpm-md
baseurl=https://build-repo.tarsier-infra.isrc.ac.cn//home:/salimterryli:/bsp:/spacemit:/k1/openeuler-24.03-sp2/
enabled=1
gpgcheck=0

View File

@@ -0,0 +1,8 @@
$(eval $(call distro_release_begin,24.03-LTS-SP2))
$(eval $(call add_receipe,userspace/base))
$(eval $(call add_receipe,userspace/xfce4))
$(foreach mk,$(wildcard $(CURRENT_DIR)bsp/*/board.mk),$(eval $(call stack_include,$(mk))))
$(eval $(call distro_release_end))

View File

@@ -0,0 +1,3 @@
#!/usr/bin/env dash
rm "${ROOTFS_DIR}/etc/yum.repos.d/bootstrap.repo"

View File

@@ -0,0 +1,2 @@
RECEIPE_NAME := base
ROOTFS_COMPRESSION := zstd

View File

@@ -0,0 +1,10 @@
# Temporary repo for openeuler-repos package installation
[OS]
name=OS
baseurl=https://mirror.isrc.ac.cn/openeuler/openEuler-24.03-LTS-SP1/OS/$basearch/
# metalink=https://mirrors.openeuler.org/metalink?repo=$releasever/OS&arch=$basearch
metadata_expire=1h
enabled=1
gpgcheck=1
gpgkey=http://mirror.isrc.ac.cn/openeuler/openEuler-24.03-LTS-SP1/OS/$basearch/RPM-GPG-KEY-openEuler

View File

@@ -0,0 +1,8 @@
xorg-*
xfwm4
xfdesktop
xfce4-*
xfce4-*-plugin
lightdm
lightdm-gtk
linux-firmware

View File

@@ -0,0 +1,3 @@
RECEIPE_NAME := xfce4
ROOTFS_COMPRESSION := zstd
EXTENDS := base

View File

@@ -0,0 +1,7 @@
$(eval $(call distro_begin,openEuler))
PACKAGE_MANAGER := yum
$(foreach mk,$(wildcard $(CURRENT_DIR)*/release.mk),$(eval $(call stack_include,$(mk))))
$(eval $(call distro_end))

1
receipes/receipes.mk Normal file
View File

@@ -0,0 +1 @@
$(foreach mk,$(wildcard $(CURRENT_DIR)*/distro.mk),$(eval $(call stack_include,$(mk))))

66
tools/build_rootfs.sh Executable file
View File

@@ -0,0 +1,66 @@
#!/usr/bin/env sh
set -o errexit
set -o nounset
. ./logging.sh
RECEIPE_DIR=""
ROOTFS_DIR=""
print_usage() {
# Keep help message within 80 cols for special consoles.
cat << EOF
Usage: $0 [-hp] [-i input [-i input [...]]] output
-h Print this help message.
-i receipe Path to directory containing the receipe
to make the rootfs.
-o rootfs Path to directory that the rootfs will
be bootstrapped into.
Environment Variables:
LOG_LEVEL Controls the log level of this script.
One of: DEBUG, INFO, WARN, ERROR
Defaults to: INFO
EOF
}
main() {
# Handling args
while getopts "i:o:h" o; do
case "${o}" in
h)
print_usage
exit 0
;;
i)
RECEIPE_DIR=${OPTARG}
;;
o)
ROOTFS_DIR=${OPTARG}
;;
*)
exit 1
;;
esac
done
shift $((OPTIND-1))
# Validate inputs
if [ -z "${RECEIPE_DIR}" ]; then
loge "No receipe is specified!"
exit 1
fi
if [ -z "${ROOTFS_DIR}" ]; then
loge "No output dir is specified!"
exit 1
fi
logi "Using receipe: ${RECEIPE_DIR}"
logi "ROOTFS_DIR=${ROOTFS_DIR}"
exit 0
}
main "$@"

254
tools/gptcfg.sh Executable file
View File

@@ -0,0 +1,254 @@
#!/usr/bin/env dash
# shellcheck shell=dash disable=SC2034
set -o errexit
set -o nounset
. ./logging.sh
TARGET_FILE=""
###################################################
readonly TYPENAME="
efi
bls_boot
linux_data
linux_root_riscv64
linux_swap
msfdata
"
readonly TYPECODE_EFI="C12A7328-F81F-11D2-BA4B-00A0C93EC93B"
readonly TYPECODE_BLS_BOOT="BC13C2FF-59E6-4262-A352-B275FD6F7172"
readonly TYPECODE_LINUX_DATA="0FC63DAF-8483-4772-8E79-3D69D8477DE4"
readonly TYPECODE_LINUX_ROOT_RISCV64="72EC70A6-CF74-40E6-BD49-4BDA08E8F224"
readonly TYPECODE_LINUX_SWAP="0657fd6d-a4ab-43c4-84e5-0933c84b4f4f"
readonly TYPECODE_MSFDATA="ebd0a0a2-b9e5-4433-87c0-68b6b72699c7"
readonly TYPECODE_MSFRESV="e3c9e316-0b5c-4db8-817d-f92df00215ae"
readonly TYPECODE_COMMON_ATTRS_LEGACY_BOOT=2
readonly TYPECODE_LINUX_ATTRS_NO_AUTO=63
readonly TYPECODE_LINUX_ATTRS_RO=60
readonly TYPECODE_LINUX_ATTRS_GROWFS=59
readonly TYPECODE_MSFDATA_ATTRS_NO_AUTO=63
readonly TYPECODE_MSFDATA_ATTRS_RO=60
readonly TYPECODE_MSFDATA_HIDDEN=62
#readonly TYPECODE_EFI_ATTRS=""
readonly TYPECODE_BLS_BOOT_ATTRS="
legacy_boot
no_auto
ro
growfs
"
readonly TYPECODE_BLS_BOOT_ATTRS_LEGACY_BOOT=$TYPECODE_COMMON_ATTRS_LEGACY_BOOT
readonly TYPECODE_BLS_BOOT_ATTRS_NO_AUTO=$TYPECODE_LINUX_ATTRS_NO_AUTO
readonly TYPECODE_BLS_BOOT_ATTRS_RO=$TYPECODE_LINUX_ATTRS_RO
readonly TYPECODE_BLS_BOOT_ATTRS_GROWFS=$TYPECODE_LINUX_ATTRS_GROWFS
#readonly TYPECODE_LINUX_DATA_ATTRS=""
readonly TYPECODE_LINUX_ROOT_RISCV64_ATTRS="
legacy_boot
no_auto
ro
growfs
"
readonly TYPECODE_LINUX_ROOT_RISCV64_ATTRS_LEGACY_BOOT=$TYPECODE_COMMON_ATTRS_LEGACY_BOOT
readonly TYPECODE_LINUX_ROOT_RISCV64_ATTRS_NO_AUTO=$TYPECODE_LINUX_ATTRS_NO_AUTO
readonly TYPECODE_LINUX_ROOT_RISCV64_ATTRS_RO=$TYPECODE_LINUX_ATTRS_RO
readonly TYPECODE_LINUX_ROOT_RISCV64_ATTRS_GROWFS=$TYPECODE_LINUX_ATTRS_GROWFS
#readonly TYPECODE_LINUX_SWAP_ATTRS=""
###################################################
str_to_upper() {
printf '%s' "$1" | tr '[:lower:]' '[:upper:]'
}
getvarbyname() {
eval printf '%s' \"\$\{"$1"\}\"
}
handle_set_uuid() {
if [ $# -eq 0 ]; then
loge "part_id not specified."
exit 2
fi
local partid; partid="$1"; shift 1
local new_uuid; new_uuid="${1:-}"
if [ -z "${new_uuid}" ]; then
new_uuid="$(uuidgen)"
fi
logd "Setting new UUID to part ${partid}: ${new_uuid}"
1>/dev/null sgdisk -u "${partid}:${new_uuid}" "${TARGET_FILE}"
}
handle_set_type() {
if [ $# -eq 0 ]; then
loge "part_id not specified."
exit 2
fi
local partid; partid="$1"; shift 1
if [ $# -eq 0 ]; then
loge "Type not specified."
exit 2
fi
local parttype; parttype="$1"; shift 1
if ! printf '%s' "${TYPENAME}" | grep -E "^${parttype}$" > /dev/null; then
loge "Unsupported type: ${parttype}"
exit 2
fi
local typecode
typecode="$(getvarbyname "TYPECODE_$(str_to_upper "${parttype}")")"
local supported_attrs=""
supported_attrs="$(getvarbyname "TYPECODE_$(str_to_upper "${parttype}")_ATTRS:-")"
local typeattrbits=""
while [ $# -ne 0 ]; do
local curr_attr="$1"
shift 1
if ! printf '%s' "${supported_attrs}" | grep -E "^${curr_attr}$" > /dev/null; then
loge "Usupported attribute for type ${parttype}: ${curr_attr}"
exit 2
fi
typeattrbits="${typeattrbits}$(getvarbyname "TYPECODE_$(str_to_upper "${parttype}")_ATTRS_$(str_to_upper "${curr_attr}")")
"
done
logd "Set type code to ${typecode} for part ${partid}"
1>/dev/null sgdisk -t "${partid}:${typecode}" "${TARGET_FILE}"
printf '%s' "${typeattrbits}" | while read -r attrbit ; do
if [ -z "${attrbit}" ]; then
continue
fi
logd "Set partition attribute bit ${attrbit} for part ${partid}"
1>/dev/null sgdisk -A "${partid}:set:${attrbit}" "${TARGET_FILE}"
done
}
handle_set_name() {
if [ $# -eq 0 ]; then
loge "part_id not specified."
exit 2
fi
local partid; partid="$1"; shift 1
if [ $# -eq 0 ]; then
loge "part name not specified."
exit 2
fi
local partname; partname="$1"; shift 1
1>/dev/null sgdisk -c "${partid}:${partname}" "${TARGET_FILE}"
}
print_usage() {
# Keep help message within 80 cols for special consoles.
cat << EOF
Configure GUID partition attributes
Usage: $0 [-h] target command [parameters]
target Path to the disk image file. Can also be a real blockdev.
Commands:
set-name part_id name
Set the GUID partition name.
Not to be confused with filesystem label!
part_id Partition index, starting from 1.
name Name of the partition. Maximum 71 bytes.
set-type part_id type [attrs [attrs [...]]]
Configure the partition into a pre-defined type.
On GUID disks, only one PartitionTypeGUID can be set but
multiple Attributes can be applied to the same partition.
The supported types are picked from BLS and DPS.
Each TypeGUID may support different attrs.
part_id Partition index, starting from 1.
type Partition usage type. Available options:
- efi
A standard EFI System Partition. Note that the filesystem
still need to be configured by other tools.
This type will set:
- PartitionTypeGUID: C12A7328-F81F-11D2-BA4B-00A0C93EC93B
- bls_boot
A Boot Loader Specification compatible partition.
Also known as XBOOTLDR. May be auto-mounted by systemd.
This type will set:
- PartitionTypeGUID: BC13C2FF-59E6-4262-A352-B275FD6F7172
Supported attributes:
- legacy_boot
- no_auto: Do not mount automatically.
- ro: Mount as read-only.
- growfs: Resize fs to its container size on mounted.
- linux_data
A normal partition that may hold any of filesystem
supported by Linux. No special function.
This type will set:
- PartitionTypeGUID: 0fc63daf-8483-4772-8e79-3d69d8477de4
- linux_root_riscv64
A root partition containing riscv64 rootfs. May be auto
-mounted by systemd.
This type will set:
- PartitionTypeGUID: 72ec70a6-cf74-40e6-bd49-4bda08e8f224
Supported attributes:
- legacy_boot
- no_auto: Do not mount automatically.
- ro: Mount as read-only.
- growfs: Resize fs to its container size on mounted.
- linux_swap
A swap partition. May be automatically enabled by systemd
This type will set:
- PartitionTypeGUID: 0657fd6d-a4ab-43c4-84e5-0933c84b4f4f
set-uuid part_id [uuid]
Set the UniquePartitionGUID.
part_id Partition index, starting from 1.
uuid UniquePartitionGUID, optional.
Leave empty for a random one.
EOF
}
main() {
while getopts "h" o; do
case "${o}" in
h)
print_usage
exit 0
;;
*)
exit 1
;;
esac
done
shift $((OPTIND-1))
if [ "$#" -eq 0 ]; then
loge "No target specified."
exit 1
fi
TARGET_FILE="$1"
shift 1
local arg_command
if [ "$#" -eq 0 ]; then
loge "No command specified."
exit 1
fi
arg_command="$1"
shift 1
if [ "${arg_command}" = "set-name" ]; then
handle_set_name "$@"
elif [ "${arg_command}" = "set-type" ]; then
handle_set_type "$@"
elif [ "${arg_command}" = "set-uuid" ]; then
handle_set_uuid "$@"
else
loge "Unrecognized command: ${arg_command}"
exit 1
fi
exit 0
}
main "$@"

137
tools/imgcat.sh Executable file
View File

@@ -0,0 +1,137 @@
#!/usr/bin/env dash
set -o errexit
set -o nounset
. ./logging.sh
INPUT_FILES=""
OUTPUT_FILE=""
ALIGNMENT_SECTOR_SIZE=""
ALIGNMENT_SECTOR_COUNT=""
ALIGNMENT_BYTES=""
calc_parts_total_size() {
local total_aligned_blocks=0
while IFS= read -r line; do
if [ -z "${line}" ]; then
continue
fi
ifile_aligned_blocks="$(du --apparent-size -B ${ALIGNMENT_BYTES} "${line}" | cut -f 1)"
logi "Input: ${ifile_aligned_blocks} blocks: ${line}"
total_aligned_blocks="$((total_aligned_blocks+ifile_aligned_blocks))"
done << EOF
$INPUT_FILES
EOF
echo "${total_aligned_blocks}"
}
probe_alignment() {
local TEMP_FILE
TEMP_FILE="$(mktemp)"
truncate -s 1G "${TEMP_FILE}"
ALIGNMENT_SECTOR_SIZE="$(sgdisk -p "${TEMP_FILE}" | sed -rn 's/Sector size \(logical\): ([0-9]+) bytes/\1/p')"
ALIGNMENT_SECTOR_COUNT="$(sgdisk -p "${TEMP_FILE}" | sed -rn 's/Partitions will be aligned on ([0-9]+)-sector boundaries/\1/p')"
ALIGNMENT_BYTES="$((ALIGNMENT_SECTOR_SIZE*ALIGNMENT_SECTOR_COUNT))"
rm "${TEMP_FILE}"
logd "Probed alignment: ${ALIGNMENT_BYTES}B (${ALIGNMENT_SECTOR_SIZE}B*${ALIGNMENT_SECTOR_COUNT})"
}
print_usage() {
# Keep help message within 80 cols for special consoles.
cat << EOF
Concatenate multiple partitions into a single GUID disk image.
This script only handles the image creation.
Usage: $0 [-hp] [-i input [-i input [...]]] output
-h Print this help message.
-p Print the alignment, in bytes
-i input Partition images to be included.
Multiple inputs are arranged as the specified order.
It is recommended to have input file size rounded according
to the alignment requirements, as the partition will always
be placed at alignment boundaries.
output The produced disk image.
Environment Variables:
LOG_LEVEL Controls the log level of this script.
One of: DEBUG, INFO, WARN, ERROR
Defaults to: INFO
EOF
}
main() {
# Handling args
while getopts "i:hp" o; do
case "${o}" in
h)
print_usage
exit 0
;;
i)
INPUT_FILES="${INPUT_FILES}${OPTARG}
"
;;
p)
probe_alignment
echo -n "${ALIGNMENT_BYTES}"
exit 0
;;
*)
exit 1
;;
esac
done
shift $((OPTIND-1))
OUTPUT_FILE="${1:-}"
# Validate inputs
if [ -z "${OUTPUT_FILE}" ]; then
loge "No output file is specified!"
exit 1
fi
if [ -z "${INPUT_FILES}" ]; then
loge "No input file is specified!"
exit 1
fi
probe_alignment
logi "Using alignment: ${ALIGNMENT_SECTOR_COUNT} sectors @ ${ALIGNMENT_SECTOR_SIZE}B"
local target_image_content_size
target_image_content_size="$(calc_parts_total_size)"
# One block is reserved at both beginning and ending, for GPT tables.
# Final image size will be target_image_content_size+2 blocks
local final_image_blocks="$((target_image_content_size+2))"
local final_image_size_b="$((final_image_blocks*ALIGNMENT_BYTES))"
logi "Allocating output image (${final_image_blocks} blocks, ${final_image_size_b}B) at ${OUTPUT_FILE}"
truncate -s "${final_image_size_b}" "${OUTPUT_FILE}"
logi "Writing GUID Partition Table"
1>/dev/null sgdisk -Z "${OUTPUT_FILE}"
1>/dev/null sgdisk -G "${OUTPUT_FILE}"
local curr_part_idx
curr_part_idx=0
local curr_part_start_sector
curr_part_start_sector="${ALIGNMENT_SECTOR_COUNT}"
while IFS= read -r line; do
if [ -z "${line}" ]; then
continue
fi
curr_part_idx="$((curr_part_idx+1))"
local curr_part_blocks
curr_part_blocks="$(du --apparent-size -B "${ALIGNMENT_BYTES}" "${line}" | cut -f 1)"
local curr_part_sectors="$((curr_part_blocks*ALIGNMENT_BYTES/ALIGNMENT_SECTOR_SIZE))"
logi "Create partition ${curr_part_idx}: ${curr_part_sectors} sectors @ ${curr_part_start_sector} sector"
1>/dev/null sgdisk --new "${curr_part_idx}:${curr_part_start_sector}:+${curr_part_sectors}" "${OUTPUT_FILE}"
logi "Copy contents to part ${curr_part_idx} from ${line}"
2>/dev/null dd if="${line}" of="${OUTPUT_FILE}" bs="${ALIGNMENT_SECTOR_SIZE}" seek="${curr_part_start_sector}" count="${curr_part_sectors}" conv=notrunc iflag=fullblock
curr_part_start_sector="$((curr_part_start_sector+curr_part_sectors))"
done << EOF
$INPUT_FILES
EOF
exit 0
}
main "$@"

67
tools/logging.sh Normal file
View File

@@ -0,0 +1,67 @@
#!/usr/bin/env false
readonly LOGLEVEL_DEBUG=0
readonly LOGLEVEL_INFO=1
readonly LOGLEVEL_WARN=2
readonly LOGLEVEL_ERROR=3
__logparselevel() {
if [ -z "${LOG_LEVEL:-}" ]; then
echo $LOGLEVEL_INFO
elif [ "${LOG_LEVEL}" = "DEBUG" ]; then
echo $LOGLEVEL_DEBUG
elif [ "${LOG_LEVEL}" = "INFO" ]; then
echo $LOGLEVEL_INFO
elif [ "${LOG_LEVEL}" = "WARN" ]; then
echo $LOGLEVEL_WARN
elif [ "${LOG_LEVEL}" = "ERROR" ]; then
echo $LOGLEVEL_ERROR
else
echo $LOGLEVEL_INFO
fi
}
__logprintraw() {
local formated_msg="$1"
>&2 echo "${formated_msg}"
}
__logprint() {
local level_fmt="$1"
local msg="$2"
local curr_time; curr_time="[$(date)]"
local formated_msg="${curr_time}${level_fmt}: ${msg}"
__logprintraw "${formated_msg}"
}
logd() {
if [ "$(__logparselevel)" -le "$LOGLEVEL_DEBUG" ]; then
__logprint "[DEBUG]" "$1"
fi
}
logdraw() {
if [ "$(__logparselevel)" -le "$LOGLEVEL_DEBUG" ]; then
__logprint "[DEBUG]" "raw print:"
__logprintraw "$1"
fi
}
logi() {
if [ "$(__logparselevel)" -le "$LOGLEVEL_INFO" ]; then
__logprint "[INFO ]" "$1"
fi
}
logw() {
if [ "$(__logparselevel)" -le "$LOGLEVEL_WARN" ]; then
__logprint "[WARN ]" "$1"
fi
}
loge() {
if [ "$(__logparselevel)" -le "$LOGLEVEL_ERROR" ]; then
__logprint "[ERROR]" "$1"
fi
}