aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rwxr-xr-xcheck.sh7
-rwxr-xr-xhelpers/llvm-projs165
-rw-r--r--modules/__init__.py0
-rw-r--r--modules/llvm.py224
-rw-r--r--scripts/llvm.py106
-rw-r--r--tests/__init__.py0
-rw-r--r--tests/cli/__init__.py0
-rw-r--r--tests/cli/testllvmprojects.py318
-rw-r--r--tests/unittests/__init__.py0
-rw-r--r--tests/unittests/testllvmsourceconfig.py572
-rw-r--r--tests/unittests/testllvmsubprojects.py64
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()