2015-05-07 05:24:35 +00:00
|
|
|
# esp-open-rtos common Makefile
|
|
|
|
#
|
2015-06-02 05:06:40 +00:00
|
|
|
# ******************************************************************
|
|
|
|
# Run 'make help' in any example subdirectory to see a usage summary
|
2015-06-02 23:15:56 +00:00
|
|
|
# (or skip to the bottom of this file!)
|
|
|
|
#
|
|
|
|
# For example, from the top level run:
|
|
|
|
# make help -C examples/http_get
|
2015-06-02 05:06:40 +00:00
|
|
|
# ******************************************************************
|
2015-05-07 05:24:35 +00:00
|
|
|
#
|
2015-06-02 23:15:56 +00:00
|
|
|
# In-depth documentation is at https://github.com/SuperHouse/esp-open-rtos/wiki/Build-Process
|
2015-06-02 05:06:40 +00:00
|
|
|
#
|
|
|
|
# Most sections Copyright 2015 Superhouse Automation Pty Ltd
|
|
|
|
# BSD Licensed as described in the file LICENSE at top level.
|
2015-05-07 05:24:35 +00:00
|
|
|
#
|
|
|
|
# This makefile is adapted from the esp-mqtt makefile by @tuanpmt
|
2015-07-21 05:02:09 +00:00
|
|
|
# https://github.com/tuanpmt/esp_mqtt, but it has changed very significantly
|
2015-05-07 05:24:35 +00:00
|
|
|
# since then.
|
2015-07-21 05:02:09 +00:00
|
|
|
|
2015-06-02 23:14:50 +00:00
|
|
|
# assume the 'root' directory (ie top of the tree) is the directory common.mk is in
|
|
|
|
ROOT := $(dir $(lastword $(MAKEFILE_LIST)))
|
|
|
|
|
2016-05-07 07:25:28 +00:00
|
|
|
include $(ROOT)parameters.mk
|
2015-05-07 05:24:35 +00:00
|
|
|
|
2015-07-21 05:02:09 +00:00
|
|
|
ifndef PROGRAM
|
2016-03-23 03:59:19 +00:00
|
|
|
$(error "Set the PROGRAM environment variable in your Makefile before including common.mk")
|
2015-07-21 05:02:09 +00:00
|
|
|
endif
|
|
|
|
|
2015-05-12 00:08:39 +00:00
|
|
|
# hacky way to get a single space value
|
|
|
|
empty :=
|
|
|
|
space := $(empty) $(empty)
|
|
|
|
|
2015-06-11 22:12:47 +00:00
|
|
|
# GNU Make lowercase function, bit of a horrorshow but works (courtesy http://stackoverflow.com/a/665045)
|
|
|
|
lc = $(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$1))))))))))))))))))))))))))
|
|
|
|
|
2015-06-12 00:26:02 +00:00
|
|
|
# assume the program dir is the directory the top-level makefile was run in
|
|
|
|
PROGRAM_DIR := $(dir $(firstword $(MAKEFILE_LIST)))
|
2015-05-07 05:24:35 +00:00
|
|
|
|
2015-05-12 00:08:39 +00:00
|
|
|
# derive various parts of compiler/linker arguments
|
2015-07-30 17:34:13 +00:00
|
|
|
SDK_LIB_ARGS = $(addprefix -l,$(SDK_LIBS))
|
|
|
|
LIB_ARGS = $(addprefix -l,$(LIBS))
|
2015-06-12 00:26:02 +00:00
|
|
|
PROGRAM_OUT = $(BUILD_DIR)$(PROGRAM).out
|
2015-11-13 01:11:37 +00:00
|
|
|
LDFLAGS += $(addprefix -T,$(LINKER_SCRIPTS))
|
2015-07-21 05:02:09 +00:00
|
|
|
|
2016-05-07 07:25:28 +00:00
|
|
|
FW_FILE = $(addprefix $(FIRMWARE_DIR),$(PROGRAM).bin)
|
2015-05-07 05:24:35 +00:00
|
|
|
|
2015-05-12 00:08:39 +00:00
|
|
|
# Common include directories, shared across all "components"
|
|
|
|
# components will add their include directories to this argument
|
|
|
|
#
|
2015-06-12 00:26:02 +00:00
|
|
|
# Placing $(PROGRAM_DIR) and $(PROGRAM_DIR)include first allows
|
|
|
|
# programs to have their own copies of header config files for components
|
2015-05-12 00:08:39 +00:00
|
|
|
# , which is useful for overriding things.
|
2015-08-21 06:30:52 +00:00
|
|
|
INC_DIRS = $(PROGRAM_DIR) $(PROGRAM_DIR)include $(ROOT)include
|
2015-05-07 05:24:35 +00:00
|
|
|
|
2015-07-15 05:09:55 +00:00
|
|
|
ifeq ($(OWN_LIBC),1)
|
|
|
|
INC_DIRS += $(ROOT)libc/xtensa-lx106-elf/include
|
|
|
|
LDFLAGS += -L$(ROOT)libc/xtensa-lx106-elf/lib
|
2016-02-22 22:21:03 +00:00
|
|
|
ifeq ($(PRINTF_SCANF_FLOAT_SUPPORT),1)
|
|
|
|
LDFLAGS += -u _printf_float -u _scanf_float
|
|
|
|
endif
|
2015-07-15 05:09:55 +00:00
|
|
|
endif
|
|
|
|
|
2015-05-07 05:24:35 +00:00
|
|
|
ifeq ("$(V)","1")
|
|
|
|
Q :=
|
|
|
|
vecho := @true
|
|
|
|
else
|
|
|
|
Q := @
|
|
|
|
vecho := @echo
|
|
|
|
endif
|
|
|
|
|
2016-07-22 11:09:50 +00:00
|
|
|
.PHONY: all clean flash erase_flash test size rebuild
|
2015-05-07 05:24:35 +00:00
|
|
|
|
2015-07-21 05:02:09 +00:00
|
|
|
all: $(PROGRAM_OUT) $(FW_FILE_1) $(FW_FILE_2) $(FW_FILE)
|
2015-05-07 05:24:35 +00:00
|
|
|
|
2015-05-12 00:08:39 +00:00
|
|
|
# component_compile_rules: Produces compilation rules for a given
|
|
|
|
# component
|
|
|
|
#
|
2015-09-08 00:36:03 +00:00
|
|
|
# For user-facing documentation, see:
|
|
|
|
# https://github.com/SuperHouse/esp-open-rtos/wiki/Build-Process#adding-a-new-component
|
|
|
|
#
|
2015-05-12 00:08:39 +00:00
|
|
|
# Call arguments are:
|
|
|
|
# $(1) - component name
|
|
|
|
#
|
|
|
|
# Expects that the following component-specific variables are defined:
|
|
|
|
#
|
|
|
|
# $(1)_ROOT = Top-level dir containing component. Can be in-tree or out-of-tree.
|
2015-09-07 23:59:59 +00:00
|
|
|
# (if this variable isn't defined, directory containing component.mk is used)
|
2015-05-12 00:08:39 +00:00
|
|
|
# $(1)_SRC_DIR = List of source directories for the component. All must be under $(1)_ROOT
|
|
|
|
# $(1)_INC_DIR = List of include directories specific for the component
|
|
|
|
#
|
|
|
|
#
|
|
|
|
# Each call appends to COMPONENT_ARS which is a list of archive files for compiled components
|
|
|
|
COMPONENT_ARS =
|
|
|
|
define component_compile_rules
|
2015-09-07 23:59:59 +00:00
|
|
|
$(1)_DEFAULT_ROOT := $(dir $(lastword $(MAKEFILE_LIST)))
|
|
|
|
$(1)_ROOT ?= $$($(1)_DEFAULT_ROOT)
|
2015-06-11 22:12:47 +00:00
|
|
|
$(1)_OBJ_DIR = $(call lc,$(BUILD_DIR)$(1)/)
|
2015-05-12 00:08:39 +00:00
|
|
|
### determine source files and object files ###
|
2015-08-10 05:51:57 +00:00
|
|
|
$(1)_SRC_FILES ?= $$(foreach sdir,$$($(1)_SRC_DIR), \
|
|
|
|
$$(wildcard $$(sdir)/*.c) $$(wildcard $$(sdir)/*.S) \
|
|
|
|
$$(wildcard $$(sdir)/*.cpp)) \
|
2015-07-28 01:27:35 +00:00
|
|
|
$$($(1)_EXTRA_SRC_FILES)
|
2015-06-12 00:27:14 +00:00
|
|
|
$(1)_REAL_SRC_FILES = $$(foreach sfile,$$($(1)_SRC_FILES),$$(realpath $$(sfile)))
|
2015-05-12 00:08:39 +00:00
|
|
|
$(1)_REAL_ROOT = $$(realpath $$($(1)_ROOT))
|
2015-06-12 00:27:14 +00:00
|
|
|
# patsubst here substitutes real component root path for the relative OBJ_DIR path, making things short again
|
2015-08-10 05:51:57 +00:00
|
|
|
$(1)_OBJ_FILES_CXX = $$(patsubst $$($(1)_REAL_ROOT)%.cpp,$$($(1)_OBJ_DIR)%.o,$$($(1)_REAL_SRC_FILES))
|
|
|
|
$(1)_OBJ_FILES_C = $$(patsubst $$($(1)_REAL_ROOT)%.c,$$($(1)_OBJ_DIR)%.o,$$($(1)_OBJ_FILES_CXX))
|
2015-07-30 00:10:37 +00:00
|
|
|
$(1)_OBJ_FILES = $$(patsubst $$($(1)_REAL_ROOT)%.S,$$($(1)_OBJ_DIR)%.o,$$($(1)_OBJ_FILES_C))
|
2015-06-11 23:51:25 +00:00
|
|
|
# the last included makefile is our component's component.mk makefile (rebuild the component if it changes)
|
|
|
|
$(1)_MAKEFILE ?= $(lastword $(MAKEFILE_LIST))
|
2015-05-12 00:08:39 +00:00
|
|
|
|
|
|
|
### determine compiler arguments ###
|
2016-01-04 00:57:43 +00:00
|
|
|
$(1)_CPPFLAGS ?= $(CPPFLAGS)
|
2015-05-12 00:08:39 +00:00
|
|
|
$(1)_CFLAGS ?= $(CFLAGS)
|
2015-07-30 17:34:13 +00:00
|
|
|
$(1)_CXXFLAGS ?= $(CXXFLAGS)
|
2016-01-04 00:57:43 +00:00
|
|
|
$(1)_CC_BASE = $(Q) $(CC) $$(addprefix -I,$$(INC_DIRS)) $$(addprefix -I,$$($(1)_INC_DIR)) $$($(1)_CPPFLAGS)
|
2015-06-11 22:12:47 +00:00
|
|
|
$(1)_AR = $(call lc,$(BUILD_DIR)$(1).a)
|
2015-05-12 00:08:39 +00:00
|
|
|
|
2015-06-11 23:51:25 +00:00
|
|
|
$$($(1)_OBJ_DIR)%.o: $$($(1)_REAL_ROOT)%.c $$($(1)_MAKEFILE) $(wildcard $(ROOT)*.mk) | $$($(1)_SRC_DIR)
|
2015-05-12 00:08:39 +00:00
|
|
|
$(vecho) "CC $$<"
|
|
|
|
$(Q) mkdir -p $$(dir $$@)
|
2016-01-04 00:57:43 +00:00
|
|
|
$$($(1)_CC_BASE) $$($(1)_CFLAGS) -c $$< -o $$@
|
|
|
|
$$($(1)_CC_BASE) $$($(1)_CFLAGS) -MM -MT $$@ -MF $$(@:.o=.d) $$<
|
2015-05-12 00:08:39 +00:00
|
|
|
|
2015-08-10 05:51:57 +00:00
|
|
|
$$($(1)_OBJ_DIR)%.o: $$($(1)_REAL_ROOT)%.cpp $$($(1)_MAKEFILE) $(wildcard $(ROOT)*.mk) | $$($(1)_SRC_DIR)
|
|
|
|
$(vecho) "C++ $$<"
|
|
|
|
$(Q) mkdir -p $$(dir $$@)
|
2016-01-04 00:57:43 +00:00
|
|
|
$$($(1)_CC_BASE) $$($(1)_CXXFLAGS) -c $$< -o $$@
|
|
|
|
$$($(1)_CC_BASE) $$($(1)_CXXFLAGS) -MM -MT $$@ -MF $$(@:.o=.d) $$<
|
2015-08-10 05:51:57 +00:00
|
|
|
|
2015-07-30 00:10:37 +00:00
|
|
|
$$($(1)_OBJ_DIR)%.o: $$($(1)_REAL_ROOT)%.S $$($(1)_MAKEFILE) $(wildcard $(ROOT)*.mk) | $$($(1)_SRC_DIR)
|
2015-07-28 01:27:35 +00:00
|
|
|
$(vecho) "AS $$<"
|
|
|
|
$(Q) mkdir -p $$(dir $$@)
|
2016-01-04 00:57:43 +00:00
|
|
|
$$($(1)_CC_BASE) -c $$< -o $$@
|
|
|
|
$$($(1)_CC_BASE) -MM -MT $$@ -MF $$(@:.o=.d) $$<
|
2015-07-28 01:27:35 +00:00
|
|
|
|
2016-10-21 19:39:50 +00:00
|
|
|
$(1)_AR_IN_FILES = $$($(1)_OBJ_FILES)
|
|
|
|
|
|
|
|
# the component is shown to depend on both obj and source files so we get
|
|
|
|
# a meaningful error message for missing explicitly named source files
|
|
|
|
ifeq ($(INCLUDE_SRC_INTO_AR),1)
|
|
|
|
$(1)_SRC_IN_AR_FILES = $$($(1)_SRC_FILES)
|
|
|
|
endif
|
|
|
|
|
|
|
|
$$($(1)_AR): $$($(1)_OBJ_FILES) $$($(1)_SRC_IN_AR_FILES)
|
2015-05-12 00:08:39 +00:00
|
|
|
$(vecho) "AR $$@"
|
2015-07-29 00:40:53 +00:00
|
|
|
$(Q) mkdir -p $$(dir $$@)
|
2015-05-12 00:08:39 +00:00
|
|
|
$(Q) $(AR) cru $$@ $$^
|
|
|
|
|
|
|
|
COMPONENT_ARS += $$($(1)_AR)
|
|
|
|
|
|
|
|
-include $$($(1)_OBJ_FILES:.o=.d)
|
|
|
|
endef
|
|
|
|
|
2015-05-30 09:11:04 +00:00
|
|
|
## Linking rules for SDK libraries
|
|
|
|
## SDK libraries are preprocessed to:
|
2015-06-16 00:02:36 +00:00
|
|
|
# - remove object files named in <libname>.remove
|
2015-05-30 09:11:04 +00:00
|
|
|
# - prefix all defined symbols with 'sdk_'
|
|
|
|
# - weaken all global symbols so they can be overriden from the open SDK side
|
2015-06-16 00:02:36 +00:00
|
|
|
#
|
2016-02-17 04:47:40 +00:00
|
|
|
# SDK binary libraries are preprocessed into $(BUILD_DIR)/sdklib
|
2015-05-30 09:11:04 +00:00
|
|
|
SDK_PROCESSED_LIBS = $(addsuffix .a,$(addprefix $(BUILD_DIR)sdklib/lib,$(SDK_LIBS)))
|
|
|
|
|
2015-06-16 00:02:36 +00:00
|
|
|
# Make rules for preprocessing each SDK library
|
2015-05-30 09:11:04 +00:00
|
|
|
|
2015-06-16 00:02:36 +00:00
|
|
|
# hacky, but prevents confusing error messages if one of these files disappears
|
|
|
|
$(ROOT)lib/%.remove:
|
|
|
|
touch $@
|
2015-05-30 09:11:04 +00:00
|
|
|
|
2015-06-16 00:02:36 +00:00
|
|
|
# Remove comment lines from <libname>.remove files
|
|
|
|
$(BUILD_DIR)sdklib/%.remove: $(ROOT)lib/%.remove | $(BUILD_DIR)sdklib
|
|
|
|
$(Q) grep -v "^#" $< | cat > $@
|
2015-05-30 09:11:04 +00:00
|
|
|
|
2015-06-16 00:02:36 +00:00
|
|
|
# Stage 1: remove unwanted object files listed in <libname>.remove alongside each library
|
|
|
|
$(BUILD_DIR)sdklib/%_stage1.a: $(ROOT)lib/%.a $(BUILD_DIR)sdklib/%.remove | $(BUILD_DIR)sdklib
|
|
|
|
@echo "SDK processing stage 1: Removing unwanted objects from $<"
|
|
|
|
$(Q) cat $< > $@
|
|
|
|
$(Q) $(AR) d $@ @$(word 2,$^)
|
2015-05-30 09:11:04 +00:00
|
|
|
|
2015-08-25 16:31:02 +00:00
|
|
|
# Stage 2: Redefine all SDK symbols as sdk_, weaken all symbols.
|
|
|
|
$(BUILD_DIR)sdklib/%.a: $(BUILD_DIR)sdklib/%_stage1.a $(ROOT)lib/allsymbols.rename
|
|
|
|
@echo "SDK processing stage 2: Renaming symbols in SDK library $< -> $@"
|
2015-06-16 00:02:36 +00:00
|
|
|
$(Q) $(OBJCOPY) --redefine-syms $(word 2,$^) --weaken $< $@
|
|
|
|
|
2015-06-12 00:26:02 +00:00
|
|
|
# include "dummy component" for the 'program' object files, defined in the Makefile
|
|
|
|
PROGRAM_SRC_DIR ?= $(PROGRAM_DIR)
|
|
|
|
PROGRAM_ROOT ?= $(PROGRAM_DIR)
|
|
|
|
PROGRAM_MAKEFILE = $(firstword $(MAKEFILE_LIST))
|
|
|
|
$(eval $(call component_compile_rules,PROGRAM))
|
2015-05-12 00:08:39 +00:00
|
|
|
|
2015-06-05 01:47:19 +00:00
|
|
|
## Include other components (this is where the actual compiler sections are generated)
|
2015-09-08 00:36:19 +00:00
|
|
|
##
|
|
|
|
## if component directory exists relative to $(ROOT), use that.
|
|
|
|
## otherwise try to resolve it as an absolute path
|
|
|
|
$(foreach component,$(COMPONENTS), \
|
|
|
|
$(if $(wildcard $(ROOT)$(component)), \
|
|
|
|
$(eval include $(ROOT)$(component)/component.mk), \
|
|
|
|
$(eval include $(component)/component.mk) \
|
|
|
|
) \
|
|
|
|
)
|
2015-07-16 06:24:33 +00:00
|
|
|
|
2015-05-12 00:08:39 +00:00
|
|
|
# final linking step to produce .elf
|
2015-11-13 01:11:37 +00:00
|
|
|
$(PROGRAM_OUT): $(COMPONENT_ARS) $(SDK_PROCESSED_LIBS) $(LINKER_SCRIPTS)
|
2015-05-07 05:24:35 +00:00
|
|
|
$(vecho) "LD $@"
|
2015-08-31 06:56:16 +00:00
|
|
|
$(Q) $(LD) $(LDFLAGS) -Wl,--start-group $(COMPONENT_ARS) $(LIB_ARGS) $(SDK_LIB_ARGS) -Wl,--end-group -o $@
|
2015-05-07 05:24:35 +00:00
|
|
|
|
2016-02-17 04:47:09 +00:00
|
|
|
$(BUILD_DIR) $(FIRMWARE_DIR) $(BUILD_DIR)sdklib:
|
2015-05-07 05:24:35 +00:00
|
|
|
$(Q) mkdir -p $@
|
|
|
|
|
2016-02-17 04:47:09 +00:00
|
|
|
$(FW_FILE_1) $(FW_FILE_2): $(PROGRAM_OUT) $(FIRMWARE_DIR)
|
2015-05-12 00:08:39 +00:00
|
|
|
$(vecho) "FW $@"
|
2016-02-17 04:47:09 +00:00
|
|
|
$(Q) $(ESPTOOL) elf2image $(ESPTOOL_ARGS) $< -o $(FIRMWARE_DIR)
|
2015-05-07 05:24:35 +00:00
|
|
|
|
2016-02-17 04:47:09 +00:00
|
|
|
$(FW_FILE): $(PROGRAM_OUT) $(FIRMWARE_DIR)
|
2016-03-23 03:59:19 +00:00
|
|
|
$(vecho) "FW $@"
|
2016-03-23 00:12:20 +00:00
|
|
|
$(Q) $(ESPTOOL) elf2image --version=2 $(ESPTOOL_ARGS) $< -o $(FW_FILE)
|
2015-07-21 05:02:09 +00:00
|
|
|
|
2016-07-22 11:09:50 +00:00
|
|
|
flash: all
|
2016-07-09 22:33:23 +00:00
|
|
|
$(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) write_flash $(ESPTOOL_ARGS) \
|
|
|
|
0x0 $(RBOOT_BIN) 0x1000 $(RBOOT_CONF) 0x2000 $(FW_FILE) $(SPIFFS_ESPTOOL_ARGS)
|
2016-03-23 03:59:19 +00:00
|
|
|
|
|
|
|
erase_flash:
|
|
|
|
$(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) erase_flash
|
|
|
|
|
2015-06-12 00:26:02 +00:00
|
|
|
size: $(PROGRAM_OUT)
|
|
|
|
$(Q) $(CROSS)size --format=sysv $(PROGRAM_OUT)
|
2015-05-13 06:41:16 +00:00
|
|
|
|
2015-05-07 05:24:35 +00:00
|
|
|
test: flash
|
2016-03-10 01:28:53 +00:00
|
|
|
$(FILTEROUTPUT) --port $(ESPPORT) --baud 115200 --elf $(PROGRAM_OUT)
|
2015-05-07 05:24:35 +00:00
|
|
|
|
2015-05-12 00:20:09 +00:00
|
|
|
# the rebuild target is written like this so it can be run in a parallel build
|
|
|
|
# environment without causing weird side effects
|
|
|
|
rebuild:
|
|
|
|
$(MAKE) clean
|
|
|
|
$(MAKE) all
|
2015-05-07 05:24:35 +00:00
|
|
|
|
|
|
|
clean:
|
|
|
|
$(Q) rm -rf $(BUILD_DIR)
|
2016-02-17 04:47:09 +00:00
|
|
|
$(Q) rm -rf $(FIRMWARE_DIR)
|
2015-05-30 09:11:04 +00:00
|
|
|
|
|
|
|
# prevent "intermediate" files from being deleted
|
|
|
|
.SECONDARY:
|
|
|
|
|
2015-06-02 05:06:40 +00:00
|
|
|
# print some useful help stuff
|
|
|
|
help:
|
|
|
|
@echo "esp-open-rtos make"
|
|
|
|
@echo ""
|
|
|
|
@echo "Other targets:"
|
|
|
|
@echo ""
|
|
|
|
@echo "all"
|
|
|
|
@echo "Default target. Will build firmware including any changed source files."
|
|
|
|
@echo
|
|
|
|
@echo "clean"
|
|
|
|
@echo "Delete all build output."
|
|
|
|
@echo ""
|
|
|
|
@echo "rebuild"
|
|
|
|
@echo "Build everything fresh from scratch."
|
|
|
|
@echo ""
|
|
|
|
@echo "flash"
|
|
|
|
@echo "Build then upload firmware to MCU. Set ESPPORT & ESPBAUD to override port/baud rate."
|
|
|
|
@echo ""
|
|
|
|
@echo "test"
|
|
|
|
@echo "'flash', then start a GNU Screen session on the same serial port to see serial output."
|
|
|
|
@echo ""
|
|
|
|
@echo "size"
|
|
|
|
@echo "Build, then print a summary of built firmware size."
|
|
|
|
@echo ""
|
|
|
|
@echo "TIPS:"
|
|
|
|
@echo "* You can use -jN for parallel builds. Much faster! Use 'make rebuild' instead of 'make clean all' for parallel builds."
|
|
|
|
@echo "* You can create a local.mk file to create local overrides of variables like ESPPORT & ESPBAUD."
|
|
|
|
@echo ""
|
|
|
|
@echo "SAMPLE COMMAND LINE:"
|
|
|
|
@echo "make -j2 test ESPPORT=/dev/ttyUSB0"
|
|
|
|
@echo ""
|
|
|
|
|
|
|
|
|