ClickHouse/docker/packager/packager

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

500 lines
16 KiB
Plaintext
Raw Normal View History

2020-10-02 16:54:07 +00:00
#!/usr/bin/env python3
2022-01-21 12:59:45 +00:00
# -*- coding: utf-8 -*-
import subprocess
import os
import argparse
import logging
2018-10-05 10:40:58 +00:00
import sys
from pathlib import Path
2023-03-24 16:24:21 +00:00
from typing import List, Optional
SCRIPT_PATH = Path(__file__).absolute()
2022-04-22 11:37:08 +00:00
IMAGE_TYPE = "binary"
2023-02-28 19:18:50 +00:00
IMAGE_NAME = f"clickhouse/{IMAGE_TYPE}-builder"
class BuildException(Exception):
pass
def check_image_exists_locally(image_name: str) -> bool:
try:
2022-01-21 12:59:45 +00:00
output = subprocess.check_output(
2022-01-21 13:15:20 +00:00
f"docker images -q {image_name} 2> /dev/null", shell=True
2022-01-21 12:59:45 +00:00
)
return output != ""
2022-01-21 13:15:20 +00:00
except subprocess.CalledProcessError:
return False
2022-01-21 12:59:45 +00:00
def pull_image(image_name: str) -> bool:
try:
2022-01-21 13:15:20 +00:00
subprocess.check_call(f"docker pull {image_name}", shell=True)
return True
2022-01-21 13:15:20 +00:00
except subprocess.CalledProcessError:
2023-02-09 12:44:38 +00:00
logging.info("Cannot pull image %s", image_name)
return False
2022-01-21 12:59:45 +00:00
def build_image(image_name: str, filepath: Path) -> None:
context = filepath.parent
2022-01-21 13:15:20 +00:00
build_cmd = f"docker build --network=host -t {image_name} -f {filepath} {context}"
logging.info("Will build image with cmd: '%s'", build_cmd)
2021-12-14 14:19:47 +00:00
subprocess.check_call(
2022-01-18 20:10:38 +00:00
build_cmd,
2021-12-14 14:19:47 +00:00
shell=True,
)
2022-01-21 12:59:45 +00:00
def pre_build(repo_path: Path, env_variables: List[str]):
if "WITH_PERFORMANCE=1" in env_variables:
current_branch = subprocess.check_output(
"git branch --show-current", shell=True, encoding="utf-8"
).strip()
is_shallow = (
subprocess.check_output(
"git rev-parse --is-shallow-repository", shell=True, encoding="utf-8"
)
== "true\n"
)
if is_shallow:
# I've spent quite some time on looking around the problem, and my
# conclusion is: in the current state the easiest way to go is to force
# unshallow repository for performance artifacts.
# To change it we need to rework our performance tests docker image
raise BuildException(
"shallow repository is not suitable for performance builds"
)
if current_branch != "master":
cmd = (
f"git -C {repo_path} fetch --no-recurse-submodules "
"--no-tags origin master:master"
)
2022-07-07 20:19:15 +00:00
logging.info("Getting master branch for performance artifact: '%s'", cmd)
subprocess.check_call(cmd, shell=True)
2022-01-21 12:59:45 +00:00
def run_docker_image_with_env(
image_name: str,
as_root: bool,
output_dir: Path,
env_variables: List[str],
ch_root: Path,
2023-03-24 16:24:21 +00:00
ccache_dir: Optional[Path],
2022-01-21 12:59:45 +00:00
):
output_dir.mkdir(parents=True, exist_ok=True)
env_part = " -e ".join(env_variables)
if env_part:
env_part = " -e " + env_part
2018-10-05 10:40:58 +00:00
if sys.stdout.isatty():
interactive = "-it"
else:
interactive = ""
2022-05-16 11:37:51 +00:00
if as_root:
user = "0:0"
else:
user = f"{os.geteuid()}:{os.getegid()}"
2023-03-24 16:24:21 +00:00
ccache_mount = f"--volume={ccache_dir}:/ccache"
if ccache_dir is None:
ccache_mount = ""
2022-01-21 13:15:20 +00:00
cmd = (
2023-03-24 16:24:21 +00:00
f"docker run --network=host --user={user} --rm {ccache_mount}"
f"--volume={output_dir}:/output --volume={ch_root}:/build {env_part} "
2023-02-28 19:18:50 +00:00
f"{interactive} {image_name}"
)
2022-01-21 13:15:20 +00:00
logging.info("Will build ClickHouse pkg with cmd: '%s'", cmd)
subprocess.check_call(cmd, shell=True)
2022-01-21 12:59:45 +00:00
def is_release_build(debug_build: bool, package_type: str, sanitizer: str) -> bool:
return not debug_build and package_type == "deb" and sanitizer == ""
2022-02-08 18:12:04 +00:00
2022-01-21 12:59:45 +00:00
def parse_env_variables(
debug_build: bool,
compiler: str,
sanitizer: str,
package_type: str,
cache: str,
s3_bucket: str,
s3_directory: str,
s3_rw_access: bool,
clang_tidy: bool,
version: str,
official: bool,
additional_pkgs: bool,
with_coverage: bool,
with_binaries: str,
2022-01-21 12:59:45 +00:00
):
2019-10-30 14:36:27 +00:00
DARWIN_SUFFIX = "-darwin"
DARWIN_ARM_SUFFIX = "-darwin-aarch64"
2019-10-30 14:36:27 +00:00
ARM_SUFFIX = "-aarch64"
2022-09-21 13:09:24 +00:00
ARM_V80COMPAT_SUFFIX = "-aarch64-v80compat"
FREEBSD_SUFFIX = "-freebsd"
2022-01-21 12:59:45 +00:00
PPC_SUFFIX = "-ppc64le"
2021-11-13 21:56:52 +00:00
RISCV_SUFFIX = "-riscv64"
AMD64_COMPAT_SUFFIX = "-amd64-compat"
2019-10-30 14:36:27 +00:00
result = []
2022-02-08 18:12:04 +00:00
result.append("OUTPUT_DIR=/output")
2022-01-21 12:59:45 +00:00
cmake_flags = ["$CMAKE_FLAGS"]
build_target = "clickhouse-bundle"
2019-04-25 12:29:28 +00:00
2019-10-30 14:36:27 +00:00
is_cross_darwin = compiler.endswith(DARWIN_SUFFIX)
is_cross_darwin_arm = compiler.endswith(DARWIN_ARM_SUFFIX)
2019-10-30 14:36:27 +00:00
is_cross_arm = compiler.endswith(ARM_SUFFIX)
2022-09-21 13:09:24 +00:00
is_cross_arm_v80compat = compiler.endswith(ARM_V80COMPAT_SUFFIX)
2021-10-11 23:10:58 +00:00
is_cross_ppc = compiler.endswith(PPC_SUFFIX)
2021-11-13 21:56:52 +00:00
is_cross_riscv = compiler.endswith(RISCV_SUFFIX)
is_cross_freebsd = compiler.endswith(FREEBSD_SUFFIX)
is_amd64_compat = compiler.endswith(AMD64_COMPAT_SUFFIX)
2019-10-30 14:36:27 +00:00
if is_cross_darwin:
2022-01-21 12:59:45 +00:00
cc = compiler[: -len(DARWIN_SUFFIX)]
2019-10-30 14:36:27 +00:00
cmake_flags.append("-DCMAKE_AR:FILEPATH=/cctools/bin/x86_64-apple-darwin-ar")
2022-01-21 12:59:45 +00:00
cmake_flags.append(
"-DCMAKE_INSTALL_NAME_TOOL=/cctools/bin/"
"x86_64-apple-darwin-install_name_tool"
)
cmake_flags.append(
"-DCMAKE_RANLIB:FILEPATH=/cctools/bin/x86_64-apple-darwin-ranlib"
)
2019-10-30 14:36:27 +00:00
cmake_flags.append("-DLINKER_NAME=/cctools/bin/x86_64-apple-darwin-ld")
2022-01-21 12:59:45 +00:00
cmake_flags.append(
"-DCMAKE_TOOLCHAIN_FILE=/build/cmake/darwin/toolchain-x86_64.cmake"
)
result.append("EXTRACT_TOOLCHAIN_DARWIN=1")
elif is_cross_darwin_arm:
2022-01-21 12:59:45 +00:00
cc = compiler[: -len(DARWIN_ARM_SUFFIX)]
cmake_flags.append("-DCMAKE_AR:FILEPATH=/cctools/bin/aarch64-apple-darwin-ar")
2022-01-21 12:59:45 +00:00
cmake_flags.append(
"-DCMAKE_INSTALL_NAME_TOOL=/cctools/bin/"
"aarch64-apple-darwin-install_name_tool"
)
cmake_flags.append(
"-DCMAKE_RANLIB:FILEPATH=/cctools/bin/aarch64-apple-darwin-ranlib"
)
cmake_flags.append("-DLINKER_NAME=/cctools/bin/aarch64-apple-darwin-ld")
2022-01-21 12:59:45 +00:00
cmake_flags.append(
"-DCMAKE_TOOLCHAIN_FILE=/build/cmake/darwin/toolchain-aarch64.cmake"
)
result.append("EXTRACT_TOOLCHAIN_DARWIN=1")
2019-10-30 14:36:27 +00:00
elif is_cross_arm:
2022-01-21 12:59:45 +00:00
cc = compiler[: -len(ARM_SUFFIX)]
cmake_flags.append(
"-DCMAKE_TOOLCHAIN_FILE=/build/cmake/linux/toolchain-aarch64.cmake"
)
2022-02-08 18:12:04 +00:00
result.append("DEB_ARCH=arm64")
2022-09-21 13:09:24 +00:00
elif is_cross_arm_v80compat:
cc = compiler[: -len(ARM_V80COMPAT_SUFFIX)]
cmake_flags.append(
"-DCMAKE_TOOLCHAIN_FILE=/build/cmake/linux/toolchain-aarch64.cmake"
)
cmake_flags.append("-DNO_ARMV81_OR_HIGHER=1")
result.append("DEB_ARCH=arm64")
elif is_cross_freebsd:
2022-01-21 12:59:45 +00:00
cc = compiler[: -len(FREEBSD_SUFFIX)]
cmake_flags.append(
"-DCMAKE_TOOLCHAIN_FILE=/build/cmake/freebsd/toolchain-x86_64.cmake"
)
2021-10-11 23:10:58 +00:00
elif is_cross_ppc:
2022-01-21 12:59:45 +00:00
cc = compiler[: -len(PPC_SUFFIX)]
cmake_flags.append(
"-DCMAKE_TOOLCHAIN_FILE=/build/cmake/linux/toolchain-ppc64le.cmake"
)
2021-11-13 21:56:52 +00:00
elif is_cross_riscv:
cc = compiler[: -len(RISCV_SUFFIX)]
2021-11-13 21:56:52 +00:00
cmake_flags.append(
"-DCMAKE_TOOLCHAIN_FILE=/build/cmake/linux/toolchain-riscv64.cmake"
)
elif is_amd64_compat:
cc = compiler[: -len(AMD64_COMPAT_SUFFIX)]
2022-09-05 17:49:00 +00:00
result.append("DEB_ARCH=amd64")
cmake_flags.append("-DNO_SSE3_OR_HIGHER=1")
else:
cc = compiler
2022-02-08 18:12:04 +00:00
result.append("DEB_ARCH=amd64")
2019-10-30 14:36:27 +00:00
2022-01-21 12:59:45 +00:00
cxx = cc.replace("gcc", "g++").replace("clang", "clang++")
2019-10-30 14:36:27 +00:00
2022-04-22 11:37:08 +00:00
if package_type == "deb":
# NOTE: This are the env for packages/build script
2022-02-08 18:12:04 +00:00
result.append("MAKE_DEB=true")
cmake_flags.append("-DENABLE_TESTS=0")
cmake_flags.append("-DENABLE_UTILS=0")
cmake_flags.append("-DCMAKE_EXPORT_NO_PACKAGE_REGISTRY=ON")
cmake_flags.append("-DCMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY=ON")
cmake_flags.append("-DCMAKE_AUTOGEN_VERBOSE=ON")
cmake_flags.append("-DCMAKE_INSTALL_PREFIX=/usr")
cmake_flags.append("-DCMAKE_INSTALL_SYSCONFDIR=/etc")
cmake_flags.append("-DCMAKE_INSTALL_LOCALSTATEDIR=/var")
# Reduce linking and building time by avoid *install/all dependencies
cmake_flags.append("-DCMAKE_SKIP_INSTALL_ALL_DEPENDENCY=ON")
# Add bridges to the build target
build_target = (
f"{build_target} clickhouse-odbc-bridge clickhouse-library-bridge"
)
if is_release_build(debug_build, package_type, sanitizer):
cmake_flags.append("-DSPLIT_DEBUG_SYMBOLS=ON")
result.append("WITH_PERFORMANCE=1")
if is_cross_arm:
2022-04-20 22:04:49 +00:00
cmake_flags.append("-DBUILD_STANDALONE_KEEPER=1")
else:
result.append("BUILD_MUSL_KEEPER=1")
2022-02-08 18:12:04 +00:00
result.append(f"CC={cc}")
result.append(f"CXX={cxx}")
2022-03-15 21:22:31 +00:00
cmake_flags.append(f"-DCMAKE_C_COMPILER={cc}")
cmake_flags.append(f"-DCMAKE_CXX_COMPILER={cxx}")
if sanitizer:
2022-01-21 13:15:20 +00:00
result.append(f"SANITIZER={sanitizer}")
if debug_build:
2023-07-04 11:22:19 +00:00
result.append("BUILD_TYPE=Debug")
2022-02-08 18:12:04 +00:00
else:
result.append("BUILD_TYPE=None")
2023-03-24 16:24:21 +00:00
if not cache:
cmake_flags.append("-DCOMPILER_CACHE=disabled")
if cache == "ccache":
cmake_flags.append("-DCOMPILER_CACHE=ccache")
2019-04-25 12:29:28 +00:00
result.append("CCACHE_DIR=/ccache")
result.append("CCACHE_COMPRESSLEVEL=5")
2019-04-25 12:29:28 +00:00
result.append("CCACHE_BASEDIR=/build")
result.append("CCACHE_NOHASHDIR=true")
result.append("CCACHE_COMPILERCHECK=content")
result.append("CCACHE_MAXSIZE=15G")
if cache == "sccache":
cmake_flags.append("-DCOMPILER_CACHE=sccache")
# see https://github.com/mozilla/sccache/blob/main/docs/S3.md
result.append(f"SCCACHE_BUCKET={s3_bucket}")
sccache_dir = "sccache"
if s3_directory:
sccache_dir = f"{s3_directory}/{sccache_dir}"
result.append(f"SCCACHE_S3_KEY_PREFIX={sccache_dir}")
if not s3_rw_access:
result.append("SCCACHE_S3_NO_CREDENTIALS=true")
if clang_tidy:
# `CTCACHE_DIR` has the same purpose as the `CCACHE_DIR` above.
# It's there to have the clang-tidy cache embedded into our standard `CCACHE_DIR`
2023-03-28 15:36:05 +00:00
if cache == "ccache":
result.append("CTCACHE_DIR=/ccache/clang-tidy-cache")
if s3_bucket:
# see https://github.com/matus-chochlik/ctcache#environment-variables
ctcache_dir = "clang-tidy-cache"
if s3_directory:
ctcache_dir = f"{s3_directory}/{ctcache_dir}"
result.append(f"CTCACHE_S3_BUCKET={s3_bucket}")
result.append(f"CTCACHE_S3_FOLDER={ctcache_dir}")
if not s3_rw_access:
result.append("CTCACHE_S3_NO_CREDENTIALS=true")
2022-03-14 21:06:53 +00:00
if additional_pkgs:
# NOTE: This are the env for packages/build script
2022-03-21 09:38:44 +00:00
result.append("MAKE_APK=true")
2022-02-08 18:12:04 +00:00
result.append("MAKE_RPM=true")
result.append("MAKE_TGZ=true")
if with_binaries == "programs":
2022-01-21 12:59:45 +00:00
result.append("BINARY_OUTPUT=programs")
elif with_binaries == "tests":
2022-01-21 12:59:45 +00:00
result.append("ENABLE_TESTS=1")
result.append("BINARY_OUTPUT=tests")
cmake_flags.append("-DENABLE_TESTS=1")
if clang_tidy:
2022-01-21 12:59:45 +00:00
cmake_flags.append("-DENABLE_CLANG_TIDY=1")
cmake_flags.append("-DENABLE_TESTS=1")
cmake_flags.append("-DENABLE_EXAMPLES=1")
2020-07-08 09:52:03 +00:00
# Don't stop on first error to find more clang-tidy errors in one run.
2022-01-21 12:59:45 +00:00
result.append("NINJA_FLAGS=-k0")
cmake_flags.append("-DENABLE_UTILS=1")
# utils are not included into clickhouse-bundle, so build everything
build_target = "all"
2019-07-05 15:12:18 +00:00
if with_coverage:
2022-01-21 12:59:45 +00:00
cmake_flags.append("-DWITH_COVERAGE=1")
2019-07-05 15:12:18 +00:00
2019-03-04 09:33:37 +00:00
if version:
2022-01-21 13:15:20 +00:00
result.append(f"VERSION_STRING='{version}'")
2019-03-04 09:33:37 +00:00
2019-04-10 10:55:47 +00:00
if official:
2022-03-28 13:53:22 +00:00
cmake_flags.append("-DCLICKHOUSE_OFFICIAL_BUILD=1")
2019-04-25 12:29:28 +00:00
2022-01-21 12:59:45 +00:00
result.append('CMAKE_FLAGS="' + " ".join(cmake_flags) + '"')
result.append(f"BUILD_TARGET='{build_target}'")
2019-04-10 10:55:47 +00:00
return result
2022-01-21 12:59:45 +00:00
def dir_name(name: str) -> Path:
path = Path(name)
if not path.is_absolute():
path = Path.cwd() / name
return path
2022-04-22 11:37:08 +00:00
2023-02-28 19:18:50 +00:00
def parse_args() -> argparse.Namespace:
2022-01-21 12:59:45 +00:00
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="ClickHouse building script using prebuilt Docker image",
2022-01-21 12:59:45 +00:00
)
parser.add_argument(
"--package-type",
2023-07-30 08:25:44 +00:00
choices=["deb", "binary"],
required=True,
2022-01-21 12:59:45 +00:00
)
parser.add_argument(
"--clickhouse-repo-path",
default=SCRIPT_PATH.parents[2],
2022-04-22 11:37:08 +00:00
type=dir_name,
help="ClickHouse git repository",
2022-01-21 12:59:45 +00:00
)
2022-04-22 11:37:08 +00:00
parser.add_argument("--output-dir", type=dir_name, required=True)
parser.add_argument("--debug-build", action="store_true")
2022-04-01 01:49:03 +00:00
2022-01-21 12:59:45 +00:00
parser.add_argument(
"--compiler",
choices=(
2023-03-18 22:45:57 +00:00
"clang-16",
"clang-16-darwin",
"clang-16-darwin-aarch64",
"clang-16-aarch64",
"clang-16-aarch64-v80compat",
"clang-16-ppc64le",
2023-07-04 19:28:05 +00:00
"clang-16-riscv64",
2023-03-18 22:45:57 +00:00
"clang-16-amd64-compat",
"clang-16-freebsd",
2022-01-21 12:59:45 +00:00
),
2023-03-18 22:45:57 +00:00
default="clang-16",
help="a compiler to use",
2022-01-21 12:59:45 +00:00
)
parser.add_argument(
"--sanitizer",
choices=("address", "thread", "memory", "undefined", ""),
default="",
)
2022-04-01 01:49:03 +00:00
parser.add_argument("--clang-tidy", action="store_true")
parser.add_argument(
"--cache",
choices=("ccache", "sccache", ""),
default="",
help="ccache or sccache for objects caching; sccache uses only S3 buckets",
)
parser.add_argument(
"--ccache-dir",
default=Path.home() / ".ccache",
2022-04-22 11:37:08 +00:00
type=dir_name,
help="a directory with ccache",
)
parser.add_argument(
"--s3-bucket",
help="an S3 bucket used for sscache and clang-tidy-cache",
)
parser.add_argument(
"--s3-directory",
default="ccache",
help="an S3 directory prefix used for sscache and clang-tidy-cache",
)
parser.add_argument(
"--s3-rw-access",
action="store_true",
help="if set, the build fails on errors writing cache to S3",
)
parser.add_argument("--force-build-image", action="store_true")
2019-03-04 09:33:37 +00:00
parser.add_argument("--version")
2019-04-10 10:55:47 +00:00
parser.add_argument("--official", action="store_true")
2022-03-14 21:06:53 +00:00
parser.add_argument("--additional-pkgs", action="store_true")
2019-07-05 15:12:18 +00:00
parser.add_argument("--with-coverage", action="store_true")
2022-01-21 12:59:45 +00:00
parser.add_argument(
"--with-binaries", choices=("programs", "tests", ""), default=""
)
parser.add_argument(
"--docker-image-version", default="latest", help="docker image tag to use"
)
2022-05-16 11:37:51 +00:00
parser.add_argument(
"--as-root", action="store_true", help="if the container should run as root"
)
args = parser.parse_args()
2022-04-22 11:37:08 +00:00
if args.additional_pkgs and args.package_type != "deb":
2023-02-28 19:18:50 +00:00
raise argparse.ArgumentTypeError(
"Can build additional packages only in deb build"
)
2023-03-24 16:24:21 +00:00
if args.cache != "ccache":
args.ccache_dir = None
if args.with_binaries != "":
if args.package_type != "deb":
2023-02-28 19:18:50 +00:00
raise argparse.ArgumentTypeError(
"Can add additional binaries only in deb build"
)
2022-01-21 13:15:20 +00:00
logging.info("Should place %s to output", args.with_binaries)
if args.cache == "sccache":
if not args.s3_bucket:
raise argparse.ArgumentTypeError("sccache must have --s3-bucket set")
2023-02-28 19:18:50 +00:00
return args
def main():
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(message)s")
args = parse_args()
ch_root = args.clickhouse_repo_path
dockerfile = ch_root / "docker/packager" / IMAGE_TYPE / "Dockerfile"
2023-02-28 19:18:50 +00:00
image_with_version = IMAGE_NAME + ":" + args.docker_image_version
2023-02-13 14:25:23 +00:00
if args.force_build_image:
build_image(image_with_version, dockerfile)
2023-02-28 19:18:50 +00:00
elif not (
check_image_exists_locally(image_with_version) or pull_image(image_with_version)
):
2023-02-13 14:25:23 +00:00
build_image(image_with_version, dockerfile)
2019-03-04 09:33:37 +00:00
env_prepared = parse_env_variables(
args.debug_build,
2022-01-21 12:59:45 +00:00
args.compiler,
args.sanitizer,
args.package_type,
args.cache,
args.s3_bucket,
args.s3_directory,
args.s3_rw_access,
2022-01-21 12:59:45 +00:00
args.clang_tidy,
args.version,
args.official,
2022-03-14 21:06:53 +00:00
args.additional_pkgs,
2022-01-21 12:59:45 +00:00
args.with_coverage,
args.with_binaries,
)
pre_build(args.clickhouse_repo_path, env_prepared)
2022-01-21 12:59:45 +00:00
run_docker_image_with_env(
2023-02-28 19:18:50 +00:00
image_with_version,
2022-05-16 11:37:51 +00:00
args.as_root,
2022-01-21 12:59:45 +00:00
args.output_dir,
env_prepared,
ch_root,
args.ccache_dir,
)
2022-01-21 13:15:20 +00:00
logging.info("Output placed into %s", args.output_dir)
if __name__ == "__main__":
main()