sonic-buildimage/Makefile.work
Kalimuthu-Velappan 7d2ebf8116
[build]: support for DPKG local caching (#4117)
DPKG caching framework provides the infrastructure to cache the sonic module/target .deb files into a local cache by tracking the target dependency files.SONIC build infrastructure is designed as a plugin framework where any new source code can be easily integrated into sonic as a module and that generates output as a .deb file. The source code compilation of a module is completely independent of other modules compilation. Inter module dependency is resolved through build artifacts like header files, libraries, and binaries in the form of Debian packages. For example module A depends on module B. While module A is being built, it uses B's .deb file to install it in the build docker.

The DPKG caching framework provides an infrastructure that caches a module's deb package and restores it back to the build directory if its dependency files are not modified. When a module is compiled for the first time, the generated deb package is stored at the DPKG cache location. On the subsequent build, first, it checks the module dependency file modification. If none of the dependent files is changed, it copies the deb package from the cache location, otherwise, it goes for local compilation and generates the deb package. The modified files should be checked-in to get the newer cache deb package.

This provides a huge improvement in build time and also supports the true incremental build by tracking the dependency files.

- How I did it
It takes two global arguments to enable the DPKG caching, the first one indicates the caching method and the second one describes the location of the cache.
SONIC_DPKG_CACHE_METHOD=cache
SONIC_DPKG_CACHE_SOURCE=

    where  SONIC_DPKG_CACHE_METHOD - Default method is 'cache' for deb package caching
                            none:     no caching
                            cache:    cache from local directory
Dependency file tracking:
Dependency files are tracked for each target in two levels.
1. Common make infrastructure files - rules/config, rules/functions, slave.mk etc.
2. Per module files - files which are specific to modules, Makefile, debian/rules, patch files, etc.

    For example: dependency files for Linux Kernel - src/sonic-linux-kernel,

            SPATH       := $($(LINUX_HEADERS_COMMON)_SRC_PATH)
            DEP_FILES   := $(SONIC_COMMON_FILES_LIST) rules/linux-kernel.mk rules/linux-kernel.dep
            DEP_FILES   += $(SONIC_COMMON_BASE_FILES_LIST)
            SMDEP_FILES := $(addprefix $(SPATH)/,$(shell cd $(SPATH) && git ls-files))

            DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST) \
                         $(KERNEL_PROCURE_METHOD) $(KERNEL_CACHE_PATH)

            $(LINUX_HEADERS_COMMON)_CACHE_MODE  := GIT_CONTENT_SHA
            $(LINUX_HEADERS_COMMON)_DEP_FLAGS   := $(DEP_FLAGS)
            $(LINUX_HEADERS_COMMON)_DEP_FILES   := $(DEP_FILES)
            $(LINUX_HEADERS_COMMON)_SMDEP_FILES := $(SMDEP_FILES)
            $(LINUX_HEADERS_COMMON)_SMDEP_PATHS := $(SPATH)
Cache file tracking:
The Cache file is a compressed TAR ball of a module's target DEB file and its derived-target DEB files.
The cache filename is formed with the following format

    FORMAT:
            <module deb filename>.<24 byte of DEP SHA hash >-<24 byte of MOD SHA hash>.tgz
            Eg:
              linux-headers-4.9.0-9-2-common_4.9.168-1+deb9u3_all.deb-23658712fd21bb776fa16f47-c0b63ef593d4a32643bca228.tgz

            < 24-byte DEP SHA value > - the SHA value is derived from all the dependent packages.
            < 24-byte MOD SHA value > - the SHA value is derived from either of the following.
                    GIT_COMMIT_SHA  - SHA value of the last git commit ID if it is a submodule
                    GIT_CONTENT_SHA - SHA value is generated from the content of the target dependency files.
Target Specific rules:
Caching can be enabled/disabled on a global level and also on the per-target level.

            $(addprefix $(DEBS_PATH)/, $(SONIC_DPKG_DEBS)) : $(DEBS_PATH)/% : .platform $$(addsuffix -install,$$(addprefix $(DEBS_PATH)/,$$($$*_DEPENDS))) \
                    $(call dpkg_depend,$(DEBS_PATH)/%.dep )
            $(HEADER)


            # Load the target deb from DPKG cache
            $(call LOAD_CACHE,$*,$@)


            # Skip building the target if it is already loaded from cache
            if [ -z '$($*_CACHE_LOADED)' ] ; then

                  .....
                 # Rules for Generating the target DEB file.
                  .....

                  # Save the target deb into DPKG cache
                  $(call SAVE_CACHE,$*,$@)
            fi


            $(FOOTER)


    The make rule-'$(call dpkg_depend,$(DEBS_PATH)/%.dep )' checks for target dependency file modification. If it is newer than the target, it will go for re-generation of that target.

    Two main macros 'LOAD_CACHE' and 'SAVE_CACHE' are used for loading and storing the cache contents.
    The 'LOAD_CACHE' macro is used to load the cache file from cache storage and extracts them into the target folder. It is done only if target dependency files are not modified by checking the GIT file status, otherwise, cache loading is skipped and full compilation is performed.
    It also updates the target-specific variable to indicate the cache is loaded or not.
    The 'SAVE_CACHE' macro generates the compressed tarball of the cache file and saves them into cache storage. Saving into the cache storage is protected with a lock.
- How to verify it

    The caching functionality is verified by enabling it in Linux kernel submodule.
    It uses the cache directory as 'target/cache' where Linux cache file gets stored on the first-time build and it is picked from the cache location during the subsequent clean build.
- Description for the changelog
The DPKG caching framework provides the infrastructure to save the module-specific deb file to be cached by tracking the module's dependency files.
If the module's dependency files are not changed, it restores the module deb files from the cache storage.

- Description for the changelog

- A picture of a cute animal (not mandatory but encouraged)

DOCUMENT PR:

           https://github.com/Azure/SONiC/pull/559
2020-03-11 20:04:52 -07:00

270 lines
13 KiB
Makefile

###############################################################################
## Wrapper for starting make inside sonic-slave container
#
# Supported parameters:
#
# * PLATFORM: Specific platform we wish to build images for.
# * BUILD_NUMBER: Desired version-number to pass to the building-system.
# * ENABLE_DHCP_GRAPH_SERVICE: Enables get-graph service to fetch minigraph files
# through http.
# * ENABLE_ZTP: Enables zero touch provisioning.
# * SHUTDOWN_BGP_ON_START: Sets admin-down state for all bgp peerings after restart.
# * ENABLE_PFCWD_ON_START: Enable PFC Watchdog (PFCWD) on server-facing ports
# * by default for TOR switch.
# * ENABLE_SYNCD_RPC: Enables rpc-based syncd builds.
# * INSTALL_DEBUG_TOOLS: Install debug tools and debug symbol packeages.
# * USERNAME: Desired username -- default at rules/config
# * PASSWORD: Desired password -- default at rules/config
# * KEEP_SLAVE_ON: Keeps slave container up and active after building process concludes.
# * Note that rm=true is still set, so once user quits from the docker
# * session, the docker will be removed.
# * Please note that with current Stretch build structure,
# * user of KEEP_SLAVE_ON feature will have to be conscious
# * about which docker to stay inside after build is done.
# * - If user desires to stay inside Jessie docker, please issue
# * make KEEP_SLAVE_ON=yes jessie
# * - If user desires to stay inside Stretch docker, please issue
# * make NOJESSIE=1 KEEP_SLAVE_ON=yes <any target>
# * SOURCE_FOLDER: host path to be mount as /var/$(USER)/src, only effective when KEEP_SLAVE_ON=yes
# * SONIC_BUILD_JOBS: Specifying number of concurrent build job(s) to run
# * VS_PREPARE_MEM: Prepare memory in VS build (drop cache and compact).
# * Default: yes
# * Values: yes, no
# * KERNEL_PROCURE_METHOD: Specifying method of obtaining kernel Debian package: download or build
# * SONIC_DPKG_CACHE_METHOD: Specifying method of obtaining the Debian packages from cache: none or cache
# * SONIC_DPKG_CACHE_SOURCE: Debian package cache location when cache enabled for debian packages
#
###############################################################################
SHELL = /bin/bash
USER := $(shell id -un)
PWD := $(shell pwd)
ifeq ($(USER), root)
$(error Add your user account to docker group and use your user account to make. root or sudo are not supported!)
endif
# Check for j2cli availability
J2_VER := $(shell j2 --version 2>&1 | grep j2cli | awk '{printf $$2}')
ifeq ($(J2_VER),)
$(error Install j2cli)
endif
# Check for minimum Docker version on build host
# Note: Using the greater of CE (17.05.0) and EE (17.06.1) versions that support ARG before FROM
docker_min := 17.06.1
docker_min_ver := $(shell echo "$(docker_min)" | awk -F. '{printf("%d%03d%03d\n",$$1,$$2,$$3);}' 2>/dev/null)
docker_ver := $(shell docker info 2>/dev/null | grep -i "server version" | rev | cut -d' ' -f1 | rev | awk -F. '{printf("%d%03d%03d\n",$$1,$$2,$$3);}' 2>/dev/null)
docker_is_valid := $(shell if [[ "$(docker_ver)" -lt $(docker_min_ver) ]] ; then echo "0"; else echo "1"; fi)
ifeq (0,$(docker_is_valid))
$(error SONiC requires Docker version $(docker_min) or later)
endif
# Remove lock file in case previous run was forcefully stopped
$(shell rm -f .screen)
MAKEFLAGS += -B
CONFIGURED_ARCH := $(shell [ -f .arch ] && cat .arch || echo $(PLATFORM_ARCH))
ifeq ($(CONFIGURED_ARCH),)
override CONFIGURED_ARCH = amd64
endif
ifeq ($(PLATFORM_ARCH),)
override PLATFORM_ARCH = $(CONFIGURED_ARCH)
endif
ifeq ($(BLDENV), stretch)
SLAVE_DIR = sonic-slave-stretch
else
SLAVE_DIR = sonic-slave-jessie
endif
SLAVE_BASE_TAG = $(shell CONFIGURED_ARCH=$(CONFIGURED_ARCH) j2 $(SLAVE_DIR)/Dockerfile.j2 > $(SLAVE_DIR)/Dockerfile && sha1sum $(SLAVE_DIR)/Dockerfile | awk '{print substr($$1,0,11);}')
SLAVE_TAG = $(shell cat $(SLAVE_DIR)/Dockerfile.user $(SLAVE_DIR)/Dockerfile | sha1sum | awk '{print substr($$1,0,11);}')
SLAVE_BASE_IMAGE = $(SLAVE_DIR)
SLAVE_IMAGE = $(SLAVE_BASE_IMAGE)-$(USER)
OVERLAY_MODULE_CHECK := \
lsmod | grep -q "^overlay " &>/dev/null || \
zgrep -q 'CONFIG_OVERLAY_FS=y' /proc/config.gz &>/dev/null || \
grep -q 'CONFIG_OVERLAY_FS=y' /boot/config-$(shell uname -r) &>/dev/null || \
(echo "ERROR: Module 'overlay' not loaded. Try running 'sudo modprobe overlay'."; exit 1)
BUILD_TIMESTAMP := $(shell date +%Y%m%d\.%H%M%S)
ifeq ($(DOCKER_BUILDER_MOUNT),)
override DOCKER_BUILDER_MOUNT := "$(PWD):/sonic"
endif
ifeq ($(DOCKER_BUILDER_WORKDIR),)
override DOCKER_BUILDER_WORKDIR := "/sonic"
endif
DOCKER_RUN := docker run --rm=true --privileged \
-v $(DOCKER_BUILDER_MOUNT) \
-w $(DOCKER_BUILDER_WORKDIR) \
-e "http_proxy=$(http_proxy)" \
-e "https_proxy=$(https_proxy)" \
-i$(if $(TERM),t,)
include rules/config
ifneq ($(SONIC_DPKG_CACHE_SOURCE),)
DOCKER_RUN += -v "$(SONIC_DPKG_CACHE_SOURCE):/dpkg_cache:rw"
endif
ifeq ($(SONIC_CONFIG_USE_NATIVE_DOCKERD_FOR_BUILD), y)
DOCKER_RUN += -v /var/run/docker.sock:/var/run/docker.sock
endif
ifneq (,$(filter $(CONFIGURED_ARCH), armhf arm64))
# Multiarch docker cannot start dockerd service due to iptables cannot run over different arch kernel
SONIC_SERVICE_DOCKERD_FOR_MULTIARCH=y
SONIC_NATIVE_DOCKERD_FOR_MUTLIARCH := dockerd --experimental=true --storage-driver=vfs \
--data-root=/var/lib/march/docker/ --exec-root=/var/run/march/docker/ \
-H unix:///var/run/march/docker.sock -p /var/run/march/docker.pid
DOCKER_RUN += -v /var/run/march/docker.sock:/var/run/docker.sock
DOCKER_RUN += -v /var/run/march/docker.pid:/var/run/docker.pid
DOCKER_RUN += -v /var/run/march/docker:/var/run/docker
DOCKER_RUN += -v /var/lib/march/docker:/var/lib/docker
SONIC_USERFACL_DOCKERD_FOR_MUTLIARCH := setfacl -m user:$(USER):rw /var/run/march/docker.sock
#Override Native config to prevent docker service
SONIC_CONFIG_USE_NATIVE_DOCKERD_FOR_BUILD=y
DOCKER_MULTIARCH_CHECK := docker inspect --type image multiarch/qemu-user-static:register &> /dev/null || (echo "multiarch docker not found ..."; docker run --rm --privileged multiarch/qemu-user-static:register --reset --credential yes)
DOCKER_SERVICE_SAFE_KILLER := (MARCH_PID=`ps -eo pid,cmd | grep "[0-9] dockerd.*march" | awk '{print $$1}'`; echo "Killing march docker $$MARCH_PID"; [ -z "$$MARCH_PID" ] || sudo kill -9 "$$MARCH_PID";)
DOCKER_SERVICE_MULTIARCH_CHECK := ($(DOCKER_SERVICE_SAFE_KILLER); sudo rm -fr /var/run/march/; (echo "Starting docker march service..."; sudo $(SONIC_NATIVE_DOCKERD_FOR_MUTLIARCH) &) &>/dev/null ; sleep 2; sudo $(SONIC_USERFACL_DOCKERD_FOR_MUTLIARCH);)
# Docker service to load the compiled dockers-*.gz
# docker 19.0 version above has path/length restriction, so replaced it with soft link in /tmp/
# Also dockerd does mkdir on the provided softlink, so used two level path "d/d"
D_ROOT=/tmp/d/d
SONIC_NATIVE_DOCKERD_FOR_DOCKERFS := rm -fr $(PWD)/dockerfs; mkdir -p $(PWD)/dockerfs; sudo rm -fr /tmp/d; mkdir -p /tmp/d; ln -s -f $(PWD)/dockerfs $(D_ROOT); \
sudo dockerd --storage-driver=overlay2 --iptables=false \
--data-root $(D_ROOT)/var/lib/docker/ --exec-root=$(D_ROOT)/var/run/docker/ \
-H unix://$(D_ROOT)/var/run/docker.sock -p $(D_ROOT)/var/run/docker.pid &
SONIC_USERFACL_DOCKERD_FOR_DOCKERFS := setfacl -m user:$(USER):rw $(D_ROOT)/var/run/docker.sock
DOCKER_SERVICE_DOCKERFS_CHECK := (sudo docker -H unix://$(D_ROOT)/var/run/docker.sock info &> /dev/null && sudo kill -9 `sudo cat $(D_ROOT)/var/run/docker.pid` && false) || (echo "Starting docker build service..."; (sudo $(SONIC_NATIVE_DOCKERD_FOR_DOCKERFS) ) &> /tmp/dockerfs.log ; sleep 1; sudo $(SONIC_USERFACL_DOCKERD_FOR_DOCKERFS);)
endif
DOCKER_BASE_BUILD = docker build --no-cache \
-t $(SLAVE_BASE_IMAGE):$(SLAVE_BASE_TAG) \
--build-arg http_proxy=$(http_proxy) \
--build-arg https_proxy=$(https_proxy) \
$(SLAVE_DIR)
DOCKER_BUILD = docker build --no-cache \
--build-arg user=$(USER) \
--build-arg uid=$(shell id -u) \
--build-arg guid=$(shell id -g) \
--build-arg hostname=$(shell echo $$HOSTNAME) \
--build-arg slave_base_tag_ref=$(SLAVE_BASE_TAG) \
-t $(SLAVE_IMAGE):$(SLAVE_TAG) \
-f $(SLAVE_DIR)/Dockerfile.user \
$(SLAVE_DIR)
SONIC_BUILD_INSTRUCTION := make \
-f slave.mk \
BLDENV=$(BLDENV) \
PLATFORM=$(PLATFORM) \
PLATFORM_ARCH=$(PLATFORM_ARCH) \
BUILD_NUMBER=$(BUILD_NUMBER) \
BUILD_TIMESTAMP=$(BUILD_TIMESTAMP) \
ENABLE_DHCP_GRAPH_SERVICE=$(ENABLE_DHCP_GRAPH_SERVICE) \
ENABLE_ZTP=$(ENABLE_ZTP) \
SHUTDOWN_BGP_ON_START=$(SHUTDOWN_BGP_ON_START) \
SONIC_ENABLE_PFCWD_ON_START=$(ENABLE_PFCWD_ON_START) \
SONIC_ENABLE_SYNCD_RPC=$(ENABLE_SYNCD_RPC) \
SONIC_INSTALL_DEBUG_TOOLS=$(INSTALL_DEBUG_TOOLS) \
MDEBUG=$(MDEBUG) \
PASSWORD=$(PASSWORD) \
USERNAME=$(USERNAME) \
SONIC_BUILD_JOBS=$(SONIC_BUILD_JOBS) \
SONIC_USE_DOCKER_BUILDKIT=$(SONIC_USE_DOCKER_BUILDKIT) \
VS_PREPARE_MEM=$(VS_PREPARE_MEM) \
KERNEL_PROCURE_METHOD=$(KERNEL_PROCURE_METHOD) \
SONIC_DPKG_CACHE_METHOD=$(SONIC_DPKG_CACHE_METHOD) \
SONIC_DPKG_CACHE_SOURCE=$(SONIC_DPKG_CACHE_SOURCE) \
HTTP_PROXY=$(http_proxy) \
HTTPS_PROXY=$(https_proxy) \
SONIC_ENABLE_SYSTEM_TELEMETRY=$(ENABLE_SYSTEM_TELEMETRY) \
SONIC_ENABLE_RESTAPI=$(ENABLE_RESTAPI) \
EXTRA_JESSIE_TARGETS=$(EXTRA_JESSIE_TARGETS) \
$(SONIC_OVERRIDE_BUILD_VARS)
.PHONY: sonic-slave-build sonic-slave-bash init reset
.DEFAULT_GOAL := all
%::
ifneq (,$(filter $(CONFIGURED_ARCH), armhf arm64))
@$(DOCKER_MULTIARCH_CHECK)
ifneq ($(BLDENV), )
@$(DOCKER_SERVICE_MULTIARCH_CHECK)
@$(DOCKER_SERVICE_DOCKERFS_CHECK)
endif
endif
@$(OVERLAY_MODULE_CHECK)
@docker inspect --type image $(SLAVE_BASE_IMAGE):$(SLAVE_BASE_TAG) &> /dev/null || \
{ echo Image $(SLAVE_BASE_IMAGE):$(SLAVE_BASE_TAG) not found. Building... ; \
$(DOCKER_BASE_BUILD) ; }
@docker inspect --type image $(SLAVE_IMAGE):$(SLAVE_TAG) &> /dev/null || \
{ echo Image $(SLAVE_IMAGE):$(SLAVE_TAG) not found. Building... ; \
$(DOCKER_BUILD) ; }
ifeq "$(KEEP_SLAVE_ON)" "yes"
ifdef SOURCE_FOLDER
@$(DOCKER_RUN) -v $(SOURCE_FOLDER):/var/$(USER)/src $(SLAVE_IMAGE):$(SLAVE_TAG) bash -c "$(SONIC_BUILD_INSTRUCTION) $@; /bin/bash"
else
@$(DOCKER_RUN) $(SLAVE_IMAGE):$(SLAVE_TAG) bash -c "$(SONIC_BUILD_INSTRUCTION) $@; /bin/bash"
endif
else
@$(DOCKER_RUN) $(SLAVE_IMAGE):$(SLAVE_TAG) $(SONIC_BUILD_INSTRUCTION) $@
endif
sonic-slave-build :
$(DOCKER_BASE_BUILD)
$(DOCKER_BUILD)
sonic-slave-bash :
@$(OVERLAY_MODULE_CHECK)
@docker inspect --type image $(SLAVE_BASE_IMAGE):$(SLAVE_BASE_TAG) &> /dev/null || \
{ echo Image $(SLAVE_BASE_IMAGE):$(SLAVE_BASE_TAG) not found. Building... ; \
$(DOCKER_BASE_BUILD) ; }
@docker inspect --type image $(SLAVE_IMAGE):$(SLAVE_TAG) &> /dev/null || \
{ echo Image $(SLAVE_IMAGE):$(SLAVE_TAG) not found. Building... ; \
$(DOCKER_BUILD) ; }
@$(DOCKER_RUN) -t $(SLAVE_IMAGE):$(SLAVE_TAG) bash
showtag:
@echo $(SLAVE_IMAGE):$(SLAVE_TAG)
@echo $(SLAVE_BASE_IMAGE):$(SLAVE_BASE_TAG)
init :
@git submodule update --init --recursive
@git submodule foreach --recursive '[ -f .git ] && echo "gitdir: $$(realpath --relative-to=. $$(cut -d" " -f2 .git))" > .git'
.ONESHELL : reset
reset :
@echo && echo -n "Warning! All local changes will be lost. Proceed? [y/N]: "
@read ans && (
if [ $$ans == y ]; then
echo "Resetting local repository. Please wait...";
$(DOCKER_RUN) $(SLAVE_IMAGE):$(SLAVE_TAG) sudo rm -rf fsroot;
if [[ "$(CONFIGURED_ARCH)" == "armhf" || "$(CONFIGURED_ARCH)" == "arm64" ]]; then
echo "Stopping march $(CONFIGURED_ARCH) docker"
sudo kill -9 `sudo cat /var/run/march/docker.pid` || true
sudo rm -f /var/run/march/docker.pid || true
fi
git clean -xfdf;
git reset --hard;
git submodule foreach --recursive git clean -xfdf;
git submodule foreach --recursive git reset --hard;
git submodule update --init --recursive;
echo "Reset complete!";
else
echo "Reset aborted";
fi )