Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] wheel.mk: Migrate to using status cookie #6389

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions mk/spksrc.common.mk
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ PIP ?= pip
# System default pip outside from build environment
PIP_SYSTEM = $(shell which pip)

# System default pip outside from build environment
PIP_NATIVE = $(WORK_DIR)/../../../native/$(or $(PYTHON_PACKAGE),$(SPK_NAME))/work-native/install/usr/local/bin/pip

# Why ask for the same thing twice? Always cache downloads
PIP_CACHE_OPT ?= --find-links $(PIP_DISTRIB_DIR) --cache-dir $(PIP_CACHE_DIR)
PIP_WHEEL_ARGS = wheel --disable-pip-version-check --no-binary :all: $(PIP_CACHE_OPT) --no-deps --wheel-dir $(WHEELHOUSE)
Expand Down
2 changes: 1 addition & 1 deletion mk/spksrc.crossenv.mk
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ export PYTHONPATH = $(PYTHON_LIB_NATIVE):$(PYTHON_STAGING_INSTALL_PREFIX)/lib/py
# >>> sys.path
#
build_crossenv_target: SHELL:=/bin/bash
build_crossenv_target: $(CROSSENV_PATH)/build/python-cc.mk
build_crossenv_target: pre_crossenv_target $(CROSSENV_PATH)/build/python-cc.mk
@$(MSG) $$(date +%Y%m%d-%H%M%S) MAKELEVEL: $(MAKELEVEL), PARALLEL_MAKE: $(PARALLEL_MAKE), ARCH: $(ARCH)-$(TCVERSION), CROSSENV: $(WHEEL) >> $(PSTAT_LOG)
@$(MSG) Python sources: $(wildcard $(PYTHON_WORK_DIR)/Python-[0-9]*)
@$(MSG) crossenv wheel packages: $(CROSSENV_DEFAULT_PIP), $(CROSSENV_DEFAULT_SETUPTOOLS), $(CROSSENV_DEFAULT_WHEEL)
Expand Down
12 changes: 8 additions & 4 deletions mk/spksrc.python-wheel.mk
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,6 @@ ifneq ($(wildcard $(PYTHON_STAGING_PREFIX)),)
STAGING_INSTALL_PREFIX := $(PYTHON_STAGING_PREFIX)
endif

## python wheel specific configurations
include ../../mk/spksrc.wheel-env.mk

### Prepare crossenv
build_crossenv_module:
@$(MSG) WHEEL="$(PKG_NAME)-$(PKG_VERS)" $(MAKE) crossenv-$(ARCH)-$(TCVERSION)
Expand All @@ -43,7 +40,8 @@ build_python_wheel_target: build_crossenv_module
@. $(CROSSENV) ; \
$(MSG) _PYTHON_HOST_PLATFORM=$(TC_TARGET) cross-python3 -m build $(BUILD_ARGS) --wheel $(WHEELS_BUILD_ARGS) --outdir $(WHEELHOUSE) ; \
$(RUN) _PYTHON_HOST_PLATFORM=$(TC_TARGET) cross-python3 -m build $(BUILD_ARGS) --wheel $(WHEELS_BUILD_ARGS) --outdir $(WHEELHOUSE)
@$(RUN) echo "$(PKG_NAME)==$(PKG_VERS)" >> $(WHEELHOUSE)/$(WHEELS_CROSS_COMPILE)
@$(MSG) $(MAKE) REQUIREMENT=\"$(PKG_NAME)==$(PKG_VERS)\" WHEEL_NAME=\"$(PKG_NAME)\" WHEEL_VERSION=\"$(PKG_VERS)\" WHEEL_TYPE=\"cross\" wheel_install
-@MAKEFLAGS= $(MAKE) REQUIREMENT="$(PKG_NAME)==$(PKG_VERS)" WHEEL_NAME="$(PKG_NAME)" WHEEL_VERSION="$(PKG_VERS)" WHEEL_TYPE="cross" --no-print-directory wheel_install

post_install_python_wheel_target: $(WHEEL_TARGET) install_python_wheel

Expand All @@ -53,3 +51,9 @@ all: install

# Use crossenv
include ../../mk/spksrc.crossenv.mk

## python wheel specific configurations
include ../../mk/spksrc.wheel-env.mk

## install wheel specific routines
include ../../mk/spksrc.wheel-install.mk
10 changes: 8 additions & 2 deletions mk/spksrc.spk.mk
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,7 @@ spkclean:

wheelclean: spkclean
rm -fr work-*/.wheel_done \
work-*/.wheel_*_done \
work-*/wheelhouse \
work-*/install/var/packages/**/target/share/wheelhouse
@make --no-print-directory dependency-flat | sort -u | grep cross/ | while read depend ; do \
Expand All @@ -552,10 +553,15 @@ wheelclean: spkclean
fi ; \
done

wheelcleancache: wheelclean
wheelclean-%: spkclean
rm -f work-*/.wheel_done \
work-*/wheelhouse/$*-*.whl
find work-* -type f -regex '.*\.wheel_\(download\|compile\|install\)-$*_done' -exec rm -f {} \;

wheelcleancache:
rm -fr work-*/pip

wheelcleanall: wheelcleancache
wheelcleanall: wheelcleancache wheelclean
rm -fr ../../distrib/pip

crossenvclean: wheelclean
Expand Down
148 changes: 148 additions & 0 deletions mk/spksrc.wheel-compile.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
### Wheel rules
# Compile wheels for modules listed in WHEELS.
#
# Targets are executed in the following order:
# wheel_compile_msg_target
# pre_wheel_compile_target (override with PRE_WHEEL_COMPILE_TARGET)
# wheel_compile_target (override with WHEEL_COMPILE_TARGET)
# post_wheel_compile_target (override with POST_WHEEL_COMPILE_TARGET)
# Variables:
# REQUIREMENT Requirement formatted wheel information
# WHEEL_NAME Name of wheel to process
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how to document, that WHEEL_NAME must not contain -?
Since name and version are separated by - the name must not contain -.

can we add a check here
or fail if name has - instead of _
or implement an auto renaming?

or can we still use = as version separator? (can't remeber which make file is affected).

# WHEEL_VERSION Version of wheel to process (can be empty)
# WHEEL_TYPE Type of wheel to process (abi3, crossenv, pure)

ifeq ($(WHEEL_VERSION),)
WHEEL_COMPILE_COOKIE = $(WORK_DIR)/.$(COOKIE_PREFIX)wheel_compile-$(WHEEL_NAME)_done
else
WHEEL_COMPILE_COOKIE = $(WORK_DIR)/.$(COOKIE_PREFIX)wheel_compile-$(WHEEL_NAME)-$(WHEEL_VERSION)_done
endif

##

ifeq ($(strip $(PRE_WHEEL_COMPILE_TARGET)),)
PRE_WHEEL_COMPILE_TARGET = pre_wheel_compile_target
else
$(PRE_WHEEL_COMPILE_TARGET): wheel_compile_msg_target
endif
ifeq ($(strip $(WHEEL_COMPILE_TARGET)),)
WHEEL_COMPILE_TARGET = wheel_compile_target
else
$(WHEEL_COMPILE_TARGET): $(BUILD_WHEEL_COMPILE_TARGET)
endif
ifeq ($(strip $(POST_WHEEL_COMPILE_TARGET)),)
POST_WHEEL_COMPILE_TARGET = post_wheel_compile_target
else
$(POST_WHEEL_COMPILE_TARGET): $(WHEEL_COMPILE_TARGET)
endif

wheel_compile_msg_target:
@$(MSG) "Processing wheels of $(NAME)"

pre_wheel_compile_target: wheel_compile_msg_target

wheel_compile_target: SHELL:=/bin/bash
wheel_compile_target:
ifeq ($(wildcard $(WHEELHOUSE)),)
@$(MSG) Creating wheelhouse directory: $(WHEELHOUSE)
@mkdir -p $(WHEELHOUSE)
endif
@$(MSG) Compiling wheel [$(WHEEL_NAME)], version [$(WHEEL_VERSION)], type [$(WHEEL_TYPE)]
ifneq ($(WHEEL_TYPE),pure)
@$(MSG) $(MAKE) WHEEL=\"$(WHEEL_NAME)-$(WHEEL_VERSION)\" crossenv-$(ARCH)-$(TCVERSION) ; \
MAKEFLAGS= $(MAKE) WHEEL="$(WHEEL_NAME)-$(WHEEL_VERSION)" crossenv-$(ARCH)-$(TCVERSION) --no-print-directory ; \
[ "$(WHEEL_TYPE)" = "$(WHEELS_LIMITED_API)" ] && abi3="--build-option=--py-limited-api=$(PYTHON_LIMITED_API)" || abi3="" ; \
global_options=$$(echo $(WHEELS_BUILD_ARGS) | sed -e 's/ \[/\n\[/g' | grep -i $(WHEEL_NAME) | cut -f2 -d] | xargs) ; \
localCFLAGS=($$(echo $(WHEELS_CFLAGS) | sed -e 's/ \[/\n\[/g' | grep -i $(WHEEL_NAME) | cut -f2 -d] | xargs)) ; \
localLDFLAGS=($$(echo $(WHEELS_LDFLAGS) | sed -e 's/ \[/\n\[/g' | grep -i $(WHEEL_NAME) | cut -f2 -d] | xargs)) ; \
localCPPFLAGS=($$(echo $(WHEELS_CPPFLAGS) | sed -e 's/ \[/\n\[/g' | grep -i $(WHEEL_NAME) | cut -f2 -d] | xargs)) ; \
localCXXFLAGS=($$(echo $(WHEELS_CXXFLAGS) | sed -e 's/ \[/\n\[/g' | grep -i $(WHEEL_NAME) | cut -f2 -d] | xargs)) ; \
$(MSG) pip build [$(WHEEL_NAME)], version: [$(WHEEL_VERSION)] \
$$([ "$$(echo $${localCFLAGS[@]})" ] && echo "CFLAGS=\"$${localCFLAGS[@]}\" ") \
$$([ "$$(echo $${localCPPFLAGS[@]})" ] && echo "CPPFLAGS=\"$${localCPPFLAGS[@]}\" ") \
$$([ "$$(echo $${localCXXFLAGS[@]})" ] && echo "CXXFLAGS=\"$${localCXXFLAGS[@]}\" ") \
$$([ "$$(echo $${localLDFLAGS[@]})" ] && echo "LDFLAGS=\"$${localLDFLAGS[@]}\" ") \
$$([ "$$(echo $${abi3})" ] && echo "$${abi3} ")" \
$${global_options}" ; \
REQUIREMENT=$(REQUIREMENT) \
WHEEL_NAME=$(WHEEL_NAME) \
WHEEL_VERSION=$(WHEEL_VERSION) \
ADDITIONAL_CFLAGS="-I$(STAGING_INSTALL_PREFIX)/$(PYTHON_INC_DIR) $${localCFLAGS[@]}" \
ADDITIONAL_CPPFLAGS="-I$(STAGING_INSTALL_PREFIX)/$(PYTHON_INC_DIR) $${localCPPFLAGS[@]}" \
ADDITIONAL_CXXFLAGS="-I$(STAGING_INSTALL_PREFIX)/$(PYTHON_INC_DIR) $${localCXXFLAGS[@]}" \
ADDITIONAL_LDFLAGS="$${localLDFLAGS[@]}" \
ABI3="$${abi3}" \
PIP_GLOBAL_OPTION="$${global_options}" \
$(MAKE) --no-print-directory \
cross-compile-wheel-$(WHEEL_NAME) || exit 1
else ifneq ($(filter 1 ON TRUE,$(WHEELS_PURE_PYTHON_PACKAGING_ENABLE)),)
@if [ -s "$(WHEELHOUSE)/$(WHEELS_PURE_PYTHON)" ]; then \
export LD= LDSHARED= CPP= NM= CC= AS= RANLIB= CXX= AR= STRIP= OBJDUMP= OBJCOPY= READELF= CFLAGS= CPPFLAGS= CXXFLAGS= LDFLAGS= && \
$(RUN) \
PATH="$(abspath $(WORK_DIR)/../../../native/$(PYTHON_PKG_NAME)/work-native/install/usr/local/bin):$(PATH)" \
LD_LIBRARY_PATH="$(abspath $(WORK_DIR)/../../../native/$(PYTHON_PKG_NAME)/work-native/install/usr/local/lib):$(LD_LIBRARY_PATH)" \
$(MSG) $(PIP_NATIVE) $(PIP_WHEEL_ARGS) $(REQUIREMENT) ; \
$(PIP_NATIVE) $(PIP_WHEEL_ARGS) $(REQUIREMENT) ; \
fi
endif

##
## crossenv PATH environment requires a combination of:
## 1) unique PATH variable from $(ENV) -> using merge + dedup macros
## Note: Multiple declarations of ENV += PATH=bla creates confusion in its interpretation.
## Solution implemented fetches all PATH from ENV and combine them in reversed order.
## 2) access to maturin from native/python<version>/.../bin -> ${PYTHON_NATIVE_PATH}/bin
## 3) access to crossenv/bin/cross* tools, mainly cross-pip -> ${CROSSENV_PATH}/bin
##
cross-compile-wheel-%: SHELL:=/bin/bash
cross-compile-wheel-%:
@for crossenv in $(WORK_DIR)/crossenv-$(WHEEL_NAME)-$(WHEEL_VERSION) $(WORK_DIR)/crossenv-$(WHEEL_NAME) $(WORK_DIR)/crossenv ; do \
[ -d $${crossenv} ] && . $${crossenv}/build/python-cc.mk && break ; \
done ; \
if [ -d "$${CROSSENV_PATH}" ] ; then \
PATH=$(call dedup, $(call merge, $(ENV), PATH, :), :):$${PYTHON_NATIVE_PATH}:$${CROSSENV_PATH}/bin:$${PATH} ; \
$(MSG) "crossenv: [$${CROSSENV_PATH}]" ; \
$(MSG) "pip: [$$(which cross-pip)]" ; \
$(MSG) "maturin: [$$(which maturin)]" ; \
else \
echo "ERROR: crossenv not found!" ; \
exit 2 ; \
fi ; \
if [ "$(PIP_GLOBAL_OPTION)" ]; then \
pip_global_option=$$(echo $(PIP_GLOBAL_OPTION) | sed 's/=\([^ ]*\)/="\1"/g; s/[^ ]*/--global-option=&/g') ; \
pip_global_option=$${pip_global_option}" --no-use-pep517" ; \
fi ; \
$(RUN) $(MSG) \
_PYTHON_HOST_PLATFORM=\"$(TC_TARGET)\" \
PATH=$${PATH} \
CMAKE_TOOLCHAIN_FILE=$${CMAKE_TOOLCHAIN_FILE} \
MESON_CROSS_FILE=$${MESON_CROSS_FILE} \
cross-pip \
$(PIP_WHEEL_ARGS_CROSSENV) \
$${pip_global_option} \
--no-build-isolation \
$(ABI3) \
$(REQUIREMENT) ; \
$(RUN) \
_PYTHON_HOST_PLATFORM="$(TC_TARGET)" \
PATH=$${PATH} \
CMAKE_TOOLCHAIN_FILE=$${CMAKE_TOOLCHAIN_FILE} \
MESON_CROSS_FILE=$${MESON_CROSS_FILE} \
cross-pip \
$(PIP_WHEEL_ARGS_CROSSENV) \
$${pip_global_option} \
--no-build-isolation \
$(ABI3) \
$(REQUIREMENT)

post_wheel_compile_target: $(WHEEL_COMPILE_TARGET)

ifeq ($(wildcard $(WHEEL_COMPILE_COOKIE)),)
wheel_compile: $(WHEEL_COMPILE_COOKIE)

$(WHEEL_COMPILE_COOKIE): $(POST_WHEEL_COMPILE_TARGET)
$(create_target_dir)
@touch -f $@
else
wheel_compile: ;
endif
92 changes: 92 additions & 0 deletions mk/spksrc.wheel-download.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
### Wheel rules
# Download wheels for modules listed in WHEELS.
#
# Targets are executed in the following order:
# wheel_download_msg_target
# pre_wheel_download_target (override with PRE_WHEEL_DOWNLOAD_TARGET)
# wheel_download_target (override with WHEEL_DOWNLOAD_TARGET)
# post_wheel_download_target (override with POST_WHEEL_DOWNLOAD_TARGET)
# Variables:
# REQUIREMENT Requirement formatted wheel information
# WHEEL_NAME Name of wheel to process
# WHEEL_VERSION Version of wheel to process (can be empty)
# WHEEL_TYPE Type of wheel to process (abi3, crossenv, pure)

ifeq ($(WHEEL_VERSION),)
WHEEL_DOWNLOAD_COOKIE = $(WORK_DIR)/.$(COOKIE_PREFIX)wheel_download-$(WHEEL_NAME)_done
else
WHEEL_DOWNLOAD_COOKIE = $(WORK_DIR)/.$(COOKIE_PREFIX)wheel_download-$(WHEEL_NAME)-$(WHEEL_VERSION)_done
endif

##

ifeq ($(strip $(PRE_WHEEL_DOWNLOAD_TARGET)),)
PRE_WHEEL_DOWNLOAD_TARGET = pre_wheel_download_target
else
$(PRE_WHEEL_DOWNLOAD_TARGET): wheel_download_msg_target
endif
ifeq ($(strip $(WHEEL_DOWNLOAD_TARGET)),)
WHEEL_DOWNLOAD_TARGET = wheel_download_target
else
$(WHEEL_DOWNLOAD_TARGET): $(BUILD_WHEEL_DOWNLOAD_TARGET)
endif
ifeq ($(strip $(POST_WHEEL_DOWNLOAD_TARGET)),)
POST_WHEEL_DOWNLOAD_TARGET = post_wheel_download_target
else
$(POST_WHEEL_DOWNLOAD_TARGET): $(WHEEL_DOWNLOAD_TARGET)
endif

wheel_download_msg_target:
@$(MSG) "Processing wheels of $(NAME)"

pre_wheel_download_target: wheel_download_msg_target

wheel_download_target: SHELL:=/bin/bash
wheel_download_target:
ifeq ($(wildcard $(PIP_DISTRIB_DIR)),)
@$(MSG) Creating pip download directory: $(PIP_DISTRIB_DIR)
@mkdir -p $(PIP_DISTRIB_DIR)
endif
ifeq ($(wildcard $(PIP_CACHE_DIR)),)
@$(MSG) Creating pip caching directory: $(PIP_CACHE_DIR)
@mkdir -p $(PIP_CACHE_DIR)
endif
@$(MSG) Downloading wheel [$(WHEEL_NAME)], version [$(WHEEL_VERSION)] ; \
# $(MSG) requirement: [$(REQUIREMENT)] ; \
# $(MSG) requirement-grep-egg: [$$(grep -s egg <<< $(REQUIREMENT))] ; \
# $(MSG) name: [$(WHEEL_NAME)] ; \
# $(MSG) type: [$(WHEEL_TYPE)] ; \
# $(MSG) version: [$(WHEEL_VERSION)] ; \
# $(MSG) type: [$(WHEEL_TYPE)] ; \
Comment on lines +55 to +60
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this does not work, it might all be commented out (\ wraps all on a single line, and all following the first # is comment)

just a guess

if [ "$$(grep -s egg <<< $(REQUIREMENT))" ] ; then \
echo "WARNING: Skipping download URL - Downloaded at build time" ; \
elif [ "$(WHEEL_TYPE)" = "pure" ] && [ ! "$(WHEELS_PURE_PYTHON_PACKAGING_ENABLE)" = "1" ]; then \
echo "WARNING: Skipping download - pure python packaging disabled" ; \
else \
query="curl -s https://pypi.org/pypi/$(WHEEL_NAME)/json" ; \
query+=" | jq -r '.releases[][]" ; \
query+=" | select(.packagetype==\"sdist\")" ; \
query+=" | select((.filename|test(\"-$(WHEEL_VERSION).tar.gz\")) or (.filename|test(\"-$(WHEEL_VERSION).zip\"))) | .url'" ; \
outFile=$$(basename $$(eval $${query} 2>/dev/null) 2</dev/null) ; \
if [ "$${outFile}" = "" ]; then \
echo "ERROR: Invalid package name [$(WHEEL_NAME)]" ; \
elif [ -s $(PIP_DISTRIB_DIR)/$${outFile} ]; then \
echo "INFO: File already exists [$${outFile}]" ; \
else \
echo "wget --secure-protocol=TLSv1_2 -nv -O $(PIP_DISTRIB_DIR)/$${outFile}.part -nc $$(eval $${query})" ; \
wget --secure-protocol=TLSv1_2 -nv -O $(PIP_DISTRIB_DIR)/$${outFile}.part -nc $$(eval $${query}) ; \
mv $(PIP_DISTRIB_DIR)/$${outFile}.part $(PIP_DISTRIB_DIR)/$${outFile} ; \
fi ; \
fi

post_wheel_download_target: $(WHEEL_DOWNLOAD_TARGET)

ifeq ($(wildcard $(WHEEL_DOWNLOAD_COOKIE)),)
wheel_download: $(WHEEL_DOWNLOAD_COOKIE)

$(WHEEL_DOWNLOAD_COOKIE): $(POST_WHEEL_DOWNLOAD_TARGET)
$(create_target_dir)
@touch -f $@
else
wheel_download: ;
endif
29 changes: 1 addition & 28 deletions mk/spksrc.wheel-env.mk
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ ifeq ($(strip $(WHEEL_DEFAULT_PREFIX)),)
ifeq ($(strip $(ARCH)),)
WHEEL_DEFAULT_PREFIX = pure
else
WHEEL_DEFAULT_PREFIX = cross
WHEEL_DEFAULT_PREFIX = crossenv
endif
endif

Expand Down Expand Up @@ -89,30 +89,3 @@ endif
ifeq ($(findstring $(ARCH),$(i686_ARCHS)),$(ARCH))
PYTHON_ARCH = i686
endif

install_python_wheel:
@if [ -d "$(WHEELHOUSE)" ] ; then \
mkdir -p $(STAGING_INSTALL_WHEELHOUSE) ; \
cd $(WHEELHOUSE) ; \
if stat -t requirements*.txt >/dev/null 2>&1; then \
$(MSG) Copying $(WHEELS_DEFAULT) to wheelhouse ; \
cp requirements*.txt $(STAGING_INSTALL_WHEELHOUSE) ; \
cat requirements*.txt >> $(STAGING_INSTALL_WHEELHOUSE)/$(WHEELS_DEFAULT) ; \
sed -i -e '/^#/! s/^.*egg=//g' $(STAGING_INSTALL_WHEELHOUSE)/requirements*.txt ; \
sort -u -o $(STAGING_INSTALL_WHEELHOUSE)/$(WHEELS_DEFAULT) $(STAGING_INSTALL_WHEELHOUSE)/$(WHEELS_DEFAULT) ; \
else \
$(MSG) [SKIP] Copying $(WHEELS_DEFAULT) to wheelhouse ; \
fi ; \
if stat -t *.whl >/dev/null 2>&1; then \
for w in *.whl; do \
if echo $${w} | grep -iq "-none-any\.whl" ; then \
_new_name=$$(echo $$w | cut -d"-" -f -3)-none-any.whl ; \
else \
_new_name=$$(echo $$w | sed -E "s/(.*-).*(linux_).*(\.whl)/\1\2$(PYTHON_ARCH)\3/") ; \
fi ; \
$(MSG) Copying to wheelhouse: $$_new_name ; \
cp -f $$w $(STAGING_INSTALL_WHEELHOUSE)/$$_new_name ; \
done ; \
fi ; \
fi

Loading
Loading