Commit 624b16f9 authored by Wez Furlong's avatar Wez Furlong Committed by Facebook Github Bot

fbcode_builder: getdeps: add testing concept

Summary:
Adds a `test` subcommand that runs the tests for project.
We're mostly interested in the 1st party facebook projects for this.

The `sandcastle` flow will run the `test` subcommand just for the "leaf" project--the one named on the command line.

Reviewed By: simpkins

Differential Revision: D14710331

fbshipit-source-id: 7d04a46cfd723894d61018de2f230140b52285ac
parent 5e3d6786
......@@ -153,10 +153,9 @@ class BuildCmd(SubCmd):
reconfigure = change_status.build_changed()
sources_changed = change_status.sources_changed()
hash = fetcher.hash()
directory = "%s-%s" % (m.name, hash)
build_dir = os.path.join(opts.scratch_dir, "build", directory)
inst_dir = os.path.join(opts.scratch_dir, "installed", directory)
dirs = opts.compute_dirs(m, fetcher)
build_dir = dirs["build_dir"]
inst_dir = dirs["inst_dir"]
built_marker = os.path.join(inst_dir, ".built-by-getdeps")
if os.path.exists(built_marker):
......@@ -175,7 +174,7 @@ class BuildCmd(SubCmd):
builder.build(install_dirs, reconfigure=reconfigure)
with open(built_marker, "w") as f:
f.write(hash)
f.write(dirs["hash"])
install_dirs.append(inst_dir)
......@@ -198,6 +197,56 @@ class BuildCmd(SubCmd):
)
@cmd("test", "test a given project")
class TestCmd(SubCmd):
def run(self, args):
opts = setup_build_options(args)
manifest = load_project(opts, args.project)
ctx = context_from_host_tuple()
projects = manifests_in_dependency_order(opts, manifest, ctx)
# Accumulate the install directories so that the test steps
# can find their dep installation
install_dirs = []
for m in projects:
fetcher = m.create_fetcher(opts, ctx)
dirs = opts.compute_dirs(m, fetcher)
build_dir = dirs["build_dir"]
inst_dir = dirs["inst_dir"]
if m == manifest or args.test_all:
built_marker = os.path.join(inst_dir, ".built-by-getdeps")
if not os.path.exists(built_marker):
print("project %s has not been built" % m.name)
# TODO: we could just go ahead and build it here, but I
# want to tackle that as part of adding build-for-test
# support.
return 1
src_dir = fetcher.get_src_dir()
builder = m.create_builder(opts, src_dir, build_dir, inst_dir, ctx)
builder.run_tests(install_dirs)
install_dirs.append(inst_dir)
def setup_parser(self, parser):
parser.add_argument(
"project",
help=(
"name of the project or path to a manifest "
"file describing the project"
),
)
parser.add_argument(
"--test-all",
action="store_true",
default=False,
help="Enable running tests for the named project and all of its deps",
)
def build_argparser():
common_args = argparse.ArgumentParser(add_help=False)
common_args.add_argument(
......
......@@ -83,6 +83,11 @@ class BuilderBase(object):
self._build(install_dirs=install_dirs, reconfigure=reconfigure)
def run_tests(self, install_dirs):
""" Execute any tests that we know how to run. If they fail,
raise an exception. """
pass
def _build(self, install_dirs, reconfigure):
""" Perform the build.
install_dirs contains the list of installation directories for
......@@ -171,6 +176,43 @@ class CMakeBuilder(BuilderBase):
return True
return False
def _compute_env(self, install_dirs):
# CMAKE_PREFIX_PATH is only respected when passed through the
# environment, so we construct an appropriate path to pass down
env = self.env.copy()
lib_path = None
if self.build_opts.is_darwin():
lib_path = "DYLD_LIBRARY_PATH"
elif self.build_opts.is_linux():
lib_path = "LD_LIBRARY_PATH"
else:
lib_path = None
for d in install_dirs:
add_path_entry(env, "CMAKE_PREFIX_PATH", d)
pkgconfig = os.path.join(d, "lib/pkgconfig")
if os.path.exists(pkgconfig):
add_path_entry(env, "PKG_CONFIG_PATH", pkgconfig)
# Allow resolving shared objects built earlier (eg: zstd
# doesn't include the full path to the dylib in its linkage
# so we need to give it an assist)
if lib_path:
for lib in ["lib", "lib64"]:
libdir = os.path.join(d, lib)
if os.path.exists(libdir):
add_path_entry(env, lib_path, libdir)
# Allow resolving binaries (eg: cmake, ninja) and dlls
# built by earlier steps
bindir = os.path.join(d, "bin")
if os.path.exists(bindir):
add_path_entry(env, "PATH", bindir, append=False)
return env
def _build(self, install_dirs, reconfigure):
reconfigure = reconfigure or self._needs_reconfigure()
......@@ -183,6 +225,24 @@ class CMakeBuilder(BuilderBase):
# medium.
"CMAKE_BUILD_TYPE": "RelWithDebInfo",
}
env = self._compute_env(install_dirs)
if self.build_opts.is_darwin():
# Try to persuade cmake to set the rpath to match the lib
# dirs of the dependencies. This isn't automatic, and to
# make things more interesting, cmake uses `;` as the path
# separator, so translate the runtime path to something
# that cmake will parse
defines["CMAKE_INSTALL_RPATH"] = ";".join(
env.get("DYLD_LIBRARY_PATH", "").split(":")
)
# Tell cmake that we want to set the rpath in the tree
# at build time. Without this the rpath is only set
# at the moment that the binaries are installed. That
# default is problematic for example when using the
# gtest integration in cmake which runs the built test
# executables during the build to discover the set of
# tests.
defines["CMAKE_BUILD_WITH_INSTALL_RPATH"] = "ON"
defines.update(self.defines)
define_args = ["-D%s=%s" % (k, v) for (k, v) in defines.items()]
......@@ -191,16 +251,6 @@ class CMakeBuilder(BuilderBase):
# define_args += ["-G", "Visual Studio 15 2017 Win64"]
define_args += ["-G", "Ninja"]
# CMAKE_PREFIX_PATH is only respected when passed through the
# environment, so we construct an appropriate path to pass down
env = self.env.copy()
for d in install_dirs:
add_path_entry(env, "CMAKE_PREFIX_PATH", d)
add_path_entry(env, "PKG_CONFIG_PATH", "%s/lib/pkgconfig" % d)
bindir = os.path.join(d, "bin")
add_path_entry(env, "PATH", bindir, append=False)
# Resolve the cmake that we installed
cmake = path_search(env, "cmake")
......@@ -223,6 +273,13 @@ class CMakeBuilder(BuilderBase):
env=env,
)
def run_tests(self, install_dirs):
env = self._compute_env(install_dirs)
ctest = path_search(env, "ctest")
self._run_cmd(
[ctest, "--output-on-failure", "-j", str(self.build_opts.num_jobs)], env=env
)
class NinjaBootstrap(BuilderBase):
def __init__(self, build_opts, ctx, manifest, build_dir, src_dir, inst_dir):
......
......@@ -89,6 +89,14 @@ class BuildOptions(object):
def is_linux(self):
return self.host_type.is_linux()
def compute_dirs(self, manifest, fetcher):
hash = fetcher.hash()
directory = "%s-%s" % (manifest.name, hash)
build_dir = os.path.join(self.scratch_dir, "build", directory)
inst_dir = os.path.join(self.scratch_dir, "installed", directory)
return {"build_dir": build_dir, "inst_dir": inst_dir, "hash": hash}
def list_win32_subst_letters():
output = subprocess.check_output(["subst"]).decode("utf-8")
......
......@@ -49,3 +49,4 @@ fbcode/folly = folly
[cmake.defines]
BUILD_SHARED_LIBS=OFF
BUILD_TESTS=ON
......@@ -12,7 +12,7 @@ builder = cmake
subdir = wangle
[cmake.defines]
BUILD_TESTS = OFF
BUILD_TESTS = ON
[dependencies]
folly
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment