diff --git a/scripts/local_build.py b/scripts/local_build.py new file mode 100755 index 00000000..ff19922b --- /dev/null +++ b/scripts/local_build.py @@ -0,0 +1,220 @@ +#!/usr/bin/python3 + +""" +This script tries to build a project with CMake, Meson, and Autotools. +It checks if CMake, Meson, and Autotools are installed, and performs +the configure, build, and optionally test steps for each build system. +""" + +import multiprocessing +import os +import random +import string +import subprocess +import sys +import shutil + + +if sys.platform == "win32": + REPO_DIR = "\\".join(os.path.abspath(__file__).split("\\")[:-2]) +else: + REPO_DIR = "/".join(os.path.abspath(__file__).split("/")[:-2]) + + +def main(): + import argparse + parser = argparse.ArgumentParser() + parser.add_argument("--test", action="store_true", help="Run tests") + parser.add_argument("--cmake", action="store_true", help="Only run CMake") + parser.add_argument("--meson", action="store_true", help="Only run Meson") + if sys.platform != "win32": + parser.add_argument("--autotools", action="store_true", help="Only run Autotools") + args = parser.parse_args() + + test = args.test + cmake_only = args.cmake + meson_only = args.meson + if sys.platform != "win32": + autotools_only = args.autotools + autotools_only = False + + result = [] + os.chdir(REPO_DIR) + + # download model + if sys.platform == "win32": + run_command("autogen.bat") + else: + run_command("./autogen.sh") + + if sys.platform != "win32" and not cmake_only and not meson_only: + result += autotools_build(test) + + if not autotools_only and not meson_only: + result += cmake_build(test) + result += cmake_build(test, extra_options="-DOPUS_NEURAL_FEC=ON") + + if not autotools_only and not cmake_only: + result += meson_build(test) + + print_result(result, test) + +def print_result(result, test=False): + if len(result) == 0: + print("No results available") + return + + headers = ["Name", "Build Passed"] + if test: + headers.append("Test Passed") + + # Calculate the maximum width for each column + column_widths = [max(len(str(row[i])) for row in result) for i in range(len(headers))] + + # Print the headers + header_row = " | ".join(f"{header: <{column_widths[i]}}" for i, header in enumerate(headers)) + print(header_row) + print("-" * len(header_row)) + + # Print the data rows + for row in result: + row_values = [str(value) for value in row[:len(headers)]] # Include values up to the last column to be printed + row_string = " | ".join(f"{row_values[i]: <{column_widths[i]}}" for i in range(len(headers))) + print(row_string) + +def autotools_build(test=False): + build = "Autotools" + autotools_build_succeeded = False + autotools_test_succeeded = False + + if not check_tool_installed("autoreconf") or not check_tool_installed("automake") or not check_tool_installed("autoconf"): + print("Autotools dependencies are not installed. Aborting.") + print("Install with: sudo apt-get install git autoconf automake libtool gcc make") + return [(build, autotools_build_succeeded, autotools_test_succeeded)] + + run_command("./configure") + if run_command("make -j {}".format(get_cpu_core_count())) == 0: + autotools_build_succeeded = True + if test: + if run_command("make check -j {}".format(get_cpu_core_count())) == 0: + autotools_test_succeeded = True + + return [(build, autotools_build_succeeded, autotools_test_succeeded)] + + +def cmake_build(test=False, extra_options=""): + cmake_build_succeeded = False + cmake_test_succeeded = False + build = "CMake" + + if not check_tool_installed("cmake"): + print("CMake is not installed. Aborting.") + if sys.platform != "win32": + print("Install with: sudo apt install cmake") + else: + print("Download and install from: https://cmake.org/download/") + return [(build, cmake_build_succeeded, cmake_test_succeeded)] + + if not check_tool_installed("ninja"): + print("Ninja is not installed. Aborting.") + if sys.platform != "win32": + print("Install with: sudo apt ninja-build ") + else: + print("Download and install from: https://ninja-build.org/") + return [(build, cmake_build_succeeded, cmake_test_succeeded)] + + cmake_build_dir = create_dir_with_random_postfix("cmake-build") + cmake_cfg_cmd = ["cmake", "-S" ".", "-B", cmake_build_dir, "-G", '"Ninja"', "-DCMAKE_BUILD_TYPE=Release", "-DOPUS_BUILD_TESTING=ON", "-DOPUS_BUILD_PROGRAMS=ON", "-DOPUS_FAST_MATH=ON", "-DOPUS_FLOAT_APPROX=ON"] + cmake_cfg_cmd += [option for option in extra_options.split(" ")] + run_command(" ".join(cmake_cfg_cmd)) + cmake_build_cmd = ["cmake", "--build", cmake_build_dir, "-j", "{}".format(get_cpu_core_count())] + cmake_test_cmd = ["ctest", "-j", "{}".format(get_cpu_core_count())] + if sys.platform == "win32": + cmake_build_cmd += ["--config", "Release"] + cmake_test_cmd += ["-C", "Release"] + + if run_command(" ".join(cmake_build_cmd)) == 0: + cmake_build_succeeded = True + + if test: + os.chdir(cmake_build_dir) + if run_command(" ".join(cmake_test_cmd)) == 0: + cmake_test_succeeded = True + os.chdir(REPO_DIR) + + shutil.rmtree(cmake_build_dir) + + if extra_options != "": + build += " with options: " + build += extra_options.replace("-D", "") + return [(build, cmake_build_succeeded, cmake_test_succeeded)] + +def meson_build(test=False, extra_options=""): + build = "Meson" + meson_build_succeeded = False + meson_test_succeeded = False + + if not check_tool_installed("meson"): + print("Meson is not installed. Aborting.") + print("Install with: pip install meson") + return [(build, meson_build_succeeded, meson_test_succeeded)] + + if not check_tool_installed("ninja"): + print("Ninja is not installed. Aborting.") + if sys.platform != "win32": + print("Install with: sudo apt ninja-build ") + else: + print("Download and install from: https://ninja-build.org/") + return [(build, meson_build_succeeded, meson_test_succeeded)] + + meson_build_dir = create_dir_with_random_postfix("meson-build") + meson_cfg_cmd = ["meson", "setup", meson_build_dir] + meson_cfg_cmd += [option for option in extra_options.split(" ")] + run_command(" ".join(meson_cfg_cmd)) + meson_build_cmd = ["meson", "compile", "-C", meson_build_dir] + meson_test_cmd = ["meson", "test", "-C", meson_build_dir] + + if run_command(" ".join(meson_build_cmd)) == 0: + meson_build_succeeded = True + + if test: + os.chdir(meson_build_dir) + if run_command(" ".join(meson_test_cmd)) == 0: + meson_test_succeeded = True + os.chdir(REPO_DIR) + + shutil.rmtree(meson_build_dir) + + if extra_options != "": + build += " with options: " + build += extra_options.replace("-D", "") + return [(build, meson_build_succeeded, meson_test_succeeded)] + +def create_dir_with_random_postfix(dir): + random_chars = "".join(random.choices(string.ascii_letters, k=3)) + dir = dir + "-" + random_chars + dir = os.path.join(os.getcwd(), dir) + os.makedirs(dir) + return dir + +def check_tool_installed(tool): + if sys.platform == "win32": + try: + # Use the "where" command to search for the executable + subprocess.check_output(["where", tool], shell=True) + return True + except subprocess.CalledProcessError: + return False + else: + return run_command(f"command -v {tool}") == 0 + +def run_command(command): + process = subprocess.Popen(command, shell=True) + process.communicate() + return process.returncode + +def get_cpu_core_count(): + return int(max(multiprocessing.cpu_count(), 1)) + +if __name__ == "__main__": + main()