diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rwxr-xr-x | check.sh | 7 | ||||
-rwxr-xr-x | helpers/llvm-projs | 165 | ||||
-rw-r--r-- | modules/__init__.py | 0 | ||||
-rw-r--r-- | modules/llvm.py | 224 | ||||
-rw-r--r-- | scripts/llvm.py | 106 | ||||
-rw-r--r-- | tests/__init__.py | 0 | ||||
-rw-r--r-- | tests/cli/__init__.py | 0 | ||||
-rw-r--r-- | tests/cli/testllvmprojects.py | 318 | ||||
-rw-r--r-- | tests/unittests/__init__.py | 0 | ||||
-rw-r--r-- | tests/unittests/testllvmsourceconfig.py | 572 | ||||
-rw-r--r-- | tests/unittests/testllvmsubprojects.py | 64 |
12 files changed, 1339 insertions, 118 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/check.sh b/check.sh new file mode 100755 index 0000000..f5b65fa --- /dev/null +++ b/check.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +echo "Running unit tests" +python -m unittest discover -s tests/unittests + +echo "Running cli tests" +python -m unittest discover -s tests/cli diff --git a/helpers/llvm-projs b/helpers/llvm-projs index 3429b89..288fc8c 100755 --- a/helpers/llvm-projs +++ b/helpers/llvm-projs @@ -1,14 +1,10 @@ #!/usr/bin/env bash -# This script keeps track of all projects that are linked to the llvm src -# directory. It can detect, enable, disable and map to specific projects, -# so different builds get to see the source dir as they saw originally. +# Shorthand script for adding/removing llvm subprojects. It has a simpler +# interface than llvm.py, but it calls it to do the actual work. -. llvm-common - -safe_run verify_env - -prog=`basename $0` +prog=$(basename $0) +progdir=$(dirname $0) syntax() { echo "Syntax: $prog [clang|lldb|lld|rt|libs|all|none] {+/-}[c|x|r|k|d|l|a|u|t]" echo " no args: List linked projects" @@ -32,86 +28,6 @@ syntax() { echo " Long options unlink everything before proceeding. Use the short options for fine-tuning" } -# Dirs and links into LLVM -clang_dir=clang -clang_link=tools/clang -clang_extra_dir=clang-tools-extra -clang_extra_link=tools/clang/tools/extra -rt_dir=compiler-rt -rt_link=projects/compiler-rt -libcxx_dir=libcxx -libcxx_link=projects/libcxx -libcxxabi_dir=libcxxabi -libcxxabi_link=projects/libcxxabi -libunwind_dir=libunwind -libunwind_link=projects/libunwind -lld_dir=lld -lld_link=tools/lld -lldb_dir=lldb -lldb_link=tools/lldb -tests_dir=test-suite -tests_link=projects/test-suite - -# Check if link exists -has_link() { - link=$1 - [ -d "$LLVM_SRC/$link" ] -} - -# Initialise status -init() { - link=$1 - if has_link $link; then - echo "ON"; - else - echo "OFF" - fi -} - -# Link/Unlink upon need -update() { - dir=$1 - link=$2 - need=$3 - if [ "$need" = ON ]; then - if ! has_link $link; then - pushdq $LLVM_SRC - branch=`get_branch` - popdq - - safe_run add_worktree $LLVM_ROOT/repos/$dir $LLVM_SRC/$link $branch - fi - else - safe_run remove_worktree $LLVM_ROOT/repos/$dir $LLVM_SRC/$link - fi -} - -# Enable/disable projects in the CMake cache -update_build_dirs() { - if [ -d $LLVM_BLD/../build ]; then - safe_run cmake $* $LLVM_BLD/../build - fi - - if [ -d $LLVM_BLD/../debug ]; then - safe_run cmake $* $LLVM_BLD/../debug - fi -} - -# Lists linked projects -list_all() { - echo "Projects linked:" - has_link $clang_link && echo " + Clang" - has_link $clang_extra_link && echo " + Clang Tools Extra" - has_link $rt_link && echo " + Compiler-RT" - has_link $libcxx_link && echo " + LibC++" - has_link $libcxxabi_link && echo " + LibC++abi" - has_link $libunwind_link && echo " + LibUnwind" - has_link $lld_link && echo " + LLD" - has_link $lldb_link && echo " + LLDB" - has_link $tests_link && echo " + Test-Suite" - echo -} - need_all() { need=$1 clang=$need @@ -125,24 +41,17 @@ need_all() { tests=$need } +llvmtool=$progdir/../scripts/llvm.py + # No args, list if [ "$1" = "" ]; then echo "Use $prog -h for options" echo - list_all + python $llvmtool projects exit fi -# Need/not need -clang=`init $clang_link` -clang_extra=`init $clang_extra_link` -rt=`init $rt_link` -libcxx=`init $libcxx_link` -libcxxabi=`init $libcxxabi_link` -libunwind=`init $libunwind_link` -lld=`init $lld_link` -lldb=`init $lldb_link` -tests=`init $tests_link` +need_all UNDEF # See if the first option is one of the long options opt=$1 @@ -188,7 +97,7 @@ case $opt in shift ;; list) - list_all + python $llvmtool projects exit ;; -h) @@ -257,23 +166,43 @@ if [ "$clang_extra" = ON -a "$clang" = OFF ]; then exit fi +add="" +remove="" + +update() { + project="$1" + flag="$2" + + case $flag in + ON) + add="$add $project" + ;; + OFF) + remove="$remove $project" + ;; + UNDEF) + # Don't care + ;; + esac +} # Update links -update $tests_dir $tests_link $tests -update $lldb_dir $lldb_link $lldb -update $lld_dir $lld_link $lld -update $libunwind_dir $libunwind_link $libunwind -update $libcxxabi_dir $libcxxabi_link $libcxxabi -update $libcxx_dir $libcxx_link $libcxx -update $rt_dir $rt_link $rt -update $clang_dir $clang_link $clang -update $clang_extra_dir $clang_extra_link $clang_extra -update_build_dirs -DLLVM_TOOL_LLDB_BUILD=$lldb \ - -DLLVM_TOOL_LLD_BUILD=$lld \ - -DLLVM_TOOL_LIBUNWIND_BUILD=$libunwind \ - -DLLVM_TOOL_LIBCXXABI_BUILD=$libcxxabi \ - -DLLVM_TOOL_LIBCXX_BUILD=$libcxx \ - -DLLVM_TOOL_COMPILER_RT_BUILD=$rt \ - -DLLVM_TOOL_CLANG_BUILD=$clang \ - -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=$clang_extra -list_all +update "test-suite" $tests +update "lldb" $lldb +update "lld" $lld +update "libunwind" $libunwind +update "libcxxabi" $libcxxabi +update "libcxx" $libcxx +update "compiler-rt" $rt +update "clang" $clang +update "clang-tools-extra" $clang_extra + +if [ "$add" != "" ]; then + add="-a $add" +fi + +if [ "$remove" != "" ]; then + remove="-r $remove" +fi + +python $llvmtool projects $add $remove diff --git a/modules/__init__.py b/modules/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/modules/__init__.py diff --git a/modules/llvm.py b/modules/llvm.py new file mode 100644 index 0000000..8922c83 --- /dev/null +++ b/modules/llvm.py @@ -0,0 +1,224 @@ +import os + +from linaropy.git.worktree import Worktree + + +class LLVMSubproject(object): + """ + Class that keeps track of everything related to an LLVM subproject (clang, + lld, compiler-rt etc): repo URL, location preferred by CMake, + CMake variable for adding or removing it from the build etc. + """ + + def __init__(self, cmake_path, cmake_var): + """Create an LLVMSubproject with the provided info. + + Parameters + ---------- + cmake_path + Path relative to the LLVM source root where this subproject should + live so that CMake can automatically pick it up during the build. + cmake_var + The name of the CMake variable that can be used to enable/disable + building this subproject. + """ + self.cmake_path = cmake_path + self.cmake_var = cmake_var + + def get_cmake_path(self, llvm_source_directory): + """ + Get the path where this subproject should live in the given LLVM tree so + that CMake can pick it up by default. + """ + return os.path.join(llvm_source_directory, self.cmake_path) + + @classmethod + def get_all_subprojects(cls): + """ + Return a dictionary of all the LLVM subprojects. + At the moment, llvm itself is not part of the subprojects, because it + always needs to be there and everything is relative to it. + """ + return { + "clang": + cls(os.path.join("tools", "clang"), "LLVM_TOOL_CLANG_BUILD"), + "compiler-rt": + cls(os.path.join("projects", "compiler-rt"), + "LLVM_TOOL_COMPILER_RT_BUILD"), + "libcxx": + cls(os.path.join("projects", "libcxx"), + "LLVM_TOOL_LIBCXX_BUILD"), + "libcxxabi": + cls(os.path.join("projects", "libcxxabi"), + "LLVM_TOOL_LIBCXXABI_BUILD"), + "libunwind": + cls(os.path.join("projects", "libunwind"), + "LLVM_TOOL_LIBUNWIND_BUILD"), + "lld": + cls(os.path.join("tools", "lld"), "LLVM_TOOL_LLD_BUILD"), + "lldb": + cls(os.path.join("tools", "lldb"), "LLVM_TOOL_LLDB_BUILD"), + "test-suite": + cls(os.path.join("projects", "test-suite"), None), + } + + +class LLVMSourceConfig(object): + """Class for managing an LLVM source tree. + + It keeps track of which subprojects are enabled in a given tree and provides + functionality for adding / removing them. + """ + + def __init__(self, proj, sourcePath, + subprojs=LLVMSubproject.get_all_subprojects()): + """ Create a source configuration. + + Parameters + ---------- + proj : Proj + Temporary project directory (used mostly for logging). + sourcePath + Must point to a valid LLVM source tree. + subprojs : dictionary + Dictionary containing a number of LLVMSubproject objects. + By default, this contains all the LLVM subprojects as returned by + LLVMSubproject.get_all_subprojects(), but any subset would work just + as well. + The keys will be used to identify the subprojects in any of this + class's methods. It is an error to invoke them with a subproj that + does not exist in this dictionary. + """ + sourcePath = str(sourcePath) + if not os.path.isdir(sourcePath): + raise EnvironmentError("Invalid path to LLVM source tree") + + self.proj = proj + self.llvmSourceTree = Worktree(proj, sourcePath) + self.subprojs = subprojs + + def get_enabled_subprojects(self): + """Get a list of the subprojects enabled in this configuration.""" + enabled = [] + for subproj in self.subprojs: + if self.__is_enabled(subproj): + enabled.append(subproj) + return enabled + + def update(self, add={}, remove=[]): + """Update the configuration by adding/removing subprojects. + + Parameters + ---------- + add : dictionary + A map of (subproject name, repo) to add to the config. The order in + which the subprojects are added is unspecified. Subprojects that are + already enabled in the config are ignored. + remove: list + A list of subproject names to remove from the config. The order in + which the subprojects are removed is unspecified. Duplicates and + subprojects that aren't enabled in the config are ignored. A + subproject may be removed even if it is in an invalid state. + + For both add and remove, the subproject name must exist in the + dictionary that the config was initialized with and the repo must be a + valid GitRepo object. + + TODO: If adding/removing a project fails, this function should try to + recover. + """ + + # Make the inputs friendly + if add is None: + add = {} + + if remove is None: + remove = [] + + for subproj in add.keys(): + if subproj in remove: + raise ValueError("Can't add and remove {} at the same time" + .format(subproj)) + + for (subproj, repo) in add.items(): + self.__add_subproject(subproj, repo) + + for subproj in remove: + self.__remove_subproject(subproj) + + def __get_subproj_object(self, subprojName): + """Get the LLVMSubproject object corresponding to subprojName.""" + if not subprojName in self.subprojs.keys(): + raise ValueError("Unknown llvm subproject {0}".format(subprojName)) + return self.subprojs[subprojName] + + def __get_subproj_cmake_path(self, subprojName): + """Get the full path to subprojName in this source tree.""" + subproj = self.__get_subproj_object(subprojName) + return subproj.get_cmake_path(self.llvmSourceTree.repodir) + + def __is_enabled(self, subprojName): + """ + Check if subproj is enabled in this configuration. A subproj is + considered to be enabled if it is a worktree on the correct branch. + """ + try: + # If this succeeds, the subproject has already been added. + existing = Worktree(self.proj, + self.__get_subproj_cmake_path(subprojName)) + except EnvironmentError: + # If it's not a worktree (for whatever reason), it's not enabled. + return False + + # If it is a worktree, but on the wrong branch, it is not enabled. + return existing.getbranch() == self.llvmSourceTree.getbranch() + + # TODO: add_subproject, remove_subproject and is_enabled should live in + # another object (AddRemoveStrategy?) that would know what we want to add + # (worktrees, links, whatever) + def __add_subproject(self, subprojName, subprojRepo): + """Add a given subproject to this configuration. + + This will make sure the subproject's sources are visible in the proper + place in the LLVM source tree that the configuration was created with. + + We currently achieve this by creating a worktree for the subproject, but + if more flexibility is needed we can add a config option. The branch + that the worktree will be created with is the same branch that the + existing LLVM source tree is on, and it will be tracking a branch + corresponding to the one that the LLVM branch was forked from. + """ + path = self.__get_subproj_cmake_path(subprojName) + + if self.__is_enabled(subprojName): + # Subproject has already been added, nothing to do. + return + + if os.path.exists(path): + raise EnvironmentError( + "{} already exists but is not a valid subproject directory." + .format(path) + + "Please make sure it is a worktree on the same branch as LLVM.") + + # Create a new worktree + branch = self.llvmSourceTree.getbranch() + if subprojRepo.branchexists(branch): + Worktree.create(self.proj, subprojRepo, path, branch) + else: + trackedBranch = "master" # TODO: track proper branch + Worktree.create( + self.proj, + subprojRepo, + path, + branch, + trackedBranch) + + def __remove_subproject(self, subprojName): + """Remove a given subproject from this build configuration.""" + path = self.__get_subproj_cmake_path(subprojName) + + if not os.path.isdir(path): + return + + worktree = Worktree(self.proj, path) + worktree.clean(False) diff --git a/scripts/llvm.py b/scripts/llvm.py new file mode 100644 index 0000000..762f9e7 --- /dev/null +++ b/scripts/llvm.py @@ -0,0 +1,106 @@ +"""This is the main tool for handling llvm builds, bisects etc.""" + +import os +from sys import exit + +from modules.llvm import LLVMSubproject, LLVMSourceConfig +from linaropy.git.clone import Clone +from linaropy.proj import Proj + +from argparse import Action, ArgumentParser, RawTextHelpFormatter + + +def die(message, config_to_dump=None): + """Print an error message and exit.""" + print message + + if config_to_dump is not None: + dump_config(config_to_dump) + + exit(1) + +# Figure out the path to the LLVM repos +if "LLVM_ROOT" not in os.environ: + die("Please, define $LLVM_ROOT to point to the root\n" + "path where the worktree setup should be performed") +llvm_repos_root = os.path.join(os.environ["LLVM_ROOT"], "repos") + +# Figure out the path to the current LLVM tree +if "LLVM_SRC" not in os.environ: + die("Please, define $LLVM_SRC to point to the current LLVM\n" + "worktree directory, or run llvm-env to set it for you") +llvm_worktree_root = os.environ["LLVM_SRC"] + + +def dump_config(config): + """Dump the list of projects that are enabled in the given config.""" + + print "Projects linked:" + enabled = config.get_enabled_subprojects() + if not enabled: + print "none" + else: + for subproj in sorted(enabled): + print " + {}".format(subproj) + + +def projects(args): + """Add/remove subprojects based on the values in args.""" + + proj = Proj() + config = LLVMSourceConfig(proj, llvm_worktree_root) + + if not args.add and not args.remove: + # Nothing to change, just print the current configuration + dump_config(config) + exit(0) + + to_add = {} + if args.add: + for subproj in args.add: + repo = Clone(proj, os.path.join(llvm_repos_root, subproj)) + to_add[subproj] = repo + + try: + config.update(to_add, args.remove) + except (EnvironmentError, ValueError) as exc: + die("Failed to update subprojects because:\n{}".format(str(exc))) + + dump_config(config) + +########################################################################## +# Command line parsing # +########################################################################## + +# If we decide we want shorthands for the subprojects, we can append to this +# list +valid_subprojects = LLVMSubproject.get_all_subprojects().keys() + +options = ArgumentParser(formatter_class=RawTextHelpFormatter) +subcommands = options.add_subparsers() + +# Subcommand for adding / removing subprojects +projs = subcommands.add_parser("projects", help="Add/remove LLVM subprojects") +projs.set_defaults(run_command=projects) + +# TODO: Overwriting previous values is not necessarily what users expect (so for +# instance --add S1 S2 --remove S3 --add S4 would lead to adding only S4). We +# can do better by using action='append', which would create a list (of lists? +# or of lists and scalars?) that we can flatten to obtain all the values passed +# by the user. +projs.add_argument( + '-a', '--add', + nargs='+', + choices=valid_subprojects, + metavar='subproject', + help="Link given subprojects. Valid values are:\n\t{}\n".format( + "\n\t".join(valid_subprojects))) +projs.add_argument( + '-r', '--remove', + nargs='+', + choices=valid_subprojects, + metavar='subproject', + help="Unlink given subprojects.") + +args = options.parse_args() +args.run_command(args) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/__init__.py diff --git a/tests/cli/__init__.py b/tests/cli/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/cli/__init__.py diff --git a/tests/cli/testllvmprojects.py b/tests/cli/testllvmprojects.py new file mode 100644 index 0000000..c33ddb5 --- /dev/null +++ b/tests/cli/testllvmprojects.py @@ -0,0 +1,318 @@ +"""Command line interface tests for llvmprojs.py + +Note that although this uses the unittest framework, it does *not* contain unit +tests. + +""" + +import shutil +import os +import subprocess +import unittest + +from functools import partial +from tempfile import mkdtemp +from uuid import uuid4 + +from linaropy.cd import cd + + +# TODO: move this somewhere more public (maybe linaropy?) +def debug(test): + """ + Decorator that dumps the output of any subprocess.CalledProcessError + exception. Use this to decorate a test function when you can't tell what the + problem is. + """ + def wrapper(*args, **kwargs): + # Catch any exceptions so we can dump all the output + try: + test(*args, **kwargs) + except subprocess.CalledProcessError as exc: + print "Error in {}:".format(test.__name__) + print "Command {} exited with error code {}:\n{}".format( + exc.cmd, exc.returncode, exc.output) + return wrapper + + +class Testllvmprojs(unittest.TestCase): + script = os.path.join("scripts", "llvm.py") + + @classmethod + def __create_dummy_commit(cls): + filename = "filethatshouldntexist" + cls.run_quietly(["touch", filename]) + cls.run_quietly(["git", "add", filename]) + cls.run_quietly(["git", "commit", "-m", "Dummy commit"]) + + @classmethod + def __create_dummy_repo(cls, repopath): + if not os.path.isdir(repopath): + os.makedirs(repopath) + + with cd(repopath): + cls.run_quietly(["git", "init"]) + cls.__create_dummy_commit() + + @classmethod + def __add_worktree(cls, repopath, worktreepath, branch): + with cd(repopath): + cls.run_quietly(["git", "worktree", "add", worktreepath, + "-b", branch]) + + @classmethod + def __get_subproj_repo(cls, subproj): + return os.path.join(cls.repos, subproj) + + @classmethod + def setUpClass(cls): + """Create the file structure and environment that llvmprojs expects""" + cls.llvm_root = mkdtemp() + cls.repos = os.path.join(cls.llvm_root, "repos") + + cls.all_repos = ("llvm", "clang", "compiler-rt", "lld", "lldb", + "libcxx", "libcxxabi", "libunwind", "test-suite") + + # Set up helper functions for running commands (this needs to be done + # before creating the repos) + # FIXME: In newer versions of Python (3.3+) we should be able to bind + # the stdout to DEVNULL, as below: + # cls.run_quietly = partial(subprocess.call, stdout=subprocess.DEVNULL) + # This doesn't work in Python 2, so we'll just have to ignore the output + # for now... + cls.run_with_output = partial(subprocess.check_output, + stderr=subprocess.STDOUT) + cls.run_quietly = cls.run_with_output + + # Create dummy repos + for reponame in cls.all_repos: + cls.__create_dummy_repo(cls.__get_subproj_repo(reponame)) + + @classmethod + def tearDownClass(cls): + shutil.rmtree(cls.llvm_root) + + @classmethod + def setUp(cls): + cls.llvm_src = os.path.join(cls.llvm_root, "src" + str(uuid4())) + + # Create LLVM worktree + cls.branch = "br" + str(uuid4()) + cls.__add_worktree(cls.__get_subproj_repo("llvm"), cls.llvm_src, + cls.branch) + + # Set up the environment variables + os.environ["LLVM_ROOT"] = cls.llvm_root + os.environ["LLVM_SRC"] = cls.llvm_src + + @classmethod + def tearDown(cls): + # Clean up the directories where we might have added subprojects. + # This isn't 100% clean, because we don't clean up the repos between + # tests (so any branches will remain), but it's good enough for the + # current tests. + for subprojdir in (os.path.join(cls.llvm_src, "projects"), + os.path.join(cls.llvm_src, "tools")): + if os.path.isdir(subprojdir): + shutil.rmtree(subprojdir) + os.makedirs(subprojdir) + + # Run prune on the original repos, to remove any dangling worktrees. + for reponame in cls.all_repos: + repopath = cls.__get_subproj_repo(reponame) + with cd(repopath): + cls.run_quietly(["git", "worktree", "prune"]) + + def test_dump_empty_config(self): + """ + Test that we're correctly dumping an empty configuration (i.e. no + projects linked) when running llvmprojs without arguments. + """ + output = self.run_with_output(["python", self.script, "projects"]) + self.assertRegexpMatches(output, "Projects linked:.*\n.*none.*") + + def test_add_remove_subprojects(self): + """ + Test that we can add and remove one or several subprojects. + """ + output = self.run_with_output(["python", self.script, "projects", + "--add", "clang"]) + self.assertRegexpMatches(output, "Projects linked:.*\n.*clang.*") + + output = self.run_with_output(["python", self.script, "projects", + "--add", "libcxx", "lldb"]) + self.assertRegexpMatches( + output, + "Projects linked:.*\n" + + ".*clang.*\n" + + ".*libcxx.*\n" + + ".*lldb.*") + + output = self.run_with_output(["python", self.script, "projects", + "--remove", "libcxx"]) + self.assertRegexpMatches(output, + "Projects linked:.*\n" + + ".*clang.*\n" + + ".*lldb.*") + + output = self.run_with_output(["python", self.script, "projects", + "--remove", "clang", "lldb"]) + self.assertRegexpMatches(output, + "Projects linked:.*\n" + + ".*none.*") + + def test_add_remove_invalid_subprojects(self): + """ + Test that we error out nicely when trying to add/remove invalid + subprojects. + """ + with self.assertRaises(subprocess.CalledProcessError) as context: + self.run_quietly(["python", self.script, "projects", + "--add", "inventedsubproject"]) + + self.assertRegexpMatches( + context.exception.output, + "(.*\n)*.*invalid choice:.*inventedsubproject(.*\n)*") + + with self.assertRaises(subprocess.CalledProcessError) as context: + self.run_quietly(["python", self.script, "projects", + "--remove", "inventedsubproject"]) + + self.assertRegexpMatches( + context.exception.output, + "(.*\n)*.*invalid choice:.*inventedsubproject(.*\n)*") + + def test_duplicate_add_remove(self): + """ + Test that we don't crash when trying to add / remove the same subproject + twice with the same command. + """ + output = self.run_with_output(["python", self.script, "projects", + "--add", "clang", "lld", "clang"]) + self.assertRegexpMatches(output, + "Projects linked:.*\n" + + ".*clang.*\n" + + ".*lld.*") + + output = self.run_with_output(["python", self.script, "projects", + "--remove", "lld", "lld", "clang"]) + self.assertRegexpMatches(output, + "Projects linked:.*\n" + + ".*none.*") + + def test_redundant_add_remove(self): + """ + Test that we can add a subproject that already exists (either because it + was added by our script or manually) or remove a subproject that doesn't + exist. + """ + self.__add_worktree(self.__get_subproj_repo("clang"), + os.path.join(self.llvm_src, "tools", "clang"), + self.branch) + self.__add_worktree( + self.__get_subproj_repo("compiler-rt"), + os.path.join(self.llvm_src, "projects", "compiler-rt"), + self.branch) + + output = self.run_with_output(["python", self.script, "projects", + "--add", "clang"]) + self.assertRegexpMatches(output, + "Projects linked:.*\n" + + ".*clang.*") + + output = self.run_with_output(["python", self.script, "projects", + "--add", "compiler-rt", "lld"]) + self.assertRegexpMatches( + output, + "Projects linked:.*\n" + + ".*clang.*\n" + + ".*compiler-rt.*\n" + + ".*lld.*") + + output = self.run_with_output(["python", self.script, "projects", + "--remove", "lldb", "libcxx", "lld"]) + self.assertRegexpMatches( + output, + "Projects linked:.*\n" + + ".*clang.*\n" + + ".*compiler-rt.*") + + def test_simultaneous_add_remove(self): + """ + Test that we error out when someone is trying to add and remove the same + project with the same command. + """ + # Try the really basic case and make sure we're not touching anything + with self.assertRaises(subprocess.CalledProcessError) as context: + self.run_quietly(["python", self.script, "projects", + "--add", "clang", "--remove", "clang"]) + + self.assertRegexpMatches( + context.exception.output, + "(.*\n)*.*Can't add and remove clang at the same time(.*\n)*") + + self.assertFalse( + os.path.exists( + os.path.join(self.llvm_src, "tools", "clang"))) + + # Try something a bit more complicated and make sure we're not touching + # anything + self.__add_worktree( + self.__get_subproj_repo("lld"), + os.path.join(self.llvm_src, "tools", "lld"), + self.branch) + + with self.assertRaises(subprocess.CalledProcessError) as context: + self.run_quietly(["python", self.script, "projects", + "--add", "clang", "lld", "libcxx", + "--remove", "lld", "libcxx"]) + self.assertRegexpMatches( + context.exception.output, + "(.*\n)*" + + ".*Can't add and remove (lld|libcxx) at the same time(.*\n)*") + + # Make sure we didn't touch anything + self.assertFalse( + os.path.exists( + os.path.join(self.llvm_src, "tools", "clang"))) + self.assertTrue( + os.path.exists( + os.path.join(self.llvm_src, "tools", "lld"))) + self.assertFalse( + os.path.exists( + os.path.join(self.llvm_src, "projects", "libcxx"))) + + def test_multiple_adds_removes(self): + """ + Test that we can have multiple --add and --remove options in the same + command and that only the last one of each kind matters. + """ + output = self.run_with_output(["python", self.script, "projects", + "--add", "libcxxabi", + "--remove", "lld", "lldb", + "--add", "clang", "libcxx", + "--remove", "libunwind"]) + self.assertRegexpMatches(output, + "Projects linked:.*\n" + + ".*clang.*\n" + + ".*libcxx.*\n") + + output = self.run_with_output(["python", self.script, "projects", + "--add", "libunwind", "libcxxabi", + "--remove", "clang", "libcxx", + "--add", "compiler-rt", + "--remove", "libcxxabi"]) + self.assertRegexpMatches(output, + "Projects linked:.*\n" + + ".*clang.*\n" + + ".*compiler-rt.*\n" + + ".*libcxx.*\n") + + output = self.run_with_output(["python", self.script, "projects", + "--add", "libcxx", "--remove", "lld", + "--add", "lld", "--remove", "libcxx"]) + self.assertRegexpMatches(output, + "Projects linked:.*\n" + + ".*clang.*\n" + + ".*compiler-rt.*\n" + + ".*lld.*\n") diff --git a/tests/unittests/__init__.py b/tests/unittests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/unittests/__init__.py diff --git a/tests/unittests/testllvmsourceconfig.py b/tests/unittests/testllvmsourceconfig.py new file mode 100644 index 0000000..1add13a --- /dev/null +++ b/tests/unittests/testllvmsourceconfig.py @@ -0,0 +1,572 @@ +import os +import unittest +import uuid + +from sh import git + +from linaropy.cd import cd +from linaropy.proj import Proj +from linaropy.git.clone import Clone +from linaropy.git.worktree import Worktree + +from modules.llvm import LLVMSourceConfig, LLVMSubproject + + +class TestLLVMSourceConfig(unittest.TestCase): + testdirprefix = "SourceConfigUT" + + def __create_dummy_commit(self): + filename = "file" + str(uuid.uuid4()) + open(filename, "a").close() + git("add", filename) + git("commit", "-m", "Branches without commits confuse git") + + def __create_dummy_repo(self, path): + if not os.path.exists(path): + os.makedirs(path) + + with cd(path): + git("init") + self.__create_dummy_commit() + + def __get_subproj_repo_path(self, subproj): + return os.path.join(self.originalLLVM.repodir, "..", subproj + "-repo") + + def __get_subproj_repo(self, subproj): + return Clone(self.proj, self.__get_subproj_repo_path(subproj)) + + def setUp(self): + # We're going to create a hierarchy with [llvm|clang|whatever]-repo + # containing dummy repos, and llvm-copy containing a worktree of + # llvm-repo + self.proj = Proj(prefix=TestLLVMSourceConfig.testdirprefix) + path = os.path.join(self.proj.projdir, "llvm-repo") + self.__create_dummy_repo(path) + self.originalLLVM = Clone(self.proj, path) + + subprojs = LLVMSubproject.get_all_subprojects() + for subproj in subprojs.keys(): + repo = self.__get_subproj_repo_path(subproj) + self.__create_dummy_repo(repo) + + self.temporaryLLVMbranch = "a-branch" + self.temporaryLLVM = Worktree.create( + self.proj, self.originalLLVM, os.path.join( + self.proj.projdir, "llvm-copy"), self.temporaryLLVMbranch) + + def tearDown(self): + self.proj.cleanup() + + def test_detect_enabled_all(self): + subprojs = LLVMSubproject.get_all_subprojects() + sourcePath = self.temporaryLLVM.repodir + + for subproj in subprojs: + path = subprojs[subproj].get_cmake_path(sourcePath) + worktree = Worktree.create( + self.proj, + self.__get_subproj_repo(subproj), + path, + self.temporaryLLVMbranch) + self.assertTrue(os.path.isdir(path), "Failed to create worktree") + + config = LLVMSourceConfig(self.proj, sourcePath) + enabled = config.get_enabled_subprojects() + + self.assertEqual(set(subprojs), set(enabled), + "Expected %s but detected only %s" % + (str(set(subprojs)), str(enabled))) + + def test_detect_enabled_none(self): + sourcePath = self.temporaryLLVM.repodir + + path = os.path.join(sourcePath, "unrelated") + os.makedirs(path) + + path = os.path.join(sourcePath, "wrong", "place", "for", "lld") + os.makedirs(path) + + path = os.path.join(sourcePath, "projects", "clang") + os.makedirs(path) + + path = os.path.join(sourcePath, "tools", "libcxx") + os.makedirs(path) + + config = LLVMSourceConfig(self.proj, sourcePath) + enabled = config.get_enabled_subprojects() + + self.assertEqual( + enabled, + [], + "Detected unexpected projects %s" % str(enabled) + ) + + def test_detect_enabled_some(self): + sourcePath = self.temporaryLLVM.repodir + subprojs = LLVMSubproject.get_all_subprojects() + + for subproj in ["lld", "libcxxabi", "clang"]: + path = subprojs[subproj].get_cmake_path(sourcePath) + worktree = Worktree.create( + self.proj, + self.__get_subproj_repo(subproj), + path, + self.temporaryLLVMbranch) + self.assertTrue(os.path.isdir(path), "Failed to create worktree") + + config = LLVMSourceConfig(self.proj, sourcePath) + enabled = config.get_enabled_subprojects() + + self.assertTrue("lld" in enabled, "Failed to detect lld") + self.assertTrue("clang" in enabled, "Failed to detect clang") + self.assertTrue("libcxxabi" in enabled, + "Failed to detect libcxxabi") + + def test_detect_enabled_not_worktree(self): + sourcePath = self.temporaryLLVM.repodir + subprojs = LLVMSubproject.get_all_subprojects() + + path = subprojs["compiler-rt"].get_cmake_path(sourcePath) + os.makedirs(path) + + config = LLVMSourceConfig(self.proj, sourcePath) + enabled = config.get_enabled_subprojects() + + # Check that if it's not a worktree, it's not enabled. + self.assertEqual( + enabled, + [], + "Detected unexpected projects %s" % str(enabled) + ) + + def test_detect_enabled_wrong_branch(self): + sourcePath = self.temporaryLLVM.repodir + subprojs = LLVMSubproject.get_all_subprojects() + + path = subprojs["compiler-rt"].get_cmake_path(sourcePath) + branch = "different-than-" + self.temporaryLLVMbranch + worktree = Worktree.create( + self.proj, + self.__get_subproj_repo("compiler-rt"), + path, + branch) + self.assertTrue(os.path.isdir(path), "Failed to create worktree") + + config = LLVMSourceConfig(self.proj, sourcePath) + enabled = config.get_enabled_subprojects() + + # Check that if it's a worktree on the wrong branch, it's not enabled. + self.assertEqual( + enabled, + [], + "Detected unexpected projects %s" % str(enabled) + ) + + def test_add_invalid_subproject(self): + config = LLVMSourceConfig(self.proj, self.temporaryLLVM.repodir) + subproj = "not-an-llvm-subproject" + subprojPath = self.originalLLVM.repodir # Dummy path + + with self.assertRaises(ValueError) as context: + config.update({subproj : Clone(self.proj, subprojPath)}) + + self.assertRegexpMatches(str(context.exception), + "Unknown llvm subproject %s" % subproj) + + def test_add_each_subproject(self): + sourcePath = self.temporaryLLVM.repodir + + config = LLVMSourceConfig(self.proj, sourcePath) + + subprojs = LLVMSubproject.get_all_subprojects() + for subproj in subprojs: + expectedPath = subprojs[subproj].get_cmake_path(sourcePath) + config.update({subproj : self.__get_subproj_repo(subproj)}) + self.assertTrue(os.path.isdir(expectedPath), + "Failed to add subproject %s" % subproj) + + def test_add_all_subprojects(self): + sourcePath = self.temporaryLLVM.repodir + + config = LLVMSourceConfig(self.proj, sourcePath) + + to_add = {} + subprojs = LLVMSubproject.get_all_subprojects() + + for subproj in subprojs: + to_add[subproj] = self.__get_subproj_repo(subproj) + + config.update(to_add) + + for subproj in subprojs: + expectedPath = subprojs[subproj].get_cmake_path(sourcePath) + self.assertTrue(os.path.isdir(expectedPath), + "Failed to add subproject %s" % subproj) + + def test_add_some_subprojects(self): + sourcePath = self.temporaryLLVM.repodir + + config = LLVMSourceConfig(self.proj, sourcePath) + + to_add = {} + to_add["clang"] = self.__get_subproj_repo("clang") + to_add["compiler-rt"] = self.__get_subproj_repo("compiler-rt") + + config.update(to_add) + + subprojs = LLVMSubproject.get_all_subprojects() + self.assertTrue( + os.path.isdir(subprojs["clang"].get_cmake_path(sourcePath)), + "Failed to add subproject clang") + self.assertTrue( + os.path.isdir(subprojs["compiler-rt"].get_cmake_path(sourcePath)), + "Failed to add subproject compiler-rt") + + def test_add_existing_subprojects(self): + sourcePath = self.temporaryLLVM.repodir + + config = LLVMSourceConfig(self.proj, sourcePath) + + subprojs = LLVMSubproject.get_all_subprojects() + existingPath = subprojs["lldb"].get_cmake_path(sourcePath) + Worktree.create( + self.proj, + self.__get_subproj_repo("lldb"), + existingPath, + self.temporaryLLVMbranch) + + config.update({ "lldb" : self.__get_subproj_repo("lldb")}) + + # If we got this far, we're probably ok, but let's be pedantic and check + # that the subproject is still there + self.assertTrue(os.path.isdir(existingPath), + "Existing subproject vanished") + + def test_add_subproject_existing_branch(self): + """ + Test that we can add a subproject that already has the branch that LLVM + is on. This can happen for instance if we have added and then removed + the subproject and now we're trying to add it again. + """ + sourcePath = self.temporaryLLVM.repodir + + config = LLVMSourceConfig(self.proj, sourcePath) + + clangRepo = self.__get_subproj_repo("clang") + with cd(clangRepo.repodir): + # Make sure that the branch that LLVM is on already exists in the + # clang repo as well. + git("checkout", "-b", self.temporaryLLVMbranch) + self.__create_dummy_commit() + git("checkout", "master") + + config.update( { "clang" : clangRepo }) + + subprojs = LLVMSubproject.get_all_subprojects() + path = subprojs["clang"].get_cmake_path(sourcePath) + self.assertTrue(os.path.isdir(path), "Failed to add subproject") + + def test_add_subproject_not_a_worktree(self): + """ + Test that we can't update a config to include a subproject that exists + but is not a worktree. + """ + sourcePath = self.temporaryLLVM.repodir + branch = "different-than-" + self.temporaryLLVMbranch + + config = LLVMSourceConfig(self.proj, sourcePath) + + subprojs = LLVMSubproject.get_all_subprojects() + existingPath = subprojs["lldb"].get_cmake_path(sourcePath) + os.makedirs(existingPath) + + with self.assertRaises(EnvironmentError) as context: + config.update({ "lldb" : self.__get_subproj_repo("lldb")}) + + self.assertRegexpMatches(str(context.exception), + "{} already exists but is not a valid subproject directory.*" + .format(existingPath)) + + # If we got this far, we're probably ok, but let's be pedantic and check + # that the subproject is still there + self.assertTrue(os.path.isdir(existingPath), + "Existing subproject vanished") + + def test_add_subproject_wrong_branch(self): + """ + Test that we can't update a config to include a subproject that exists + but is on the wrong branch. + """ + sourcePath = self.temporaryLLVM.repodir + branch = "different-than-" + self.temporaryLLVMbranch + + config = LLVMSourceConfig(self.proj, sourcePath) + + subprojs = LLVMSubproject.get_all_subprojects() + existingPath = subprojs["lldb"].get_cmake_path(sourcePath) + Worktree.create( + self.proj, + self.__get_subproj_repo("lldb"), + existingPath, + branch) + + with self.assertRaises(EnvironmentError) as context: + config.update({ "lldb" : self.__get_subproj_repo("lldb")}) + + self.assertRegexpMatches(str(context.exception), + "{} already exists but is not a valid subproject directory.*" + .format(existingPath)) + + # If we got this far, we're probably ok, but let's be pedantic and check + # that the subproject is still there + self.assertTrue(os.path.isdir(existingPath), + "Existing subproject vanished") + + def test_remove_subproject(self): + sourcePath = self.temporaryLLVM.repodir + + subprojs = LLVMSubproject.get_all_subprojects() + + lldPath = subprojs["lld"].get_cmake_path(sourcePath) + lldWorktree = Worktree.create( + self.proj, + self.__get_subproj_repo("lld"), + lldPath, + self.temporaryLLVMbranch) + + clangPath = subprojs["clang"].get_cmake_path(sourcePath) + clangWorktree = Worktree.create( + self.proj, + self.__get_subproj_repo("clang"), + clangPath, + self.temporaryLLVMbranch) + + config = LLVMSourceConfig(self.proj, sourcePath) + config.update(remove=["lld"]) + + self.assertFalse(os.path.isdir(lldPath), "Failed to remove subproject") + self.assertTrue(os.path.isdir(clangPath), "Removed sibling subproject") + + def test_remove_some_subprojects(self): + sourcePath = self.temporaryLLVM.repodir + + subprojs = LLVMSubproject.get_all_subprojects() + + for subproj in ["clang", "compiler-rt", "lld", "lldb", "libunwind"]: + path = subprojs[subproj].get_cmake_path(sourcePath) + worktree = Worktree.create( + self.proj, + self.__get_subproj_repo(subproj), + path, + self.temporaryLLVMbranch) + + config = LLVMSourceConfig(self.proj, sourcePath) + config.update(remove=["compiler-rt", "lld"]) + + for subproj in ["compiler-rt", "lld"]: + path = subprojs[subproj].get_cmake_path(sourcePath) + self.assertFalse( + os.path.isdir(path), + "Failed to remove subproject") + + for subproj in ["clang", "lldb", "libunwind"]: + path = subprojs[subproj].get_cmake_path(sourcePath) + self.assertTrue(os.path.isdir(path), "Removed sibling subproject") + + def test_remove_all_subprojects(self): + sourcePath = self.temporaryLLVM.repodir + + subprojs = LLVMSubproject.get_all_subprojects() + + for subproj in subprojs.keys(): + path = subprojs[subproj].get_cmake_path(sourcePath) + worktree = Worktree.create( + self.proj, + self.__get_subproj_repo(subproj), + path, + self.temporaryLLVMbranch) + self.assertTrue(os.path.isdir(path), "Failed to create worktree") + + config = LLVMSourceConfig(self.proj, sourcePath) + config.update(remove=subprojs.keys()) + + for subproj in subprojs.keys(): + path = subprojs[subproj].get_cmake_path(sourcePath) + self.assertFalse( + os.path.isdir(path), + "Failed to remove subproject") + + def test_remove_each_subproject(self): + sourcePath = self.temporaryLLVM.repodir + + subprojs = LLVMSubproject.get_all_subprojects() + + for subproj in subprojs.keys(): + path = subprojs[subproj].get_cmake_path(sourcePath) + worktree = Worktree.create( + self.proj, + self.__get_subproj_repo(subproj), + path, + self.temporaryLLVMbranch) + self.assertTrue(os.path.isdir(path), "Failed to create worktree") + + config = LLVMSourceConfig(self.proj, sourcePath) + + for subproj in subprojs.keys(): + config.update(remove=[subproj]) + + for subproj in subprojs.keys(): + path = subprojs[subproj].get_cmake_path(sourcePath) + self.assertFalse( + os.path.isdir(path), + "Failed to remove subproject") + + def test_remove_duplicates(self): + sourcePath = self.temporaryLLVM.repodir + + subprojs = LLVMSubproject.get_all_subprojects() + + for subproj in ["clang", "compiler-rt", "lld", "lldb", "libunwind"]: + path = subprojs[subproj].get_cmake_path(sourcePath) + worktree = Worktree.create( + self.proj, + self.__get_subproj_repo(subproj), + path, + self.temporaryLLVMbranch) + + config = LLVMSourceConfig(self.proj, sourcePath) + config.update(remove=["compiler-rt", "lld", "lld", "compiler-rt"]) + + for subproj in ["compiler-rt", "lld"]: + path = subprojs[subproj].get_cmake_path(sourcePath) + self.assertFalse( + os.path.isdir(path), + "Failed to remove subproject") + + for subproj in ["clang", "lldb", "libunwind"]: + path = subprojs[subproj].get_cmake_path(sourcePath) + self.assertTrue(os.path.isdir(path), "Removed sibling subproject") + + def test_remove_invalid_subproject(self): + sourcePath = self.temporaryLLVM.repodir + config = LLVMSourceConfig(self.proj, sourcePath) + + subproj = "not-an-llvm-subproject" + + with self.assertRaises(ValueError) as context: + config.update(remove=[subproj]) + + self.assertRegexpMatches(str(context.exception), + "Unknown llvm subproject %s" % subproj) + + def test_remove_inexistent_subproject(self): + sourcePath = self.temporaryLLVM.repodir + + subprojs = LLVMSubproject.get_all_subprojects() + + lldPath = subprojs["lld"].get_cmake_path(sourcePath) + lldWorktree = Worktree.create( + self.proj, + self.__get_subproj_repo("lld"), + lldPath, + self.temporaryLLVMbranch) + + clangPath = subprojs["clang"].get_cmake_path(sourcePath) + + config = LLVMSourceConfig(self.proj, sourcePath) + config.update(remove=["clang"]) + + self.assertFalse( + os.path.isdir(clangPath), + "Failed to remove subproject") + self.assertTrue(os.path.isdir(lldPath), "Removed sibling subproject") + + def test_add_after_remove(self): + sourcePath = self.temporaryLLVM.repodir + + subprojs = LLVMSubproject.get_all_subprojects() + lldbRepo = self.__get_subproj_repo("lldb") + + config = LLVMSourceConfig(self.proj, sourcePath) + + config.update({"lldb" : lldbRepo}) + self.assertTrue( + os.path.isdir(subprojs["lldb"].get_cmake_path(sourcePath)), + "Failed to add lldb") + + config.update(remove=["lldb"]) + self.assertFalse( + os.path.isdir(subprojs["lldb"].get_cmake_path(sourcePath)), + "Failed to remove lldb") + + config.update({"lldb" : lldbRepo }) + self.assertTrue( + os.path.isdir(subprojs["lldb"].get_cmake_path(sourcePath)), + "Failed to add lldb") + + def test_mixed_adds_removes(self): + sourcePath = self.temporaryLLVM.repodir + + subprojs = LLVMSubproject.get_all_subprojects() + + for subproj in ["clang", "compiler-rt", "lld", "lldb", "libunwind"]: + path = subprojs[subproj].get_cmake_path(sourcePath) + worktree = Worktree.create( + self.proj, + self.__get_subproj_repo(subproj), + path, + self.temporaryLLVMbranch) + + config = LLVMSourceConfig(self.proj, sourcePath) + config.update({ + "libcxx" : self.__get_subproj_repo("libcxx"), + "libcxxabi" : self.__get_subproj_repo("libcxxabi")}, + ["compiler-rt", "lld"]) + + for subproj in ["libcxx", "libcxxabi"]: + path = subprojs[subproj].get_cmake_path(sourcePath) + self.assertTrue(os.path.isdir(path), "Failed to add subproject") + + for subproj in ["compiler-rt", "lld"]: + path = subprojs[subproj].get_cmake_path(sourcePath) + self.assertFalse( + os.path.isdir(path), + "Failed to remove subproject") + + for subproj in ["clang", "lldb", "libunwind"]: + path = subprojs[subproj].get_cmake_path(sourcePath) + self.assertTrue(os.path.isdir(path), "Removed sibling subproject") + + def test_simultaneous_add_remove(self): + sourcePath = self.temporaryLLVM.repodir + subprojs = LLVMSubproject.get_all_subprojects() + + clangRepo = self.__get_subproj_repo("clang") + lldRepo = self.__get_subproj_repo("lld") + libunwindRepo = self.__get_subproj_repo("libunwind") + + config = LLVMSourceConfig(self.proj, sourcePath) + with self.assertRaises(ValueError) as context: + config.update( + { "clang" : clangRepo, "lld" : lldRepo, "libunwind" : + libunwindRepo}, ["libcxx", "lld", "libcxxabi"]) + + self.assertEqual(str(context.exception), + "Can't add and remove lld at the same time") + + # Make sure we didn't add any of the others either + for subproj in ["clang", "libunwind"]: + path = subprojs[subproj].get_cmake_path(sourcePath) + self.assertFalse( + os.path.isdir(path), + "Incorrectly added subproject") + + # TODO: test with a different dictionary than the default one (not + # necessarily containing subprojects - it can contain "potato", "banana" and + # "gazpacho" for all we care); in fact, it would probably be best to move the + # existing tests to that... + + # TODO: test that CMake gets our layout + +if __name__ == "__main__": + unittest.main() diff --git a/tests/unittests/testllvmsubprojects.py b/tests/unittests/testllvmsubprojects.py new file mode 100644 index 0000000..c39e7ae --- /dev/null +++ b/tests/unittests/testllvmsubprojects.py @@ -0,0 +1,64 @@ +import os +import unittest + +from modules.llvm import LLVMSubproject + + +class TestLLVMSubproject(unittest.TestCase): + """Test that we have the right info on LLVM subprojects.""" + + def test_build_paths(self): + subprojects = LLVMSubproject.get_all_subprojects() + + self.assertEqual(subprojects["clang"].cmake_path, + os.path.join("tools", "clang")) + self.assertEqual(subprojects["compiler-rt"].cmake_path, + os.path.join("projects", "compiler-rt")) + self.assertEqual(subprojects["libcxx"].cmake_path, + os.path.join("projects", "libcxx")) + self.assertEqual(subprojects["libcxxabi"].cmake_path, + os.path.join("projects", "libcxxabi")) + self.assertEqual(subprojects["libunwind"].cmake_path, + os.path.join("projects", "libunwind")) + self.assertEqual(subprojects["lld"].cmake_path, + os.path.join("tools", "lld")) + self.assertEqual(subprojects["lldb"].cmake_path, + os.path.join("tools", "lldb")) + self.assertEqual(subprojects["test-suite"].cmake_path, + os.path.join("projects", "test-suite")) + + def test_cmake_vars(self): + subprojects = LLVMSubproject.get_all_subprojects() + + self.assertEqual( + subprojects["clang"].cmake_var, + "LLVM_TOOL_CLANG_BUILD") + self.assertEqual(subprojects["compiler-rt"].cmake_var, + "LLVM_TOOL_COMPILER_RT_BUILD") + self.assertEqual( + subprojects["libcxx"].cmake_var, + "LLVM_TOOL_LIBCXX_BUILD") + self.assertEqual(subprojects["libcxxabi"].cmake_var, + "LLVM_TOOL_LIBCXXABI_BUILD") + self.assertEqual( + subprojects["libunwind"].cmake_var, + "LLVM_TOOL_LIBUNWIND_BUILD") + self.assertEqual(subprojects["lld"].cmake_var, "LLVM_TOOL_LLD_BUILD") + self.assertEqual(subprojects["lldb"].cmake_var, "LLVM_TOOL_LLDB_BUILD") + self.assertEqual(subprojects["test-suite"].cmake_var, None) + + def test_get_cmake_path(self): + subproject = LLVMSubproject( + os.path.join( + "path", + "to", + "subproj"), + None) + cmake_path = subproject.get_cmake_path(os.path.join("llvm", "sources")) + + self.assertEqual( + str(cmake_path), + os.path.join("llvm", "sources", "path", "to", "subproj")) + +if __name__ == "__main__": + unittest.main() |