Merge "fs_config: introduce TARGET_FS_CONFIG_GEN"
This commit is contained in:
commit
54cb05c01a
3 changed files with 439 additions and 11 deletions
|
@ -23,20 +23,31 @@ LOCAL_CFLAGS := -Werror
|
|||
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
|
||||
# To Build the custom target binary for the host to generate the fs_config
|
||||
# override files. The executable is hard coded to include the
|
||||
# $(TARGET_ANDROID_FILESYSTEM_CONFIG_H) file if it exists.
|
||||
# Expectations:
|
||||
# device/<vendor>/<device>/android_filesystem_config.h
|
||||
# fills in struct fs_path_config android_device_dirs[] and
|
||||
# struct fs_path_config android_device_files[]
|
||||
# device/<vendor>/<device>/device.mk
|
||||
# PRODUCT_PACKAGES += fs_config_dirs fs_config_files
|
||||
|
||||
# If not specified, check if default one to be found
|
||||
# One can override the default android_filesystem_config.h file in one of two ways:
|
||||
#
|
||||
# 1. The old way:
|
||||
# To Build the custom target binary for the host to generate the fs_config
|
||||
# override files. The executable is hard coded to include the
|
||||
# $(TARGET_ANDROID_FILESYSTEM_CONFIG_H) file if it exists.
|
||||
# Expectations:
|
||||
# device/<vendor>/<device>/android_filesystem_config.h
|
||||
# fills in struct fs_path_config android_device_dirs[] and
|
||||
# struct fs_path_config android_device_files[]
|
||||
# device/<vendor>/<device>/device.mk
|
||||
# PRODUCT_PACKAGES += fs_config_dirs fs_config_files
|
||||
# If not specified, check if default one to be found
|
||||
#
|
||||
# 2. The new way:
|
||||
# set TARGET_FS_CONFIG_GEN to contain a list of intermediate format files
|
||||
# for generating the android_filesystem_config.h file.
|
||||
#
|
||||
# More information can be found in the README
|
||||
ANDROID_FS_CONFIG_H := android_filesystem_config.h
|
||||
|
||||
ifneq ($(TARGET_ANDROID_FILESYSTEM_CONFIG_H),)
|
||||
ifneq ($(TARGET_FS_CONFIG_GEN),)
|
||||
$(error Cannot set TARGET_ANDROID_FILESYSTEM_CONFIG_H and TARGET_FS_CONFIG_GEN simultaneously)
|
||||
endif
|
||||
|
||||
# One and only one file can be specified.
|
||||
ifneq ($(words $(TARGET_ANDROID_FILESYSTEM_CONFIG_H)),1)
|
||||
|
@ -51,20 +62,43 @@ endif
|
|||
|
||||
my_fs_config_h := $(TARGET_ANDROID_FILESYSTEM_CONFIG_H)
|
||||
else ifneq ($(wildcard $(TARGET_DEVICE_DIR)/$(ANDROID_FS_CONFIG_H)),)
|
||||
|
||||
ifneq ($(TARGET_FS_CONFIG_GEN),)
|
||||
$(error Cannot provide $(TARGET_DEVICE_DIR)/$(ANDROID_FS_CONFIG_H) and set TARGET_FS_CONFIG_GEN simultaneously)
|
||||
endif
|
||||
my_fs_config_h := $(TARGET_DEVICE_DIR)/$(ANDROID_FS_CONFIG_H)
|
||||
|
||||
else
|
||||
my_fs_config_h := $(LOCAL_PATH)/default/$(ANDROID_FS_CONFIG_H)
|
||||
endif
|
||||
|
||||
##################################
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := fs_config_generate.c
|
||||
LOCAL_MODULE := fs_config_generate_$(TARGET_DEVICE)
|
||||
LOCAL_MODULE_CLASS := EXECUTABLES
|
||||
LOCAL_SHARED_LIBRARIES := libcutils
|
||||
LOCAL_CFLAGS := -Werror -Wno-error=\#warnings
|
||||
|
||||
ifneq ($(TARGET_FS_CONFIG_GEN),)
|
||||
gen := $(local-generated-sources-dir)/$(ANDROID_FS_CONFIG_H)
|
||||
$(gen): PRIVATE_LOCAL_PATH := $(LOCAL_PATH)
|
||||
$(gen): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN)
|
||||
$(gen): PRIVATE_CUSTOM_TOOL = $(PRIVATE_LOCAL_PATH)/fs_config_generator.py $(PRIVATE_TARGET_FS_CONFIG_GEN) > $@
|
||||
$(gen): $(TARGET_FS_CONFIG_GEN) $(LOCAL_PATH)/fs_config_generator.py
|
||||
$(transform-generated-source)
|
||||
|
||||
LOCAL_GENERATED_SOURCES := $(gen)
|
||||
my_fs_config_h := $(gen)
|
||||
gen :=
|
||||
endif
|
||||
|
||||
LOCAL_C_INCLUDES := $(dir $(my_fs_config_h))
|
||||
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
fs_config_generate_bin := $(LOCAL_INSTALLED_MODULE)
|
||||
|
||||
##################################
|
||||
# Generate the system/etc/fs_config_dirs binary file for the target
|
||||
# Add fs_config_dirs to PRODUCT_PACKAGES in the device make file to enable
|
||||
include $(CLEAR_VARS)
|
||||
|
@ -76,6 +110,7 @@ $(LOCAL_BUILT_MODULE): $(fs_config_generate_bin)
|
|||
@mkdir -p $(dir $@)
|
||||
$< -D -o $@
|
||||
|
||||
##################################
|
||||
# Generate the system/etc/fs_config_files binary file for the target
|
||||
# Add fs_config_files to PRODUCT_PACKAGES in the device make file to enable
|
||||
include $(CLEAR_VARS)
|
||||
|
|
116
tools/fs_config/README
Normal file
116
tools/fs_config/README
Normal file
|
@ -0,0 +1,116 @@
|
|||
_____ _____ _____ _____ __ __ _____
|
||||
/ _ \/ __\/ _ \| _ \/ \/ \/ __\
|
||||
| _ <| __|| _ || | || \/ || __|
|
||||
\__|\_/\_____/\__|__/|_____/\__ \__/\_____/
|
||||
|
||||
|
||||
Generating the android_filesystem_config.h
|
||||
|
||||
To generate the android_filesystem_config.h file, one can choose from
|
||||
one of two methods. The first method, is to declare
|
||||
TARGET_ANDROID_FILESYSTEM_CONFIG_H in the device BoardConfig.mk file. This
|
||||
variable can only have one item in it, and it is used directly as the
|
||||
android_filesystem_config.h header when building
|
||||
fs_config_generate_$(TARGET_DEVICE) which is used to generate fs_config_files
|
||||
and fs_config_dirs target executable.
|
||||
|
||||
The limitation with this, is that it can only be set once, thus if the device
|
||||
has a make hierarchy, then each device needs its own file, and cannot share
|
||||
from a common source or that common source needs to include everything from
|
||||
both devices.
|
||||
|
||||
The other way is to set TARGET_FS_CONFIG_GEN, which can be a list of
|
||||
intermediate fs configuration files. It is a build error on any one
|
||||
these conditions:
|
||||
* Specify TARGET_FS_CONFIG_GEN and TARGET_ANDROID_FILESYSTEM_CONFIG_H
|
||||
* Specify TARGET_FS_CONFIG_GEN and provide
|
||||
$(TARGET_DEVICE_DIR)/android_filesystem_config.h
|
||||
|
||||
The parsing of the config file follows the Python ConfigParser specification,
|
||||
with the sections and fields as defined below. There are two types of sections,
|
||||
both sections require all options to be specified. The first section type is
|
||||
the "caps" section.
|
||||
|
||||
The "caps" section follows the following syntax:
|
||||
|
||||
[path]
|
||||
mode: Octal file mode
|
||||
user: AID_<user>
|
||||
group: AID_<group>
|
||||
caps: cap*
|
||||
|
||||
Where:
|
||||
|
||||
[path]
|
||||
The filesystem path to configure. A path ending in / is considered a dir,
|
||||
else its a file.
|
||||
|
||||
mode:
|
||||
A valid octal file mode of at least 3 digits. If 3 is specified, it is
|
||||
prefixed with a 0, else mode is used as is.
|
||||
|
||||
user:
|
||||
The exact, C define for a valid AID. Note custom AIDs can be defined in the
|
||||
AID section documented below.
|
||||
|
||||
group:
|
||||
The exact, C define for a valid AID. Note custom AIDs can be defined in the
|
||||
AID section documented below.
|
||||
|
||||
caps:
|
||||
The name as declared in
|
||||
system/core/include/private/android_filesystem_capability.h without the
|
||||
leading CAP_. Mixed case is allowed. Caps can also be the raw:
|
||||
* binary (0b0101)
|
||||
* octal (0455)
|
||||
* int (42)
|
||||
* hex (0xFF)
|
||||
For multiple caps, just separate by whitespace.
|
||||
|
||||
It is an error to specify multiple sections with the same [path]. Per the ini
|
||||
specifications enforced by Pythons ConfigParser.
|
||||
|
||||
|
||||
The next section type is the "AID" section, for specifying OEM specific AIDS.
|
||||
|
||||
The AID section follows the following syntax:
|
||||
|
||||
[AID_<name>]
|
||||
value: <number>
|
||||
|
||||
Where:
|
||||
|
||||
[AID_<name>]
|
||||
The <name> can be any valid character for a #define identifier in C.
|
||||
|
||||
value:
|
||||
A valid C style number string. Hex, octal, binary and decimal are supported. See "caps"
|
||||
above for more details on number formatting.
|
||||
|
||||
It is an error to specify multiple sections with the same [AID_<name>]. Per the ini
|
||||
specifications enforced by Pythons ConfigParser. It is also an error to specify
|
||||
multiple sections with the same value option. It is also an error to specify a value
|
||||
that is outside of the OEM range AID_OEM_RESERVED_START(2900) and AID_OEM_RESERVED_END(2999)
|
||||
as defined by system/core/include/private/android_filesystem_config.h.
|
||||
|
||||
Ordering within the TARGET_FS_CONFIG_GEN files is not relevant. The paths for files are sorted
|
||||
like so within their respective array definition:
|
||||
* specified path before prefix match
|
||||
** ie foo before f*
|
||||
* lexicographical less than before other
|
||||
** ie boo before foo
|
||||
|
||||
Given these paths:
|
||||
|
||||
paths=['ac', 'a', 'acd', 'an', 'a*', 'aa', 'ac*']
|
||||
|
||||
The sort order would be:
|
||||
paths=['a', 'aa', 'ac', 'acd', 'an', 'ac*', 'a*']
|
||||
|
||||
Thus the fs_config tools will match on specified paths before attempting prefix, and match on the
|
||||
longest matching prefix.
|
||||
|
||||
The declared AIDS are sorted in ascending numerical order based on the option "value". The string
|
||||
representation of value is preserved. Both choices were made for maximum readability of the generated
|
||||
file and to line up files. Sync lines are placed with the source file as comments in the generated
|
||||
header file.
|
277
tools/fs_config/fs_config_generator.py
Executable file
277
tools/fs_config/fs_config_generator.py
Executable file
|
@ -0,0 +1,277 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import ConfigParser
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
GENERATED = '''
|
||||
/*
|
||||
* THIS IS AN AUTOGENERATED FILE! DO NOT MODIFY
|
||||
*/
|
||||
'''
|
||||
|
||||
INCLUDE = '#include <private/android_filesystem_config.h>'
|
||||
|
||||
DEFINE_NO_DIRS = '#define NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS\n'
|
||||
DEFINE_NO_FILES = '#define NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_FILES\n'
|
||||
|
||||
DEFAULT_WARNING = '#warning No device-supplied android_filesystem_config.h, using empty default.'
|
||||
|
||||
NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS_ENTRY = '{ 00000, AID_ROOT, AID_ROOT, 0, "system/etc/fs_config_dirs" },'
|
||||
NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_FILES_ENTRY = '{ 00000, AID_ROOT, AID_ROOT, 0, "system/etc/fs_config_files" },'
|
||||
|
||||
IFDEF_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS = '#ifdef NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS'
|
||||
ENDIF = '#endif'
|
||||
|
||||
OPEN_FILE_STRUCT = 'static const struct fs_path_config android_device_files[] = {'
|
||||
OPEN_DIR_STRUCT = 'static const struct fs_path_config android_device_dirs[] = {'
|
||||
CLOSE_FILE_STRUCT = '};'
|
||||
|
||||
GENERIC_DEFINE = "#define %s\t%s"
|
||||
|
||||
FILE_COMMENT = '// Defined in file: \"%s\"'
|
||||
|
||||
# from system/core/include/private/android_filesystem_config.h
|
||||
AID_OEM_RESERVED_START = 2900
|
||||
AID_OEM_RESERVED_END = 2999
|
||||
|
||||
|
||||
AID_MATCH = re.compile('AID_[a-zA-Z]+')
|
||||
|
||||
def handle_aid(file_name, section_name, config, aids, seen_aids):
|
||||
value = config.get(section_name, 'value')
|
||||
|
||||
errmsg = '%s for: \"' + section_name + '" file: \"' + file_name + '\"'
|
||||
|
||||
if not value:
|
||||
raise Exception(errmsg % 'Found specified but unset "value"')
|
||||
|
||||
v = convert_int(value)
|
||||
if not v:
|
||||
raise Exception(errmsg % ('Invalid "value", not a number, got: \"%s\"' % value))
|
||||
|
||||
# Values must be within OEM range
|
||||
if (v < AID_OEM_RESERVED_START) or (v > AID_OEM_RESERVED_END):
|
||||
s = '"value" not in valid range %d - %d, got: %s'
|
||||
s = s % (AID_OEM_RESERVED_START, AID_OEM_RESERVED_END, value)
|
||||
raise Exception(errmsg % s)
|
||||
|
||||
# use the normalized int value in the dict and detect
|
||||
# duplicate definitions of the same vallue
|
||||
v = str(v)
|
||||
if v in seen_aids[1]:
|
||||
# map of value to aid name
|
||||
a = seen_aids[1][v]
|
||||
|
||||
# aid name to file
|
||||
f = seen_aids[0][a]
|
||||
|
||||
s = 'Duplicate AID value "%s" found on AID: "%s".' % (value, seen_aids[1][v])
|
||||
s += ' Previous found in file: "%s."' % f
|
||||
raise Exception(errmsg % s)
|
||||
|
||||
seen_aids[1][v] = section_name
|
||||
|
||||
# Append a tuple of (AID_*, base10(value), str(value))
|
||||
# We keep the str version of value so we can print that out in the
|
||||
# generated header so investigating parties can identify parts.
|
||||
# We store the base10 value for sorting, so everything is ascending
|
||||
# later.
|
||||
aids.append((file_name, section_name, v, value))
|
||||
|
||||
def convert_int(num):
|
||||
|
||||
try:
|
||||
if num.startswith('0x'):
|
||||
return int(num, 16)
|
||||
elif num.startswith('0b'):
|
||||
return int(num, 2)
|
||||
elif num.startswith('0'):
|
||||
return int(num, 8)
|
||||
else:
|
||||
return int(num, 10)
|
||||
except ValueError:
|
||||
pass
|
||||
return None
|
||||
|
||||
def handle_path(file_name, section_name, config, files, dirs):
|
||||
|
||||
mode = config.get(section_name, 'mode')
|
||||
user = config.get(section_name, 'user')
|
||||
group = config.get(section_name, 'group')
|
||||
caps = config.get(section_name, 'caps')
|
||||
|
||||
errmsg = 'Found specified but unset option: \"%s" in file: \"' + file_name + '\"'
|
||||
|
||||
if not mode:
|
||||
raise Exception(errmsg % 'mode')
|
||||
|
||||
if not user:
|
||||
raise Exception(errmsg % 'user')
|
||||
|
||||
if not group:
|
||||
raise Exception(errmsg % 'group')
|
||||
|
||||
if not caps:
|
||||
raise Exception(errmsg % 'caps')
|
||||
|
||||
caps = caps.split()
|
||||
|
||||
tmp = []
|
||||
for x in caps:
|
||||
if convert_int(x):
|
||||
tmp.append('(' + x + ')')
|
||||
else:
|
||||
tmp.append('(1ULL << CAP_' + x.upper() + ')')
|
||||
|
||||
caps = tmp
|
||||
|
||||
path = '"' + section_name + '"'
|
||||
|
||||
if len(mode) == 3:
|
||||
mode = '0' + mode
|
||||
|
||||
try:
|
||||
int(mode, 8)
|
||||
except:
|
||||
raise Exception('Mode must be octal characters, got: "' + mode + '"')
|
||||
|
||||
if len(mode) != 4:
|
||||
raise Exception('Mode must be 3 or 4 characters, got: "' + mode + '"')
|
||||
|
||||
|
||||
caps = '|'.join(caps)
|
||||
|
||||
x = [ mode, user, group, caps, section_name ]
|
||||
if section_name[-1] == '/':
|
||||
dirs.append((file_name, x))
|
||||
else:
|
||||
files.append((file_name, x))
|
||||
|
||||
def handle_dup(name, file_name, section_name, seen):
|
||||
if section_name in seen:
|
||||
dups = '"' + seen[section_name] + '" and '
|
||||
dups += file_name
|
||||
raise Exception('Duplicate ' + name + ' "' + section_name + '" found in files: ' + dups)
|
||||
|
||||
def parse(file_name, files, dirs, aids, seen_paths, seen_aids):
|
||||
|
||||
config = ConfigParser.ConfigParser()
|
||||
config.read(file_name)
|
||||
|
||||
for s in config.sections():
|
||||
|
||||
if AID_MATCH.match(s) and config.has_option(s, 'value'):
|
||||
handle_dup('AID', file_name, s, seen_aids[0])
|
||||
seen_aids[0][s] = file_name
|
||||
handle_aid(file_name, s, config, aids, seen_aids)
|
||||
else:
|
||||
handle_dup('path', file_name, s, seen_paths)
|
||||
seen_paths[s] = file_name
|
||||
handle_path(file_name, s, config, files, dirs)
|
||||
|
||||
def generate(files, dirs, aids):
|
||||
print GENERATED
|
||||
print INCLUDE
|
||||
print
|
||||
|
||||
are_dirs = len(dirs) > 0
|
||||
are_files = len(files) > 0
|
||||
are_aids = len(aids) > 0
|
||||
|
||||
if are_aids:
|
||||
# sort on value of (file_name, name, value, strvalue)
|
||||
aids.sort(key=lambda x: x[2])
|
||||
for a in aids:
|
||||
# use the preserved str value
|
||||
print FILE_COMMENT % a[0]
|
||||
print GENERIC_DEFINE % (a[1], a[2])
|
||||
|
||||
print
|
||||
|
||||
if not are_dirs:
|
||||
print DEFINE_NO_DIRS
|
||||
|
||||
if not are_files:
|
||||
print DEFINE_NO_FILES
|
||||
|
||||
if not are_files and not are_dirs and not are_aids:
|
||||
print DEFAULT_WARNING
|
||||
return
|
||||
|
||||
if are_files:
|
||||
print OPEN_FILE_STRUCT
|
||||
for tup in files:
|
||||
f = tup[0]
|
||||
c = tup[1]
|
||||
c[4] = '"' + c[4] + '"'
|
||||
c = '{ ' + ' ,'.join(c) + ' },'
|
||||
print FILE_COMMENT % f
|
||||
print ' ' + c
|
||||
|
||||
if not are_dirs:
|
||||
print IFDEF_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS
|
||||
print ' ' + NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS_ENTRY
|
||||
print ENDIF
|
||||
print CLOSE_FILE_STRUCT
|
||||
|
||||
if are_dirs:
|
||||
print OPEN_DIR_STRUCT
|
||||
for d in dirs:
|
||||
f[4] = '"' + f[4] + '"'
|
||||
d = '{ ' + ' ,'.join(d) + ' },'
|
||||
print ' ' + d
|
||||
|
||||
print CLOSE_FILE_STRUCT
|
||||
|
||||
def file_key(x):
|
||||
|
||||
# Wrapper class for custom prefix matching strings
|
||||
class S(object):
|
||||
def __init__(self, str):
|
||||
|
||||
self.orig = str
|
||||
self.is_prefix = str[-1] == '*'
|
||||
if self.is_prefix:
|
||||
self.str = str[:-1]
|
||||
else:
|
||||
self.str = str
|
||||
|
||||
def __lt__(self, other):
|
||||
|
||||
# if were both suffixed the smallest string
|
||||
# is 'bigger'
|
||||
if self.is_prefix and other.is_prefix:
|
||||
b = len(self.str) > len(other.str)
|
||||
# If I am an the suffix match, im bigger
|
||||
elif self.is_prefix:
|
||||
b = False
|
||||
# If other is the suffix match, he's bigger
|
||||
elif other.is_prefix:
|
||||
b = True
|
||||
# Alphabetical
|
||||
else:
|
||||
b = self.str < other.str
|
||||
return b
|
||||
|
||||
return S(x[4])
|
||||
|
||||
def main():
|
||||
|
||||
files = []
|
||||
dirs = []
|
||||
aids = []
|
||||
seen_paths = {}
|
||||
|
||||
# (name to file, value to aid)
|
||||
seen_aids = ({}, {})
|
||||
|
||||
for x in sys.argv[1:]:
|
||||
parse(x, files, dirs, aids, seen_paths, seen_aids)
|
||||
|
||||
files.sort(key= lambda x: file_key(x[1]))
|
||||
generate(files, dirs, aids)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
Loading…
Reference in a new issue