Merge "Build xz using cmake"
diff --git a/ndk/autoconf.py b/ndk/autoconf.py
index 95384c1..22f324f 100644
--- a/ndk/autoconf.py
+++ b/ndk/autoconf.py
@@ -130,7 +130,7 @@
if env:
subproc_env.update(env)
if self.additional_env:
- subproc_env.update(env)
+ subproc_env.update(self.additional_env)
if subproc_env != os.environ:
pp_env = pprint.pformat(env, indent=4)
diff --git a/ndk/checkbuild.py b/ndk/checkbuild.py
index cb2567c..6266cfd 100755
--- a/ndk/checkbuild.py
+++ b/ndk/checkbuild.py
@@ -68,6 +68,7 @@
import ndk.ansi
import ndk.autoconf
import ndk.builds
+import ndk.cmake
import ndk.config
import ndk.deps
import ndk.ext.shutil
@@ -1223,12 +1224,10 @@
def lzma_builder(self) -> ndk.autoconf.AutoconfBuilder:
"""Returns the lazily initialized lzma builder for this module."""
if self._lzma_builder is None:
- self._lzma_builder = ndk.autoconf.AutoconfBuilder(
- self.lzma_src / 'configure',
+ self._lzma_builder = ndk.cmake.CMakeBuilder(
+ self.lzma_src,
self.intermediate_out_dir / 'lzma',
- self.host,
- add_toolchain_to_path=True,
- use_clang=True)
+ self.host)
return self._lzma_builder
@property
@@ -1273,15 +1272,9 @@
def build_lzma(self) -> None:
"""Builds the liblzma dependency."""
- self.lzma_builder.build([
- '--disable-shared',
- '--enable-static',
- '--disable-xz',
- '--disable-xzdec',
- '--disable-lzmadev',
- '--disable-scripts',
- '--disable-doc',
- ])
+ self.lzma_builder.build({
+ 'BUILD_SHARED_LIBS': 'OFF',
+ })
def build_gdb(self) -> None:
"""Builds GDB itself."""
diff --git a/ndk/cmake.py b/ndk/cmake.py
new file mode 100644
index 0000000..f7eb116
--- /dev/null
+++ b/ndk/cmake.py
@@ -0,0 +1,193 @@
+#
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""APIs for dealing with cmake scripts."""
+
+import os
+from pathlib import Path
+import pprint
+import shlex
+import shutil
+import subprocess
+from typing import Dict, List, Optional
+
+from ndk.hosts import Host, get_default_host
+import ndk.paths
+import ndk.toolchains
+
+SYSTEM_NAME_MAP = {
+ Host.Darwin: 'Darwin',
+ Host.Linux: 'Linux',
+ Host.Windows64: 'Windows'
+}
+
+HOST_TRIPLE_MAP = {
+ Host.Darwin: 'x86_64-apple-darwin',
+ Host.Linux: 'x86_64-linux-gnu',
+ Host.Windows64: 'x86_64-w64-mingw32',
+}
+
+
+class CMakeBuilder:
+ """Builder for an cmake project."""
+
+ toolchain: ndk.toolchains.Toolchain
+
+ def __init__(self,
+ src_path: Path,
+ build_dir: Path,
+ host: Host,
+ additional_flags: List[str] = None,
+ additional_env: Optional[Dict[str, str]] = None) -> None:
+ """Initializes an autoconf builder.
+
+ Args:
+ src_path: Path to the cmake project.
+ build_dir: Directory to use for building. If the directory exists,
+ it will be deleted and recreated to ensure the build is correct.
+ host: Host to be used for the --host argument (the
+ cross-compilation target).
+ additional_flags: Additional flags to pass to the compiler.
+ additional_env: Additional environment to set, used during
+ configure, build, and install.
+ """
+ self.src_path = src_path
+ self.build_directory = build_dir
+ self.host = host
+ self.additional_flags = additional_flags
+ self.additional_env = additional_env
+
+ self.working_directory = self.build_directory / 'build'
+ self.install_directory = self.build_directory / 'install'
+
+ self.toolchain = ndk.toolchains.ClangToolchain(self.host)
+
+ @property
+ def flags(self) -> List[str]:
+ """Returns default cflags for the target."""
+ # TODO: Are these the flags we want? These are what we've used
+ # historically.
+ flags = [
+ '-Os',
+ '-fomit-frame-pointer',
+ '-s',
+ ]
+ if self.additional_flags:
+ flags.extend(self.additional_flags)
+ return flags
+
+ def _run(self, cmd: List[str]) -> None:
+ """Runs and logs execution of a subprocess."""
+ subproc_env = dict(os.environ)
+ if self.additional_env:
+ subproc_env.update(self.additional_env)
+
+ pp_cmd = shlex.join(cmd)
+ if subproc_env != os.environ:
+ pp_env = pprint.pformat(self.additional_env, indent=4)
+ print('Running: {} with env:\n{}'.format(pp_cmd, pp_env))
+ else:
+ print('Running: {}'.format(pp_cmd))
+
+ subprocess.check_call(cmd, env=subproc_env, cwd=self.working_directory)
+
+ @property
+ def _cmake(self) -> Path:
+ return (ndk.paths.ANDROID_DIR / 'prebuilts' / 'cmake' /
+ (get_default_host().value + '-x86') / 'bin' / 'cmake')
+
+ @property
+ def _ninja(self) -> Path:
+ return (ndk.paths.ANDROID_DIR / 'prebuilts' / 'ninja' /
+ (get_default_host().value + '-x86') / 'ninja')
+
+ @property
+ def cmake_defines(self) -> Dict[str, str]:
+ """CMake defines."""
+ flags = self.toolchain.flags + self.flags
+ cflags = ' '.join(flags)
+ cxxflags = ' '.join(flags + ['-stdlib=libc++'])
+ defines: Dict[str, str] = {
+ 'CMAKE_C_COMPILER': str(self.toolchain.cc),
+ 'CMAKE_C_COMPILER_TARGET': HOST_TRIPLE_MAP[self.host],
+ 'CMAKE_CXX_COMPILER': str(self.toolchain.cxx),
+ 'CMAKE_CXX_COMPILER_TARGET': HOST_TRIPLE_MAP[self.host],
+ 'CMAKE_AR': str(self.toolchain.ar),
+ 'CMAKE_RANLIB': str(self.toolchain.ranlib),
+ 'CMAKE_NM': str(self.toolchain.nm),
+ 'CMAKE_STRIP': str(self.toolchain.strip),
+ 'CMAKE_LINKER': str(self.toolchain.ld),
+ 'CMAKE_ASM_FLAGS': cflags,
+ 'CMAKE_C_FLAGS': cflags,
+ 'CMAKE_CXX_FLAGS': cxxflags,
+ 'CMAKE_BUILD_TYPE': 'Release',
+ 'CMAKE_INSTALL_PREFIX': str(self.install_directory),
+ 'CMAKE_MAKE_PROGRAM': str(self._ninja),
+ 'CMAKE_SYSTEM_NAME': SYSTEM_NAME_MAP[self.host],
+ 'CMAKE_SYSTEM_PROCESSOR': 'x86_64',
+ 'CMAKE_FIND_ROOT_PATH_MODE_INCLUDE': 'ONLY',
+ 'CMAKE_FIND_ROOT_PATH_MODE_LIBRARY': 'ONLY',
+ 'CMAKE_FIND_ROOT_PATH_MODE_PACKAGE': 'ONLY',
+ 'CMAKE_FIND_ROOT_PATH_MODE_PROGRAM': 'NEVER',
+ }
+ if self.host.is_windows:
+ defines['CMAKE_RC'] = str(self.toolchain.rescomp)
+ return defines
+
+ def clean(self) -> None:
+ """Cleans output directory.
+
+ If necessary, existing output directory will be removed. After
+ removal, the inner directories (working directory, install directory,
+ and toolchain directory) will be created.
+ """
+ if self.build_directory.exists():
+ shutil.rmtree(self.build_directory)
+
+ self.working_directory.mkdir(parents=True)
+ self.install_directory.mkdir(parents=True)
+
+ def configure(self, additional_defines: Dict[str, str]) -> None:
+ """Invokes cmake configure."""
+ cmake_cmd = [str(self._cmake), '-GNinja']
+ defines = self.cmake_defines
+ defines.update(additional_defines)
+ cmake_cmd.extend(f'-D{key}={val}' for key, val in defines.items())
+ cmake_cmd.append(str(self.src_path))
+
+ self._run(cmake_cmd)
+
+ def make(self) -> None:
+ """Builds the project."""
+ self._run([str(self._ninja)])
+
+ def install(self) -> None:
+ """Installs the project."""
+ self._run([str(self._ninja), 'install'])
+
+ def build(self,
+ additional_defines: Optional[Dict[str, str]] = None) -> None:
+ """Configures and builds an cmake project.
+
+ Args:
+ configure_args: List of arguments to be passed to configure. Does
+ not need to include --prefix, --build, or --host. Those are set
+ up automatically.
+ """
+ self.clean()
+ self.configure(
+ {} if additional_defines is None else additional_defines)
+ self.make()
+ self.install()