From f4cbe3c5b05cd59361c51af8cbbde0bd6737adc7 Mon Sep 17 00:00:00 2001 From: Adam Simpkins Date: Fri, 20 Sep 2019 14:12:29 -0700 Subject: [PATCH] fbcode_builder: implement automatic project detection from the current repo Summary: This updates fbcode_builder to try and automatically detect the current repository's project name by looking for a `.projectid` file in the repository root. If the project name can be detected: - The current repository will automatically be used as the source directory for this project (instead of fetching the sources into the scratch directory). - If an explicit project name was not specified on the command line, the current project will be built by default. This also changes the repository detection logic to use the current working directory, rather than the directory where the fbcode_builder code lives. This will allow this logic to work even if we move fbcode_builder into its own repository in the future, and have projects depend on it using git submodules. This does mean that callers need to invoke fbcode_builder.py from inside the repository. Reviewed By: wez Differential Revision: D17088938 fbshipit-source-id: f14d28fdcfaa330ff837ea52b8bdd4e358b81c61 --- build/fbcode_builder/getdeps.py | 30 +++++++++++++++++++++ build/fbcode_builder/getdeps/buildopts.py | 32 ++++++++++++++++++----- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/build/fbcode_builder/getdeps.py b/build/fbcode_builder/getdeps.py index f9ba16216..53396faf1 100755 --- a/build/fbcode_builder/getdeps.py +++ b/build/fbcode_builder/getdeps.py @@ -71,6 +71,23 @@ class ShowHostType(SubCmd): class ProjectCmdBase(SubCmd): def run(self, args): opts = setup_build_options(args) + + if args.current_project is not None: + opts.repo_project = args.current_project + if args.project is None: + if opts.repo_project is None: + raise UsageError( + "no project name specified, and no .projectid file found" + ) + if opts.repo_project == "fbsource": + # The fbsource repository is a little special. There is no project + # manifest file for it. A specific project must always be explicitly + # specified when building from fbsource. + raise UsageError( + "no project name specified (required when building in fbsource)" + ) + args.project = opts.repo_project + ctx_gen = opts.get_context_generator(facebook_internal=args.facebook_internal) if args.test_dependencies: ctx_gen.set_value_for_all_projects("test", "on") @@ -101,6 +118,12 @@ class ProjectCmdBase(SubCmd): return project, os.path.abspath(path) + # If we are currently running from a project repository, + # use the current repository for the project sources. + build_opts = loader.build_opts + if build_opts.repo_project is not None and build_opts.repo_root is not None: + loader.set_project_src_dir(build_opts.repo_project, build_opts.repo_root) + for arg in args.src_dir: project, path = parse_project_arg(arg, "--src-dir") loader.set_project_src_dir(project, path) @@ -116,6 +139,7 @@ class ProjectCmdBase(SubCmd): def setup_parser(self, parser): parser.add_argument( "project", + nargs="?", help=( "name of the project or path to a manifest " "file describing the project" @@ -133,6 +157,12 @@ class ProjectCmdBase(SubCmd): action="store_true", help="Enable building tests for dependencies as well.", ) + parser.add_argument( + "--current-project", + help="Specify the name of the fbcode_builder manifest file for the " + "current repository. If not specified, the code will attempt to find " + "this in a .projectid file in the repository root.", + ) parser.add_argument( "--src-dir", default=[], diff --git a/build/fbcode_builder/getdeps/buildopts.py b/build/fbcode_builder/getdeps/buildopts.py index e730cd613..910361ce7 100644 --- a/build/fbcode_builder/getdeps/buildopts.py +++ b/build/fbcode_builder/getdeps/buildopts.py @@ -35,10 +35,28 @@ def containing_repo_type(path): parent = os.path.dirname(path) if parent == path: - return None + return None, None path = parent +def detect_project(path): + repo_type, repo_root = containing_repo_type(path) + if repo_type is None: + return None, None + + # Look for a .projectid file. If it exists, read the project name from it. + project_id_path = os.path.join(repo_root, ".projectid") + try: + with open(project_id_path, "r") as f: + project_name = f.read().strip() + return repo_root, project_name + except EnvironmentError as ex: + if ex.errno != errno.ENOENT: + raise + + return repo_root, None + + class BuildOptions(object): def __init__( self, @@ -78,11 +96,13 @@ class BuildOptions(object): self.project_hashes = hashes break - # Use a simplistic heuristic to figure out if we're in fbsource - # and where the root of fbsource can be found - repo_type, repo_root = containing_repo_type(fbcode_builder_dir) - if repo_type == "hg": - self.fbsource_dir = repo_root + # Detect what repository and project we are being run from. + self.repo_root, self.repo_project = detect_project(os.getcwd()) + + # If we are running from an fbsource repository, set self.fbsource_dir + # to allow the ShipIt-based fetchers to use it. + if self.repo_project == "fbsource": + self.fbsource_dir = self.repo_root else: self.fbsource_dir = None