mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
178 lines
4.8 KiB
Python
178 lines
4.8 KiB
Python
|
#!/usr/bin/env python3
|
||
|
|
||
|
import os
|
||
|
import sys
|
||
|
import argparse
|
||
|
import pathlib
|
||
|
import posixpath
|
||
|
import io
|
||
|
import itertools
|
||
|
import mmap
|
||
|
import shutil
|
||
|
from binascii import hexlify
|
||
|
|
||
|
C_HEADER = """/* This file was automatically generated by mkconstfs2.
|
||
|
* !!!! DO NOT EDIT !!!!!
|
||
|
*/
|
||
|
|
||
|
#include <stdint.h>
|
||
|
#include "fs/constfs.h"
|
||
|
|
||
|
"""
|
||
|
|
||
|
FILE_TEMPLATE = """ {{
|
||
|
.path = "{target_name}",
|
||
|
.data = {buff_name},
|
||
|
.size = sizeof({buff_name})
|
||
|
}},
|
||
|
"""
|
||
|
|
||
|
C_FOOTER = """
|
||
|
static const constfs_t _fs_data = {{
|
||
|
.files = _files,
|
||
|
.nfiles = sizeof(_files) / sizeof(_files[0]),
|
||
|
}};
|
||
|
|
||
|
vfs_mount_t {constfs_name} = {{
|
||
|
.fs = &constfs_file_system,
|
||
|
.mount_point = "{mount_pount}",
|
||
|
.private_data = (void *)&_fs_data,
|
||
|
}};
|
||
|
"""
|
||
|
|
||
|
FILES_DECL = """
|
||
|
static const constfs_file_t _files[] = {
|
||
|
"""
|
||
|
|
||
|
BLOB_DECL = """
|
||
|
/** {fname} **/
|
||
|
static const uint8_t {varname}[] = {{
|
||
|
"""
|
||
|
|
||
|
|
||
|
def _relpath_p(path, start):
|
||
|
return posixpath.relpath(pathlib.Path(os.path.abspath(path)).as_posix(),
|
||
|
pathlib.Path(os.path.abspath(start)).as_posix())
|
||
|
|
||
|
|
||
|
def mkconstfs(files, root_path, mount_point, constfs_name):
|
||
|
"""Generate a C file containing a constant file system
|
||
|
|
||
|
Return
|
||
|
------
|
||
|
|
||
|
chunks: Iterator yielding fragments of the of the output file.
|
||
|
"""
|
||
|
|
||
|
filemap = {f: (_mkident(i), _relpath_p(f, root_path))
|
||
|
for i, f in enumerate(files)}
|
||
|
|
||
|
yield C_HEADER
|
||
|
yield from itertools.chain.from_iterable(
|
||
|
print_file_data(local_f, *f_data) for local_f, f_data in filemap.items())
|
||
|
|
||
|
yield FILES_DECL
|
||
|
|
||
|
yield from (FILE_TEMPLATE.format(target_name=_addroot(relp),
|
||
|
buff_name=ident)
|
||
|
for ident, relp in sorted(filemap.values()))
|
||
|
|
||
|
yield "};\n"
|
||
|
|
||
|
yield C_FOOTER.format(constfs_name=constfs_name, mount_pount=mount_point)
|
||
|
|
||
|
|
||
|
def _addroot(fname):
|
||
|
return "/" + fname if not fname.startswith("/") else fname
|
||
|
|
||
|
|
||
|
def _mkident(k):
|
||
|
return "_file{:02X}".format(k)
|
||
|
|
||
|
|
||
|
def print_file_data(local_fname, varname, target_fname=""):
|
||
|
"""Convert a file into a static C array:
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
|
||
|
local_fname: real Path (where the file is on this machine's fs)
|
||
|
target_fname: name that the file will have in the constfs.
|
||
|
output_file: File-like object where the array will be written.
|
||
|
|
||
|
Return
|
||
|
------
|
||
|
|
||
|
chunks: Iterator yielding fragments of the of the output text.
|
||
|
"""
|
||
|
|
||
|
yield BLOB_DECL.format(fname=target_fname, varname=varname)
|
||
|
|
||
|
def byte2s(b):
|
||
|
return "0x{},".format(hexlify(b).decode('utf-8'))
|
||
|
|
||
|
def chunk(iterable, blocksize):
|
||
|
"""Break a single iterable into chunks of maximum size 'blocksize'"""
|
||
|
return (x for _, x in itertools.groupby(enumerate(iterable),
|
||
|
lambda x: x[0]//blocksize))
|
||
|
|
||
|
with open(local_fname, 'rb') as f, mmap.mmap(f.fileno(), 0,
|
||
|
access=mmap.ACCESS_READ
|
||
|
) as bfile:
|
||
|
yield from map(lambda x: x[1],
|
||
|
itertools.chain.from_iterable(
|
||
|
map(lambda l: itertools.chain(l, [(0, "\n")]),
|
||
|
chunk(map(byte2s, bfile), 16)
|
||
|
)
|
||
|
)
|
||
|
)
|
||
|
|
||
|
yield "};\n"
|
||
|
|
||
|
|
||
|
def main():
|
||
|
parser = argparse.ArgumentParser(
|
||
|
description="Embed files into a constant file system")
|
||
|
|
||
|
parser.add_argument("-m", '--mount', metavar="mountpoint",
|
||
|
help="Where to mount the resulting fs", default="/")
|
||
|
|
||
|
parser.add_argument("-o", '--output', metavar="output_file",
|
||
|
help="Write the output to a file instead of stdout. "
|
||
|
"The file is only written if the command is successful "
|
||
|
"(i.e. there is no partial output")
|
||
|
|
||
|
parser.add_argument("-r", '--root', metavar="root_base_path",
|
||
|
type=pathlib.Path,
|
||
|
help="Paths on the constf will be generated for the real "
|
||
|
"path of the files by considering this path to be the root "
|
||
|
"By default the current directory (.) is used",
|
||
|
default=pathlib.Path())
|
||
|
|
||
|
parser.add_argument("name", help="Name for the vfs_mount_t structure")
|
||
|
|
||
|
parser.add_argument("files", nargs="+", type=pathlib.Path,
|
||
|
help="Files to be included.")
|
||
|
|
||
|
ns = parser.parse_args()
|
||
|
|
||
|
f_chunks = mkconstfs(ns.files, ns.root, ns.mount, ns.name)
|
||
|
|
||
|
if ns.output:
|
||
|
tmp_out = io.StringIO()
|
||
|
else:
|
||
|
tmp_out = sys.stdout
|
||
|
|
||
|
tmp_out.writelines(f_chunks)
|
||
|
|
||
|
if ns.output:
|
||
|
with open(ns.output, "w+") as f:
|
||
|
tmp_out.seek(0)
|
||
|
shutil.copyfileobj(tmp_out, f)
|
||
|
|
||
|
return 0
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
exit(main())
|