Commit b2c3257c authored by Adam Simpkins's avatar Adam Simpkins Committed by Facebook Github Bot

add a new ManifestLoader class

Summary:
Add a new ManifestLoader class to handle loading manifests and computing
dependencies.

For now the main thing this class does is maintain the `manifest_by_name`
mapping.  In subsequent diffs we should be able to move some additional logic
into this class, which will help clean up the code and eliminate some redudant
work.  In particular, we can have this class cache project hashes, which will
avoid re-computing hashes over and over again for the same projects as we do
in many cases today.  We should also be able to save and re-use some of the
project dependency ordering computation in some cases as well.

Reviewed By: strager

Differential Revision: D16477400

fbshipit-source-id: f06f62f77d8443fccaa69fe4c1306e39c395b325
parent c0c0f5cf
...@@ -18,7 +18,7 @@ import sys ...@@ -18,7 +18,7 @@ import sys
from getdeps.buildopts import setup_build_options from getdeps.buildopts import setup_build_options
from getdeps.dyndeps import create_dyn_dep_munger from getdeps.dyndeps import create_dyn_dep_munger
from getdeps.errors import TransientFailure from getdeps.errors import TransientFailure
from getdeps.load import load_project, manifests_in_dependency_order from getdeps.load import ManifestLoader
from getdeps.manifest import ManifestParser from getdeps.manifest import ManifestParser
from getdeps.platform import HostType from getdeps.platform import HostType
from getdeps.subcmd import SubCmd, add_subcommands, cmd from getdeps.subcmd import SubCmd, add_subcommands, cmd
...@@ -84,14 +84,14 @@ class FetchCmd(SubCmd): ...@@ -84,14 +84,14 @@ class FetchCmd(SubCmd):
def run(self, args): def run(self, args):
opts = setup_build_options(args) opts = setup_build_options(args)
ctx_gen = opts.get_context_generator() loader = ManifestLoader(opts)
manifest = load_project(opts, args.project) manifest = loader.load_manifest(args.project)
if args.recursive: if args.recursive:
projects = manifests_in_dependency_order(opts, manifest, ctx_gen) projects = loader.manifests_in_dependency_order()
else: else:
projects = [manifest] projects = [manifest]
for m in projects: for m in projects:
fetcher = m.create_fetcher(opts, ctx_gen.get_context(m.name)) fetcher = m.create_fetcher(opts, loader.ctx_gen.get_context(m.name))
fetcher.update() fetcher.update()
...@@ -99,10 +99,10 @@ class FetchCmd(SubCmd): ...@@ -99,10 +99,10 @@ class FetchCmd(SubCmd):
class ListDepsCmd(SubCmd): class ListDepsCmd(SubCmd):
def run(self, args): def run(self, args):
opts = setup_build_options(args) opts = setup_build_options(args)
ctx_gen = opts.get_context_generator() loader = ManifestLoader(opts)
ctx_gen.set_value_for_project(args.project, "test", "on") loader.ctx_gen.set_value_for_project(args.project, "test", "on")
manifest = load_project(opts, args.project) loader.load_manifest(args.project)
for m in manifests_in_dependency_order(opts, manifest, ctx_gen): for m in loader.manifests_in_dependency_order():
print(m.name) print(m.name)
return 0 return 0
...@@ -142,11 +142,10 @@ class CleanCmd(SubCmd): ...@@ -142,11 +142,10 @@ class CleanCmd(SubCmd):
class ShowInstDirCmd(SubCmd): class ShowInstDirCmd(SubCmd):
def run(self, args): def run(self, args):
opts = setup_build_options(args) opts = setup_build_options(args)
ctx_gen = opts.get_context_generator() loader = ManifestLoader(opts)
ctx_gen.set_value_for_project(args.project, "test", "on") loader.ctx_gen.set_value_for_project(args.project, "test", "on")
manifest = load_project(opts, args.project) manifest = loader.load_manifest(args.project)
projects = manifests_in_dependency_order(opts, manifest, ctx_gen) projects = loader.manifests_in_dependency_order()
manifests_by_name = {m.name: m for m in projects}
if args.recursive: if args.recursive:
manifests = projects manifests = projects
...@@ -154,9 +153,11 @@ class ShowInstDirCmd(SubCmd): ...@@ -154,9 +153,11 @@ class ShowInstDirCmd(SubCmd):
manifests = [manifest] manifests = [manifest]
for m in manifests: for m in manifests:
ctx = ctx_gen.get_context(m.name) ctx = loader.ctx_gen.get_context(m.name)
fetcher = m.create_fetcher(opts, ctx) fetcher = m.create_fetcher(opts, ctx)
dirs = opts.compute_dirs(m, fetcher, manifests_by_name, ctx_gen) dirs = opts.compute_dirs(
m, fetcher, loader.manifests_by_name, loader.ctx_gen
)
inst_dir = dirs["inst_dir"] inst_dir = dirs["inst_dir"]
print(inst_dir) print(inst_dir)
...@@ -180,17 +181,17 @@ class ShowInstDirCmd(SubCmd): ...@@ -180,17 +181,17 @@ class ShowInstDirCmd(SubCmd):
class ShowSourceDirCmd(SubCmd): class ShowSourceDirCmd(SubCmd):
def run(self, args): def run(self, args):
opts = setup_build_options(args) opts = setup_build_options(args)
ctx_gen = opts.get_context_generator() loader = ManifestLoader(opts)
ctx_gen.set_value_for_project(args.project, "test", "on") loader.ctx_gen.set_value_for_project(args.project, "test", "on")
manifest = load_project(opts, args.project) manifest = loader.load_manifest(args.project)
if args.recursive: if args.recursive:
manifests = manifests_in_dependency_order(opts, manifest, ctx_gen) manifests = loader.manifests_in_dependency_order()
else: else:
manifests = [manifest] manifests = [manifest]
for m in manifests: for m in manifests:
fetcher = m.create_fetcher(opts, ctx_gen.get_context(m.name)) fetcher = m.create_fetcher(opts, loader.ctx_gen.get_context(m.name))
print(fetcher.get_src_dir()) print(fetcher.get_src_dir())
def setup_parser(self, parser): def setup_parser(self, parser):
...@@ -213,17 +214,18 @@ class ShowSourceDirCmd(SubCmd): ...@@ -213,17 +214,18 @@ class ShowSourceDirCmd(SubCmd):
class BuildCmd(SubCmd): class BuildCmd(SubCmd):
def run(self, args): def run(self, args):
opts = setup_build_options(args) opts = setup_build_options(args)
if args.clean:
clean_dirs(opts)
ctx_gen = opts.get_context_generator(facebook_internal=args.facebook_internal) ctx_gen = opts.get_context_generator(facebook_internal=args.facebook_internal)
if args.enable_tests: if args.enable_tests:
ctx_gen.set_value_for_project(args.project, "test", "on") ctx_gen.set_value_for_project(args.project, "test", "on")
manifest = load_project(opts, args.project) loader = ManifestLoader(opts, ctx_gen)
if args.clean:
clean_dirs(opts)
manifest = loader.load_manifest(args.project)
print("Building on %s" % ctx_gen.get_context(args.project)) print("Building on %s" % ctx_gen.get_context(args.project))
projects = manifests_in_dependency_order(opts, manifest, ctx_gen) projects = loader.manifests_in_dependency_order()
manifests_by_name = {m.name: m for m in projects}
# Accumulate the install directories so that the build steps # Accumulate the install directories so that the build steps
# can find their dep installation # can find their dep installation
...@@ -236,7 +238,7 @@ class BuildCmd(SubCmd): ...@@ -236,7 +238,7 @@ class BuildCmd(SubCmd):
if args.clean: if args.clean:
fetcher.clean() fetcher.clean()
dirs = opts.compute_dirs(m, fetcher, manifests_by_name, ctx_gen) dirs = opts.compute_dirs(m, fetcher, loader.manifests_by_name, ctx_gen)
build_dir = dirs["build_dir"] build_dir = dirs["build_dir"]
inst_dir = dirs["inst_dir"] inst_dir = dirs["inst_dir"]
...@@ -318,10 +320,10 @@ class FixupDeps(SubCmd): ...@@ -318,10 +320,10 @@ class FixupDeps(SubCmd):
if args.enable_tests: if args.enable_tests:
ctx_gen.set_value_for_project(args.project, "test", "on") ctx_gen.set_value_for_project(args.project, "test", "on")
manifest = load_project(opts, args.project) loader = ManifestLoader(opts, ctx_gen)
manifest = loader.load_manifest(args.project)
projects = manifests_in_dependency_order(opts, manifest, ctx_gen) projects = loader.manifests_in_dependency_order()
manifests_by_name = {m.name: m for m in projects}
# Accumulate the install directories so that the build steps # Accumulate the install directories so that the build steps
# can find their dep installation # can find their dep installation
...@@ -331,7 +333,7 @@ class FixupDeps(SubCmd): ...@@ -331,7 +333,7 @@ class FixupDeps(SubCmd):
ctx = ctx_gen.get_context(m.name) ctx = ctx_gen.get_context(m.name)
fetcher = m.create_fetcher(opts, ctx) fetcher = m.create_fetcher(opts, ctx)
dirs = opts.compute_dirs(m, fetcher, manifests_by_name, ctx_gen) dirs = opts.compute_dirs(m, fetcher, loader.manifests_by_name, ctx_gen)
inst_dir = dirs["inst_dir"] inst_dir = dirs["inst_dir"]
install_dirs.append(inst_dir) install_dirs.append(inst_dir)
...@@ -376,9 +378,9 @@ class TestCmd(SubCmd): ...@@ -376,9 +378,9 @@ class TestCmd(SubCmd):
else: else:
ctx_gen.set_value_for_project(args.project, "test", "on") ctx_gen.set_value_for_project(args.project, "test", "on")
manifest = load_project(opts, args.project) loader = ManifestLoader(opts, ctx_gen)
projects = manifests_in_dependency_order(opts, manifest, ctx_gen) manifest = loader.load_manifest(args.project)
manifests_by_name = {m.name: m for m in projects} projects = loader.manifests_in_dependency_order()
# Accumulate the install directories so that the test steps # Accumulate the install directories so that the test steps
# can find their dep installation # can find their dep installation
...@@ -388,7 +390,7 @@ class TestCmd(SubCmd): ...@@ -388,7 +390,7 @@ class TestCmd(SubCmd):
ctx = ctx_gen.get_context(m.name) ctx = ctx_gen.get_context(m.name)
fetcher = m.create_fetcher(opts, ctx) fetcher = m.create_fetcher(opts, ctx)
dirs = opts.compute_dirs(m, fetcher, manifests_by_name, ctx_gen) dirs = opts.compute_dirs(m, fetcher, loader.manifests_by_name, ctx_gen)
build_dir = dirs["build_dir"] build_dir = dirs["build_dir"]
inst_dir = dirs["inst_dir"] inst_dir = dirs["inst_dir"]
......
...@@ -83,18 +83,58 @@ def load_all_manifests(build_opts): ...@@ -83,18 +83,58 @@ def load_all_manifests(build_opts):
return LOADER.load_all(build_opts) return LOADER.load_all(build_opts)
def manifests_in_dependency_order(build_opts, manifest, ctx_gen): class ManifestLoader(object):
""" Given a manifest, expand its dependencies and return a list """ ManifestLoader stores information about project manifest relationships for a
of the manifest objects that would need to be built in the order given set of (build options + platform) configuration.
that they would need to be built. This does not evaluate whether
a build is needed; it just returns the list in the order specified The ManifestLoader class primarily serves as a location to cache project dependency
by the manifest files. """ relationships and project hash values for this build configuration.
# A dict to save loading a project multiple times """
manifests_by_name = {manifest.name: manifest}
def __init__(self, build_opts, ctx_gen=None):
self._loader = LOADER
self.build_opts = build_opts
if ctx_gen is None:
self.ctx_gen = self.build_opts.get_context_generator()
else:
self.ctx_gen = ctx_gen
self.manifests_by_name = {}
self._loaded_all = False
def load_manifest(self, name):
manifest = self.manifests_by_name.get(name)
if manifest is None:
manifest = self._loader.load_project(self.build_opts, name)
self.manifests_by_name[name] = manifest
return manifest
def load_all_manifests(self):
if not self._loaded_all:
self.manifests_by_name = self._loader.load_all(self.build_opts)
self._loaded_all = True
return self.manifests_by_name
def manifests_in_dependency_order(self, manifest=None):
""" Compute all dependencies of the specified project. Returns a list of the
dependencies plus the project itself, in topologically sorted order.
Each entry in the returned list only depends on projects that appear before it
in the list.
If the input manifest is None, the dependencies for all currently loaded
projects will be computed. i.e., if you call load_all_manifests() followed by
manifests_in_dependency_order() this will return a global dependency ordering of
all projects. """
# The list of deps that have been fully processed # The list of deps that have been fully processed
seen = set() seen = set()
# The list of deps which have yet to be evaluated. This # The list of deps which have yet to be evaluated. This
# can potentially contain duplicates. # can potentially contain duplicates.
if manifest is None:
deps = list(self.manifests_by_name.values())
else:
assert manifest.name in self.manifests_by_name
deps = [manifest] deps = [manifest]
# The list of manifests in dependency order # The list of manifests in dependency order
dep_order = [] dep_order = []
...@@ -109,7 +149,7 @@ def manifests_in_dependency_order(build_opts, manifest, ctx_gen): ...@@ -109,7 +149,7 @@ def manifests_in_dependency_order(build_opts, manifest, ctx_gen):
# a correct order even if they aren't sorted, but we prefer # a correct order even if they aren't sorted, but we prefer
# to produce the same order regardless of how they are listed # to produce the same order regardless of how they are listed
# in the project manifest files. # in the project manifest files.
ctx = ctx_gen.get_context(m.name) ctx = self.ctx_gen.get_context(m.name)
dep_list = sorted(m.get_section_as_dict("dependencies", ctx).keys()) dep_list = sorted(m.get_section_as_dict("dependencies", ctx).keys())
builder = m.get("build", "builder", ctx=ctx) builder = m.get("build", "builder", ctx=ctx)
if builder == "cmake": if builder == "cmake":
...@@ -125,14 +165,13 @@ def manifests_in_dependency_order(build_opts, manifest, ctx_gen): ...@@ -125,14 +165,13 @@ def manifests_in_dependency_order(build_opts, manifest, ctx_gen):
dep_list.append("libtool") dep_list.append("libtool")
dep_count = 0 dep_count = 0
for dep in dep_list: for dep_name in dep_list:
# If we're not sure whether it is done, queue it up # If we're not sure whether it is done, queue it up
if dep not in seen: if dep_name not in seen:
if dep not in manifests_by_name: dep = self.manifests_by_name.get(dep_name)
dep = load_project(build_opts, dep) if dep is None:
manifests_by_name[dep.name] = dep dep = self._loader.load_project(self.build_opts, dep_name)
else: self.manifests_by_name[dep.name] = dep
dep = manifests_by_name[dep]
deps.append(dep) deps.append(dep)
dep_count += 1 dep_count += 1
......
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